diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index faeab3da7..f173337a5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,36 +1,247 @@ +# From gist https://gist.github.com/NickNaso/0d478f1481686d5bcc868cac06620a60 # This is a basic workflow to help you get started with Actions +# workflow - цепочка действий +# Имя процесса Билдится на всех типах 📦 🐍 name: MapViewer CI -# Controls when the workflow will run +# Controls when the action will run. Triggers the workflow on push on: - # Triggers the workflow on push or pull request events but only for the master branch push: branches: [ master ] pull_request: branches: [ master ] + release: - # Allows you to run this workflow manually from the Actions tab +# Allows you to run this workflow manually from the Actions tab workflow_dispatch: + # tags: + # - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: ubuntu-latest + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} # будет запускаться по очереди на всех типах машин + strategy: + fail-fast: false + matrix: + config: + - { + name: "Windows Latest MSVC", + os: windows-latest, + artifact: "windows_msvc.7z", + build_type: "Release", + cc: "cl", + cxx: "cl", + environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", + archiver: "7z a", + generators: "Visual Studio 17 2022" + } + # - { + # name: "Windows Latest MinGW", + # os: windows-latest, + # artifact: "windows_mingw.7z", + # build_type: "Release", + # cc: "gcc", + # cxx: "g++", + # archiver: "7z a", + # generators: "Ninja" + # } + - { + name: "Ubuntu_Latest_GCC", + os: ubuntu-latest, + artifact: "ubuntu_gcc.7z", + build_type: "Release", + cc: "gcc", + cxx: "g++", + archiver: "7z a", + generators: "Ninja" + } + - { + name: "Emscripten", + os: ubuntu-latest, + artifact: "emscripten.7z", + build_type: "Release", + cc: "emcc", + cxx: "em++", + archiver: "7z a", + generators: "Ninja" + } + - { + name: "Ubuntu_GCC_9", + os: ubuntu-latest, + artifact: "ubuntu_gcc9.7z", + build_type: "Release", + cc: "gcc", + cxx: "g++", + archiver: "7z a", + generators: "Ninja" + } + - { + name: "macOS Latest Clang", + os: macos-latest, + artifact: "macos_clang.7z", + build_type: "Release", + cc: "clang", + cxx: "clang++", + archiver: "7za a", + generators: "Ninja" + } - # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v3 + with: + submodules: 'recursive' + # Installs python 3.10 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Print env + run: | + echo github.event.action: ${{ github.event.action }} + echo github.event_name: ${{ github.event_name }} + - name: Install dependencies on windows + if: startsWith(matrix.config.os, 'windows') + run: | + choco install ninja cmake + ninja --version + cmake --version + # cmd "${{ matrix.config.environment_script }}" + - name: Install emscripten on Emscripten target + if: startsWith(matrix.config.name, 'emscripten') + run: | + sudo mv /usr/local/bin/cmake /usr/local/bin/cmake-old + wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add - + sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' + sudo apt-get update + sudo apt-get install ninja-build cmake + sudo apt-get install xorg-dev libglu1-mesa-dev + git clone https://github.com/emscripten-core/emsdk.git + cd emsdk + git pull + ./emsdk install latest + ./emsdk activate latest + ninja --version + cmake --version + gcc --version + cd .. + - name: Install dependencies on ubuntu + if: startsWith(matrix.config.name, 'Ubuntu_Latest_GCC') + run: | + sudo apt-get update + sudo apt-get install ninja-build cmake + sudo apt-get install xorg-dev libglu1-mesa-dev + ninja --version + cmake --version + gcc --version + - name: Install dependencies on ubuntu9 + if: startsWith(matrix.config.name, 'Ubuntu_GCC_9') + run: | + echo Update gcc-9 ======================================================================= + echo gcc version before + gcc --version + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install ninja-build cmake gcc-9 g++-9 + sudo apt-get install xorg-dev libglu1-mesa-dev + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9 + echo gcc version after + gcc --version + echo Update ninja ======================================================================= + echo ninja version before + ninja --version + # wget https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip + wget https://github.com/ninja-build/ninja/releases/latest/download/ninja-linux.zip + sudo unzip ninja-linux.zip -d /usr/local/bin/ + sudo update-alternatives --install /usr/bin/ninja ninja /usr/local/bin/ninja 1 --force + echo ninja version after + ninja --version + echo Update cmake ======================================================================= + echo cmake version before + cmake --version + # curl --silent "https://api.github.com/repos/Kitware/CMake/releases/latest" | sed -n 's/.*tag_name":\s"\(.*\)".*/\1/p' | head -2 + # wget https://github.com/Kitware/CMake/releases/latest/download/cmake-3.16.5-Linux-x86_64.sh + cmake_version=$(curl --silent "https://api.github.com/repos/Kitware/CMake/releases/latest" | sed -n 's/.*tag_name":\s"\(.*\)".*/\1/p' | head -2 | cut -c 2-) + echo cmake download latest v$cmake_version version + wget https://github.com/Kitware/CMake/releases/download/v$cmake_version/cmake-$cmake_version-Linux-x86_64.sh + chmod +x cmake-$cmake_version-Linux-x86_64.sh + sudo mkdir /opt/cmake + sudo ./cmake-$cmake_version-Linux-x86_64.sh --prefix=/opt/cmake --skip-license + sudo update-alternatives --install /usr/bin/cmake cmake /opt/cmake/bin/cmake 1 --force + echo cmake version after + cmake --version + - name: Install dependencies on macos + if: startsWith(matrix.config.os, 'macos') + run: | + brew install p7zip cmake ninja + ninja --version + cmake --version + + - name: Configure standalone + if: ${{ !startsWith(matrix.config.name, 'emscripten') }} + shell: bash + run: | + mkdir build + mkdir instdir + cd build + cmake \ + -S ../ \ + -B . \ + -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ + -G "${{ matrix.config.generators }}" \ + -DCMAKE_INSTALL_PREFIX:PATH=../instdir + - name: Configure emscripten + if: startsWith(matrix.config.name, 'emscripten') + shell: bash + run: | + cd emsdk + source ./emsdk_env.sh + TOOLCHAIN_LOC="$(find ~+ . -name 'Emscripten.cmake' -print -quit)" + cd .. + mkdir build + mkdir instdir + cd build + cmake \ + -S ../emscripten_port \ + -B . \ + -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ + -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_LOC} \ + -DCMAKE_INSTALL_PREFIX:PATH=../instdir + - name: Build + shell: bash + working-directory: build + run: cmake --build . --config ${{ matrix.config.build_type }} - # Runs a single command using the runners shell - - name: Run a one-line script - run: echo Hello, world! + - name: Install Strip + shell: bash + working-directory: build + run: cmake --install . --strip - # Runs a set of commands using the runners shell - - name: Run a multi-line script + - name: Pack + shell: bash + working-directory: instdir run: | - echo Add other actions to build, - echo test, and deploy your project. + ls -laR + ${{ matrix.config.archiver }} ../${{ matrix.config.artifact }} . + - name: Upload + uses: actions/upload-artifact@v1 + with: + path: ./${{ matrix.config.artifact }} + name: ${{ matrix.config.artifact }} + + - name: Upload release asset + if: github.event_name == 'release' && (github.event.action == 'published' || github.event.action == 'created') + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./${{ matrix.config.artifact }} + asset_name: ${{ matrix.config.artifact }}.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore index e1accb9a8..9d6a11da7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ cache cache__ cache_old -cmake-build-* \ No newline at end of file +cmake-build-* +venv \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 3f398c12d..d49f4c5c3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -38,3 +38,22 @@ [submodule "3rdparty/libpng"] path = 3rdparty/libpng url = https://github.com/glennrp/libpng.git +[submodule "wowViewerLib/3rdparty/tbb"] + path = wowViewerLib/3rdparty/tbb + url = https://github.com/Deamon87/tbb.git +[submodule "3rdparty/filesystem_impl"] + path = 3rdparty/filesystem_impl + url = https://github.com/gulrak/filesystem.git +[submodule "wowViewerLib/3rdparty/tinygltf"] + path = wowViewerLib/3rdparty/tinygltf + url = https://github.com/syoyo/tinygltf +[submodule "emscripten_port/3rdparty/zipper"] + path = emscripten_port/3rdparty/zipper + url = https://github.com/sebastiandev/zipper.git +[submodule "3rdparty/libressl"] + path = 3rdparty/libressl + url = https://github.com/libressl-portable/portable.git + +[submodule "3rdparty/mdebtls"] + path = 3rdparty/mdebtls + url = https://github.com/ARMmbed/mbedtls diff --git a/3rdparty/casclib b/3rdparty/casclib index 50b9df687..37a948bdb 160000 --- a/3rdparty/casclib +++ b/3rdparty/casclib @@ -1 +1 @@ -Subproject commit 50b9df687052cb63acc3735a798e339d9ad42cd7 +Subproject commit 37a948bdb5f493b6a0959489baa07e1636002c3b diff --git a/3rdparty/filesystem_impl b/3rdparty/filesystem_impl new file mode 160000 index 000000000..1edf4a333 --- /dev/null +++ b/3rdparty/filesystem_impl @@ -0,0 +1 @@ +Subproject commit 1edf4a333930f126f5dcd2f7919f0bf4fa3acecc diff --git a/3rdparty/libressl b/3rdparty/libressl new file mode 160000 index 000000000..867b577d8 --- /dev/null +++ b/3rdparty/libressl @@ -0,0 +1 @@ +Subproject commit 867b577d858d855140288a998f44f6c026456ad0 diff --git a/3rdparty/mdebtls b/3rdparty/mdebtls new file mode 160000 index 000000000..59d97a16d --- /dev/null +++ b/3rdparty/mdebtls @@ -0,0 +1 @@ +Subproject commit 59d97a16d6488c744a55f7f73fd3a202a3c1b309 diff --git a/CMakeLists.txt b/CMakeLists.txt index bc52a6c82..8e60dbbb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,13 +36,17 @@ if (CMAKE_NDK_BUILD MATCHES 1) endif() endif() + + if (WIN32 AND MSVC) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /DWIN64") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /DWIN64") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /DWIN64") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /DWIN64") - endif() + endif() + + add_definitions(-DWIN32_LEAN_AND_MEAN) endif() message(CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}) #set(CMAKE_BUILD_TYPE Release) @@ -51,11 +55,15 @@ message(CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") message("This is clang") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -static -femulated-tls ") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -static -femulated-tls -std=c++1z") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -static -femulated-tls ") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -static -femulated-tls -std=c++1z") +# set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ") +# set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ") + #Do not force libstdc++ on MacOS system + if (NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -stdlib=libstdc++") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -stdlib=libstdc++") + endif() +# link_libraries(libstdc++fs) #set(_GLIBCXX_USE_C99_CHECK 0) @@ -77,52 +85,69 @@ endif() if(WIN32) link_libraries(wsock32 ws2_32) endif() -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - link_libraries(libws2_32.a) -endif() +#if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") +# link_libraries(libws2_32.a) +#endif() #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -pthread") #ZLib library -set(BUILD_SHARED_LIBS OFF) -add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/zlib) +set(BUILD_SHARED_LIBS OFF CACHE BOOL "") +add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/zlib EXCLUDE_FROM_ALL) include_directories(${CMAKE_CURRENT_BINARY_DIR}/3rdparty/zlib) -set(BUILD_SHARED_LIBS OFF) -if (MSVC) +set(BUILD_SHARED_LIBS OFF CACHE BOOL "") + +#find_package(ZLIB) + +if (TRUE) set(ZLIB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/zlib ${CMAKE_BINARY_DIR}/3rdparty/zlib) -# message("CMAKE_BINARY_DIR = ${CMAKE_BINARY_DIR}") - if (CMAKE_BUILD_TYPE EQUAL "Debug") - set(ZLIB_LIBRARY "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlibd.lib") - else() - set(ZLIB_LIBRARY "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib.lib") - endif() - set(ZLIB_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/3rdparty/zlib) + message("CMAKE_BINARY_DIR = ${CMAKE_BINARY_DIR}") + message("CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}") + + set(ZLIB_LIBRARY "$" CACHE BOOL "") endif() #libpng -if (MSVC) - set(PNG_BUILD_ZLIB ON) -else() - set(PNG_BUILD_ZLIB OFF) -endif() +set(PNG_BUILD_ZLIB OFF CACHE BOOL "") -add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/libpng) +add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/libpng EXCLUDE_FROM_ALL) include_directories(${CMAKE_SOURCE_DIR}/3rdparty/libpng) include_directories(${CMAKE_SOURCE_DIR}/3rdparty/SQLiteCpp/sqlite3) include_directories(${CMAKE_CURRENT_BINARY_DIR}/3rdparty/libpng) if (NOT CMAKE_NDK_BUILD MATCHES 1) - set(BUILD_SHARED_LIBS OFF) - add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/glfw) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "") + add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/glfw EXCLUDE_FROM_ALL) endif() -#bz2renderFlag -set(BUILD_SHARED_LIBS OFF) -add_subdirectory(3rdparty/bzip) - - +#c++ 17 FS implementation +add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/filesystem_impl EXCLUDE_FROM_ALL) +#bz2renderFlag +set(BUILD_SHARED_LIBS OFF CACHE BOOL "") +add_subdirectory(3rdparty/bzip EXCLUDE_FROM_ALL) + +include(FetchContent) + +#cpr +check_include_file("limits.h" HAVE_LIMITS_H) +add_compile_definitions(HAVE_LIMITS_H) +SET(CMAKE_USE_OPENSSL 0) +SET(CMAKE_USE_WOLFSSL 0) +SET(CURL_ZLIB OFF CACHE STRING "" FORCE) # disable this lib to download the zlib as external project +FetchContent_Declare(cpr GIT_REPOSITORY https://github.com/whoshuu/cpr.git GIT_TAG db351ffbbadc6c4e9239daaa26e9aefa9f0ec82d) # the commit hash for 1.8.3 + +FetchContent_GetProperties(cpr) +message("cpr_POPULATED = ${cpr_POPULATED}") +if(NOT cpr_POPULATED) + # Fetch the content using previously declared details + FetchContent_Populate(cpr) + message("cpr_BINARY_DIR = ${cpr_BINARY_DIR}") + message("cpr_SOURCE_DIR = ${cpr_SOURCE_DIR}") + # Bring the populated content into the build + add_subdirectory(${cpr_SOURCE_DIR} ${cpr_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() #PolyM #add_subdirectory(3rdparty/PolyM) @@ -143,9 +168,11 @@ endif() if (WIN32) set(WITH_LIBTOMCRYPT true) endif() -option(CASC_BUILD_STATIC_LIB "" ON) -option(CASC_BUILD_SHARED_LIB "" OFF) -add_subdirectory(3rdparty/casclib) + +set(CASC_BUILD_STATIC_LIB 1) +set(CASC_BUILD_STATIC_LIB ON CACHE BOOL "Set static lib from main project") +set(CASC_BUILD_SHARED_LIB true CACHE BOOL "Turn off shared lib ") +add_subdirectory(3rdparty/casclib EXCLUDE_FROM_ALL) if (CMAKE_NDK_BUILD MATCHES 1) @@ -154,14 +181,18 @@ if (CMAKE_NDK_BUILD MATCHES 1) endif() add_subdirectory(wowViewerLib) +if (LINK_EGL) + add_definitions(-DLINK_EGL) + add_definitions(-DWITH_GLESv2) +endif() include_directories(${GLEW_INCLUDE_DIRS}) set(SOURCE_FILES -# src/persistance/httpFile/httpFile.cpp -# src/persistance/httpFile/httpFile.h + src/persistance/httpFile/httpFile.cpp + src/persistance/httpFile/httpFile.h src/persistance/RequestProcessor.cpp src/persistance/RequestProcessor.h src/persistance/ZipRequestProcessor.cpp @@ -170,8 +201,8 @@ set(SOURCE_FILES # src/persistance/HttpZipRequestProcessor.h src/persistance/CascRequestProcessor.cpp src/persistance/CascRequestProcessor.h -# src/persistance/HttpRequestProcessor.cpp -# src/persistance/HttpRequestProcessor.h + src/persistance/HttpRequestProcessor.cpp + src/persistance/HttpRequestProcessor.h src/ui/imguiLib/imgui.cpp src/ui/imguiLib/imgui_demo.cpp @@ -180,16 +211,37 @@ set(SOURCE_FILES src/ui/imguiLib/imguiImpl/imgui_impl_glfw.cpp src/ui/FrontendUI.cpp src/ui/FrontendUI.h src/database/CSqliteDB.cpp src/database/CSqliteDB.h -) - + src/minimapGenerator/minimapGenerator.cpp + src/screenshots/screenshotMaker.h + src/screenshots/screenshotMaker.cpp + src/screenshots/lodepng/lodepng.cpp + src/screenshots/lodepng/lodepng.h + + src/exporters/gltfExporter/GLTFExporter.cpp + src/exporters/gltfExporter/GLTFExporter.h + + src/ui/childWindow/mapConstructionWindow.cpp + src/ui/childWindow/mapConstructionWindow.h + src/ui/imguiLib/groupPanel/groupPanel.cpp + src/ui/imguiLib/groupPanel/groupPanel.h + + src/main.cpp + src/ui/imguiLib/disablableButton/disablableButton.cpp + src/ui/imguiLib/disablableButton/disablableButton.h + src/minimapGenerator/storage/CMinimapDataDB.cpp + src/minimapGenerator/storage/CMinimapDataDB.h + src/minimapGenerator/entities.h + src/ui/imguiLib/compactColorPicker/compactColorPicker.cpp + src/ui/imguiLib/compactColorPicker/compactColorPicker.h + src/ui/imguiLib/imageButton2/imageButton2.cpp + src/ui/imguiLib/imageButton2/imageButton2.h + src/exporters/dataExporter/DataExporterClass.cpp + src/exporters/dataExporter/DataExporterClass.h + src/database/CEmptySqliteDB.cpp + src/database/CEmptySqliteDB.h + src/ui/imguiLib/stateSaver/stateSaver.h + src/ui/imguiLib/stateSaver/stateSaver.cpp) -if (NOT CMAKE_NDK_BUILD MATCHES 1) - set(SOURCE_FILES ${SOURCE_FILES} - src/main.cpp) -else() - set(SOURCE_FILES ${SOURCE_FILES} - src/jniLib.cpp src/database/dbStructs.h src/ui/imguiLib/imguiCustomConfig.h) -endif() ######################################################### @@ -228,10 +280,13 @@ find_package( Threads REQUIRED ) #endif() -add_subdirectory(${PROJECT_SOURCE_DIR}/3rdparty/SQLiteCpp) +add_subdirectory(${PROJECT_SOURCE_DIR}/3rdparty/SQLiteCpp EXCLUDE_FROM_ALL) -#add_definitions(-DLINK_VULKAN) add_executable(AWebWoWViewerCpp ${SOURCE_FILES}) +if (Vulkan_FOUND) + add_definitions(-DLINK_VULKAN) +endif() + message(ccp WoWViewerLib_INCLUDE_DIRS = ${WoWViewerLib_INCLUDE_DIRS}) target_include_directories(AWebWoWViewerCpp PUBLIC ${WoWViewerLib_INCLUDE_DIRS}) target_include_directories(AWebWoWViewerCpp PUBLIC ${PROJECT_SOURCE_DIR}/WoWViewerLib/3rdparty/mathfu/include) @@ -246,26 +301,71 @@ add_dependencies(AWebWoWViewerCpp WoWViewerLib) add_dependencies(AWebWoWViewerCpp casc_static) #TODO: platform dependant!! -if (NOT CMAKE_NDK_BUILD MATCHES 1) - add_dependencies(AWebWoWViewerCpp glfw) - set_property(TARGET AWebWoWViewerCpp PROPERTY CXX_STANDARD 17) +add_dependencies(AWebWoWViewerCpp glfw) +if (NOT MSVC) + check_cxx_compiler_flag(-std=c++17 HAVE_FLAG_STD_CXX17) + if(HAVE_FLAG_STD_CXX17) + # Have -std=c++17, use it + message("WOWLIB HAVE_FLAG_STD_CXX17 is supported") + # set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++17" ) + # set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++17" ) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++17" ) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++17" ) + else() + check_cxx_compiler_flag(-std=c++1z HAVE_FLAG_STD_CXX1Z) + if(HAVE_FLAG_STD_CXX1Z) + # set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++1z" ) + # set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++1z" ) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++1z") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++1z") + else() + message(ERROR "No supported flags") + endif() + endif() +endif() + +if (MSVC) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("/std:c++17" _cpp_17_flag_supported) + message("MSVC Is on") + if (_cpp_17_flag_supported) + message("/std:c++17 is supported") + #target_compile_options(AWebWoWViewerCpp "/std:c++17") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /std:c++17") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /std:c++17") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /std:c++17") + #target_compile_options(AWebWoWViewerCpp /std:c++17) + endif() endif() -link_libraries(AWebWoWViewerCpp ${GLEW_LIBRARIES}) +target_link_libraries(AWebWoWViewerCpp ${GLEW_LIBRARIES}) target_link_libraries(AWebWoWViewerCpp ${OPENGL_LIBRARIES}) target_link_libraries(AWebWoWViewerCpp ${OPENGL_LIB}) target_link_libraries(AWebWoWViewerCpp zlibstatic) target_link_libraries(AWebWoWViewerCpp SQLiteCpp sqlite3) -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" ) - target_link_libraries(AWebWoWViewerCpp ssp) -endif() -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" ) - target_link_libraries(AWebWoWViewerCpp ssp) -endif() +if (NOT MSVC) + find_library(DL_EXISTS dl) + message("DL_EXISTS = ${DL_EXISTS}") + if (NOT DL_EXISTS STREQUAL "DL_EXISTS-NOTFOUND") + message("Linking against DL") + target_link_libraries(AWebWoWViewerCpp dl) + else() + + # find_library(SSP_EXISTS ssp) + # message("SSP_EXISTS = ${SSP_EXISTS}") + # if (SSP_EXISTS STREQUAL "SSP_EXISTS-NOTFOUND" ) + + message("Linking against SSP") + message("SSP_EXISTS = ${SSP_EXISTS}") + target_compile_options(AWebWoWViewerCpp PUBLIC -fstack-protector) + target_link_libraries(AWebWoWViewerCpp ssp) + endif() +endif(NOT MSVC) + target_link_libraries(AWebWoWViewerCpp WoWViewerLib) - target_link_libraries(AWebWoWViewerCpp Threads::Threads) +target_link_libraries(AWebWoWViewerCpp Threads::Threads) if (NOT CMAKE_NDK_BUILD MATCHES 1) target_link_libraries(AWebWoWViewerCpp glfw) else() @@ -279,7 +379,7 @@ endif() target_link_libraries(AWebWoWViewerCpp casc_static) target_link_libraries(AWebWoWViewerCpp png_static) - +target_link_libraries(AWebWoWViewerCpp cpr::cpr) target_link_libraries(AWebWoWViewerCpp ${glew_lib}) @@ -288,11 +388,37 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_link_libraries(AWebWoWViewerCpp opengl32) target_link_libraries(AWebWoWViewerCpp -lm) target_link_libraries(AWebWoWViewerCpp -lwininet) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + FIND_LIBRARY(OpenGL_LIBRARY OpenGL) + MARK_AS_ADVANCED(OpenGL_LIBRARY) + + target_link_libraries(AWebWoWViewerCpp ${OpenGL_LIBRARY}) else() target_link_libraries(AWebWoWViewerCpp GL) endif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") -#Link OPENSSL only for MSVC +#target_link_options(AWebWoWViewerCpp PUBLIC "SHELL: -llibg") + +install(TARGETS AWebWoWViewerCpp + EXPORT ${PROJECT_NAME}Targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT libraries) + +INSTALL(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION bin COMPONENT Libraries) + +if( MINGW ) + message( STATUS " Installing system-libraries: MinGW DLLs." ) + get_filename_component( Mingw_Path ${CMAKE_CXX_COMPILER} PATH ) + set( CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${Mingw_Path}/libgcc_s_seh-1.dll ${Mingw_Path}/libstdc++-6.dll ${Mingw_Path}/libssp-0.dll ${Mingw_Path}/libwinpthread-1.dll) +endif( MINGW ) +include( InstallRequiredSystemLibraries ) + +# Actually install it when make install is called. +# Note, this works with CPack +if( CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ) + install( PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION bin COMPONENT System ) +endif( CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ) -INSTALL(TARGETS AWebWoWViewerCpp DESTINATION BIN) \ No newline at end of file +include(CPack) \ No newline at end of file diff --git a/android_app/.gitignore b/android_app/.gitignore new file mode 100644 index 000000000..aa724b770 --- /dev/null +++ b/android_app/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/android_app/app/.gitignore b/android_app/app/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/android_app/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/android_app/app/build.gradle b/android_app/app/build.gradle new file mode 100644 index 000000000..a73372554 --- /dev/null +++ b/android_app/app/build.gradle @@ -0,0 +1,83 @@ +plugins { + id 'com.android.application' +} + +def Properties properties = new Properties() +def String python_exe = new String("") +def String glsl_validator_exe = new String("") +def String spirv_reflection_exe = new String("") +def String assetsFolderAbsolutePath = files("./src/main/assets")[0].getAbsolutePath(); +assetsFolderAbsolutePath = assetsFolderAbsolutePath.replaceAll("\\\\", "/").toString() + +if (project.rootProject.file('local.properties').canRead()) { + properties.load(new FileInputStream(project.rootProject.file('local.properties'))) + glsl_validator_exe = properties.getProperty("glsl.validator.exe").toString().trim() + python_exe = properties.getProperty("python.exe").toString() + spirv_reflection_exe = properties.getProperty("spirv.reflection.exe").toString().trim() +} else { + throw new GradleException("Could not read local.properties!") +} + +android { + compileSdk 29 + buildToolsVersion "30.0.0" + ndkVersion "23.0.7599858" + + defaultConfig { + applicationId "com.deamon.wow.viewer" + minSdkVersion 24 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + ndk { + abiFilters 'arm64-v8a' + } + externalNativeBuild { + cmake { + targets "AWebWoWViewerCpp" + arguments "-DPATH_TO_NATIVE_GLSL_VALIDATOR=${glsl_validator_exe} ", + "-DPATH_TO_NATIVE_SPIRV_REFLECTION=${spirv_reflection_exe} ", + "-DANDROID_ASSETSFOLDER =${assetsFolderAbsolutePath}", + "-DPYTHON_EXECUTABLE=${python_exe} " + cppFlags "-DUSEES3=1" + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + externalNativeBuild { + cmake { + version "3.18.1" + path file('cpp/CMakeLists.txt') + +// cppFlags.add("-std=c++11") +// targets "AWebWoWViewerCpp" + } + } + packagingOptions{ + doNotStrip "*/armeabi/*.so" + doNotStrip "*/armeabi-v7a/*.so" + doNotStrip "*/arm64-v8a/*.so" + doNotStrip "*/x86/*.so" + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} \ No newline at end of file diff --git a/android_app/app/cpp/CMakeLists.txt b/android_app/app/cpp/CMakeLists.txt new file mode 100644 index 000000000..3f1785da9 --- /dev/null +++ b/android_app/app/cpp/CMakeLists.txt @@ -0,0 +1,322 @@ +cmake_minimum_required(VERSION 3.18) + +project(AWebWoWViewerCpp) +include(ExternalProject) + +enable_language( C CXX ASM ) +message(WARNING ANDROID_PLATFORM_LEVEL is ${ANDROID_PLATFORM_LEVEL}) +message(WARNING CMAKE_ANDROID_NATIVE_LIB_DIRECTORIES is ${CMAKE_ANDROID_NATIVE_LIB_DIRECTORIES}) +message(WARNING ANDROID_NATIVE_LIB_DIRECTORIES is ${ANDROID_NATIVE_LIB_DIRECTORIES}) +set(CMAKE_VERBOSE_MAKEFILE ON) + +if(POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() +if(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +endif(COMMAND cmake_policy) +message("Test message") + +set(USE_NEON 0) + +include_directories(BEFORE SYSTEM "${NDK_PATH}/include/c++/4.9.x/" "${NDK_PATH}/sysroot/usr/include/") +message(ERROR " CMAKE_SYSTEM_PROCESSOR = ${CMAKE_SYSTEM_PROCESSOR}") +message(ERROR " ANDROID_ABI = ${ANDROID_ABI}") +#TODO: check ANDROID_ARM_NEON too +if(${ANDROID_ABI} STREQUAL "armeabi-v7a") + include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi) + set(USE_NEON 1) +elseif(${ANDROID_ABI} STREQUAL "arm64-v8a") + include_directories(${ANDROID_SYSROOT}/usr/include/aarch64-linux-android) + set(USE_NEON 1) +elseif(${ANDROID_ABI} STREQUAL "x86_64") + include_directories(${ANDROID_SYSROOT}/usr/include/x86_64-linux-android) + set(USE_NEON 0) +else() + include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi) + set(USE_NEON 0) +endif() + + + +if (WIN32 AND MSVC) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /DWIN64") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /DWIN64") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /DWIN64") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /DWIN64") + endif() + endif() +message(CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}) +#set(CMAKE_BUILD_TYPE Release) +#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC ") +#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC ") +#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -pthread") + +#ZLib library +set(BUILD_SHARED_LIBS OFF) +set(VIEWER_IMPORT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) +add_subdirectory(${VIEWER_IMPORT_DIR}/3rdparty/zlib ${CMAKE_CURRENT_BINARY_DIR}/zlib) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/3rdparty/zlib) +set(BUILD_SHARED_LIBS OFF) +if (MSVC) + set(ZLIB_INCLUDE_DIR ${VIEWER_IMPORT_DIR}/3rdparty/zlib ${CMAKE_BINARY_DIR}/3rdparty/zlib) + message("CMAKE_BINARY_DIR = ${CMAKE_BINARY_DIR}") + message("CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}") + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(ZLIB_LIBRARY "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlibd.lib") + else() + set(ZLIB_LIBRARY "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib.lib") + endif() + set(ZLIB_INCLUDE_DIRS ${VIEWER_IMPORT_DIR}/3rdparty/zlib) +endif() + + +#libpng +if (MSVC) + #set(PNG_BUILD_ZLIB ON) + set(PNG_BUILD_ZLIB OFF) +else() + set(PNG_BUILD_ZLIB OFF) +endif() + +set(PNG_ARM_NEON off CACHE STRING "Shut off .s files for clang compiler" FORCE) + +add_subdirectory(${VIEWER_IMPORT_DIR}/3rdparty/libpng ${CMAKE_CURRENT_BINARY_DIR}/libpng) +include_directories(${VIEWER_IMPORT_DIR}/3rdparty/libpng) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/libpng) +include_directories(${VIEWER_IMPORT_DIR}/3rdparty/SQLiteCpp/sqlite3) + +#c++ 17 FS implementation +add_subdirectory(${VIEWER_IMPORT_DIR}/3rdparty/filesystem_impl ${CMAKE_CURRENT_BINARY_DIR}/filesystem_impl) + +#bz2renderFlag +set(BUILD_SHARED_LIBS OFF) +add_subdirectory(${VIEWER_IMPORT_DIR}/3rdparty/bzip ${CMAKE_CURRENT_BINARY_DIR}/bzip) + +#mdebtls as replacement for openssl +set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "Use static" ) +set(USE_SHARED_MBEDTLS_LIBRARY ON CACHE BOOL "Do not use shared" ) +add_subdirectory(${VIEWER_IMPORT_DIR}/3rdparty/mdebtls ${CMAKE_CURRENT_BINARY_DIR}/mdebtls) +SET(MBEDCRYPTO_LIBRARY "mbedcrypto.so") +SET(MBEDTLS_LIBRARY "mbedx509.so") +SET(MBEDX509_LIBRARY "mbedtls.so") +set(MBEDTLS_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/mdebtls/include ${CMAKE_CURRENT_SOURCE_DIR}/src/includes) + +#CascLib +add_definitions(-DCASCLIB_NO_AUTO_LINK_LIBRARY) + +if (CMAKE_NDK_BUILD MATCHES 1) + #set(CASC_BUILD_SHARED_LIB ON) + set(CMAKE_SYSTEM_NAME_TEMPTEMP ${CMAKE_SYSTEM_NAME}) + set(CMAKE_SYSTEM_NAME "Linux") +endif() +if (WIN32) + set(WITH_LIBTOMCRYPT true) +endif() + +option(CASC_BUILD_SHARED_LIB "" OFF) +set(CASC_BUILD_STATIC_LIB ON CACHE BOOL "Set static lib from main project") +add_subdirectory(${VIEWER_IMPORT_DIR}/3rdparty/casclib ${CMAKE_CURRENT_BINARY_DIR}/casclib) + +if (CMAKE_NDK_BUILD MATCHES 1) + target_compile_definitions(casc PUBLIC -DCMAKE_SYSTEM_NAME=Linux) + set(CMAKE_SYSTEM_NAME "${CMAKE_SYSTEM_NAME_TEMPTEMP}") +endif() + +add_subdirectory(${VIEWER_IMPORT_DIR}/wowViewerLib ${CMAKE_CURRENT_BINARY_DIR}/wowViewerLib) + + + +set(SOURCE_FILES + src/main.cpp + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c + ${VIEWER_IMPORT_DIR}/src/persistance/httpFile/httpFile.cpp + ${VIEWER_IMPORT_DIR}/src/persistance/httpFile/httpFile.h + ${VIEWER_IMPORT_DIR}/src/persistance/RequestProcessor.cpp + ${VIEWER_IMPORT_DIR}/src/persistance/RequestProcessor.h + ${VIEWER_IMPORT_DIR}/src/persistance/ZipRequestProcessor.cpp + ${VIEWER_IMPORT_DIR}/src/persistance/ZipRequestProcessor.h +# src/persistance/HttpZipRequestProcessor.cpp +# src/persistance/HttpZipRequestProcessor.h + ${VIEWER_IMPORT_DIR}/src/persistance/CascRequestProcessor.cpp + ${VIEWER_IMPORT_DIR}/src/persistance/CascRequestProcessor.h + ${VIEWER_IMPORT_DIR}/src/persistance/HttpRequestProcessor.cpp + ${VIEWER_IMPORT_DIR}/src/persistance/HttpRequestProcessor.h + + src/HttpRequestProcessor.cpp + src/HttpRequestProcessor.h + + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/imgui.cpp + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/imgui_demo.cpp + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/imgui_draw.cpp + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/imgui_widgets.cpp + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/imguiImpl/imgui_impl_android.cpp + ${VIEWER_IMPORT_DIR}/src/ui/FrontendUI.cpp + ${VIEWER_IMPORT_DIR}/src/ui/FrontendUI.h + ${VIEWER_IMPORT_DIR}/src/database/CSqliteDB.cpp + ${VIEWER_IMPORT_DIR}/src/database/CSqliteDB.h + ${VIEWER_IMPORT_DIR}/src/minimapGenerator/minimapGenerator.cpp + ${VIEWER_IMPORT_DIR}/src/screenshots/screenshotMaker.h + ${VIEWER_IMPORT_DIR}/src/screenshots/screenshotMaker.cpp + ${VIEWER_IMPORT_DIR}/src/screenshots/lodepng/lodepng.cpp + ${VIEWER_IMPORT_DIR}/src/screenshots/lodepng/lodepng.h + + ${VIEWER_IMPORT_DIR}/src/exporters/gltfExporter/GLTFExporter.cpp + ${VIEWER_IMPORT_DIR}/src/exporters/gltfExporter/GLTFExporter.h + + ${VIEWER_IMPORT_DIR}/src/ui/childWindow/mapConstructionWindow.cpp + ${VIEWER_IMPORT_DIR}/src/ui/childWindow/mapConstructionWindow.h + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/groupPanel/groupPanel.cpp + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/groupPanel/groupPanel.h + + + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/disablableButton/disablableButton.cpp + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/disablableButton/disablableButton.h + ${VIEWER_IMPORT_DIR}/src/minimapGenerator/storage/CMinimapDataDB.cpp + ${VIEWER_IMPORT_DIR}/src/minimapGenerator/storage/CMinimapDataDB.h + ${VIEWER_IMPORT_DIR}/src/minimapGenerator/entities.h + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/compactColorPicker/compactColorPicker.cpp + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/compactColorPicker/compactColorPicker.h + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/imageButton2/imageButton2.cpp + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/imageButton2/imageButton2.h + ${VIEWER_IMPORT_DIR}/src/exporters/dataExporter/DataExporterClass.cpp + ${VIEWER_IMPORT_DIR}/src/exporters/dataExporter/DataExporterClass.h + ${VIEWER_IMPORT_DIR}/src/database/CEmptySqliteDB.cpp + ${VIEWER_IMPORT_DIR}/src/database/CEmptySqliteDB.h + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/stateSaver/stateSaver.h + ${VIEWER_IMPORT_DIR}/src/ui/imguiLib/stateSaver/stateSaver.cpp + ) + +string(TOUPPER ${CMAKE_SYSTEM_NAME} CMAKE_SYSTEM_NAME_LOWERCASE) + +######################################################### +# FIND OPENGL +######################################################### +if (NOT CMAKE_SYSTEM_NAME_LOWERCASE MATCHES "ANDROID") + set(OpenGL_GL_PREFERENCE GLVND) + find_package(OpenGL REQUIRED) + include_directories(${OpenGL_INCLUDE_DIRS}) + link_directories(${OpenGL_LIBRARY_DIRS}) + if(NOT OPENGL_FOUND) + message(ERROR " OPENGL not found!") + endif(NOT OPENGL_FOUND) +else() +# if (${ANDROID_PLATFORM_LEVEL} LESS 12) +# message(FATAL_ERROR "OpenGL 2 is not supported before API level 11 / +# (currently using ${ANDROID_PLATFORM_LEVEL}).") +# return() +# elseif (${ANDROID_PLATFORM_LEVEL} LESS 18) + add_definitions("-DDYNAMIC_ES3") + set(GL3STUB_SRC gl3stub.c) + set(OPENGL_LIB GLESv3) +# else () +# set(OPENGL_LIB GLESv3) +# endif (${ANDROID_PLATFORM_LEVEL} LESS 12) +endif() + +#Build the executiable +#set(CMAKE_THREAD_PREFER_PTHREAD TRUE) +#set(THREADS_PREFER_PTHREAD_FLAG TRUE) +#find_package( Threads REQUIRED ) + +#if(CMAKE_USE_PTHREADS_INIT) +# set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "-pthread") +#endif() + + +add_subdirectory(${VIEWER_IMPORT_DIR}/3rdparty/SQLiteCpp ${CMAKE_CURRENT_BINARY_DIR}/SQLiteCpp) + +set(LINK_VULKAN OFF CACHE BOOL "Disable vulkan linking" ) + +add_library(AWebWoWViewerCpp SHARED ${SOURCE_FILES}) +message(ccp WoWViewerLib_INCLUDE_DIRS = ${WoWViewerLib_INCLUDE_DIRS}) +target_include_directories(AWebWoWViewerCpp PUBLIC ${WoWViewerLib_INCLUDE_DIRS}) +target_include_directories(AWebWoWViewerCpp PUBLIC ${PROJECT_SOURCE_DIR}/WoWViewerLib/3rdparty/mathfu/include) +target_include_directories(AWebWoWViewerCpp PUBLIC ${PROJECT_SOURCE_DIR}/WoWViewerLib/3rdparty/mathfu/include) + +#target_compile_options(AWebWoWViewerCpp PRIVATE "-static") +target_compile_definitions(AWebWoWViewerCpp PUBLIC -DIMGUI_USER_CONFIG=) +target_include_directories(AWebWoWViewerCpp PUBLIC ${VIEWER_IMPORT_DIR}/src/ui/imguiLib) +target_include_directories(AWebWoWViewerCpp PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + +add_dependencies(AWebWoWViewerCpp WoWViewerLib) +#add_dependencies(AWebWoWViewerCpp storm) +add_dependencies(AWebWoWViewerCpp casc_static) + + +#TODO: platform dependant!! +check_cxx_compiler_flag(-std=c++17 HAVE_FLAG_STD_CXX17) +if(HAVE_FLAG_STD_CXX17) + # Have -std=c++17, use it + message("WOWLIB HAVE_FLAG_STD_CXX17 is supported") + # set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++17" ) + # set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++17" ) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++17" ) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++17" ) +else() + check_cxx_compiler_flag(-std=c++1z HAVE_FLAG_STD_CXX1Z) + if(HAVE_FLAG_STD_CXX1Z) + # set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++1z" ) + # set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++1z" ) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++1z") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++1z") + else() + message(ERROR "No supported flags") + endif() +endif() + +if (MSVC) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("/std:c++17" _cpp_17_flag_supported) + message("MSVC Is on") + if (_cpp_17_flag_supported) + message("/std:c++17 is supported") + #target_compile_options(AWebWoWViewerCpp "/std:c++17") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /std:c++17") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /std:c++17") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /std:c++17") + #target_compile_options(AWebWoWViewerCpp /std:c++17) + endif() +endif() + +target_link_libraries(AWebWoWViewerCpp zlibstatic) + +target_link_libraries(AWebWoWViewerCpp SQLiteCpp sqlite3) +#if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" ) +# target_link_libraries(AWebWoWViewerCpp ssp) +#endif() +#if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" ) +# target_link_libraries(AWebWoWViewerCpp ssp) +#endif() +target_link_libraries(AWebWoWViewerCpp WoWViewerLib) + +# target_link_libraries(AWebWoWViewerCpp Threads::Threads) + +find_library(log-lib log REQUIRED) +find_library(libGLESv3 GLESv3 REQUIRED) +find_library(libEGL EGL REQUIRED) +find_library(libAndroid android REQUIRED) + + +#TODO:!!!! +target_link_libraries(AWebWoWViewerCpp "${libGLESv3}") +target_link_libraries(AWebWoWViewerCpp "${log-lib}") +target_link_libraries(AWebWoWViewerCpp "${libEGL}") +target_link_libraries(AWebWoWViewerCpp "${libAndroid}") +# target_link_libraries(AWebWoWViewerCpp android) + +#target_link_libraries(AWebWoWViewerCpp polym) +#target_link_libraries(AWebWoWViewerCpp storm) +target_link_libraries(AWebWoWViewerCpp casc_static) + +target_link_libraries(AWebWoWViewerCpp png_static) + +if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") + include_directories(AWebWoWViewerCpp "./wowViewerLib/src/opengl") + target_link_libraries(AWebWoWViewerCpp opengl32) + target_link_libraries(AWebWoWViewerCpp -lm) + target_link_libraries(AWebWoWViewerCpp -lwininet) +else() +# target_link_libraries(AWebWoWViewerCpp GL) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") diff --git a/android_app/app/cpp/src/HttpRequestProcessor.cpp b/android_app/app/cpp/src/HttpRequestProcessor.cpp new file mode 100644 index 000000000..76afb0918 --- /dev/null +++ b/android_app/app/cpp/src/HttpRequestProcessor.cpp @@ -0,0 +1,181 @@ +// +// Created by deamon on 24.10.17. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include "HttpRequestProcessor.h" + +struct UserDataForRequest { + std::string fileName; + std::string url; + CacheHolderType holderType; + HttpRequestProcessorAndroid *processor; + std::shared_ptr s_file; +}; + +//void downloadProgress(emscripten_fetch_t *fetch) { +// UserDataForRequest *userDataForRequest = (UserDataForRequest *) fetch->userData; +// if (userDataForRequest->fileContent == nullptr) { +// userDataForRequest->fileContent = std::make_shared(fetch->numBytes); +// } +// +// +//} + +void downloadSucceeded(JNIEnv *env, jobject, jlong userDataPtr, jbyteArray array) { +// printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url); +// +// try { +// std::cout << "Creating FileContent with " << fetch->numBytes << " bytes" << std::endl; + jbyte* bufferPtr = env->GetByteArrayElements(array, nullptr); + jsize lengthOfArray = env->GetArrayLength(array); + + HFileContent fileContent = std::make_shared(lengthOfArray); +// std::cout << "FileContent was created" << std::endl; + std::copy(bufferPtr, bufferPtr + lengthOfArray, fileContent->begin()); + + env->ReleaseByteArrayElements(array, bufferPtr, JNI_ABORT); + + +// HFileContent fileContent(&fetch->data[0], &fetch->data[fetch->numBytes]); + UserDataForRequest *userDataForRequest = (UserDataForRequest *) userDataPtr; + + if (userDataForRequest->holderType != CacheHolderType::CACHE_ANIM && fileContent->size() > 4 && + (*(uint32_t *) fileContent->data() == 0)) { + std::cout << "Encountered encrypted file " << std::string(userDataForRequest->url) << std::endl; + return; + } + + userDataForRequest->s_file->process(fileContent, userDataForRequest->fileName); + userDataForRequest->processor->toBeProcessed--; + + // The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1]; + delete userDataForRequest; + // } catch (...) { +// printf("Exception on Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url); +// } + +} + +void downloadFailed(JNIEnv *, jobject, jlong userDataPtr) { + UserDataForRequest * userDataForRequest = (UserDataForRequest *)userDataPtr; + userDataForRequest->processor->toBeProcessed--; +} + +extern "C" { + JNIEXPORT void JNICALL Java_com_deamon_wow_viewer_MainActivity_downloadSucceeded(JNIEnv *env, + jobject thiz, + jlong userDataPtr, + jbyteArray array) { + downloadSucceeded(env, thiz, userDataPtr, array); + } + + JNIEXPORT void JNICALL Java_com_deamon_wow_viewer_MainActivity_downloadFailed(JNIEnv *env, jobject thiz, + jlong userDataPtr) { + downloadFailed(env, thiz, userDataPtr); + } +} + +template< typename T > +static std::string int_to_hex( T i ) +{ + std::stringstream stream; + stream << "0x" + << std::setfill ('0') << std::setw(sizeof(T)*2) + << std::hex << i; + return stream.str(); +} +static std::string char_to_escape( char i ) +{ + std::stringstream stream; + stream << "%" + << std::setfill ('0') << std::setw(2) + << std::hex << (int)i; + return stream.str(); +} + +static std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) { + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); // Handles case where 'to' is a substring of 'from' + } + return str; +} + +void HttpRequestProcessorAndroid::processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) { + auto perstFile = s_file.lock(); + if (perstFile == nullptr){ + toBeProcessed--; + return; + } +// std::cout << "processFileRequest : filename = " << fileName << std::endl; + + const std::string charsToEscape = " !*'();:@&=+$,/?#[]"; + + std::string escapedFileName = fileName; + for (int i = 0; i < charsToEscape.size(); i++) { + char c = charsToEscape[i]; + escapedFileName = ReplaceAll(escapedFileName, std::string(1, c), char_to_escape(c)); + } + + std::string fullUrl; + if (escapedFileName.find("File") == 0) { + std::stringstream ss; + std::string fileDataIdHex = escapedFileName.substr(4, escapedFileName.find(".")-4); + uint32_t fileDataId; + ss << std::hex << fileDataIdHex; + ss >> fileDataId; + + if (fileDataId == 0) { + toBeProcessed--; + return; + } + + fullUrl = m_urlBaseFileId + std::to_string(fileDataId); + } else { + fullUrl = m_urlBase + escapedFileName; + } + +// fullUrl = urlEncode(fullUrl); + + UserDataForRequest * userDataForRequest = new UserDataForRequest(); + userDataForRequest->fileName = fileName; + userDataForRequest->url = fullUrl; + userDataForRequest->holderType = holderType; + userDataForRequest->processor = this; + userDataForRequest->s_file = perstFile; + + JNIEnv* java_env = NULL; + + JavaVM* java_vm = m_app->activity->vm; + jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); + if (jni_return == JNI_ERR) + return ; + + jni_return = java_vm->AttachCurrentThread(&java_env, NULL); + if (jni_return != JNI_OK) + return ; + + jclass native_activity_clazz = java_env->GetObjectClass(m_app->activity->clazz); + if (native_activity_clazz == NULL) + return; + + jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "downloadFile", "(Ljava/lang/String;J)V"); + if (method_id == NULL) + return; + + jstring jurl = java_env->NewStringUTF(fullUrl.c_str()); + java_env->CallVoidMethod(m_app->activity->clazz, method_id, jurl, userDataForRequest); + + jni_return = java_vm->DetachCurrentThread(); + if (jni_return != JNI_OK) + return; + +} diff --git a/android_app/app/cpp/src/HttpRequestProcessor.h b/android_app/app/cpp/src/HttpRequestProcessor.h new file mode 100644 index 000000000..7f7473acd --- /dev/null +++ b/android_app/app/cpp/src/HttpRequestProcessor.h @@ -0,0 +1,36 @@ +// +// Created by deamon on 24.10.17. +// + +#ifndef WEBWOWVIEWERCPP_HTTPREQUESTPROCESSOR_H +#define WEBWOWVIEWERCPP_HTTPREQUESTPROCESSOR_H + +#include <../../../../src/persistance/RequestProcessor.h> + +class HttpRequestProcessorAndroid : public RequestProcessor { +public: + HttpRequestProcessorAndroid(const char *urlBase, const char *urlBaseFileId, android_app* app ) { + + m_urlBase = urlBase; + m_urlBaseFileId = urlBaseFileId; + m_app = app; + } +private: + std::string m_urlBase; + std::string m_urlBaseFileId; + android_app* m_app = nullptr; +protected: + void processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) override; + +public: + friend void downloadFailed(JNIEnv *, jobject, jlong lp); + friend void downloadSucceeded(JNIEnv *env, jobject, jlong userDataPtr, jbyteArray array); + + void setUrls(const char *urlBase, const char *urlBaseFileId) { + m_urlBase = std::string(urlBase); + m_urlBaseFileId = std::string(urlBaseFileId); + } +}; + + +#endif //WEBWOWVIEWERCPP_HTTPREQUESTPROCESSOR_H diff --git a/android_app/app/cpp/src/includes/mbedtls/certs.h b/android_app/app/cpp/src/includes/mbedtls/certs.h new file mode 100644 index 000000000..2002a9584 --- /dev/null +++ b/android_app/app/cpp/src/includes/mbedtls/certs.h @@ -0,0 +1,3 @@ +#ifndef MBEDTLS_SSL_MINOR_VERSION_0 +#define MBEDTLS_SSL_MINOR_VERSION_0 0 +#endif diff --git a/android_app/app/cpp/src/main.cpp b/android_app/app/cpp/src/main.cpp new file mode 100644 index 000000000..9944e1bc0 --- /dev/null +++ b/android_app/app/cpp/src/main.cpp @@ -0,0 +1,473 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../../../../wowViewerLib/src/engine/ApiContainer.h" +#include "../../../../wowViewerLib/src/gapi/IDeviceFactory.h" +#include "../../../../src/database/CEmptySqliteDB.h" +#include "../../../../wowViewerLib/src/engine/camera/firstPersonCamera.h" +#include "../../../../wowViewerLib/src/engine/SceneComposer.h" +#include "../../../../src/ui/FrontendUI.h" +#include "../../../../src/ui/imguiLib/imguiImpl/imgui_impl_android.h" +#include "HttpRequestProcessor.h" +#include "../../../../wowViewerLib/src/engine/androidLogSupport.h" + +SceneComposer *sceneComposer= nullptr; +HApiContainer apiContainer= nullptr; +std::shared_ptr frontendUI = nullptr; + +static EGLDisplay g_EglDisplay = EGL_NO_DISPLAY; +static EGLSurface g_EglSurface = EGL_NO_SURFACE; +static EGLContext g_EglContext = EGL_NO_CONTEXT; +static struct android_app* g_App = NULL; +static bool g_Initialized = false; + +static char g_LogTag[] = "wowViewer"; + +bool stopMouse; +bool stopKeyboard; +int canvWidth; +int canvHeight; + +AAssetManager * g_assetMgr = nullptr; +static JavaVM *gJavaVM; + +#define logExecution {} +//#define logExecution { \ +// std::cout << "Passed "<<__FUNCTION__<<" line " << __LINE__ << std::endl;\ +//} + +std::shared_ptr m_processor = nullptr; + +static int ShowSoftKeyboardInput(); +static int PollUnicodeChars(); + +int32_t getDensityDpi(android_app* app) { + AConfiguration* config = AConfiguration_new(); + AConfiguration_fromAssetManager(config, app->activity->assetManager); + int32_t density = AConfiguration_getDensity(config); + AConfiguration_delete(config); + return density; +} + +void init(struct android_app* app) +{ + if (g_Initialized) + return; + + g_App = app; + ANativeWindow_acquire(g_App->window); + + + // Initialize EGL + // This is mostly boilerplate code for EGL... + { + g_EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (g_EglDisplay == EGL_NO_DISPLAY) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglGetDisplay(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY"); + + if (eglInitialize(g_EglDisplay, 0, 0) != EGL_TRUE) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglInitialize() returned with an error"); + + const EGLint egl_attributes[] = { EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; + EGLint num_configs = 0; + if (eglChooseConfig(g_EglDisplay, egl_attributes, nullptr, 0, &num_configs) != EGL_TRUE) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig() returned with an error"); + if (num_configs == 0) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig() returned 0 matching config"); + + // Get the first matching config + EGLConfig egl_config; + eglChooseConfig(g_EglDisplay, egl_attributes, &egl_config, 1, &num_configs); + EGLint egl_format; + eglGetConfigAttrib(g_EglDisplay, egl_config, EGL_NATIVE_VISUAL_ID, &egl_format); + ANativeWindow_setBuffersGeometry(g_App->window, 0, 0, egl_format); + + const EGLint egl_context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; + g_EglContext = eglCreateContext(g_EglDisplay, egl_config, EGL_NO_CONTEXT, egl_context_attributes); + + if (g_EglContext == EGL_NO_CONTEXT) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglCreateContext() returned EGL_NO_CONTEXT"); + + g_EglSurface = eglCreateWindowSurface(g_EglDisplay, egl_config, g_App->window, NULL); + eglMakeCurrent(g_EglDisplay, g_EglSurface, g_EglSurface, g_EglContext); + } + + int32_t window_width = ANativeWindow_getWidth(g_App->window); + int32_t window_height = ANativeWindow_getHeight(g_App->window); + + canvHeight = window_height; + canvWidth = window_width; + + g_assetMgr = g_App->activity->assetManager; + + void *callback = nullptr; + + //Open Sql storage + apiContainer = std::make_shared(); + + //Create device + std::string rendererName = "ogl3"; + logExecution + auto hdevice = IDeviceFactory::createDevice(rendererName, &callback); + logExecution + apiContainer->databaseHandler = std::make_shared() ; + logExecution + apiContainer->hDevice = hdevice; + logExecution + apiContainer->camera = std::make_shared(); + logExecution + + sceneComposer = new SceneComposer(apiContainer); + logExecution + + + logExecution + frontendUI = std::make_shared(apiContainer, nullptr); + logExecution + frontendUI->setUIScale(3); //TODO:!! + logExecution + + //----------------------------------------------------- + ///DEFAULT provider + + { + + const char * url = "https://wow.tools/casc/file/fname?buildconfig=822a5e72301f9ae6de1840bb5f9961ef&cdnconfig=a22fd94489c2cc9e2debe3f1a8e6b377&filename="; + const char * urlFileId = "https://wow.tools/casc/file/fdid?buildconfig=822a5e72301f9ae6de1840bb5f9961ef&cdnconfig=a22fd94489c2cc9e2debe3f1a8e6b377&filename=data&filedataid="; +// +//Classics +// const char * url = "https://wow.tools/casc/file/fname?buildconfig=bf24b9d67a4a9c7cc0ce59d63df459a8&cdnconfig=2b5b60cdbcd07c5f88c23385069ead40&filename="; +// const char * urlFileId = "https://wow.tools/casc/file/fdid?buildconfig=bf24b9d67a4a9c7cc0ce59d63df459a8&cdnconfig=2b5b60cdbcd07c5f88c23385069ead40&filename=data&filedataid="; +// processor = new HttpZipRequestProcessor(url); +//// processor = new ZipRequestProcessor(filePath); +//// processor = new MpqRequestProcessor(filePath); + m_processor = std::make_shared(url, urlFileId, g_App); +// m_processor = std::make_shared("e:\\games\\wow beta\\World of Warcraft Beta\\:wowt"); +//// processor->setThreaded(false); +//// + m_processor->setThreaded(true); + apiContainer->cacheStorage = std::make_shared(m_processor.get()); + m_processor->setFileRequester(apiContainer->cacheStorage.get()); + frontendUI->overrideCascOpened(true); + +// { +// std::vector replacementTextureFDids = {0,0,3607739}; +//// replacementTextureFDids[2] = 3607739; +// openM2SceneByfdid(4062864, replacementTextureFDids); +// } + + }; + + //----------------------------------------------------- + + + + +// auto window = ANativeWindow_fromSurface(env, surface); + auto window = g_App->window; + logExecution + frontendUI->initImgui(window); + logExecution + + g_Initialized = true; +} + +auto currentFrame = std::chrono::high_resolution_clock::now();; +auto lastFrame = std::chrono::high_resolution_clock::now(); + +void tick() +{ + ImGuiIO& io = ImGui::GetIO(); + if (g_EglDisplay == EGL_NO_DISPLAY) + return; + + // Our state + static bool show_demo_window = true; + static bool show_another_window = false; + static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Poll Unicode characters via JNI + // FIXME: do not call this every frame because of JNI overhead + PollUnicodeChars(); + + //Render + frontendUI->newFrame(); + frontendUI->composeUI(); + + stopMouse = frontendUI->getStopMouse(); + stopKeyboard = frontendUI->getStopKeyboard(); + + logExecution + { + auto processor = frontendUI->getProcessor(); + if (frontendUI->getProcessor()) { + + if (!processor->getThreaded()) { + processor->processRequests(false); + } + } + } + + int32_t window_width = ANativeWindow_getWidth(g_App->window); + int32_t window_height = ANativeWindow_getHeight(g_App->window); + canvHeight = window_height; + canvWidth = window_width; + + currentFrame = std::chrono::high_resolution_clock::now();; + double deltaTime = std::chrono::duration(currentFrame - lastFrame).count() ; + lastFrame = currentFrame; + + apiContainer->camera->tick(deltaTime*(1000.0f)); + //DrawStage for screenshot +// needToMakeScreenshot = true; + if (apiContainer->getConfig()->pauseAnimation) { + deltaTime = 0.0; + } + auto sceneScenario = frontendUI->createFrameScenario(canvWidth, canvHeight, deltaTime); + sceneComposer->draw(sceneScenario); + + + // Open on-screen (soft) input if requested by Dear ImGui + static bool WantTextInputLast = false; + if (io.WantTextInput && !WantTextInputLast) + ShowSoftKeyboardInput(); + WantTextInputLast = io.WantTextInput; + + eglSwapBuffers(g_EglDisplay, g_EglSurface); +} + +void shutdown() +{ + if (!g_Initialized) + return; + + // Cleanup + ImGui_ImplAndroid_Shutdown(); + ImGui::DestroyContext(); + + if (g_EglDisplay != EGL_NO_DISPLAY) + { + eglMakeCurrent(g_EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (g_EglContext != EGL_NO_CONTEXT) + eglDestroyContext(g_EglDisplay, g_EglContext); + + if (g_EglSurface != EGL_NO_SURFACE) + eglDestroySurface(g_EglDisplay, g_EglSurface); + + eglTerminate(g_EglDisplay); + } + + g_EglDisplay = EGL_NO_DISPLAY; + g_EglContext = EGL_NO_CONTEXT; + g_EglSurface = EGL_NO_SURFACE; + ANativeWindow_release(g_App->window); + + g_Initialized = false; +} + +static void handleAppCmd(struct android_app* app, int32_t appCmd) +{ + switch (appCmd) + { + case APP_CMD_SAVE_STATE: + break; + case APP_CMD_INIT_WINDOW: + init(app); + break; + case APP_CMD_TERM_WINDOW: + shutdown(); + break; + case APP_CMD_GAINED_FOCUS: + break; + case APP_CMD_LOST_FOCUS: + break; + } +} + +static int32_t handleInputEvent(struct android_app* app, AInputEvent* inputEvent) +{ + return ImGui_ImplAndroid_HandleInputEvent(inputEvent); +} + + +int cdToExtStorage(void) { + + // Make JNI calls to get the external storage directory, and cd to it. + + // To begin, get a reference to the env and attach to it. + JNIEnv *env; + int isAttached = 0; + int ret = 0; + jthrowable exception; + if ((gJavaVM->GetEnv((void**)&env, JNI_VERSION_1_6)) < 0) { + // Couldn't get JNI environment, so this thread is native. + if ((gJavaVM->AttachCurrentThread(&env, NULL)) < 0) { + fprintf(stderr, "Error: Couldn't attach to Java VM.\n"); + return (-1); + } + isAttached = 1; + } + + // Get File object for the external storage directory. + jclass classEnvironment = env->FindClass("android/os/Environment"); + if (!classEnvironment) { + std::cout << "Error: JNI call failure.\n" << std::endl; + }; + jmethodID methodIDgetExternalStorageDirectory = env->GetStaticMethodID(classEnvironment, "getExternalStorageDirectory", "()Ljava/io/File;"); // public static File getExternalStorageDirectory () + if (!methodIDgetExternalStorageDirectory) { + std::cout << "Error: JNI call failure.\n" << std::endl; + }; + jobject objectFile = env->CallStaticObjectMethod(classEnvironment, methodIDgetExternalStorageDirectory); + exception = env->ExceptionOccurred(); + if (exception) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + // Call method on File object to retrieve String object. + jclass classFile = env->GetObjectClass(objectFile); + if (!classFile) { + std::cout << "Error: JNI call failure.\n" << std::endl; + } + jmethodID methodIDgetAbsolutePath = env->GetMethodID(classFile, "getAbsolutePath", "()Ljava/lang/String;"); + if (!methodIDgetAbsolutePath) { + std::cout << "Error: JNI call failure.\n" << std::endl; + } + + jstring stringPath = static_cast(env->CallObjectMethod(objectFile, + methodIDgetAbsolutePath)); + exception = env->ExceptionOccurred(); + if (exception) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + // Extract a C string from the String object, and chdir() to it. + const char *wpath3 = env->GetStringUTFChars(stringPath, NULL); + if (chdir(wpath3) != 0) { + fprintf(stderr, "Error: Unable to change working directory to %s.\n", wpath3); + perror(NULL); + } + + env->ReleaseStringUTFChars(stringPath, wpath3); + + if (isAttached) gJavaVM->DetachCurrentThread(); // Clean up. + return (ret); +} + +void android_main(struct android_app* app) { + gJavaVM = app->activity->vm; + std::cout.rdbuf(new androidbuf); + + cdToExtStorage(); + + app->onAppCmd = handleAppCmd; + app->onInputEvent = handleInputEvent; + + while (true) + { + int out_events; + struct android_poll_source* out_data; + + // Poll all events. If the app is not visible, this loop blocks until g_Initialized == true. + while (ALooper_pollAll(g_Initialized ? 0 : -1, NULL, &out_events, (void**)&out_data) >= 0) + { + // Process one event + if (out_data != NULL) + out_data->process(app, out_data); + + // Exit the app by returning from within the infinite loop + if (app->destroyRequested != 0) + { + // shutdown() should have been called already while processing the + // app command APP_CMD_TERM_WINDOW. But we play save here + if (!g_Initialized) + shutdown(); + + return; + } + } + + // Initiate a new frame + tick(); + } +} + +// Unfortunately, the native KeyEvent implementation has no getUnicodeChar() function. +// Therefore, we implement the processing of KeyEvents in MainActivity.kt and poll +// the resulting Unicode characters here via JNI and send them to Dear ImGui. +static int PollUnicodeChars() +{ + JavaVM* java_vm = g_App->activity->vm; + JNIEnv* java_env = NULL; + + jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); + if (jni_return == JNI_ERR) + return -1; + + jni_return = java_vm->AttachCurrentThread(&java_env, NULL); + if (jni_return != JNI_OK) + return -2; + + jclass native_activity_clazz = java_env->GetObjectClass(g_App->activity->clazz); + if (native_activity_clazz == NULL) + return -3; + + jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "pollUnicodeChar", "()I"); + if (method_id == NULL) + return -4; + + // Send the actual characters to Dear ImGui + ImGuiIO& io = ImGui::GetIO(); + jint unicode_character; + while ((unicode_character = java_env->CallIntMethod(g_App->activity->clazz, method_id)) != 0) + io.AddInputCharacter(unicode_character); + + jni_return = java_vm->DetachCurrentThread(); + if (jni_return != JNI_OK) + return -5; + + return 0; +} + +// Unfortunately, there is no way to show the on-screen input from native code. +// Therefore, we call ShowSoftKeyboardInput() of the main activity implemented in MainActivity.kt via JNI. +static int ShowSoftKeyboardInput() +{ + JavaVM* java_vm = g_App->activity->vm; + JNIEnv* java_env = NULL; + + jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); + if (jni_return == JNI_ERR) + return -1; + + jni_return = java_vm->AttachCurrentThread(&java_env, NULL); + if (jni_return != JNI_OK) + return -2; + + jclass native_activity_clazz = java_env->GetObjectClass(g_App->activity->clazz); + if (native_activity_clazz == NULL) + return -3; + + jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "showSoftInput", "()V"); + if (method_id == NULL) + return -4; + + java_env->CallVoidMethod(g_App->activity->clazz, method_id); + + jni_return = java_vm->DetachCurrentThread(); + if (jni_return != JNI_OK) + return -5; + + return 0; +} \ No newline at end of file diff --git a/android_app/app/proguard-rules.pro b/android_app/app/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/android_app/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/android_app/app/src/main/AndroidManifest.xml b/android_app/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..f6cbdc0ba --- /dev/null +++ b/android_app/app/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android_app/app/src/main/java/com/deamon/wow/viewer/MainActivity.java b/android_app/app/src/main/java/com/deamon/wow/viewer/MainActivity.java new file mode 100644 index 000000000..8b8abc88c --- /dev/null +++ b/android_app/app/src/main/java/com/deamon/wow/viewer/MainActivity.java @@ -0,0 +1,100 @@ +package com.deamon.wow.viewer; + +import android.app.NativeActivity; +import android.content.Context; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.inputmethod.InputMethodManager; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.LinkedBlockingQueue; + +public class MainActivity extends NativeActivity { + + static { + System.loadLibrary("AWebWoWViewerCpp"); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + void showSoftInput() { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(this.getWindow().getDecorView(), 0); + } + + void hideSoftInput() { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(this.getWindow().getDecorView().getWindowToken(), 0); + } + + // Queue for the Unicode characters to be polled from native code (via pollUnicodeChar()) + private LinkedBlockingQueue unicodeCharacterQueue = new LinkedBlockingQueue(); + + // We assume dispatchKeyEvent() of the NativeActivity is actually called for every + // KeyEvent and not consumed by any View before it reaches here + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + unicodeCharacterQueue.offer(event.getUnicodeChar(event.getMetaState())); + } + return super.dispatchKeyEvent(event); + } + + int pollUnicodeChar() { + Integer a = unicodeCharacterQueue.poll(); + return (a != null) ? a : 0; + } + + public void downloadFile(String url, long userDataForRequest) { + try { + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + InputStream in = urlConnection.getInputStream(); + + byte[] data = readAllBytes(in); +// downloadFailed(userDataForRequest); + downloadSucceeded(userDataForRequest, data); + } catch (Exception e) { + System.out.println(e.getMessage()); + downloadFailed(userDataForRequest); + } + + } + + public static byte[] readAllBytes(InputStream inputStream) throws IOException { + final int bufLen = 4 * 0x400; // 4KB + byte[] buf = new byte[bufLen]; + int readLen; + IOException exception = null; + + try { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) + outputStream.write(buf, 0, readLen); + + return outputStream.toByteArray(); + } + } catch (IOException e) { + exception = e; + throw e; + } finally { + if (exception == null) inputStream.close(); + else try { + inputStream.close(); + } catch (IOException e) { + exception.addSuppressed(e); + } + } + } + + public native void downloadSucceeded(long userDataPtr, byte[] data ); + public native void downloadFailed(long userDataPtr); +} diff --git a/android_app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android_app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000..fde1368fc --- /dev/null +++ b/android_app/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/drawable/ic_launcher_background.xml b/android_app/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..1e4408cae --- /dev/null +++ b/android_app/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android_app/app/src/main/res/layout/activity_main.xml b/android_app/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..0b15a2095 --- /dev/null +++ b/android_app/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android_app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..eca70cfe5 --- /dev/null +++ b/android_app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android_app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..eca70cfe5 --- /dev/null +++ b/android_app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android_app/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..a571e6009 Binary files /dev/null and b/android_app/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android_app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android_app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..61da551c5 Binary files /dev/null and b/android_app/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android_app/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android_app/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..c41dd2853 Binary files /dev/null and b/android_app/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android_app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android_app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..db5080a75 Binary files /dev/null and b/android_app/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android_app/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android_app/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..6dba46dab Binary files /dev/null and b/android_app/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android_app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android_app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..da31a871c Binary files /dev/null and b/android_app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android_app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android_app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..15ac68172 Binary files /dev/null and b/android_app/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android_app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android_app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..b216f2d31 Binary files /dev/null and b/android_app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android_app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android_app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..f25a41974 Binary files /dev/null and b/android_app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android_app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android_app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..e96783ccc Binary files /dev/null and b/android_app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android_app/app/src/main/res/values-night-v27/themes.xml b/android_app/app/src/main/res/values-night-v27/themes.xml new file mode 100644 index 000000000..a85142054 --- /dev/null +++ b/android_app/app/src/main/res/values-night-v27/themes.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/values-night/themes.xml b/android_app/app/src/main/res/values-night/themes.xml new file mode 100644 index 000000000..6a358a745 --- /dev/null +++ b/android_app/app/src/main/res/values-night/themes.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/values-v27/themes.xml b/android_app/app/src/main/res/values-v27/themes.xml new file mode 100644 index 000000000..b394b4dd8 --- /dev/null +++ b/android_app/app/src/main/res/values-v27/themes.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/values/colors.xml b/android_app/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..f8c6127d3 --- /dev/null +++ b/android_app/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/android_app/app/src/main/res/values/strings.xml b/android_app/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..6cf1054df --- /dev/null +++ b/android_app/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + WowViewerAndroid + \ No newline at end of file diff --git a/android_app/app/src/main/res/values/themes.xml b/android_app/app/src/main/res/values/themes.xml new file mode 100644 index 000000000..d619fa2eb --- /dev/null +++ b/android_app/app/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/android_app/app/src/test/java/com/deamon/wow/viewer/ExampleUnitTest.java b/android_app/app/src/test/java/com/deamon/wow/viewer/ExampleUnitTest.java new file mode 100644 index 000000000..19f89b6ff --- /dev/null +++ b/android_app/app/src/test/java/com/deamon/wow/viewer/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.deamon.wow.viewer; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/android_app/build.gradle b/android_app/build.gradle new file mode 100644 index 000000000..ac86c700b --- /dev/null +++ b/android_app/build.gradle @@ -0,0 +1,26 @@ +ext { + targetCompatibility = JavaVersion.VERSION_1_7 +}// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath "com.android.tools.build:gradle:4.1.1" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/android_app/gradle.properties b/android_app/gradle.properties new file mode 100644 index 000000000..52f5917cb --- /dev/null +++ b/android_app/gradle.properties @@ -0,0 +1,19 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true \ No newline at end of file diff --git a/android_app/gradlew b/android_app/gradlew new file mode 100644 index 000000000..cccdd3d51 --- /dev/null +++ b/android_app/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/android_app/gradlew.bat b/android_app/gradlew.bat new file mode 100644 index 000000000..f9553162f --- /dev/null +++ b/android_app/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android_app/settings.gradle b/android_app/settings.gradle new file mode 100644 index 000000000..a5c357fac --- /dev/null +++ b/android_app/settings.gradle @@ -0,0 +1,2 @@ +include ':app' +rootProject.name = "WowViewerAndroid" \ No newline at end of file diff --git a/emscripten_port/3rdparty/zipper b/emscripten_port/3rdparty/zipper new file mode 160000 index 000000000..8f799a04c --- /dev/null +++ b/emscripten_port/3rdparty/zipper @@ -0,0 +1 @@ +Subproject commit 8f799a04cd252e2c910a1028bc8e75c7a05a1984 diff --git a/emscripten_port/CMakeLists.txt b/emscripten_port/CMakeLists.txt index 56fe38763..f15505847 100644 --- a/emscripten_port/CMakeLists.txt +++ b/emscripten_port/CMakeLists.txt @@ -7,15 +7,21 @@ include(CheckCXXCompilerFlag) set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_EXECUTABLE_SUFFIX .js) +set(CMAKE_BUILD_PARALLEL_LEVEL 1) + add_link_options("SHELL: -s ALLOW_MEMORY_GROWTH=1") add_link_options("SHELL: -s DISABLE_EXCEPTION_CATCHING=0") add_link_options("SHELL: -s TOTAL_MEMORY=40MB") -message( EMSCRIPTEN_ROOT = emc${EMSCRIPTEN_ROOT}) +message( EMSCRIPTEN_ROOT_PATH = ${EMSCRIPTEN_ROOT_PATH}) -#set(CMAKE_C_COMPILER /home/deamon/Downloads/emsdk-master/upstream/emscripten/emcc) -#set(CMAKE_CXX_COMPILER /home/deamon/Downloads/emsdk-master/upstream/emscripten/em++) +option(COMPILE_WITH_SIMD "Compile with experimental WASM SIMD support" OFF) +message(COMPILE_WITH_SIMD = ${COMPILE_WITH_SIMD}) +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} --emit-symbol-map" ) +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} --emit-symbol-map" ) +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} --emit-symbol-map") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} --emit-symbol-map") check_cxx_compiler_flag(-std=c++17 HAVE_FLAG_STD_CXX17) if(HAVE_FLAG_STD_CXX17) @@ -40,28 +46,78 @@ endif() #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-exceptions") #set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions") - - set(EMSCRIPTEN 1) +set(OUTPUTDIR "build_packed") +if (COMPILE_WITH_SIMD) + set(OUTPUTDIR "simd_version/") + set(EMSCRIPTEN_SIMD 1) +endif() + +set(BUILD_SHARED_VERSION 0) +set(BUILD_STATIC_VERSION 1) +add_subdirectory(${PROJECT_SOURCE_DIR}/../3rdparty/zlib ${CMAKE_CURRENT_BINARY_DIR}/zlib EXCLUDE_FROM_ALL) +# +set (LIBZ_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../3rdparty/zlib") +set (ZLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../3rdparty/zlib/") +set (ZLIB_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/zlib/libz.a") +#include_directories(${CMAKE_CURRENT_BINARY_DIR}/zlib) + +add_subdirectory(3rdparty/zipper EXCLUDE_FROM_ALL) +add_dependencies(staticZipper zlibstatic) +target_include_directories(staticZipper PUBLIC $) +#set(PNG_BUILD_ZLIB OFF) +#add_subdirectory(../3rdparty/libpng ${CMAKE_CURRENT_BINARY_DIR}/libpng) +#include_directories(../3rdparty/libpng) add_subdirectory(../wowViewerLib ${CMAKE_CURRENT_BINARY_DIR}/wowViewerLib) + set(SOURCE_FILES src/main.cpp src/RequestProcessor.cpp src/HttpRequestProcessor.cpp + ../src/exporters/gltfExporter/GLTFExporter.cpp + ../src/exporters/gltfExporter/GLTFExporter.h + ../src/screenshots/lodepng/lodepng.cpp + ../src/screenshots/lodepng/lodepng.h + ../src/screenshots/screenshotMaker.cpp ) add_executable(project ${SOURCE_FILES}) add_dependencies(project WoWViewerLib) + +target_link_libraries(project zlibstatic) target_link_libraries(project WoWViewerLib) +target_link_libraries(project staticZipper) + + target_link_options(project PUBLIC "SHELL: --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/prejs.js --js-library ${CMAKE_CURRENT_SOURCE_DIR}/jsLib.js") target_link_options(project PUBLIC "SHELL: -s USE_GLFW=3 --preload-file glsl -s ALLOW_MEMORY_GROWTH=1") target_link_options(project PUBLIC "SHELL: -s TOTAL_MEMORY=40MB") target_link_options(project PUBLIC "SHELL: -s FETCH=1 -s WASM=1 -s USE_WEBGL2=1 -s FULL_ES3=1") -target_link_options(project PUBLIC "SHELL: -fno-rtti --llvm-lto 1") + +target_link_options(project PUBLIC "SHELL: -fno-rtti ") +target_link_options(project PUBLIC "SHELL: -s EXPORTED_RUNTIME_METHODS=[\"FS\"]") + +target_link_options(project PUBLIC "SHELL: -s ENVIRONMENT=\"web\" " ) +target_link_options(project PUBLIC "SHELL: -s USE_PTHREADS=0") +target_link_options(project PUBLIC "SHELL: --bind ") +#target_link_options(project PUBLIC "SHELL: -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2") +#target_link_options(project PUBLIC "SHELL: -s ENVIRONMENT=\"web,worker\" " ) + + set_target_properties(project PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../" - ) \ No newline at end of file + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../${OUTPUTDIR}" + ) + +install(TARGETS project + EXPORT ${PROJECT_NAME}Targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT Runtime) + +install(DIRECTORY "${CMAKE_BINARY_DIR}/../${OUTPUTDIR}/" + TYPE BIN + FILES_MATCHING PATTERN "*.*") \ No newline at end of file diff --git a/emscripten_port/build.sh b/emscripten_port/build.sh deleted file mode 100755 index fe06dfade..000000000 --- a/emscripten_port/build.sh +++ /dev/null @@ -1,2 +0,0 @@ - emconfigure cmake -DCMAKE_BUILD_TYPE=Debug $1 . && emmake make - emcc libAWoWWebViewerJs.a ./A/libWoWViewerLib.a --pre-js prejs.js --js-library jsLib.js --source-map-base "/" -s USE_GLFW=3 --preload-file glsl -s ASSERTIONS=2 -s DEMANGLE_SUPPORT=1 -O0 -g4 -s ALLOW_MEMORY_GROWTH=1 -s FETCH=1 -s WASM=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s "EXPORTED_FUNCTIONS=['_createWebJsScene', '_gameloop', '_createWoWScene', '_setScene', '_setSceneSize', '_setSceneFileDataId', '_setNewUrls', '_addCameraViewOffset', '_addHorizontalViewDir', '_addVerticalViewDir', '_zoomInFromMouseScroll', '_startMovingForward', '_startMovingBackwards', '_stopMovingForward', '_stopMovingBackwards']" -o project.js \ No newline at end of file diff --git a/emscripten_port/buildSIMD.sh b/emscripten_port/buildSIMD.sh deleted file mode 100755 index 1f3c31c94..000000000 --- a/emscripten_port/buildSIMD.sh +++ /dev/null @@ -1,2 +0,0 @@ - emconfigure cmake -DCMAKE_BUILD_TYPE=Debug -DEMSCRIPTEN_SIMD=1 $1 . && emmake make - emcc libAWoWWebViewerJs.a ./A/libWoWViewerLib.a --pre-js prejs.js --js-library jsLib.js --source-map-base "/" -s USE_GLFW=3 --preload-file glsl -s ASSERTIONS=2 -s DEMANGLE_SUPPORT=1 -O0 -g4 -s ALLOW_MEMORY_GROWTH=1 -s FETCH=1 -s WASM=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s "EXPORTED_FUNCTIONS=['_createWebJsScene', '_gameloop', '_createWoWScene', '_setScene', '_setSceneSize', '_setSceneFileDataId', '_setNewUrls', '_addCameraViewOffset', '_addHorizontalViewDir', '_addVerticalViewDir', '_zoomInFromMouseScroll', '_startMovingForward', '_startMovingBackwards', '_stopMovingForward', '_stopMovingBackwards']" -o project.js \ No newline at end of file diff --git a/emscripten_port/build_release.sh b/emscripten_port/build_release.sh deleted file mode 100755 index f3d4f2fb0..000000000 --- a/emscripten_port/build_release.sh +++ /dev/null @@ -1,2 +0,0 @@ -emconfigure cmake -DCMAKE_BUILD_TYPE=Release $1 && emmake make VERBOSE=1 -s ALLOW_MEMORY_GROWTH=1 -emcc libAWoWWebViewerJs.a ./A/libWoWViewerLib.a --pre-js prejs.js --js-library jsLib.js -s USE_GLFW=3 --preload-file glsl -O3 -s ALLOW_MEMORY_GROWTH=1 -s FETCH=1 -s WASM=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s "EXPORTED_FUNCTIONS=['_createWebJsScene', '_gameloop', '_createWoWScene', '_setScene', '_setSceneSize', '_setSceneFileDataId', '_setNewUrls', '_addCameraViewOffset', '_addHorizontalViewDir', '_addVerticalViewDir', '_zoomInFromMouseScroll', '_startMovingForward', '_startMovingBackwards', '_stopMovingForward', '_stopMovingBackwards']" -o project.js diff --git a/emscripten_port/build_releaseSIMD.sh b/emscripten_port/build_releaseSIMD.sh deleted file mode 100755 index 87ae522e6..000000000 --- a/emscripten_port/build_releaseSIMD.sh +++ /dev/null @@ -1,2 +0,0 @@ -emconfigure cmake -DCMAKE_BUILD_TYPE=Release -DEMSCRIPTEN_SIMD=1 $1 && emmake make VERBOSE=1 -s ALLOW_MEMORY_GROWTH=1 -emcc libAWoWWebViewerJs.a ./A/libWoWViewerLib.a --pre-js prejs.js --js-library jsLib.js -s USE_GLFW=3 --preload-file glsl -O3 -s ALLOW_MEMORY_GROWTH=1 -s ASSERTIONS=1 -s FETCH=1 -s WASM=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s "EXPORTED_FUNCTIONS=['_createWebJsScene', '_gameloop', '_createWoWScene', '_setScene', '_setSceneSize', '_setSceneFileDataId', '_setNewUrls', '_addCameraViewOffset', '_addHorizontalViewDir', '_addVerticalViewDir', '_zoomInFromMouseScroll', '_startMovingForward', '_startMovingBackwards', '_stopMovingForward', '_stopMovingBackwards']" -o project.js diff --git a/emscripten_port/jsLib.js b/emscripten_port/jsLib.js index 4613a56e8..8dc5b691f 100644 --- a/emscripten_port/jsLib.js +++ b/emscripten_port/jsLib.js @@ -1,8 +1,36 @@ mergeInto(LibraryManager.library, { - supplyPointer: function(arrPtr, length) { + supplyAnimationList: function(arrPtr, length) { var animationIdArr = Module.HEAP32.subarray(arrPtr / 4, arrPtr / 4 + length); console.log(animationIdArr); Module['animationArrayCallback'](animationIdArr); }, + supplyMeshIds: function(arrPtr, length) { + var meshIdArr = Module.HEAP32.subarray(arrPtr / 4, arrPtr / 4 + length); + console.log(meshIdArr); + + if (Module['meshIdArrayCallback']) { + Module['meshIdArrayCallback'](meshIdArr); + } + }, + offerFileAsDownload : function(filename_ptr, filename_len) { + let mime = "application/octet-stream"; + + let filename = AsciiToString(filename_ptr); + + let content = Module.FS.readFile(filename); + console.log(`Offering download of "${filename}", with ${content.length} bytes...`); + + var a = document.createElement('a'); + a.download = filename; + a.href = URL.createObjectURL(new Blob([content], {type: mime})); + a.style.display = 'none'; + + document.body.appendChild(a); + a.click(); + setTimeout(() => { + document.body.removeChild(a); + URL.revokeObjectURL(a.href); + }, 2000); + } }); diff --git a/emscripten_port/project.data b/emscripten_port/project.data index e56251922..5e81d53e1 100644 --- a/emscripten_port/project.data +++ b/emscripten_port/project.data @@ -88,6 +88,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -116,6 +117,9 @@ struct PSFog vec4 sunPercentage; }; +const vec3 _221[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _228[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + struct meshWideBlockPS { vec4 uHeightScale; @@ -123,23 +127,22 @@ struct meshWideBlockPS mat4 animationMat[4]; }; -uniform meshWideBlockPS _387; +uniform meshWideBlockPS _465; -struct sceneWideBlockVSPS +struct modelWideBlockPS { - SceneWideParams scene; - PSFog fogData; + ivec4 uUseHeightMixFormula; }; -uniform sceneWideBlockVSPS _621; +uniform modelWideBlockPS _505; -struct modelWideBlockPS +struct sceneWideBlockVSPS { - vec4 uFogStartAndFogEnd; - vec4 uFogColor; + SceneWideParams scene; + PSFog fogData; }; -uniform modelWideBlockPS _741; +uniform sceneWideBlockVSPS _747; uniform sampler2D uAlphaTexture; uniform sampler2D uLayerHeight0; @@ -157,27 +160,32 @@ varying vec3 vNormal; varying vec3 vVertexLighting; varying vec3 vPosition; +vec4 mixTextures(vec4 tex0, vec4 tex1, float alpha) +{ + return ((tex1 - tex0) * alpha) + tex0; +} + vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, vec3 accumLight, vec3 precomputedLight, vec3 specular) { vec3 localDiffuse = accumLight; - bool _42 = !applyLight; - if (_42) + bool _57 = !applyLight; + if (_57) { return matDiffuse; } vec3 lDiffuse = vec3(0.0); vec3 normalizedN = normalize(vNormal_1); - bool _59 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + bool _73 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; vec3 currColor; - if (_59) + if (_73) { float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; - bool _97 = nDotUp >= 0.0; - if (_97) + bool _110 = nDotUp >= 0.0; + if (_110) { currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); } @@ -190,14 +198,14 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE lDiffuse = sceneParams.extLight.uExteriorDirectColor.xyz * nDotL; currColor = mix(groundColor, skyColor, vec3(0.5 + (0.5 * nDotL))); } - bool _137 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; - if (_137) + bool _150 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; + if (_150) { float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; - bool _161 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - if (_161) + bool _174 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + if (_174) { lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); @@ -215,7 +223,12 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; } -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) +vec3 validateFogColor(vec3 fogColor, int blendMode) +{ + return mix(fogColor, _221[blendMode], vec3(_228[blendMode])); +} + +vec4 makeFog(PSFog fogData, vec4 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace, int blendMode) { vec4 l_densityParams = fogData.densityParams; vec4 l_heightPlane = fogData.heightPlane; @@ -234,24 +247,38 @@ vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInVie float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); float finalFog = mix(expFog, expFogHeight, heightFog); float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; + float alpha = 1.0; + bool _316 = blendMode == 13; + if (_316) + { + alpha = min(finalFog, endFadeFog); + } + vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + vec3 endColor = validateFogColor(param, param_1); vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; float end2 = vLength / l_heightColor_and_endFogDistance.w; float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _334 = nDotSun > 0.0; - if (_334) + bool _394 = nDotSun > 0.0; + if (_394) { nDotSun = (nDotSun * nDotSun) * nDotSun; fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } void main() @@ -259,69 +286,92 @@ void main() vec2 vTexCoord = vChunkCoords; vec2 alphaCoord = vec2(vChunkCoords.x / 8.0, vChunkCoords.y / 8.0); vec3 alphaBlend = texture2D(uAlphaTexture, alphaCoord).yzw; - vec2 tcLayer0 = (_387.animationMat[0] * vec4(vTexCoord, 0.0, 1.0)).xy; - vec2 tcLayer1 = (_387.animationMat[1] * vec4(vTexCoord, 0.0, 1.0)).xy; - vec2 tcLayer2 = (_387.animationMat[2] * vec4(vTexCoord, 0.0, 1.0)).xy; - vec2 tcLayer3 = (_387.animationMat[3] * vec4(vTexCoord, 0.0, 1.0)).xy; - float minusAlphaBlendSum = 1.0 - clamp(dot(alphaBlend, vec3(1.0)), 0.0, 1.0); - vec4 weightsVector = vec4(minusAlphaBlendSum, alphaBlend); - float weightedTexture_x = minusAlphaBlendSum * ((texture2D(uLayerHeight0, tcLayer0).w * _387.uHeightScale.x) + _387.uHeightOffset.x); - float weightedTexture_y = weightsVector.y * ((texture2D(uLayerHeight1, tcLayer1).w * _387.uHeightScale.y) + _387.uHeightOffset.y); - float weightedTexture_z = weightsVector.z * ((texture2D(uLayerHeight2, tcLayer2).w * _387.uHeightScale.z) + _387.uHeightOffset.z); - float weightedTexture_w = weightsVector.w * ((texture2D(uLayerHeight3, tcLayer3).w * _387.uHeightScale.w) + _387.uHeightOffset.w); - vec4 weights = vec4(weightedTexture_x, weightedTexture_y, weightedTexture_z, weightedTexture_w); - vec4 weights_temp = weights * (vec4(1.0) - clamp(vec4(max(max(weightedTexture_x, weightedTexture_y), max(weightedTexture_z, weightedTexture_w))) - weights, vec4(0.0), vec4(1.0))); - vec4 weightsNormalized = weights_temp / vec4(dot(vec4(1.0), weights_temp)); - vec4 weightedLayer_0 = texture2D(uLayer0, tcLayer0) * weightsNormalized.x; - vec3 matDiffuse_0 = weightedLayer_0.xyz; - float specBlend_0 = weightedLayer_0.w; - vec4 weightedLayer_1 = texture2D(uLayer1, tcLayer1) * weightsNormalized.y; - vec3 matDiffuse_1 = matDiffuse_0 + weightedLayer_1.xyz; - float specBlend_1 = specBlend_0 + weightedLayer_1.w; - vec4 weightedLayer_2 = texture2D(uLayer2, tcLayer1) * weightsNormalized.z; - vec3 matDiffuse_2 = matDiffuse_1 + weightedLayer_2.xyz; - float specBlend_2 = specBlend_1 + weightedLayer_2.w; - vec4 weightedLayer_3 = texture2D(uLayer3, tcLayer1) * weightsNormalized.w; - vec3 matDiffuse_3 = matDiffuse_2 + weightedLayer_3.xyz; - float specBlend_3 = specBlend_2 + weightedLayer_3.w; - vec4 final = vec4(matDiffuse_3, specBlend_3); + vec2 tcLayer0 = (_465.animationMat[0] * vec4(vTexCoord, 0.0, 1.0)).xy; + vec2 tcLayer1 = (_465.animationMat[1] * vec4(vTexCoord, 0.0, 1.0)).xy; + vec2 tcLayer2 = (_465.animationMat[2] * vec4(vTexCoord, 0.0, 1.0)).xy; + vec2 tcLayer3 = (_465.animationMat[3] * vec4(vTexCoord, 0.0, 1.0)).xy; + bool _509 = _505.uUseHeightMixFormula.x > 0; + vec4 final; + if (_509) + { + float minusAlphaBlendSum = 1.0 - clamp(dot(alphaBlend, vec3(1.0)), 0.0, 1.0); + vec4 weightsVector = vec4(minusAlphaBlendSum, alphaBlend); + float weightedTexture_x = minusAlphaBlendSum * ((texture2D(uLayerHeight0, tcLayer0).w * _465.uHeightScale.x) + _465.uHeightOffset.x); + float weightedTexture_y = weightsVector.y * ((texture2D(uLayerHeight1, tcLayer1).w * _465.uHeightScale.y) + _465.uHeightOffset.y); + float weightedTexture_z = weightsVector.z * ((texture2D(uLayerHeight2, tcLayer2).w * _465.uHeightScale.z) + _465.uHeightOffset.z); + float weightedTexture_w = weightsVector.w * ((texture2D(uLayerHeight3, tcLayer3).w * _465.uHeightScale.w) + _465.uHeightOffset.w); + vec4 weights = vec4(weightedTexture_x, weightedTexture_y, weightedTexture_z, weightedTexture_w); + vec4 weights_temp = weights * (vec4(1.0) - clamp(vec4(max(max(weightedTexture_x, weightedTexture_y), max(weightedTexture_z, weightedTexture_w))) - weights, vec4(0.0), vec4(1.0))); + vec4 weightsNormalized = weights_temp / vec4(dot(vec4(1.0), weights_temp)); + vec4 weightedLayer_0 = texture2D(uLayer0, tcLayer0) * weightsNormalized.x; + vec3 matDiffuse_0 = weightedLayer_0.xyz; + float specBlend_0 = weightedLayer_0.w; + vec4 weightedLayer_1 = texture2D(uLayer1, tcLayer1) * weightsNormalized.y; + vec3 matDiffuse_1 = matDiffuse_0 + weightedLayer_1.xyz; + float specBlend_1 = specBlend_0 + weightedLayer_1.w; + vec4 weightedLayer_2 = texture2D(uLayer2, tcLayer2) * weightsNormalized.z; + vec3 matDiffuse_2 = matDiffuse_1 + weightedLayer_2.xyz; + float specBlend_2 = specBlend_1 + weightedLayer_2.w; + vec4 weightedLayer_3 = texture2D(uLayer3, tcLayer3) * weightsNormalized.w; + vec3 matDiffuse_3 = matDiffuse_2 + weightedLayer_3.xyz; + float specBlend_3 = specBlend_2 + weightedLayer_3.w; + final = vec4(matDiffuse_3, specBlend_3); + } + else + { + vec4 tex1 = texture2D(uLayer0, tcLayer0); + vec4 tex2 = texture2D(uLayer1, tcLayer1); + vec4 tex3 = texture2D(uLayer2, tcLayer2); + vec4 tex4 = texture2D(uLayer3, tcLayer3); + vec4 param = tex1; + vec4 param_1 = tex2; + float param_2 = alphaBlend.x; + vec4 param_3 = mixTextures(param, param_1, param_2); + vec4 param_4 = tex3; + float param_5 = alphaBlend.y; + vec4 param_6 = mixTextures(param_3, param_4, param_5); + vec4 param_7 = tex4; + float param_8 = alphaBlend.z; + final = mixTextures(param_6, param_7, param_8); + } vec3 matDiffuse = (final.xyz * 2.0) * vColor.xyz; - vec3 param = matDiffuse; - vec3 param_1 = vNormal; - bool param_2 = true; - float param_3 = 0.0; - SceneWideParams param_4; - param_4.uLookAtMat = _621.scene.uLookAtMat; - param_4.uPMatrix = _621.scene.uPMatrix; - param_4.uViewUp = _621.scene.uViewUp; - param_4.uInteriorSunDir = _621.scene.uInteriorSunDir; - param_4.extLight.uExteriorAmbientColor = _621.scene.extLight.uExteriorAmbientColor; - param_4.extLight.uExteriorHorizontAmbientColor = _621.scene.extLight.uExteriorHorizontAmbientColor; - param_4.extLight.uExteriorGroundAmbientColor = _621.scene.extLight.uExteriorGroundAmbientColor; - param_4.extLight.uExteriorDirectColor = _621.scene.extLight.uExteriorDirectColor; - param_4.extLight.uExteriorDirectColorDir = _621.scene.extLight.uExteriorDirectColorDir; - InteriorLightParam param_5 = InteriorLightParam(vec4(0.0), vec4(0.0, 0.0, 0.0, 1.0)); - vec3 param_6 = vVertexLighting; - vec4 finalColor = vec4(calcLight(param, param_1, param_2, param_3, param_4, param_5, param_6, vec3(0.0), vec3(0.0)), 1.0); + vec3 param_9 = matDiffuse; + vec3 param_10 = vNormal; + bool param_11 = true; + float param_12 = 0.0; + SceneWideParams param_13; + param_13.uLookAtMat = _747.scene.uLookAtMat; + param_13.uPMatrix = _747.scene.uPMatrix; + param_13.uViewUp = _747.scene.uViewUp; + param_13.uInteriorSunDir = _747.scene.uInteriorSunDir; + param_13.extLight.uExteriorAmbientColor = _747.scene.extLight.uExteriorAmbientColor; + param_13.extLight.uExteriorHorizontAmbientColor = _747.scene.extLight.uExteriorHorizontAmbientColor; + param_13.extLight.uExteriorGroundAmbientColor = _747.scene.extLight.uExteriorGroundAmbientColor; + param_13.extLight.uExteriorDirectColor = _747.scene.extLight.uExteriorDirectColor; + param_13.extLight.uExteriorDirectColorDir = _747.scene.extLight.uExteriorDirectColorDir; + param_13.extLight.adtSpecMult = _747.scene.extLight.adtSpecMult; + InteriorLightParam param_14 = InteriorLightParam(vec4(0.0), vec4(0.0, 0.0, 0.0, 1.0)); + vec3 param_15 = vVertexLighting; + vec4 finalColor = vec4(calcLight(param_9, param_10, param_11, param_12, param_13, param_14, param_15, vec3(0.0), vec3(0.0)), 1.0); float specBlend = final.w; - vec3 halfVec = -normalize(_621.scene.extLight.uExteriorDirectColorDir.xyz + normalize(vPosition)); - vec3 lSpecular = _621.scene.extLight.uExteriorDirectColor.xyz * pow(max(0.0, dot(halfVec, vNormal)), 20.0); - vec3 specTerm = vec3(specBlend) * lSpecular; - vec3 _699 = finalColor.xyz + specTerm; - finalColor = vec4(_699.x, _699.y, _699.z, finalColor.w); + vec3 halfVec = -normalize(_747.scene.extLight.uExteriorDirectColorDir.xyz + normalize(vPosition)); + vec3 lSpecular = _747.scene.extLight.uExteriorDirectColor.xyz * pow(max(0.0, dot(halfVec, vNormal)), 20.0); + vec3 specTerm = (vec3(specBlend) * lSpecular) * _747.scene.extLight.adtSpecMult.x; + vec3 _830 = finalColor.xyz + specTerm; + finalColor = vec4(_830.x, _830.y, _830.z, finalColor.w); PSFog arg; - arg.densityParams = _621.fogData.densityParams; - arg.heightPlane = _621.fogData.heightPlane; - arg.color_and_heightRate = _621.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _621.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _621.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _621.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _621.fogData.sunPercentage; - vec3 param_7 = finalColor.xyz; - vec3 param_8 = vPosition; - vec3 param_9 = _621.scene.extLight.uExteriorDirectColorDir.xyz; - vec3 _732 = makeFog(arg, param_7, param_8, param_9); - finalColor = vec4(_732.x, _732.y, _732.z, finalColor.w); + arg.densityParams = _747.fogData.densityParams; + arg.heightPlane = _747.fogData.heightPlane; + arg.color_and_heightRate = _747.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _747.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _747.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _747.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _747.fogData.sunPercentage; + vec4 param_16 = finalColor; + vec3 param_17 = vPosition; + vec3 param_18 = _747.scene.extLight.uExteriorDirectColorDir.xyz; + int param_19 = 0; + finalColor = makeFog(arg, param_16, param_17, param_18, param_19); finalColor.w = 1.0; gl_FragData[0] = finalColor; } @@ -336,6 +386,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -371,7 +422,7 @@ struct meshWideBlockVS vec4 uPos; }; -uniform meshWideBlockVS _131; +uniform meshWideBlockVS _139; attribute float aIndex; attribute vec3 aHeight; @@ -410,69 +461,6 @@ void main() } -#version 100 - -struct meshWideBlockPS -{ - vec4 color; -}; - -uniform meshWideBlockPS _12; - -uniform sampler2D uTexture; - -varying vec2 vTextCoords; -varying vec3 vPosition; - -void main() -{ - gl_FragData[0] = vec4(_12.color.xyz * texture2D(uTexture, vTextCoords).xyz, 0.699999988079071044921875); -} - - -#version 100 - -struct SceneExteriorLight -{ - vec4 uExteriorAmbientColor; - vec4 uExteriorHorizontAmbientColor; - vec4 uExteriorGroundAmbientColor; - vec4 uExteriorDirectColor; - vec4 uExteriorDirectColorDir; -}; - -struct SceneWideParams -{ - mat4 uLookAtMat; - mat4 uPMatrix; - vec4 uViewUp; - vec4 uInteriorSunDir; - SceneExteriorLight extLight; -}; - -struct sceneWideBlockVSPS -{ - SceneWideParams scene; -}; - -uniform sceneWideBlockVSPS _27; - -attribute vec4 aPositionTransp; -varying vec2 vTextCoords; -varying vec3 vPosition; -attribute vec2 aTexCoord; - -void main() -{ - vec4 aPositionVec4 = vec4(aPositionTransp.xyz, 1.0); - mat4 cameraMatrix = _27.scene.uLookAtMat; - vec4 cameraPoint = cameraMatrix * aPositionVec4; - vTextCoords = aPositionVec4.xy / vec2(33.333332061767578125); - gl_Position = _27.scene.uPMatrix * cameraPoint; - vPosition = cameraPoint.xyz; -} - - #version 100 struct modelWideBlockVS @@ -716,21 +704,22 @@ void main() struct meshWideBlockVS { - float uWidth; - float uHeight; - float uX; - float uY; + vec4 uWidth_uHeight_uX_uY; }; -uniform meshWideBlockVS _36; +uniform meshWideBlockVS _12; varying vec2 texCoord; attribute vec2 position; void main() { + float uWidth = _12.uWidth_uHeight_uX_uY.x; + float uHeight = _12.uWidth_uHeight_uX_uY.y; + float uX = _12.uWidth_uHeight_uX_uY.z; + float uY = _12.uWidth_uHeight_uX_uY.w; texCoord = (position * 0.5) + vec2(0.5); - gl_Position = vec4((((((position.x + 1.0) / 2.0) * _36.uWidth) + _36.uX) * 2.0) - 1.0, (((((position.y + 1.0) / 2.0) * _36.uHeight) + _36.uY) * 2.0) - 1.0, 0.5, 1.0); + gl_Position = vec4((((((position.x + 1.0) / 2.0) * uWidth) + uX) * 2.0) - 1.0, (((((position.y + 1.0) / 2.0) * uHeight) + uY) * 2.0) - 1.0, 0.5, 1.0); } @@ -806,6 +795,7 @@ void main() struct modelWideBlockVS { mat4 ProjMtx; + vec4 uiScale; }; uniform modelWideBlockVS _30; @@ -820,7 +810,7 @@ void main() { Frag_UV = UV; Frag_Color = Color; - gl_Position = _30.ProjMtx * vec4(Position, 0.0, 1.0); + gl_Position = _30.ProjMtx * vec4(Position * _30.uiScale.x, 0.0, 1.0); } @@ -837,6 +827,9 @@ struct PSFog vec4 sunPercentage; }; +const vec3 _37[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _44[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + struct SceneExteriorLight { vec4 uExteriorAmbientColor; @@ -844,6 +837,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -858,10 +852,10 @@ struct SceneWideParams struct meshWideBlockPS { vec4 uAlphaTestv; - ivec4 uPixelShaderv; + ivec4 uPixelShaderBlendModev; }; -uniform meshWideBlockPS _212; +uniform meshWideBlockPS _277; struct sceneWideBlockVSPS { @@ -869,7 +863,7 @@ struct sceneWideBlockVSPS PSFog fogData; }; -uniform sceneWideBlockVSPS _378; +uniform sceneWideBlockVSPS _485; uniform sampler2D uTexture; uniform sampler2D uTexture2; @@ -881,7 +875,12 @@ varying vec2 vTexcoord2; varying vec4 vColor; varying vec3 vPosition; -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) +vec3 validateFogColor(vec3 fogColor, int blendMode) +{ + return mix(fogColor, _37[blendMode], vec3(_44[blendMode])); +} + +vec4 makeFog(PSFog fogData, vec4 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace, int blendMode) { vec4 l_densityParams = fogData.densityParams; vec4 l_heightPlane = fogData.heightPlane; @@ -900,24 +899,38 @@ vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInVie float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); float finalFog = mix(expFog, expFogHeight, heightFog); float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; + float alpha = 1.0; + bool _139 = blendMode == 13; + if (_139) + { + alpha = min(finalFog, endFadeFog); + } + vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + vec3 endColor = validateFogColor(param, param_1); vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; float end2 = vLength / l_heightColor_and_endFogDistance.w; float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _162 = nDotSun > 0.0; - if (_162) + bool _218 = nDotSun > 0.0; + if (_218) { nDotSun = (nDotSun * nDotSun) * nDotSun; fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } void main() @@ -925,24 +938,24 @@ void main() vec4 tex = texture2D(uTexture, vTexcoord0); vec4 tex2 = texture2D(uTexture2, vTexcoord1); vec4 tex3 = texture2D(uTexture3, vTexcoord2); - float uAlphaTest = _212.uAlphaTestv.x; - bool _219 = tex.w < uAlphaTest; - if (_219) + float uAlphaTest = _277.uAlphaTestv.x; + bool _284 = tex.w < uAlphaTest; + if (_284) { discard; } vec4 finalColor = vec4((tex * vColor).xyz, tex.w * vColor.w); - int uNonOptPixelShader = _212.uPixelShaderv.x; - bool _246 = uNonOptPixelShader == 0; - if (_246) + int uNonOptPixelShader = _277.uPixelShaderBlendModev.x; + bool _310 = uNonOptPixelShader == 0; + if (_310) { vec3 matDiffuse = vColor.xyz * tex.xyz; finalColor = vec4(matDiffuse, tex.w * vColor.w); } else { - bool _267 = uNonOptPixelShader == 1; - if (_267) + bool _331 = uNonOptPixelShader == 1; + if (_331) { vec4 textureMod = tex * tex2; float texAlpha = textureMod.w * tex3.w; @@ -952,8 +965,8 @@ void main() } else { - bool _299 = uNonOptPixelShader == 2; - if (_299) + bool _363 = uNonOptPixelShader == 2; + if (_363) { vec4 textureMod_1 = (tex * tex2) * tex3; float texAlpha_1 = textureMod_1.w; @@ -963,8 +976,8 @@ void main() } else { - bool _330 = uNonOptPixelShader == 3; - if (_330) + bool _394 = uNonOptPixelShader == 3; + if (_394) { vec4 textureMod_2 = (tex * tex2) * tex3; float texAlpha_2 = textureMod_2.w; @@ -974,33 +987,42 @@ void main() } else { - bool _361 = uNonOptPixelShader == 4; - if (_361) + bool _425 = uNonOptPixelShader == 4; + if (_425) { + float t0_973 = tex.x; + float t1_978 = tex2.y; + float t2_983 = tex3.z; + float textureMod_986 = ((t0_973 * t1_978) * t2_983) * 4.0; + float depthScale_991 = 1.0 - clamp(vPosition.z * 0.00999999977648258209228515625, 0.0, 1.0); + float textureMod_992 = textureMod_986 * depthScale_991; + float height_995 = textureMod_992 * vColor.x; + float alpha_997 = textureMod_992 * vColor.w; + finalColor = vec4(height_995, 0.0, 0.0, alpha_997); } } } } } - bool _367 = finalColor.w < uAlphaTest; - if (_367) + bool _474 = finalColor.w < uAlphaTest; + if (_474) { discard; } - vec3 sunDir = _378.scene.extLight.uExteriorDirectColorDir.xyz; + vec3 sunDir = _485.scene.extLight.uExteriorDirectColorDir.xyz; PSFog arg; - arg.densityParams = _378.fogData.densityParams; - arg.heightPlane = _378.fogData.heightPlane; - arg.color_and_heightRate = _378.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _378.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _378.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _378.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _378.fogData.sunPercentage; - vec3 param = finalColor.xyz; + arg.densityParams = _485.fogData.densityParams; + arg.heightPlane = _485.fogData.heightPlane; + arg.color_and_heightRate = _485.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _485.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _485.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _485.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _485.fogData.sunPercentage; + vec4 param = finalColor; vec3 param_1 = vPosition; vec3 param_2 = sunDir; - vec3 _413 = makeFog(arg, param, param_1, param_2); - finalColor = vec4(_413.x, _413.y, _413.z, finalColor.w); + int param_3 = _277.uPixelShaderBlendModev.y; + finalColor = makeFog(arg, param, param_1, param_2, param_3); gl_FragData[0] = finalColor; } @@ -1014,6 +1036,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -1077,6 +1100,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -1105,6 +1129,9 @@ struct PSFog vec4 sunPercentage; }; +const vec3 _215[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _222[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + struct LocalLight { vec4 color; @@ -1114,12 +1141,13 @@ struct LocalLight struct meshWideBlockPS { - ivec4 PixelShader_UnFogged_IsAffectedByLight; + ivec4 PixelShader_UnFogged_IsAffectedByLight_blendMode; vec4 uFogColorAndAlphaTest; + vec4 uTexSampleAlpha; vec4 uPcColor; }; -uniform meshWideBlockPS _409; +uniform meshWideBlockPS _472; struct modelWideBlockPS { @@ -1129,7 +1157,7 @@ struct modelWideBlockPS vec4 interiorExteriorBlend; }; -uniform modelWideBlockPS _433; +uniform modelWideBlockPS _495; struct sceneWideBlockVSPS { @@ -1137,7 +1165,7 @@ struct sceneWideBlockVSPS PSFog fogData; }; -uniform sceneWideBlockVSPS _472; +uniform sceneWideBlockVSPS _534; struct modelWideBlockVS { @@ -1145,7 +1173,7 @@ struct modelWideBlockVS mat4 uBoneMatrixes[220]; }; -uniform modelWideBlockVS _480; +uniform modelWideBlockVS _542; uniform sampler2D uTexture; uniform sampler2D uTexture2; @@ -1162,24 +1190,24 @@ varying vec3 vNormal; vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, vec3 accumLight, vec3 precomputedLight, vec3 specular) { vec3 localDiffuse = accumLight; - bool _42 = !applyLight; - if (_42) + bool _51 = !applyLight; + if (_51) { return matDiffuse; } vec3 lDiffuse = vec3(0.0); vec3 normalizedN = normalize(vNormal_1); - bool _59 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + bool _67 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; vec3 currColor; - if (_59) + if (_67) { float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; - bool _97 = nDotUp >= 0.0; - if (_97) + bool _104 = nDotUp >= 0.0; + if (_104) { currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); } @@ -1192,14 +1220,14 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE lDiffuse = sceneParams.extLight.uExteriorDirectColor.xyz * nDotL; currColor = mix(groundColor, skyColor, vec3(0.5 + (0.5 * nDotL))); } - bool _137 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; - if (_137) + bool _144 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; + if (_144) { float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; - bool _161 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - if (_161) + bool _168 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + if (_168) { lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); @@ -1217,7 +1245,12 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; } -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) +vec3 validateFogColor(vec3 fogColor, int blendMode) +{ + return mix(fogColor, _215[blendMode], vec3(_222[blendMode])); +} + +vec4 makeFog(PSFog fogData, vec4 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace, int blendMode) { vec4 l_densityParams = fogData.densityParams; vec4 l_heightPlane = fogData.heightPlane; @@ -1236,24 +1269,38 @@ vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInVie float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); float finalFog = mix(expFog, expFogHeight, heightFog); float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; + float alpha = 1.0; + bool _310 = blendMode == 13; + if (_310) + { + alpha = min(finalFog, endFadeFog); + } + vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + vec3 endColor = validateFogColor(param, param_1); vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; float end2 = vLength / l_heightColor_and_endFogDistance.w; float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _334 = nDotSun > 0.0; - if (_334) + bool _388 = nDotSun > 0.0; + if (_388) { nDotSun = (nDotSun * nDotSun) * nDotSun; fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } void main() @@ -1269,26 +1316,26 @@ void main() vec4 tex4WithTextCoord2 = texture2D(uTexture4, texCoord2); vec4 finalColor = vec4(0.0); vec4 meshResColor = vDiffuseColor; - bool _413 = _409.PixelShader_UnFogged_IsAffectedByLight.z == 1; + bool _476 = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.z == 1; vec3 accumLight; - if (_413) + if (_476) { vec3 vPos3 = vPosition; vec3 vNormal3 = normalize(vNormal); vec3 lightColor = vec3(0.0); - int count = int(_433.pc_lights[0].attenuation.w); + int count = int(_495.pc_lights[0].attenuation.w); LocalLight lightRecord; for (int index = 0; index < 4; index++) { - bool _449 = index >= _433.lightCountAndBcHack.x; - if (_449) + bool _511 = index >= _495.lightCountAndBcHack.x; + if (_511) { break; } - lightRecord.color = _433.pc_lights[index].color; - lightRecord.position = _433.pc_lights[index].position; - lightRecord.attenuation = _433.pc_lights[index].attenuation; - vec3 vectorToLight = (_472.scene.uLookAtMat * (_480.uPlacementMat * lightRecord.position)).xyz - vPos3; + lightRecord.color = _495.pc_lights[index].color; + lightRecord.position = _495.pc_lights[index].position; + lightRecord.attenuation = _495.pc_lights[index].attenuation; + vec3 vectorToLight = (_534.scene.uLookAtMat * (_542.uPlacementMat * lightRecord.position)).xyz - vPos3; float distanceToLightSqr = dot(vectorToLight, vectorToLight); float distanceToLightInv = inversesqrt(distanceToLightSqr); float distanceToLight = distanceToLightSqr * distanceToLightInv; @@ -1298,9 +1345,9 @@ void main() vec3 attenuatedColor = lightRecord.color.xyz * attenuation; lightColor += vec3((attenuatedColor * attenuatedColor) * diffuseTerm1); } - vec3 _547 = clamp(lightColor, vec3(0.0), vec3(1.0)); - meshResColor = vec4(_547.x, _547.y, _547.z, meshResColor.w); - accumLight = mix(lightColor, meshResColor.xyz, vec3(float(_433.lightCountAndBcHack.y))); + vec3 _609 = clamp(lightColor, vec3(0.0), vec3(1.0)); + meshResColor = vec4(_609.x, _609.y, _609.z, meshResColor.w); + accumLight = mix(lightColor, meshResColor.xyz, vec3(float(_495.lightCountAndBcHack.y))); } float finalOpacity = 0.0; vec3 specular = vec3(0.0); @@ -1309,285 +1356,282 @@ void main() genericParams[0] = vec4(1.0); genericParams[1] = vec4(1.0); genericParams[2] = vec4(1.0); - int uPixelShader = _409.PixelShader_UnFogged_IsAffectedByLight.x; - bool _574 = uPixelShader == 0; + bool canDiscard = false; + float discardAlpha = 1.0; + int uPixelShader = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.x; + bool _638 = uPixelShader == 0; vec3 matDiffuse; - float opacity; #if (FRAGMENTSHADER == 0) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _595 = uPixelShader == 1; + bool _651 = uPixelShader == 1; #if (FRAGMENTSHADER == 1) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _615 = uPixelShader == 2; + bool _665 = uPixelShader == 2; #if (FRAGMENTSHADER == 2) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = tex2.w * vDiffuseColor.w; - opacity = tex2.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex2.w; + canDiscard = true; #endif - bool _643 = uPixelShader == 3; + bool _681 = uPixelShader == 3; #if (FRAGMENTSHADER == 3) matDiffuse = (((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz) * 2.0; - opacity = (tex2.w * 2.0) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex2.w * 2.0; + canDiscard = true; #endif - bool _668 = uPixelShader == 4; + bool _699 = uPixelShader == 4; #if (FRAGMENTSHADER == 4) matDiffuse = (((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz) * 2.0; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _689 = uPixelShader == 5; + bool _714 = uPixelShader == 5; #if (FRAGMENTSHADER == 5) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _709 = uPixelShader == 6; + bool _728 = uPixelShader == 6; #if (FRAGMENTSHADER == 6) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = (tex.w * tex2.w) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w * tex2.w; + canDiscard = true; #endif - bool _736 = uPixelShader == 7; + bool _748 = uPixelShader == 7; #if (FRAGMENTSHADER == 7) matDiffuse = (((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz) * 2.0; - opacity = ((tex.w * tex2.w) * 2.0) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = (tex.w * tex2.w) * 2.0; + canDiscard = true; #endif - bool _765 = uPixelShader == 8; + bool _770 = uPixelShader == 8; #if (FRAGMENTSHADER == 8) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = (tex.w + tex2.w) * vDiffuseColor.w; + discardAlpha = tex.w + tex2.w; + canDiscard = true; specular = tex2.xyz; - finalOpacity = opacity * visParams.x; #endif - bool _791 = uPixelShader == 9; + bool _789 = uPixelShader == 9; #if (FRAGMENTSHADER == 9) matDiffuse = (((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz) * 2.0; - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _816 = uPixelShader == 10; + bool _807 = uPixelShader == 10; #if (FRAGMENTSHADER == 10) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = tex.w * vDiffuseColor.w; + discardAlpha = tex.w; + canDiscard = true; specular = tex2.xyz; - finalOpacity = opacity * visParams.x; #endif - bool _839 = uPixelShader == 11; + bool _823 = uPixelShader == 11; #if (FRAGMENTSHADER == 11) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _863 = uPixelShader == 12; + bool _840 = uPixelShader == 12; #if (FRAGMENTSHADER == 12) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix((tex.xyz * tex2.xyz) * 2.0, tex.xyz, vec3(tex.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _891 = uPixelShader == 13; + bool _861 = uPixelShader == 13; #if (FRAGMENTSHADER == 13) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; specular = tex2.xyz * tex2.w; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _914 = uPixelShader == 14; + bool _878 = uPixelShader == 14; #if (FRAGMENTSHADER == 14) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; specular = (tex2.xyz * tex2.w) * (1.0 - tex.w); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _941 = uPixelShader == 15; + bool _899 = uPixelShader == 15; #if (FRAGMENTSHADER == 15) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix((tex.xyz * tex2.xyz) * 2.0, tex.xyz, vec3(tex.w)); - specular = (tex3.xyz * tex3.w) * genericParams[0].z; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + specular = (tex3.xyz * tex3.w) * _472.uTexSampleAlpha.z; #endif - bool _977 = uPixelShader == 16; + bool _929 = uPixelShader == 16; #if (FRAGMENTSHADER == 16) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = tex.w * vDiffuseColor.w; + discardAlpha = tex.w; + canDiscard = true; specular = tex2.xyz * tex2.w; - finalOpacity = opacity * visParams.x; #endif - bool _1003 = uPixelShader == 17; + bool _948 = uPixelShader == 17; #if (FRAGMENTSHADER == 17) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = (tex.w + (tex2.w * (((0.300000011920928955078125 * tex2.x) + (0.589999973773956298828125 * tex2.y)) + (0.10999999940395355224609375 * tex2.z)))) * vDiffuseColor.w; + discardAlpha = tex.w + (tex2.w * (((0.300000011920928955078125 * tex2.x) + (0.589999973773956298828125 * tex2.y)) + (0.10999999940395355224609375 * tex2.z))); + canDiscard = true; specular = (tex2.xyz * tex2.w) * (1.0 - tex.w); - finalOpacity = opacity * visParams.x; #endif - bool _1051 = uPixelShader == 18; + bool _989 = uPixelShader == 18; #if (FRAGMENTSHADER == 18) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex.xyz, tex2.xyz, vec3(tex2.w)), tex.xyz, vec3(tex.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1081 = uPixelShader == 19; + bool _1013 = uPixelShader == 19; #if (FRAGMENTSHADER == 19) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix((tex.xyz * tex2.xyz) * 2.0, tex3.xyz, vec3(tex3.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1109 = uPixelShader == 20; + bool _1035 = uPixelShader == 20; #if (FRAGMENTSHADER == 20) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - specular = (tex2.xyz * tex2.w) * genericParams[0].y; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + specular = (tex2.xyz * tex2.w) * _472.uTexSampleAlpha.y; #endif - bool _1135 = uPixelShader == 21; + bool _1055 = uPixelShader == 21; #if (FRAGMENTSHADER == 21) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = (tex.w + tex2.w) * vDiffuseColor.w; + discardAlpha = tex.w + tex2.w; + canDiscard = true; specular = tex2.xyz * (1.0 - tex.w); - finalOpacity = opacity * visParams.x; #endif - bool _1165 = uPixelShader == 22; + bool _1078 = uPixelShader == 22; #if (FRAGMENTSHADER == 22) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz * tex2.xyz, tex.xyz, vec3(tex.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1192 = uPixelShader == 23; + bool _1099 = uPixelShader == 23; #if (FRAGMENTSHADER == 23) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = tex.w * vDiffuseColor.w; - specular = (tex2.xyz * tex2.w) * genericParams[0].y; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; + specular = (tex2.xyz * tex2.w) * _472.uTexSampleAlpha.y; #endif - bool _1221 = uPixelShader == 24; + bool _1121 = uPixelShader == 24; #if (FRAGMENTSHADER == 24) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz, tex2.xyz, vec3(tex2.w)); - specular = (tex.xyz * tex.w) * genericParams[0].x; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + specular = (tex.xyz * tex.w) * _472.uTexSampleAlpha.x; #endif - bool _1253 = uPixelShader == 25; + bool _1147 = uPixelShader == 25; #if (FRAGMENTSHADER == 25) - float glowOpacity = clamp(tex3.w * genericParams[0].z, 0.0, 1.0); + float glowOpacity = clamp(tex3.w * _472.uTexSampleAlpha.z, 0.0, 1.0); matDiffuse = ((vDiffuseColor.xyz * 2.0) * mix((tex.xyz * tex2.xyz) * 2.0, tex.xyz, vec3(tex.w))) * (1.0 - glowOpacity); specular = tex3.xyz * glowOpacity; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1295 = uPixelShader == 26; + bool _1183 = uPixelShader == 26; #if (FRAGMENTSHADER == 26) - matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(genericParams[0].z, 0.0, 1.0))).xyz; - opacity = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(genericParams[0].z, 0.0, 1.0))).w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.z, 0.0, 1.0))).xyz; + discardAlpha = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.z, 0.0, 1.0))).w; + canDiscard = true; #endif - bool _1340 = uPixelShader == 27; + bool _1221 = uPixelShader == 27; #if (FRAGMENTSHADER == 27) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix((tex.xyz * tex2.xyz) * 2.0, tex3.xyz, vec3(tex3.w)), tex.xyz, vec3(tex.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1374 = uPixelShader == 28; + bool _1249 = uPixelShader == 28; #if (FRAGMENTSHADER == 28) - matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(genericParams[0].z, 0.0, 1.0))).xyz; - opacity = (mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(genericParams[0].z, 0.0, 1.0))).w * tex4WithTextCoord2.w) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.z, 0.0, 1.0))).xyz; + discardAlpha = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.z, 0.0, 1.0))).w * tex4WithTextCoord2.w; + canDiscard = true; #endif - bool _1422 = uPixelShader == 29; + bool _1290 = uPixelShader == 29; #if (FRAGMENTSHADER == 29) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz, tex2.xyz, vec3(tex2.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1446 = uPixelShader == 30; + bool _1308 = uPixelShader == 30; #if (FRAGMENTSHADER == 30) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz * mix(genericParams[0].xyz, tex2.xyz * genericParams[1].xyz, vec3(tex2.w)), tex3.xyz * genericParams[2].xyz, vec3(tex3.w)); - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _1491 = uPixelShader == 31; + bool _1346 = uPixelShader == 31; #if (FRAGMENTSHADER == 31) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * mix(genericParams[0].xyz, tex2.xyz * genericParams[1].xyz, vec3(tex2.w)); - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _1526 = uPixelShader == 32; + bool _1374 = uPixelShader == 32; #if (FRAGMENTSHADER == 32) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz * mix(genericParams[0].xyz, tex2.xyz * genericParams[1].xyz, vec3(tex2.w)), tex3.xyz * genericParams[2].xyz, vec3(tex3.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1568 = uPixelShader == 33; + bool _1410 = uPixelShader == 33; #if (FRAGMENTSHADER == 33) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = (tex.w * vDiffuseColor.w) * visParams.x; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _1592 = uPixelShader == 34; + bool _1424 = uPixelShader == 34; #if (FRAGMENTSHADER == 34) - finalColor = vec4(1.0); + discardAlpha = tex.w; + canDiscard = true; #endif - bool _1598 = uPixelShader == 35; + bool _1432 = uPixelShader == 35; #if (FRAGMENTSHADER == 35) matDiffuse = (vDiffuseColor.xyz * 2.0) * (((tex * tex2) * tex3) * genericParams[0]).xyz; - opacity = (((tex * tex2) * tex3) * genericParams[0]).w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = (((tex * tex2) * tex3) * genericParams[0]).w; + canDiscard = true; #endif - bool _1633 = uPixelShader == 36; + bool _1460 = uPixelShader == 36; #if (FRAGMENTSHADER == 36) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = (tex.w * tex2.w) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w * tex2.w; + canDiscard = true; #endif + int blendMode = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.w; + bool _1481 = blendMode == 13; + if (_1481) + { + finalOpacity = discardAlpha * vDiffuseColor.w; + } + else + { + bool _1491 = blendMode == 1; + if (_1491) + { + finalOpacity = vDiffuseColor.w; + bool _1500 = canDiscard && (discardAlpha < 0.501960813999176025390625); + if (_1500) + { + discard; + } + finalOpacity = vDiffuseColor.w; + } + else + { + bool _1508 = blendMode == 0; + if (_1508) + { + finalOpacity = vDiffuseColor.w; + } + else + { + finalOpacity = discardAlpha * vDiffuseColor.w; + } + } + } vec3 param = matDiffuse; vec3 param_1 = vNormal; - bool param_2 = _409.PixelShader_UnFogged_IsAffectedByLight.z > 0; - float param_3 = _433.interiorExteriorBlend.x; + bool param_2 = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.z > 0; + float param_3 = _495.interiorExteriorBlend.x; SceneWideParams param_4; - param_4.uLookAtMat = _472.scene.uLookAtMat; - param_4.uPMatrix = _472.scene.uPMatrix; - param_4.uViewUp = _472.scene.uViewUp; - param_4.uInteriorSunDir = _472.scene.uInteriorSunDir; - param_4.extLight.uExteriorAmbientColor = _472.scene.extLight.uExteriorAmbientColor; - param_4.extLight.uExteriorHorizontAmbientColor = _472.scene.extLight.uExteriorHorizontAmbientColor; - param_4.extLight.uExteriorGroundAmbientColor = _472.scene.extLight.uExteriorGroundAmbientColor; - param_4.extLight.uExteriorDirectColor = _472.scene.extLight.uExteriorDirectColor; - param_4.extLight.uExteriorDirectColorDir = _472.scene.extLight.uExteriorDirectColorDir; + param_4.uLookAtMat = _534.scene.uLookAtMat; + param_4.uPMatrix = _534.scene.uPMatrix; + param_4.uViewUp = _534.scene.uViewUp; + param_4.uInteriorSunDir = _534.scene.uInteriorSunDir; + param_4.extLight.uExteriorAmbientColor = _534.scene.extLight.uExteriorAmbientColor; + param_4.extLight.uExteriorHorizontAmbientColor = _534.scene.extLight.uExteriorHorizontAmbientColor; + param_4.extLight.uExteriorGroundAmbientColor = _534.scene.extLight.uExteriorGroundAmbientColor; + param_4.extLight.uExteriorDirectColor = _534.scene.extLight.uExteriorDirectColor; + param_4.extLight.uExteriorDirectColorDir = _534.scene.extLight.uExteriorDirectColorDir; + param_4.extLight.adtSpecMult = _534.scene.extLight.adtSpecMult; InteriorLightParam param_5; - param_5.uInteriorAmbientColorAndApplyInteriorLight = _433.intLight.uInteriorAmbientColorAndApplyInteriorLight; - param_5.uInteriorDirectColorAndApplyExteriorLight = _433.intLight.uInteriorDirectColorAndApplyExteriorLight; + param_5.uInteriorAmbientColorAndApplyInteriorLight = _495.intLight.uInteriorAmbientColorAndApplyInteriorLight; + param_5.uInteriorDirectColorAndApplyExteriorLight = _495.intLight.uInteriorDirectColorAndApplyExteriorLight; vec3 param_6 = accumLight; finalColor = vec4(calcLight(param, param_1, param_2, param_3, param_4, param_5, param_6, vec3(0.0), specular), finalOpacity); - bool _1715 = finalColor.w < _409.uFogColorAndAlphaTest.w; - if (_1715) - { - discard; - } - int uUnFogged = _409.PixelShader_UnFogged_IsAffectedByLight.y; - bool _1723 = uUnFogged == 0; - if (_1723) + int uUnFogged = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.y; + bool _1578 = uUnFogged == 0; + if (_1578) { - vec3 sunDir = mix(_472.scene.uInteriorSunDir, _472.scene.extLight.uExteriorDirectColorDir, vec4(_433.interiorExteriorBlend.x)).xyz; + vec3 sunDir = mix(_534.scene.uInteriorSunDir, _534.scene.extLight.uExteriorDirectColorDir, vec4(_495.interiorExteriorBlend.x)).xyz; PSFog arg; - arg.densityParams = _472.fogData.densityParams; - arg.heightPlane = _472.fogData.heightPlane; - arg.color_and_heightRate = _472.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _472.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _472.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _472.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _472.fogData.sunPercentage; - vec3 param_7 = finalColor.xyz; + arg.densityParams = _534.fogData.densityParams; + arg.heightPlane = _534.fogData.heightPlane; + arg.color_and_heightRate = _534.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _534.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _534.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _534.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _534.fogData.sunPercentage; + vec4 param_7 = finalColor; vec3 param_8 = vPosition; vec3 param_9 = sunDir; - vec3 _1765 = makeFog(arg, param_7, param_8, param_9); - finalColor = vec4(_1765.x, _1765.y, _1765.z, finalColor.w); + int param_10 = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.w; + finalColor = makeFog(arg, param_7, param_8, param_9, param_10); } gl_FragData[0] = finalColor; } @@ -1602,6 +1646,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -1917,6 +1962,9 @@ struct PSFog vec4 sunPercentage; }; +const vec3 _37[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _44[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + struct SceneExteriorLight { vec4 uExteriorAmbientColor; @@ -1924,6 +1972,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -1935,13 +1984,22 @@ struct SceneWideParams SceneExteriorLight extLight; }; +struct meshWideBlockPS +{ + vec4 uAlphaTestScalev; + ivec4 uPixelShaderv; + vec4 uTextureTranslate; +}; + +uniform meshWideBlockPS _256; + struct sceneWideBlockVSPS { SceneWideParams scene; PSFog fogData; }; -uniform sceneWideBlockVSPS _221; +uniform sceneWideBlockVSPS _304; uniform sampler2D uTexture; @@ -1949,7 +2007,12 @@ varying vec2 vTexcoord0; varying vec4 vColor; varying vec3 vPosition; -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) +vec3 validateFogColor(vec3 fogColor, int blendMode) +{ + return mix(fogColor, _37[blendMode], vec3(_44[blendMode])); +} + +vec4 makeFog(PSFog fogData, vec4 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace, int blendMode) { vec4 l_densityParams = fogData.densityParams; vec4 l_heightPlane = fogData.heightPlane; @@ -1968,44 +2031,60 @@ vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInVie float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); float finalFog = mix(expFog, expFogHeight, heightFog); float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; + float alpha = 1.0; + bool _139 = blendMode == 13; + if (_139) + { + alpha = min(finalFog, endFadeFog); + } + vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + vec3 endColor = validateFogColor(param, param_1); vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; float end2 = vLength / l_heightColor_and_endFogDistance.w; float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _162 = nDotSun > 0.0; - if (_162) + bool _218 = nDotSun > 0.0; + if (_218) { nDotSun = (nDotSun * nDotSun) * nDotSun; fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } void main() { - vec4 tex = texture2D(uTexture, vTexcoord0); + vec2 textCoordScale = _256.uAlphaTestScalev.yz; + vec2 texcoord = (vTexcoord0 * textCoordScale) + _256.uTextureTranslate.xy; + vec4 tex = texture2D(uTexture, texcoord); vec4 finalColor = vec4(vColor.xyz * tex.xyz, tex.w * vColor.w); - vec3 sunDir = _221.scene.extLight.uExteriorDirectColorDir.xyz; + vec3 sunDir = _304.scene.extLight.uExteriorDirectColorDir.xyz; PSFog arg; - arg.densityParams = _221.fogData.densityParams; - arg.heightPlane = _221.fogData.heightPlane; - arg.color_and_heightRate = _221.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _221.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _221.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _221.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _221.fogData.sunPercentage; - vec3 param = finalColor.xyz; + arg.densityParams = _304.fogData.densityParams; + arg.heightPlane = _304.fogData.heightPlane; + arg.color_and_heightRate = _304.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _304.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _304.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _304.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _304.fogData.sunPercentage; + vec4 param = finalColor; vec3 param_1 = vPosition; vec3 param_2 = sunDir; - vec3 _256 = makeFog(arg, param, param_1, param_2); - finalColor = vec4(_256.x, _256.y, _256.z, finalColor.w); + int param_3 = _256.uPixelShaderv.y; + finalColor = makeFog(arg, param, param_1, param_2, param_3); gl_FragData[0] = finalColor; } @@ -2019,6 +2098,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -2076,7 +2156,7 @@ varying vec4 vColor; void main() { - gl_FragData[0] = vec4(vColor.xyz, 1.0); + gl_FragData[0] = vec4(vColor.xyz, vColor.w); } @@ -2089,6 +2169,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -2100,19 +2181,31 @@ struct SceneWideParams SceneExteriorLight extLight; }; +struct PSFog +{ + vec4 densityParams; + vec4 heightPlane; + vec4 color_and_heightRate; + vec4 heightDensity_and_endColor; + vec4 sunAngle_and_sunColor; + vec4 heightColor_and_endFogDistance; + vec4 sunPercentage; +}; + struct sceneWideBlockVSPS { SceneWideParams scene; + PSFog fogData; }; -uniform sceneWideBlockVSPS _50; +uniform sceneWideBlockVSPS _26; struct meshWideBlockVS { vec4 skyColor[6]; }; -uniform meshWideBlockVS _82; +uniform meshWideBlockVS _67; attribute vec4 aPosition; varying vec4 vColor; @@ -2120,107 +2213,14 @@ varying vec4 vColor; void main() { vec3 inputPos = aPosition.xyz; - vec2 _20 = inputPos.xy / vec2(0.6875); - inputPos = vec3(_20.x, _20.y, inputPos.z); - bool _30 = inputPos.z > 0.0; - float _31; - if (_30) - { - _31 = inputPos.z / 0.292800009250640869140625; - } - else - { - _31 = inputPos.z; - } - inputPos.z = _31; - vec4 cameraPos = _50.scene.uLookAtMat * vec4(inputPos, 1.0); - vec3 _70 = cameraPos.xyz - _50.scene.uLookAtMat[3].xyz; - cameraPos = vec4(_70.x, _70.y, _70.z, cameraPos.w); + inputPos *= 33.33300018310546875; + vec4 cameraPos = _26.scene.uLookAtMat * vec4(inputPos, 1.0); + vec3 _46 = cameraPos.xyz - _26.scene.uLookAtMat[3].xyz; + cameraPos = vec4(_46.x, _46.y, _46.z, cameraPos.w); cameraPos.z = cameraPos.z; - vColor = vec4(_82.skyColor[int(aPosition.w)].xyz, 1.0); - gl_Position = _50.scene.uPMatrix * cameraPos; -} - - -#version 100 - -struct meshWideBlockPS -{ - ivec4 waterTypeV; -}; - -uniform meshWideBlockPS _12; - -uniform sampler2D uTexture; - -varying vec3 vPosition; - -void main() -{ - int waterType = _12.waterTypeV.x; - bool _22 = waterType == 13; - if (_22) - { - gl_FragData[0] = vec4(0.0, 0.0, 0.300000011920928955078125, 0.5); - } - else - { - bool _36 = waterType == 14; - if (_36) - { - gl_FragData[0] = vec4(0.0, 0.0, 0.800000011920928955078125, 0.800000011920928955078125); - } - else - { - bool _44 = waterType == 19; - if (_44) - { - gl_FragData[0] = vec4(0.300000011920928955078125, 0.0, 0.0, 0.5); - } - else - { - bool _51 = waterType == 20; - if (_51) - { - gl_FragData[0] = vec4(0.0, 0.5, 0.0, 0.5); - } - else - { - gl_FragData[0] = vec4(0.5); - } - } - } - } -} - - -#version 100 - -struct sceneWideBlockVSPS -{ - mat4 uLookAtMat; - mat4 uPMatrix; -}; - -uniform sceneWideBlockVSPS _24; - -struct modelWideBlockVS -{ - mat4 uPlacementMat; -}; - -uniform modelWideBlockVS _32; - -attribute vec3 aPosition; -varying vec3 vPosition; - -void main() -{ - vec4 aPositionVec4 = vec4(aPosition, 1.0); - mat4 cameraMatrix = _24.uLookAtMat * _32.uPlacementMat; - vec4 cameraPoint = cameraMatrix * aPositionVec4; - gl_Position = _24.uPMatrix * cameraPoint; - vPosition = cameraPoint.xyz; + vec4 vertPosInNDC = _26.scene.uPMatrix * cameraPos; + vColor = _67.skyColor[int(aPosition.w)]; + gl_Position = _26.scene.uPMatrix * cameraPos; } @@ -2233,6 +2233,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -2263,11 +2264,15 @@ struct PSFog struct meshWideBlockPS { - ivec4 UseLitColor_EnableAlpha_PixelShader; - vec4 FogColor_AlphaTest; + vec4 values0; + vec4 values1; + vec4 values2; + vec4 values3; + vec4 values4; + vec4 baseColor; }; -uniform meshWideBlockPS _383; +uniform meshWideBlockPS _219; struct sceneWideBlockVSPS { @@ -2275,48 +2280,65 @@ struct sceneWideBlockVSPS PSFog fogData; }; -uniform sceneWideBlockVSPS _848; +uniform sceneWideBlockVSPS _487; -struct modelWideBlockPS +struct modelWideBlockVS { - InteriorLightParam intLight; + mat4 uPlacementMat; + mat4 uBoneMatrixes[220]; }; -uniform modelWideBlockPS _852; +uniform modelWideBlockVS _570; -uniform sampler2D uTexture; -uniform sampler2D uTexture2; -uniform sampler2D uTexture3; +uniform sampler2D uNormalTex; +uniform sampler2D uNoise; +uniform sampler2D uWhiteWater; +uniform sampler2D uMask; +varying vec2 vTexCoord2_animated; +varying vec3 vPosition; +varying vec3 vNormal; varying vec2 vTexCoord; varying vec2 vTexCoord2; -varying vec2 vTexCoord3; -varying vec4 vColor; -varying vec4 vColor2; -varying vec3 vNormal; -varying vec4 vPosition; + +vec3 PerturbNormal(vec3 surf_pos, vec3 surf_norm) +{ + vec2 dBdUV = ((texture2D(uNormalTex, vTexCoord2_animated).xy * 2.0) - vec2(1.0)) * (_219.values3.x * 100.0); + vec2 duv1 = dFdx(vTexCoord2_animated); + vec2 duv2 = dFdy(vTexCoord2_animated); + vec3 vSigmaS = dFdx(surf_pos); + vec3 vSigmaT = dFdy(surf_pos); + vec3 vN = surf_norm; + vec3 vR1 = cross(vSigmaT, vN); + vec3 vR2 = cross(vN, vSigmaS); + float fDet = dot(vSigmaS, vR1); + float dBs = (dBdUV.x * duv1.x) + (dBdUV.y * duv1.y); + float dBt = (dBdUV.x * duv2.x) + (dBdUV.y * duv2.y); + vec3 vSurfGrad = ((vR1 * dBs) + (vR2 * dBt)) * sign(fDet); + return normalize((vN * abs(fDet)) - vSurfGrad); +} vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, vec3 accumLight, vec3 precomputedLight, vec3 specular) { vec3 localDiffuse = accumLight; - bool _42 = !applyLight; - if (_42) + bool _39 = !applyLight; + if (_39) { return matDiffuse; } vec3 lDiffuse = vec3(0.0); vec3 normalizedN = normalize(vNormal_1); - bool _59 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + bool _56 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; vec3 currColor; - if (_59) + if (_56) { float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; - bool _97 = nDotUp >= 0.0; - if (_97) + bool _94 = nDotUp >= 0.0; + if (_94) { currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); } @@ -2329,14 +2351,14 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE lDiffuse = sceneParams.extLight.uExteriorDirectColor.xyz * nDotL; currColor = mix(groundColor, skyColor, vec3(0.5 + (0.5 * nDotL))); } - bool _137 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; - if (_137) + bool _134 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; + if (_134) { float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; - bool _161 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - if (_161) + bool _158 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + if (_158) { lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); @@ -2354,183 +2376,680 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; } -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) +void main() { - vec4 l_densityParams = fogData.densityParams; - vec4 l_heightPlane = fogData.heightPlane; - vec4 l_color_and_heightRate = fogData.color_and_heightRate; - vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; - float start = l_densityParams.x; - float end = l_densityParams.y; - float density = l_densityParams.z; - float bias = l_densityParams.w; - float vLength = length(vertexInViewSpace); - float z = vLength - bias; - float expMax = max(0.0, z - start); - float expFog = 1.0 / exp(expMax * density); - float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); - float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; - float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); - float finalFog = mix(expFog, expFogHeight, heightFog); - float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; - vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; - float end2 = vLength / l_heightColor_and_endFogDistance.w; - float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); - fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); - float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); - nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _334 = nDotSun > 0.0; - if (_334) - { - nDotSun = (nDotSun * nDotSun) * nDotSun; - fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); - } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + vec3 param = vPosition; + vec3 param_1 = normalize(vNormal); + vec3 perturbedNormal = PerturbNormal(param, param_1); + vec2 vTexCoordNorm = vTexCoord / vec2(_219.values1.x); + float noise0 = texture2D(uNoise, vec2(vTexCoordNorm.x - _219.values1.z, (vTexCoordNorm.y - _219.values1.z) - _219.values2.z)).x; + float _noise1 = texture2D(uNoise, vec2((vTexCoordNorm.x - _219.values1.z) + 0.4180000126361846923828125, ((vTexCoordNorm.y + 0.3549999892711639404296875) + _219.values1.z) - _219.values2.z)).x; + float _noise2 = texture2D(uNoise, vec2((vTexCoordNorm.x + _219.values1.z) + 0.8650000095367431640625, ((vTexCoordNorm.y + 0.1480000019073486328125) - _219.values1.z) - _219.values2.z)).x; + float _noise3 = texture2D(uNoise, vec2((vTexCoordNorm.x + _219.values1.z) + 0.65100002288818359375, ((vTexCoordNorm.y + 0.75199997425079345703125) + _219.values1.z) - _219.values2.z)).x; + float noise_avr = abs(((noise0 + _noise1) + _noise2) + _noise3) * 0.25; + float noiseFinal = clamp(exp((_219.values0.x * log2(noise_avr)) * 2.2000000476837158203125) * _219.values0.y, 0.0, 1.0); + vec4 whiteWater_val = texture2D(uWhiteWater, vTexCoord2_animated); + vec4 mask_val_0 = texture2D(uMask, vTexCoord); + vec4 mask_val_1 = texture2D(uMask, vec2(vTexCoord.x, vTexCoord.y + _219.values3.z)); + float mix_alpha = clamp((((((whiteWater_val.w * noiseFinal) - (mask_val_1.y * mask_val_0.x)) * 2.0) + _219.values0.z) * ((_219.values0.w * 2.0) + 1.0)) - _219.values0.w, 0.0, 1.0); + vec4 whiteWater_val_baseColor_mix = mix(_219.baseColor, whiteWater_val, vec4(mix_alpha)); + vec3 param_2 = whiteWater_val_baseColor_mix.xyz; + vec3 param_3 = perturbedNormal; + bool param_4 = true; + float param_5 = 0.0; + SceneWideParams param_6; + param_6.uLookAtMat = _487.scene.uLookAtMat; + param_6.uPMatrix = _487.scene.uPMatrix; + param_6.uViewUp = _487.scene.uViewUp; + param_6.uInteriorSunDir = _487.scene.uInteriorSunDir; + param_6.extLight.uExteriorAmbientColor = _487.scene.extLight.uExteriorAmbientColor; + param_6.extLight.uExteriorHorizontAmbientColor = _487.scene.extLight.uExteriorHorizontAmbientColor; + param_6.extLight.uExteriorGroundAmbientColor = _487.scene.extLight.uExteriorGroundAmbientColor; + param_6.extLight.uExteriorDirectColor = _487.scene.extLight.uExteriorDirectColor; + param_6.extLight.uExteriorDirectColorDir = _487.scene.extLight.uExteriorDirectColorDir; + param_6.extLight.adtSpecMult = _487.scene.extLight.adtSpecMult; + InteriorLightParam param_7 = InteriorLightParam(vec4(0.0), vec4(0.0, 0.0, 0.0, 1.0)); + vec3 param_8 = vec3(0.0); + vec3 colorAfterLight = calcLight(param_2, param_3, param_4, param_5, param_6, param_7, param_8, vec3(0.0), vec3(0.0)); + float w_clamped = clamp((1.0 - mask_val_0.w) * _219.values1.w, 0.0, 1.0); + float w_alpha_combined = clamp(w_clamped + mix_alpha, 0.0, 1.0); + vec4 finalColor = vec4(mix(colorAfterLight, whiteWater_val_baseColor_mix.xyz, vec3(_219.values3.w)), w_alpha_combined); + gl_FragData[0] = finalColor; } -void main() + +#version 100 +#extension GL_ARB_shader_texture_lod : require + +struct SceneExteriorLight +{ + vec4 uExteriorAmbientColor; + vec4 uExteriorHorizontAmbientColor; + vec4 uExteriorGroundAmbientColor; + vec4 uExteriorDirectColor; + vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; +}; + +struct SceneWideParams +{ + mat4 uLookAtMat; + mat4 uPMatrix; + vec4 uViewUp; + vec4 uInteriorSunDir; + SceneExteriorLight extLight; +}; + +struct PSFog +{ + vec4 densityParams; + vec4 heightPlane; + vec4 color_and_heightRate; + vec4 heightDensity_and_endColor; + vec4 sunAngle_and_sunColor; + vec4 heightColor_and_endFogDistance; + vec4 sunPercentage; +}; + +struct meshWideBlockVS +{ + vec4 bumpScale; + mat4 uTextMat[2]; +}; + +uniform meshWideBlockVS _55; + +struct modelWideBlockVS +{ + mat4 uPlacementMat; + mat4 uBoneMatrixes[220]; +}; + +uniform modelWideBlockVS _104; + +struct sceneWideBlockVSPS +{ + SceneWideParams scene; + PSFog fogData; +}; + +uniform sceneWideBlockVSPS _199; + +uniform sampler2D uBumpTexture; + +attribute vec2 aTexCoord2; +attribute vec3 aNormal; +attribute vec3 aPosition; +attribute vec4 boneWeights; +attribute vec4 bones; +varying vec3 vNormal; +varying vec3 vPosition; +varying vec2 vTexCoord; +attribute vec2 aTexCoord; +varying vec2 vTexCoord2_animated; +varying vec2 vTexCoord2; + +mat3 blizzTranspose(mat4 value) +{ + return mat3(vec3(value[0].xyz), vec3(value[1].xyz), vec3(value[2].xyz)); +} + +void main() +{ + vec2 texCoord2 = (_55.uTextMat[0] * vec4(aTexCoord2, 0.0, 1.0)).xy; + vec4 bumpValue = texture2DLod(uBumpTexture, texCoord2, 0.0); + vec3 pos = ((aNormal * _55.bumpScale.x) * bumpValue.z) + aPosition; + mat4 boneTransformMat = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); + mat4 _113 = _104.uBoneMatrixes[bones.x] * boneWeights.x; + boneTransformMat = mat4(boneTransformMat[0] + _113[0], boneTransformMat[1] + _113[1], boneTransformMat[2] + _113[2], boneTransformMat[3] + _113[3]); + mat4 _135 = _104.uBoneMatrixes[bones.y] * boneWeights.y; + boneTransformMat = mat4(boneTransformMat[0] + _135[0], boneTransformMat[1] + _135[1], boneTransformMat[2] + _135[2], boneTransformMat[3] + _135[3]); + mat4 _156 = _104.uBoneMatrixes[bones.z] * boneWeights.z; + boneTransformMat = mat4(boneTransformMat[0] + _156[0], boneTransformMat[1] + _156[1], boneTransformMat[2] + _156[2], boneTransformMat[3] + _156[3]); + mat4 _178 = _104.uBoneMatrixes[bones.w] * boneWeights.w; + boneTransformMat = mat4(boneTransformMat[0] + _178[0], boneTransformMat[1] + _178[1], boneTransformMat[2] + _178[2], boneTransformMat[3] + _178[3]); + mat4 cameraMatrix = (_199.scene.uLookAtMat * _104.uPlacementMat) * boneTransformMat; + vec4 cameraPoint = cameraMatrix * vec4(pos, 1.0); + mat4 param = _199.scene.uLookAtMat; + mat4 param_1 = _104.uPlacementMat; + mat4 param_2 = boneTransformMat; + mat3 viewModelMatTransposed = (blizzTranspose(param) * blizzTranspose(param_1)) * blizzTranspose(param_2); + vNormal = ((_199.scene.uLookAtMat * _104.uPlacementMat) * vec4(aNormal, 0.0)).xyz; + vPosition = pos; + vTexCoord = aTexCoord; + vTexCoord2_animated = texCoord2; + vTexCoord2 = aTexCoord2; + gl_Position = _199.scene.uPMatrix * cameraPoint; +} + + +#version 100 + +struct PSFog +{ + vec4 densityParams; + vec4 heightPlane; + vec4 color_and_heightRate; + vec4 heightDensity_and_endColor; + vec4 sunAngle_and_sunColor; + vec4 heightColor_and_endFogDistance; + vec4 sunPercentage; +}; + +const vec3 _37[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _44[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + +struct SceneExteriorLight +{ + vec4 uExteriorAmbientColor; + vec4 uExteriorHorizontAmbientColor; + vec4 uExteriorGroundAmbientColor; + vec4 uExteriorDirectColor; + vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; +}; + +struct SceneWideParams +{ + mat4 uLookAtMat; + mat4 uPMatrix; + vec4 uViewUp; + vec4 uInteriorSunDir; + SceneExteriorLight extLight; +}; + +struct meshWideBlockPS +{ + vec4 color; +}; + +uniform meshWideBlockPS _253; + +struct sceneWideBlockVSPS +{ + SceneWideParams scene; + PSFog fogData; +}; + +uniform sceneWideBlockVSPS _277; + +uniform sampler2D uTexture; + +varying vec2 vTextCoords; +varying vec3 vPosition; + +vec3 validateFogColor(vec3 fogColor, int blendMode) +{ + return mix(fogColor, _37[blendMode], vec3(_44[blendMode])); +} + +vec4 makeFog(PSFog fogData, vec4 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace, int blendMode) +{ + vec4 l_densityParams = fogData.densityParams; + vec4 l_heightPlane = fogData.heightPlane; + vec4 l_color_and_heightRate = fogData.color_and_heightRate; + vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; + float start = l_densityParams.x; + float end = l_densityParams.y; + float density = l_densityParams.z; + float bias = l_densityParams.w; + float vLength = length(vertexInViewSpace); + float z = vLength - bias; + float expMax = max(0.0, z - start); + float expFog = 1.0 / exp(expMax * density); + float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); + float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; + float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); + float finalFog = mix(expFog, expFogHeight, heightFog); + float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); + float alpha = 1.0; + bool _139 = blendMode == 13; + if (_139) + { + alpha = min(finalFog, endFadeFog); + } + vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + vec3 endColor = validateFogColor(param, param_1); + vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; + float end2 = vLength / l_heightColor_and_endFogDistance.w; + float end2_cube = end2 * (end2 * end2); + vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); + float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); + vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); + nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); + bool _218 = nDotSun > 0.0; + if (_218) + { + nDotSun = (nDotSun * nDotSun) * nDotSun; + fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); + } + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); +} + +void main() +{ + vec3 finalColor = _253.color.xyz + texture2D(uTexture, vTextCoords).xyz; + vec3 sunDir = _277.scene.extLight.uExteriorDirectColorDir.xyz; + PSFog arg; + arg.densityParams = _277.fogData.densityParams; + arg.heightPlane = _277.fogData.heightPlane; + arg.color_and_heightRate = _277.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _277.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _277.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _277.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _277.fogData.sunPercentage; + vec4 param = vec4(finalColor, 1.0); + vec3 param_1 = vPosition; + vec3 param_2 = sunDir; + int param_3 = 2; + finalColor = makeFog(arg, param, param_1, param_2, param_3).xyz; + gl_FragData[0] = vec4(finalColor, 0.699999988079071044921875); +} + + +#version 100 + +struct SceneExteriorLight +{ + vec4 uExteriorAmbientColor; + vec4 uExteriorHorizontAmbientColor; + vec4 uExteriorGroundAmbientColor; + vec4 uExteriorDirectColor; + vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; +}; + +struct SceneWideParams +{ + mat4 uLookAtMat; + mat4 uPMatrix; + vec4 uViewUp; + vec4 uInteriorSunDir; + SceneExteriorLight extLight; +}; + +struct PSFog +{ + vec4 densityParams; + vec4 heightPlane; + vec4 color_and_heightRate; + vec4 heightDensity_and_endColor; + vec4 sunAngle_and_sunColor; + vec4 heightColor_and_endFogDistance; + vec4 sunPercentage; +}; + +struct sceneWideBlockVSPS +{ + SceneWideParams scene; + PSFog fogData; +}; + +uniform sceneWideBlockVSPS _28; + +struct modelWideBlockVS +{ + mat4 uPlacementMat; +}; + +uniform modelWideBlockVS _36; + +attribute vec4 aPositionTransp; +varying vec2 vTextCoords; +varying vec3 vPosition; +attribute vec2 aTexCoord; + +void main() +{ + vec4 aPositionVec4 = vec4(aPositionTransp.xyz, 1.0); + mat4 cameraMatrix = _28.scene.uLookAtMat * _36.uPlacementMat; + vec4 cameraPoint = cameraMatrix * aPositionVec4; + vTextCoords = aPositionVec4.xy * 0.02999999932944774627685546875; + gl_Position = _28.scene.uPMatrix * cameraPoint; + vPosition = cameraPoint.xyz; +} + + +#version 100 + +struct SceneExteriorLight +{ + vec4 uExteriorAmbientColor; + vec4 uExteriorHorizontAmbientColor; + vec4 uExteriorGroundAmbientColor; + vec4 uExteriorDirectColor; + vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; +}; + +struct SceneWideParams +{ + mat4 uLookAtMat; + mat4 uPMatrix; + vec4 uViewUp; + vec4 uInteriorSunDir; + SceneExteriorLight extLight; +}; + +struct InteriorLightParam +{ + vec4 uInteriorAmbientColorAndApplyInteriorLight; + vec4 uInteriorDirectColorAndApplyExteriorLight; +}; + +struct PSFog +{ + vec4 densityParams; + vec4 heightPlane; + vec4 color_and_heightRate; + vec4 heightDensity_and_endColor; + vec4 sunAngle_and_sunColor; + vec4 heightColor_and_endFogDistance; + vec4 sunPercentage; +}; + +const vec3 _215[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _222[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + +struct meshWideBlockPS +{ + ivec4 UseLitColor_EnableAlpha_PixelShader_BlendMode; + vec4 FogColor_AlphaTest; +}; + +uniform meshWideBlockPS _446; + +struct sceneWideBlockVSPS +{ + SceneWideParams scene; + PSFog fogData; +}; + +uniform sceneWideBlockVSPS _909; + +struct modelWideBlockPS +{ + InteriorLightParam intLight; +}; + +uniform modelWideBlockPS _913; + +uniform sampler2D uTexture; +uniform sampler2D uTexture2; +uniform sampler2D uTexture3; + +varying vec2 vTexCoord; +varying vec2 vTexCoord2; +varying vec2 vTexCoord3; +varying vec4 vColor; +varying vec4 vColor2; +varying vec3 vNormal; +varying vec4 vPosition; + +vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, vec3 accumLight, vec3 precomputedLight, vec3 specular) +{ + vec3 localDiffuse = accumLight; + bool _51 = !applyLight; + if (_51) + { + return matDiffuse; + } + vec3 lDiffuse = vec3(0.0); + vec3 normalizedN = normalize(vNormal_1); + bool _67 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + vec3 currColor; + if (_67) + { + float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); + float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); + vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; + vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; + vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; + bool _104 = nDotUp >= 0.0; + if (_104) + { + currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); + } + else + { + currColor = mix(adjHorizAmbient, adjGroundAmbient, vec3(-nDotUp)); + } + vec3 skyColor = currColor * 1.10000002384185791015625; + vec3 groundColor = currColor * 0.699999988079071044921875; + lDiffuse = sceneParams.extLight.uExteriorDirectColor.xyz * nDotL; + currColor = mix(groundColor, skyColor, vec3(0.5 + (0.5 * nDotL))); + } + bool _144 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; + if (_144) + { + float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); + vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; + vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; + bool _168 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + if (_168) + { + lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); + currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); + } + else + { + lDiffuse = lDiffuseInterior; + currColor = interiorAmbient; + } + } + vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); + vec3 linearDiffTerm = (matDiffuse * matDiffuse) * localDiffuse; + vec3 specTerm = specular; + vec3 emTerm = vec3(0.0); + return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; +} + +vec3 validateFogColor(vec3 fogColor, int blendMode) +{ + return mix(fogColor, _215[blendMode], vec3(_222[blendMode])); +} + +vec4 makeFog(PSFog fogData, vec4 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace, int blendMode) +{ + vec4 l_densityParams = fogData.densityParams; + vec4 l_heightPlane = fogData.heightPlane; + vec4 l_color_and_heightRate = fogData.color_and_heightRate; + vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; + float start = l_densityParams.x; + float end = l_densityParams.y; + float density = l_densityParams.z; + float bias = l_densityParams.w; + float vLength = length(vertexInViewSpace); + float z = vLength - bias; + float expMax = max(0.0, z - start); + float expFog = 1.0 / exp(expMax * density); + float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); + float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; + float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); + float finalFog = mix(expFog, expFogHeight, heightFog); + float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); + float alpha = 1.0; + bool _310 = blendMode == 13; + if (_310) + { + alpha = min(finalFog, endFadeFog); + } + vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + vec3 endColor = validateFogColor(param, param_1); + vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; + float end2 = vLength / l_heightColor_and_endFogDistance.w; + float end2_cube = end2 * (end2 * end2); + vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); + float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); + vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); + nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); + bool _388 = nDotSun > 0.0; + if (_388) + { + nDotSun = (nDotSun * nDotSun) * nDotSun; + fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); + } + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); +} + +void main() { vec4 tex = texture2D(uTexture, vTexCoord); vec4 tex2 = texture2D(uTexture2, vTexCoord2); vec4 tex3 = texture2D(uTexture3, vTexCoord3); - bool _387 = _383.UseLitColor_EnableAlpha_PixelShader.y == 1; - if (_387) + bool _450 = _446.UseLitColor_EnableAlpha_PixelShader_BlendMode.y == 1; + if (_450) { - bool _394 = (tex.w - 0.501960813999176025390625) < 0.0; - if (_394) + bool _457 = (tex.w - 0.501960813999176025390625) < 0.0; + if (_457) { discard; } } - int uPixelShader = _383.UseLitColor_EnableAlpha_PixelShader.z; + int uPixelShader = _446.UseLitColor_EnableAlpha_PixelShader_BlendMode.z; vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0); vec3 matDiffuse = vec3(0.0); vec3 env = vec3(0.0); float finalOpacity = 0.0; - bool _409 = uPixelShader == (-1); + bool _471 = uPixelShader == (-1); #if (FRAGMENTSHADER == (-1)) matDiffuse = (tex.xyz * vColor.xyz) + (tex2.xyz * vColor2.zyx); finalOpacity = tex.w; #endif - bool _430 = uPixelShader == 0; + bool _492 = uPixelShader == 0; #if (FRAGMENTSHADER == 0) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _445 = uPixelShader == 1; + bool _507 = uPixelShader == 1; #if (FRAGMENTSHADER == 1) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _458 = uPixelShader == 2; + bool _520 = uPixelShader == 2; #if (FRAGMENTSHADER == 2) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _471 = uPixelShader == 3; + bool _533 = uPixelShader == 3; #if (FRAGMENTSHADER == 3) matDiffuse = tex.xyz * (vColor.xyz * 2.0); env = tex2.xyz * tex.w; finalOpacity = vColor.w; #endif - bool _489 = uPixelShader == 4; + bool _551 = uPixelShader == 4; #if (FRAGMENTSHADER == 4) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _502 = uPixelShader == 5; + bool _564 = uPixelShader == 5; #if (FRAGMENTSHADER == 5) matDiffuse = tex.xyz * (vColor.xyz * 2.0); env = (tex.xyz * tex.w) * tex2.xyz; finalOpacity = vColor.w; #endif - bool _523 = uPixelShader == 6; + bool _585 = uPixelShader == 6; #if (FRAGMENTSHADER == 6) vec3 layer1 = tex.xyz; vec3 layer2 = mix(layer1, tex2.xyz, vec3(tex2.w)); matDiffuse = (vColor.xyz * 2.0) * mix(layer2, layer1, vec3(vColor2.w)); finalOpacity = vColor.w; #endif - bool _552 = uPixelShader == 7; + bool _614 = uPixelShader == 7; #if (FRAGMENTSHADER == 7) vec4 colorMix = mix(tex2, tex, vec4(vColor2.w)); matDiffuse = colorMix.xyz * (vColor.xyz * 2.0); env = (colorMix.xyz * colorMix.w) * tex3.xyz; finalOpacity = vColor.w; #endif - bool _581 = uPixelShader == 8; + bool _643 = uPixelShader == 8; #if (FRAGMENTSHADER == 8) vec3 layer1_1 = tex.xyz; vec3 layer2_1 = tex2.xyz; matDiffuse = (vColor.xyz * 2.0) * mix(layer2_1, layer1_1, vec3(vColor2.w)); finalOpacity = vColor.w; #endif - bool _605 = uPixelShader == 9; + bool _667 = uPixelShader == 9; #if (FRAGMENTSHADER == 9) matDiffuse = tex.xyz * (vColor.xyz * 2.0); env = (tex2.xyz * tex2.w) * vColor2.w; finalOpacity = vColor.w; #endif - bool _627 = uPixelShader == 10; + bool _689 = uPixelShader == 10; #if (FRAGMENTSHADER == 10) float mixFactor = clamp(tex3.w * vColor2.w, 0.0, 1.0); matDiffuse = (vColor.xyz * 2.0) * mix(mix((tex.xyz * tex2.xyz) * 2.0, tex3.xyz, vec3(mixFactor)), tex.xyz, vec3(tex.w)); finalOpacity = vColor.w; #endif - bool _663 = uPixelShader == 11; + bool _725 = uPixelShader == 11; #if (FRAGMENTSHADER == 11) matDiffuse = tex.xyz * (vColor.xyz * 2.0); env = ((tex.xyz * tex.w) * tex2.xyz) + ((tex3.xyz * tex3.w) * vColor2.w); finalOpacity = vColor.w; #endif - bool _694 = uPixelShader == 12; + bool _756 = uPixelShader == 12; #if (FRAGMENTSHADER == 12) matDiffuse = (vColor.xyz * 2.0) * mix(tex2.xyz, tex.xyz, vec3(vColor2.w)); finalOpacity = vColor.w; #endif - bool _714 = uPixelShader == 13; + bool _775 = uPixelShader == 13; #if (FRAGMENTSHADER == 13) vec3 t1diffuse = tex2.xyz * (1.0 - tex2.w); matDiffuse = (vColor.xyz * 2.0) * mix(t1diffuse, tex.xyz, vec3(vColor2.w)); env = (tex2.xyz * tex2.w) * (1.0 - vColor2.w); finalOpacity = vColor.w; #endif - bool _748 = uPixelShader == 13; + bool _809 = uPixelShader == 13; #if (FRAGMENTSHADER == 13) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _762 = uPixelShader == 14; + bool _823 = uPixelShader == 14; #if (FRAGMENTSHADER == 14) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _776 = uPixelShader == 15; + bool _837 = uPixelShader == 15; #if (FRAGMENTSHADER == 15) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _789 = uPixelShader == 16; + bool _850 = uPixelShader == 16; #if (FRAGMENTSHADER == 16) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _802 = uPixelShader == 17; + bool _863 = uPixelShader == 17; #if (FRAGMENTSHADER == 17) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _815 = uPixelShader == 18; + bool _876 = uPixelShader == 18; #if (FRAGMENTSHADER == 18) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _828 = uPixelShader == 19; + bool _889 = uPixelShader == 19; #if (FRAGMENTSHADER == 19) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; @@ -2540,39 +3059,39 @@ void main() bool param_2 = true; float param_3 = vPosition.w; SceneWideParams param_4; - param_4.uLookAtMat = _848.scene.uLookAtMat; - param_4.uPMatrix = _848.scene.uPMatrix; - param_4.uViewUp = _848.scene.uViewUp; - param_4.uInteriorSunDir = _848.scene.uInteriorSunDir; - param_4.extLight.uExteriorAmbientColor = _848.scene.extLight.uExteriorAmbientColor; - param_4.extLight.uExteriorHorizontAmbientColor = _848.scene.extLight.uExteriorHorizontAmbientColor; - param_4.extLight.uExteriorGroundAmbientColor = _848.scene.extLight.uExteriorGroundAmbientColor; - param_4.extLight.uExteriorDirectColor = _848.scene.extLight.uExteriorDirectColor; - param_4.extLight.uExteriorDirectColorDir = _848.scene.extLight.uExteriorDirectColorDir; + param_4.uLookAtMat = _909.scene.uLookAtMat; + param_4.uPMatrix = _909.scene.uPMatrix; + param_4.uViewUp = _909.scene.uViewUp; + param_4.uInteriorSunDir = _909.scene.uInteriorSunDir; + param_4.extLight.uExteriorAmbientColor = _909.scene.extLight.uExteriorAmbientColor; + param_4.extLight.uExteriorHorizontAmbientColor = _909.scene.extLight.uExteriorHorizontAmbientColor; + param_4.extLight.uExteriorGroundAmbientColor = _909.scene.extLight.uExteriorGroundAmbientColor; + param_4.extLight.uExteriorDirectColor = _909.scene.extLight.uExteriorDirectColor; + param_4.extLight.uExteriorDirectColorDir = _909.scene.extLight.uExteriorDirectColorDir; + param_4.extLight.adtSpecMult = _909.scene.extLight.adtSpecMult; InteriorLightParam param_5; - param_5.uInteriorAmbientColorAndApplyInteriorLight = _852.intLight.uInteriorAmbientColorAndApplyInteriorLight; - param_5.uInteriorDirectColorAndApplyExteriorLight = _852.intLight.uInteriorDirectColorAndApplyExteriorLight; + param_5.uInteriorAmbientColorAndApplyInteriorLight = _913.intLight.uInteriorAmbientColorAndApplyInteriorLight; + param_5.uInteriorDirectColorAndApplyExteriorLight = _913.intLight.uInteriorDirectColorAndApplyExteriorLight; vec3 param_6 = vec3(0.0); finalColor = vec4(calcLight(param, param_1, param_2, param_3, param_4, param_5, param_6, vColor2.xyz, vec3(0.0)), finalOpacity); - bool _909 = finalColor.w < _383.FogColor_AlphaTest.w; - if (_909) + bool _972 = finalColor.w < _446.FogColor_AlphaTest.w; + if (_972) { discard; } PSFog arg; - arg.densityParams = _848.fogData.densityParams; - arg.heightPlane = _848.fogData.heightPlane; - arg.color_and_heightRate = _848.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _848.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _848.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _848.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _848.fogData.sunPercentage; - vec3 param_7 = finalColor.xyz; + arg.densityParams = _909.fogData.densityParams; + arg.heightPlane = _909.fogData.heightPlane; + arg.color_and_heightRate = _909.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _909.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _909.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _909.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _909.fogData.sunPercentage; + vec4 param_7 = finalColor; vec3 param_8 = vPosition.xyz; - vec3 param_9 = _848.scene.extLight.uExteriorDirectColorDir.xyz; - vec3 _945 = makeFog(arg, param_7, param_8, param_9); - finalColor = vec4(_945.x, _945.y, _945.z, finalColor.w); - finalColor.w = 1.0; + vec3 param_9 = _909.scene.extLight.uExteriorDirectColorDir.xyz; + int param_10 = _446.UseLitColor_EnableAlpha_PixelShader_BlendMode.w; + finalColor = makeFog(arg, param_7, param_8, param_9, param_10); gl_FragData[0] = finalColor; } @@ -2586,6 +3105,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -2727,52 +3247,54 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform modelWideBlockPS { - vec4 uViewUp; - vec4 uSunDir_FogStart; - vec4 uSunColor_uFogEnd; - vec4 uAmbientLight; - vec4 FogColor; + highp vec4 uViewUp; + highp vec4 uSunDir_FogStart; + highp vec4 uSunColor_uFogEnd; + highp vec4 uAmbientLight; + highp vec4 FogColor; int uNewFormula; } _65; -uniform sampler2D uDiffuseTexture; -uniform sampler2D uNormalTexture; +uniform highp sampler2D uDiffuseTexture; +uniform highp sampler2D uNormalTexture; -in vec2 vChunkCoords; -in vec3 vPosition; -layout(location = 0) out vec4 fragColor; +in highp vec2 vChunkCoords; +in highp vec3 vPosition; +layout(location = 0) out highp vec4 fragColor; void main() { - vec2 TextureCoords = vec2(vChunkCoords.x, vChunkCoords.y); - vec4 texDiffuse = texture(uDiffuseTexture, TextureCoords); - vec3 matDiffuse = texDiffuse.xyz; - vec3 vNormal = (texture(uNormalTexture, TextureCoords).xyz * 2.0) - vec3(1.0); + highp vec2 TextureCoords = vec2(vChunkCoords.x, vChunkCoords.y); + highp vec4 texDiffuse = texture(uDiffuseTexture, TextureCoords); + highp vec3 matDiffuse = texDiffuse.xyz; + highp vec3 vNormal = (texture(uNormalTexture, TextureCoords).xyz * 2.0) - vec3(1.0); vNormal = vec3(-vNormal.z, -vNormal.x, vNormal.y); - vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0); - vec3 fogColor = _65.FogColor.xyz; - float fog_start = _65.uSunDir_FogStart.w; - float fog_end = _65.uSunColor_uFogEnd.w; - float fog_rate = 1.5; - float fog_bias = 0.00999999977648258209228515625; - float distanceToCamera = length(vPosition); - float z_depth = distanceToCamera - fog_bias; - float expFog = 1.0 / exp(max(0.0, z_depth - fog_start) * fog_rate); - float heightFog = 1.0; + highp vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0); + highp vec3 fogColor = _65.FogColor.xyz; + highp float fog_start = _65.uSunDir_FogStart.w; + highp float fog_end = _65.uSunColor_uFogEnd.w; + highp float fog_rate = 1.5; + highp float fog_bias = 0.00999999977648258209228515625; + highp float distanceToCamera = length(vPosition); + highp float z_depth = distanceToCamera - fog_bias; + highp float expFog = 1.0 / exp(max(0.0, z_depth - fog_start) * fog_rate); + highp float heightFog = 1.0; expFog += heightFog; - float endFadeFog = clamp((fog_end - distanceToCamera) / (0.699999988079071044921875 * fog_end), 0.0, 1.0); - vec3 _123 = mix(fogColor, finalColor.xyz, vec3(min(expFog, endFadeFog))); + highp float endFadeFog = clamp((fog_end - distanceToCamera) / (0.699999988079071044921875 * fog_end), 0.0, 1.0); + highp vec3 _123 = mix(fogColor, finalColor.xyz, vec3(min(expFog, endFadeFog))); finalColor = vec4(_123.x, _123.y, _123.z, finalColor.w); finalColor.w = 1.0; fragColor = finalColor; } -#version 330 +#version 300 es layout(std140) uniform modelWideBlockVS { @@ -2805,100 +3327,110 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; struct SceneExteriorLight { - vec4 uExteriorAmbientColor; - vec4 uExteriorHorizontAmbientColor; - vec4 uExteriorGroundAmbientColor; - vec4 uExteriorDirectColor; - vec4 uExteriorDirectColorDir; + highp vec4 uExteriorAmbientColor; + highp vec4 uExteriorHorizontAmbientColor; + highp vec4 uExteriorGroundAmbientColor; + highp vec4 uExteriorDirectColor; + highp vec4 uExteriorDirectColorDir; + highp vec4 adtSpecMult; }; struct SceneWideParams { - mat4 uLookAtMat; - mat4 uPMatrix; - vec4 uViewUp; - vec4 uInteriorSunDir; + highp mat4 uLookAtMat; + highp mat4 uPMatrix; + highp vec4 uViewUp; + highp vec4 uInteriorSunDir; SceneExteriorLight extLight; }; struct InteriorLightParam { - vec4 uInteriorAmbientColorAndApplyInteriorLight; - vec4 uInteriorDirectColorAndApplyExteriorLight; + highp vec4 uInteriorAmbientColorAndApplyInteriorLight; + highp vec4 uInteriorDirectColorAndApplyExteriorLight; }; struct PSFog { - vec4 densityParams; - vec4 heightPlane; - vec4 color_and_heightRate; - vec4 heightDensity_and_endColor; - vec4 sunAngle_and_sunColor; - vec4 heightColor_and_endFogDistance; - vec4 sunPercentage; + highp vec4 densityParams; + highp vec4 heightPlane; + highp vec4 color_and_heightRate; + highp vec4 heightDensity_and_endColor; + highp vec4 sunAngle_and_sunColor; + highp vec4 heightColor_and_endFogDistance; + highp vec4 sunPercentage; }; +const vec3 _221[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _228[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + layout(std140) uniform meshWideBlockPS { - vec4 uHeightScale; - vec4 uHeightOffset; - mat4 animationMat[4]; -} _387; + highp vec4 uHeightScale; + highp vec4 uHeightOffset; + highp mat4 animationMat[4]; +} _465; + +layout(std140) uniform modelWideBlockPS +{ + ivec4 uUseHeightMixFormula; +} _505; layout(std140) uniform sceneWideBlockVSPS { SceneWideParams scene; PSFog fogData; -} _621; - -layout(std140) uniform modelWideBlockPS -{ - vec4 uFogStartAndFogEnd; - vec4 uFogColor; -} _741; - -uniform sampler2D uAlphaTexture; -uniform sampler2D uLayerHeight0; -uniform sampler2D uLayerHeight1; -uniform sampler2D uLayerHeight2; -uniform sampler2D uLayerHeight3; -uniform sampler2D uLayer0; -uniform sampler2D uLayer1; -uniform sampler2D uLayer2; -uniform sampler2D uLayer3; - -in vec2 vChunkCoords; -in vec4 vColor; -in vec3 vNormal; -in vec3 vVertexLighting; -in vec3 vPosition; -layout(location = 0) out vec4 outColor; +} _747; + +uniform highp sampler2D uAlphaTexture; +uniform highp sampler2D uLayerHeight0; +uniform highp sampler2D uLayerHeight1; +uniform highp sampler2D uLayerHeight2; +uniform highp sampler2D uLayerHeight3; +uniform highp sampler2D uLayer0; +uniform highp sampler2D uLayer1; +uniform highp sampler2D uLayer2; +uniform highp sampler2D uLayer3; + +in highp vec2 vChunkCoords; +in highp vec4 vColor; +in highp vec3 vNormal; +in highp vec3 vVertexLighting; +in highp vec3 vPosition; +layout(location = 0) out highp vec4 outColor; + +highp vec4 mixTextures(highp vec4 tex0, highp vec4 tex1, highp float alpha) +{ + return ((tex1 - tex0) * alpha) + tex0; +} -vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, vec3 accumLight, vec3 precomputedLight, vec3 specular) +highp vec3 calcLight(highp vec3 matDiffuse, highp vec3 vNormal_1, bool applyLight, highp float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, highp vec3 accumLight, highp vec3 precomputedLight, highp vec3 specular) { - vec3 localDiffuse = accumLight; - bool _42 = !applyLight; - if (_42) + highp vec3 localDiffuse = accumLight; + bool _57 = !applyLight; + if (_57) { return matDiffuse; } - vec3 lDiffuse = vec3(0.0); - vec3 normalizedN = normalize(vNormal_1); - bool _59 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - vec3 currColor; - if (_59) + highp vec3 lDiffuse = vec3(0.0); + highp vec3 normalizedN = normalize(vNormal_1); + bool _73 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + highp vec3 currColor; + if (_73) { - float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); - float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); - vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; - vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; - vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; - bool _97 = nDotUp >= 0.0; - if (_97) + highp float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); + highp float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); + highp vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; + highp vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; + highp vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; + bool _110 = nDotUp >= 0.0; + if (_110) { currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); } @@ -2906,149 +3438,191 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE { currColor = mix(adjHorizAmbient, adjGroundAmbient, vec3(-nDotUp)); } - vec3 skyColor = currColor * 1.10000002384185791015625; - vec3 groundColor = currColor * 0.699999988079071044921875; + highp vec3 skyColor = currColor * 1.10000002384185791015625; + highp vec3 groundColor = currColor * 0.699999988079071044921875; lDiffuse = sceneParams.extLight.uExteriorDirectColor.xyz * nDotL; currColor = mix(groundColor, skyColor, vec3(0.5 + (0.5 * nDotL))); - } - bool _137 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; - if (_137) - { - float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); - vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; - vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; - bool _161 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - if (_161) - { - lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); - currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); - } - else - { - lDiffuse = lDiffuseInterior; - currColor = interiorAmbient; - } - } - vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); - vec3 linearDiffTerm = (matDiffuse * matDiffuse) * localDiffuse; - vec3 specTerm = specular; - vec3 emTerm = vec3(0.0); - return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; -} - -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) -{ - vec4 l_densityParams = fogData.densityParams; - vec4 l_heightPlane = fogData.heightPlane; - vec4 l_color_and_heightRate = fogData.color_and_heightRate; - vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; - float start = l_densityParams.x; - float end = l_densityParams.y; - float density = l_densityParams.z; - float bias = l_densityParams.w; - float vLength = length(vertexInViewSpace); - float z = vLength - bias; - float expMax = max(0.0, z - start); - float expFog = 1.0 / exp(expMax * density); - float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); - float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; - float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); - float finalFog = mix(expFog, expFogHeight, heightFog); - float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; - vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; - float end2 = vLength / l_heightColor_and_endFogDistance.w; - float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + } + bool _150 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; + if (_150) + { + highp float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); + highp vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; + highp vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; + bool _174 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + if (_174) + { + lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); + currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); + } + else + { + lDiffuse = lDiffuseInterior; + currColor = interiorAmbient; + } + } + highp vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); + highp vec3 linearDiffTerm = (matDiffuse * matDiffuse) * localDiffuse; + highp vec3 specTerm = specular; + highp vec3 emTerm = vec3(0.0); + return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; +} + +highp vec3 validateFogColor(highp vec3 fogColor, int blendMode) +{ + return mix(fogColor, _221[blendMode], vec3(_228[blendMode])); +} + +highp vec4 makeFog(PSFog fogData, highp vec4 final, highp vec3 vertexInViewSpace, highp vec3 sunDirInViewSpace, int blendMode) +{ + highp vec4 l_densityParams = fogData.densityParams; + highp vec4 l_heightPlane = fogData.heightPlane; + highp vec4 l_color_and_heightRate = fogData.color_and_heightRate; + highp vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; + highp float start = l_densityParams.x; + highp float end = l_densityParams.y; + highp float density = l_densityParams.z; + highp float bias = l_densityParams.w; + highp float vLength = length(vertexInViewSpace); + highp float z = vLength - bias; + highp float expMax = max(0.0, z - start); + highp float expFog = 1.0 / exp(expMax * density); + highp float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); + highp float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; + highp float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); + highp float finalFog = mix(expFog, expFogHeight, heightFog); + highp float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); + highp float alpha = 1.0; + bool _316 = blendMode == 13; + if (_316) + { + alpha = min(finalFog, endFadeFog); + } + highp vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + highp vec3 endColor = validateFogColor(param, param_1); + highp vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; + highp float end2 = vLength / l_heightColor_and_endFogDistance.w; + highp float end2_cube = end2 * (end2 * end2); + highp vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + highp vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + highp vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + highp vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); - float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + highp float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); + highp vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + highp vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _334 = nDotSun > 0.0; - if (_334) + bool _394 = nDotSun > 0.0; + if (_394) { nDotSun = (nDotSun * nDotSun) * nDotSun; fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } void main() { - vec2 vTexCoord = vChunkCoords; - vec2 alphaCoord = vec2(vChunkCoords.x / 8.0, vChunkCoords.y / 8.0); - vec3 alphaBlend = texture(uAlphaTexture, alphaCoord).yzw; - vec2 tcLayer0 = (_387.animationMat[0] * vec4(vTexCoord, 0.0, 1.0)).xy; - vec2 tcLayer1 = (_387.animationMat[1] * vec4(vTexCoord, 0.0, 1.0)).xy; - vec2 tcLayer2 = (_387.animationMat[2] * vec4(vTexCoord, 0.0, 1.0)).xy; - vec2 tcLayer3 = (_387.animationMat[3] * vec4(vTexCoord, 0.0, 1.0)).xy; - float minusAlphaBlendSum = 1.0 - clamp(dot(alphaBlend, vec3(1.0)), 0.0, 1.0); - vec4 weightsVector = vec4(minusAlphaBlendSum, alphaBlend); - float weightedTexture_x = minusAlphaBlendSum * ((texture(uLayerHeight0, tcLayer0).w * _387.uHeightScale.x) + _387.uHeightOffset.x); - float weightedTexture_y = weightsVector.y * ((texture(uLayerHeight1, tcLayer1).w * _387.uHeightScale.y) + _387.uHeightOffset.y); - float weightedTexture_z = weightsVector.z * ((texture(uLayerHeight2, tcLayer2).w * _387.uHeightScale.z) + _387.uHeightOffset.z); - float weightedTexture_w = weightsVector.w * ((texture(uLayerHeight3, tcLayer3).w * _387.uHeightScale.w) + _387.uHeightOffset.w); - vec4 weights = vec4(weightedTexture_x, weightedTexture_y, weightedTexture_z, weightedTexture_w); - vec4 weights_temp = weights * (vec4(1.0) - clamp(vec4(max(max(weightedTexture_x, weightedTexture_y), max(weightedTexture_z, weightedTexture_w))) - weights, vec4(0.0), vec4(1.0))); - vec4 weightsNormalized = weights_temp / vec4(dot(vec4(1.0), weights_temp)); - vec4 weightedLayer_0 = texture(uLayer0, tcLayer0) * weightsNormalized.x; - vec3 matDiffuse_0 = weightedLayer_0.xyz; - float specBlend_0 = weightedLayer_0.w; - vec4 weightedLayer_1 = texture(uLayer1, tcLayer1) * weightsNormalized.y; - vec3 matDiffuse_1 = matDiffuse_0 + weightedLayer_1.xyz; - float specBlend_1 = specBlend_0 + weightedLayer_1.w; - vec4 weightedLayer_2 = texture(uLayer2, tcLayer1) * weightsNormalized.z; - vec3 matDiffuse_2 = matDiffuse_1 + weightedLayer_2.xyz; - float specBlend_2 = specBlend_1 + weightedLayer_2.w; - vec4 weightedLayer_3 = texture(uLayer3, tcLayer1) * weightsNormalized.w; - vec3 matDiffuse_3 = matDiffuse_2 + weightedLayer_3.xyz; - float specBlend_3 = specBlend_2 + weightedLayer_3.w; - vec4 final = vec4(matDiffuse_3, specBlend_3); - vec3 matDiffuse = (final.xyz * 2.0) * vColor.xyz; - vec3 param = matDiffuse; - vec3 param_1 = vNormal; - bool param_2 = true; - float param_3 = 0.0; - SceneWideParams param_4; - param_4.uLookAtMat = _621.scene.uLookAtMat; - param_4.uPMatrix = _621.scene.uPMatrix; - param_4.uViewUp = _621.scene.uViewUp; - param_4.uInteriorSunDir = _621.scene.uInteriorSunDir; - param_4.extLight.uExteriorAmbientColor = _621.scene.extLight.uExteriorAmbientColor; - param_4.extLight.uExteriorHorizontAmbientColor = _621.scene.extLight.uExteriorHorizontAmbientColor; - param_4.extLight.uExteriorGroundAmbientColor = _621.scene.extLight.uExteriorGroundAmbientColor; - param_4.extLight.uExteriorDirectColor = _621.scene.extLight.uExteriorDirectColor; - param_4.extLight.uExteriorDirectColorDir = _621.scene.extLight.uExteriorDirectColorDir; - InteriorLightParam param_5 = InteriorLightParam(vec4(0.0), vec4(0.0, 0.0, 0.0, 1.0)); - vec3 param_6 = vVertexLighting; - vec4 finalColor = vec4(calcLight(param, param_1, param_2, param_3, param_4, param_5, param_6, vec3(0.0), vec3(0.0)), 1.0); - float specBlend = final.w; - vec3 halfVec = -normalize(_621.scene.extLight.uExteriorDirectColorDir.xyz + normalize(vPosition)); - vec3 lSpecular = _621.scene.extLight.uExteriorDirectColor.xyz * pow(max(0.0, dot(halfVec, vNormal)), 20.0); - vec3 specTerm = vec3(specBlend) * lSpecular; - vec3 _699 = finalColor.xyz + specTerm; - finalColor = vec4(_699.x, _699.y, _699.z, finalColor.w); + highp vec2 vTexCoord = vChunkCoords; + highp vec2 alphaCoord = vec2(vChunkCoords.x / 8.0, vChunkCoords.y / 8.0); + highp vec3 alphaBlend = texture(uAlphaTexture, alphaCoord).yzw; + highp vec2 tcLayer0 = (_465.animationMat[0] * vec4(vTexCoord, 0.0, 1.0)).xy; + highp vec2 tcLayer1 = (_465.animationMat[1] * vec4(vTexCoord, 0.0, 1.0)).xy; + highp vec2 tcLayer2 = (_465.animationMat[2] * vec4(vTexCoord, 0.0, 1.0)).xy; + highp vec2 tcLayer3 = (_465.animationMat[3] * vec4(vTexCoord, 0.0, 1.0)).xy; + bool _509 = _505.uUseHeightMixFormula.x > 0; + highp vec4 final; + if (_509) + { + highp float minusAlphaBlendSum = 1.0 - clamp(dot(alphaBlend, vec3(1.0)), 0.0, 1.0); + highp vec4 weightsVector = vec4(minusAlphaBlendSum, alphaBlend); + highp float weightedTexture_x = minusAlphaBlendSum * ((texture(uLayerHeight0, tcLayer0).w * _465.uHeightScale.x) + _465.uHeightOffset.x); + highp float weightedTexture_y = weightsVector.y * ((texture(uLayerHeight1, tcLayer1).w * _465.uHeightScale.y) + _465.uHeightOffset.y); + highp float weightedTexture_z = weightsVector.z * ((texture(uLayerHeight2, tcLayer2).w * _465.uHeightScale.z) + _465.uHeightOffset.z); + highp float weightedTexture_w = weightsVector.w * ((texture(uLayerHeight3, tcLayer3).w * _465.uHeightScale.w) + _465.uHeightOffset.w); + highp vec4 weights = vec4(weightedTexture_x, weightedTexture_y, weightedTexture_z, weightedTexture_w); + highp vec4 weights_temp = weights * (vec4(1.0) - clamp(vec4(max(max(weightedTexture_x, weightedTexture_y), max(weightedTexture_z, weightedTexture_w))) - weights, vec4(0.0), vec4(1.0))); + highp vec4 weightsNormalized = weights_temp / vec4(dot(vec4(1.0), weights_temp)); + highp vec4 weightedLayer_0 = texture(uLayer0, tcLayer0) * weightsNormalized.x; + highp vec3 matDiffuse_0 = weightedLayer_0.xyz; + highp float specBlend_0 = weightedLayer_0.w; + highp vec4 weightedLayer_1 = texture(uLayer1, tcLayer1) * weightsNormalized.y; + highp vec3 matDiffuse_1 = matDiffuse_0 + weightedLayer_1.xyz; + highp float specBlend_1 = specBlend_0 + weightedLayer_1.w; + highp vec4 weightedLayer_2 = texture(uLayer2, tcLayer2) * weightsNormalized.z; + highp vec3 matDiffuse_2 = matDiffuse_1 + weightedLayer_2.xyz; + highp float specBlend_2 = specBlend_1 + weightedLayer_2.w; + highp vec4 weightedLayer_3 = texture(uLayer3, tcLayer3) * weightsNormalized.w; + highp vec3 matDiffuse_3 = matDiffuse_2 + weightedLayer_3.xyz; + highp float specBlend_3 = specBlend_2 + weightedLayer_3.w; + final = vec4(matDiffuse_3, specBlend_3); + } + else + { + highp vec4 tex1 = texture(uLayer0, tcLayer0); + highp vec4 tex2 = texture(uLayer1, tcLayer1); + highp vec4 tex3 = texture(uLayer2, tcLayer2); + highp vec4 tex4 = texture(uLayer3, tcLayer3); + highp vec4 param = tex1; + highp vec4 param_1 = tex2; + highp float param_2 = alphaBlend.x; + highp vec4 param_3 = mixTextures(param, param_1, param_2); + highp vec4 param_4 = tex3; + highp float param_5 = alphaBlend.y; + highp vec4 param_6 = mixTextures(param_3, param_4, param_5); + highp vec4 param_7 = tex4; + highp float param_8 = alphaBlend.z; + final = mixTextures(param_6, param_7, param_8); + } + highp vec3 matDiffuse = (final.xyz * 2.0) * vColor.xyz; + highp vec3 param_9 = matDiffuse; + highp vec3 param_10 = vNormal; + bool param_11 = true; + highp float param_12 = 0.0; + SceneWideParams param_13; + param_13.uLookAtMat = _747.scene.uLookAtMat; + param_13.uPMatrix = _747.scene.uPMatrix; + param_13.uViewUp = _747.scene.uViewUp; + param_13.uInteriorSunDir = _747.scene.uInteriorSunDir; + param_13.extLight.uExteriorAmbientColor = _747.scene.extLight.uExteriorAmbientColor; + param_13.extLight.uExteriorHorizontAmbientColor = _747.scene.extLight.uExteriorHorizontAmbientColor; + param_13.extLight.uExteriorGroundAmbientColor = _747.scene.extLight.uExteriorGroundAmbientColor; + param_13.extLight.uExteriorDirectColor = _747.scene.extLight.uExteriorDirectColor; + param_13.extLight.uExteriorDirectColorDir = _747.scene.extLight.uExteriorDirectColorDir; + param_13.extLight.adtSpecMult = _747.scene.extLight.adtSpecMult; + InteriorLightParam param_14 = InteriorLightParam(vec4(0.0), vec4(0.0, 0.0, 0.0, 1.0)); + highp vec3 param_15 = vVertexLighting; + highp vec4 finalColor = vec4(calcLight(param_9, param_10, param_11, param_12, param_13, param_14, param_15, vec3(0.0), vec3(0.0)), 1.0); + highp float specBlend = final.w; + highp vec3 halfVec = -normalize(_747.scene.extLight.uExteriorDirectColorDir.xyz + normalize(vPosition)); + highp vec3 lSpecular = _747.scene.extLight.uExteriorDirectColor.xyz * pow(max(0.0, dot(halfVec, vNormal)), 20.0); + highp vec3 specTerm = (vec3(specBlend) * lSpecular) * _747.scene.extLight.adtSpecMult.x; + highp vec3 _830 = finalColor.xyz + specTerm; + finalColor = vec4(_830.x, _830.y, _830.z, finalColor.w); PSFog arg; - arg.densityParams = _621.fogData.densityParams; - arg.heightPlane = _621.fogData.heightPlane; - arg.color_and_heightRate = _621.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _621.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _621.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _621.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _621.fogData.sunPercentage; - vec3 param_7 = finalColor.xyz; - vec3 param_8 = vPosition; - vec3 param_9 = _621.scene.extLight.uExteriorDirectColorDir.xyz; - vec3 _732 = makeFog(arg, param_7, param_8, param_9); - finalColor = vec4(_732.x, _732.y, _732.z, finalColor.w); + arg.densityParams = _747.fogData.densityParams; + arg.heightPlane = _747.fogData.heightPlane; + arg.color_and_heightRate = _747.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _747.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _747.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _747.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _747.fogData.sunPercentage; + highp vec4 param_16 = finalColor; + highp vec3 param_17 = vPosition; + highp vec3 param_18 = _747.scene.extLight.uExteriorDirectColorDir.xyz; + int param_19 = 0; + finalColor = makeFog(arg, param_16, param_17, param_18, param_19); finalColor.w = 1.0; outColor = finalColor; } -#version 330 +#version 300 es struct SceneExteriorLight { @@ -3057,6 +3631,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -3088,7 +3663,7 @@ layout(std140) uniform sceneWideBlockVSPS layout(std140) uniform meshWideBlockVS { vec4 uPos; -} _131; +} _139; layout(location = 4) in float aIndex; layout(location = 0) in vec3 aHeight; @@ -3127,86 +3702,28 @@ void main() } -#version 330 - -layout(std140) uniform meshWideBlockPS -{ - vec4 color; -} _12; - -uniform sampler2D uTexture; - -layout(location = 0) out vec4 outputColor; -in vec2 vTextCoords; -in vec3 vPosition; - -void main() -{ - outputColor = vec4(_12.color.xyz * texture(uTexture, vTextCoords).xyz, 0.699999988079071044921875); -} - - -#version 330 - -struct SceneExteriorLight -{ - vec4 uExteriorAmbientColor; - vec4 uExteriorHorizontAmbientColor; - vec4 uExteriorGroundAmbientColor; - vec4 uExteriorDirectColor; - vec4 uExteriorDirectColorDir; -}; - -struct SceneWideParams -{ - mat4 uLookAtMat; - mat4 uPMatrix; - vec4 uViewUp; - vec4 uInteriorSunDir; - SceneExteriorLight extLight; -}; - -layout(std140) uniform sceneWideBlockVSPS -{ - SceneWideParams scene; -} _27; - -layout(location = 0) in vec4 aPositionTransp; -out vec2 vTextCoords; -out vec3 vPosition; -layout(location = 1) in vec2 aTexCoord; - -void main() -{ - vec4 aPositionVec4 = vec4(aPositionTransp.xyz, 1.0); - mat4 cameraMatrix = _27.scene.uLookAtMat; - vec4 cameraPoint = cameraMatrix * aPositionVec4; - vTextCoords = aPositionVec4.xy / vec2(33.333332061767578125); - gl_Position = _27.scene.uPMatrix * cameraPoint; - vPosition = cameraPoint.xyz; -} - - -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform modelWideBlockVS { - mat4 uPlacementMat; - vec4 uBBScale; - vec4 uBBCenter; - vec4 uColor; + highp mat4 uPlacementMat; + highp vec4 uBBScale; + highp vec4 uBBCenter; + highp vec4 uColor; } _13; -layout(location = 0) out vec4 outColor; +layout(location = 0) out highp vec4 outColor; void main() { - vec4 finalColor = _13.uColor; + highp vec4 finalColor = _13.uColor; outColor = finalColor; } -#version 330 +#version 300 es layout(std140) uniform modelWideBlockVS { @@ -3231,29 +3748,31 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform meshWideBlockPS { int drawDepth; - float uFarPlane; - float uNearPlane; + highp float uFarPlane; + highp float uNearPlane; } _10; -uniform sampler2D diffuse; +uniform highp sampler2D diffuse; -in vec2 texCoord; -layout(location = 0) out vec4 fragColor; +in highp vec2 texCoord; +layout(location = 0) out highp vec4 fragColor; void main() { bool _17 = _10.drawDepth == 1; - vec4 finalColor; + highp vec4 finalColor; if (_17) { - float f = _10.uFarPlane; - float n = _10.uNearPlane; - float z = (2.0 * n) / ((f + n) - (texture(diffuse, texCoord).x * (f - n))); + highp float f = _10.uFarPlane; + highp float n = _10.uNearPlane; + highp float z = (2.0 * n) / ((f + n) - (texture(diffuse, texCoord).x * (f - n))); finalColor = vec4(z, z, z, 255.0); } else @@ -3264,24 +3783,26 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform meshWideBlockPS { - vec3 uColor; + highp vec3 uColor; } _22; -layout(location = 0) out vec4 fragColor; +layout(location = 0) out highp vec4 fragColor; void main() { - vec4 finalColor = vec4(1.0); + highp vec4 finalColor = vec4(1.0); finalColor.w = 1.0; fragColor = finalColor; } -#version 330 +#version 300 es layout(std140) uniform sceneWideBlockVSPS { @@ -3299,23 +3820,25 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform modelWideBlockPS { - vec3 uColor; + highp vec3 uColor; } _19; -layout(location = 0) out vec4 fragColor; +layout(location = 0) out highp vec4 fragColor; void main() { - vec4 finalColor = vec4(1.0, 1.0, 0.0, 1.0); + highp vec4 finalColor = vec4(1.0, 1.0, 0.0, 1.0); fragColor = finalColor; } -#version 330 +#version 300 es layout(std140) uniform sceneWideBlockVSPS { @@ -3331,15 +3854,17 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform modelWideBlockVS { - vec3 uColor; + highp vec3 uColor; } _13; -layout(location = 0) out vec4 fragColor; -in vec4 vPos; +layout(location = 0) out highp vec4 fragColor; +in highp vec4 vPos; void main() { @@ -3347,7 +3872,7 @@ void main() } -#version 330 +#version 300 es layout(std140) uniform sceneWideBlockVSPS { @@ -3370,23 +3895,25 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform modelWideBlockVS { - vec4 uColor; + highp vec4 uColor; } _12; -layout(location = 0) out vec4 fragColor; +layout(location = 0) out highp vec4 fragColor; void main() { - vec4 finalColor = _12.uColor; + highp vec4 finalColor = _12.uColor; fragColor = finalColor; } -#version 330 +#version 300 es layout(std140) uniform sceneWideBlockVSPS { @@ -3408,43 +3935,46 @@ void main() } -#version 330 +#version 300 es layout(std140) uniform meshWideBlockVS { - float uWidth; - float uHeight; - float uX; - float uY; -} _36; + vec4 uWidth_uHeight_uX_uY; +} _12; out vec2 texCoord; layout(location = 0) in vec2 position; void main() { + float uWidth = _12.uWidth_uHeight_uX_uY.x; + float uHeight = _12.uWidth_uHeight_uX_uY.y; + float uX = _12.uWidth_uHeight_uX_uY.z; + float uY = _12.uWidth_uHeight_uX_uY.w; texCoord = (position * 0.5) + vec2(0.5); - gl_Position = vec4((((((position.x + 1.0) / 2.0) * _36.uWidth) + _36.uX) * 2.0) - 1.0, (((((position.y + 1.0) / 2.0) * _36.uHeight) + _36.uY) * 2.0) - 1.0, 0.5, 1.0); + gl_Position = vec4((((((position.x + 1.0) / 2.0) * uWidth) + uX) * 2.0) - 1.0, (((((position.y + 1.0) / 2.0) * uHeight) + uY) * 2.0) - 1.0, 0.5, 1.0); } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform meshWideBlockPS { - vec4 texOffsetX; - vec4 texOffsetY; + highp vec4 texOffsetX; + highp vec4 texOffsetY; } _33; -uniform sampler2D texture0; +uniform highp sampler2D texture0; -in vec2 texCoord; -layout(location = 0) out vec4 out_result; +in highp vec2 texCoord; +layout(location = 0) out highp vec4 out_result; void main() { - vec2 tex_offset = vec2(0.001000000047497451305389404296875); - vec3 result = texture(texture0, texCoord).xyz * 0.0; + highp vec2 tex_offset = vec2(0.001000000047497451305389404296875); + highp vec3 result = texture(texture0, texCoord).xyz * 0.0; result = vec3(0.0); result += (texture(texture0, texCoord + vec2(_33.texOffsetX.x * tex_offset.x, _33.texOffsetY.x * tex_offset.y)).xyz * 0.125); result += (texture(texture0, texCoord + vec2(_33.texOffsetX.y * tex_offset.x, _33.texOffsetY.y * tex_offset.y)).xyz * 0.375); @@ -3454,39 +3984,40 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform meshWideBlockPS { - vec4 blurAmount; + highp vec4 blurAmount; } _34; -uniform sampler2D screenTex; -uniform sampler2D blurTex; +uniform highp sampler2D screenTex; +uniform highp sampler2D blurTex; -in vec2 texCoord; -layout(location = 0) out vec4 Out_Color; +in highp vec2 texCoord; +layout(location = 0) out highp vec4 Out_Color; void main() { - vec4 screen = texture(screenTex, texCoord); - vec3 blurred = texture(blurTex, texCoord).xyz; - vec3 mixed = mix(screen.xyz, blurred, vec3(_34.blurAmount.z)); - vec3 glow = (blurred * blurred) * _34.blurAmount.w; + highp vec4 screen = texture(screenTex, texCoord); + highp vec3 blurred = texture(blurTex, texCoord).xyz; + highp vec3 mixed = mix(screen.xyz, blurred, vec3(_34.blurAmount.z)); + highp vec3 glow = (blurred * blurred) * _34.blurAmount.w; Out_Color = vec4(mixed + glow, screen.w); } -#version 330 -#ifdef GL_ARB_shading_language_420pack -#extension GL_ARB_shading_language_420pack : require -#endif +#version 300 es +precision mediump float; +precision highp int; -uniform sampler2D Texture; +uniform highp sampler2D Texture; -layout(location = 0) out vec4 Out_Color; -in vec4 Frag_Color; -in vec2 Frag_UV; +layout(location = 0) out highp vec4 Out_Color; +in highp vec4 Frag_Color; +in highp vec2 Frag_UV; void main() { @@ -3494,11 +4025,12 @@ void main() } -#version 330 +#version 300 es layout(std140) uniform modelWideBlockVS { mat4 ProjMtx; + vec4 uiScale; } _30; out vec2 Frag_UV; @@ -3511,189 +4043,223 @@ void main() { Frag_UV = UV; Frag_Color = Color; - gl_Position = _30.ProjMtx * vec4(Position, 0.0, 1.0); + gl_Position = _30.ProjMtx * vec4(Position * _30.uiScale.x, 0.0, 1.0); } -#version 330 +#version 300 es +precision mediump float; +precision highp int; struct PSFog { - vec4 densityParams; - vec4 heightPlane; - vec4 color_and_heightRate; - vec4 heightDensity_and_endColor; - vec4 sunAngle_and_sunColor; - vec4 heightColor_and_endFogDistance; - vec4 sunPercentage; + highp vec4 densityParams; + highp vec4 heightPlane; + highp vec4 color_and_heightRate; + highp vec4 heightDensity_and_endColor; + highp vec4 sunAngle_and_sunColor; + highp vec4 heightColor_and_endFogDistance; + highp vec4 sunPercentage; }; +const vec3 _37[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _44[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + struct SceneExteriorLight { - vec4 uExteriorAmbientColor; - vec4 uExteriorHorizontAmbientColor; - vec4 uExteriorGroundAmbientColor; - vec4 uExteriorDirectColor; - vec4 uExteriorDirectColorDir; + highp vec4 uExteriorAmbientColor; + highp vec4 uExteriorHorizontAmbientColor; + highp vec4 uExteriorGroundAmbientColor; + highp vec4 uExteriorDirectColor; + highp vec4 uExteriorDirectColorDir; + highp vec4 adtSpecMult; }; struct SceneWideParams { - mat4 uLookAtMat; - mat4 uPMatrix; - vec4 uViewUp; - vec4 uInteriorSunDir; + highp mat4 uLookAtMat; + highp mat4 uPMatrix; + highp vec4 uViewUp; + highp vec4 uInteriorSunDir; SceneExteriorLight extLight; }; layout(std140) uniform meshWideBlockPS { - vec4 uAlphaTestv; - ivec4 uPixelShaderv; -} _212; + highp vec4 uAlphaTestv; + ivec4 uPixelShaderBlendModev; +} _277; layout(std140) uniform sceneWideBlockVSPS { SceneWideParams scene; PSFog fogData; -} _378; +} _485; -uniform sampler2D uTexture; -uniform sampler2D uTexture2; -uniform sampler2D uTexture3; +uniform highp sampler2D uTexture; +uniform highp sampler2D uTexture2; +uniform highp sampler2D uTexture3; -in vec2 vTexcoord0; -in vec2 vTexcoord1; -in vec2 vTexcoord2; -in vec4 vColor; -in vec3 vPosition; -layout(location = 0) out vec4 outputColor; +in highp vec2 vTexcoord0; +in highp vec2 vTexcoord1; +in highp vec2 vTexcoord2; +in highp vec4 vColor; +in highp vec3 vPosition; +layout(location = 0) out highp vec4 outputColor; -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) +highp vec3 validateFogColor(highp vec3 fogColor, int blendMode) { - vec4 l_densityParams = fogData.densityParams; - vec4 l_heightPlane = fogData.heightPlane; - vec4 l_color_and_heightRate = fogData.color_and_heightRate; - vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; - float start = l_densityParams.x; - float end = l_densityParams.y; - float density = l_densityParams.z; - float bias = l_densityParams.w; - float vLength = length(vertexInViewSpace); - float z = vLength - bias; - float expMax = max(0.0, z - start); - float expFog = 1.0 / exp(expMax * density); - float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); - float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; - float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); - float finalFog = mix(expFog, expFogHeight, heightFog); - float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; - vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; - float end2 = vLength / l_heightColor_and_endFogDistance.w; - float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + return mix(fogColor, _37[blendMode], vec3(_44[blendMode])); +} + +highp vec4 makeFog(PSFog fogData, highp vec4 final, highp vec3 vertexInViewSpace, highp vec3 sunDirInViewSpace, int blendMode) +{ + highp vec4 l_densityParams = fogData.densityParams; + highp vec4 l_heightPlane = fogData.heightPlane; + highp vec4 l_color_and_heightRate = fogData.color_and_heightRate; + highp vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; + highp float start = l_densityParams.x; + highp float end = l_densityParams.y; + highp float density = l_densityParams.z; + highp float bias = l_densityParams.w; + highp float vLength = length(vertexInViewSpace); + highp float z = vLength - bias; + highp float expMax = max(0.0, z - start); + highp float expFog = 1.0 / exp(expMax * density); + highp float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); + highp float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; + highp float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); + highp float finalFog = mix(expFog, expFogHeight, heightFog); + highp float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); + highp float alpha = 1.0; + bool _139 = blendMode == 13; + if (_139) + { + alpha = min(finalFog, endFadeFog); + } + highp vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + highp vec3 endColor = validateFogColor(param, param_1); + highp vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; + highp float end2 = vLength / l_heightColor_and_endFogDistance.w; + highp float end2_cube = end2 * (end2 * end2); + highp vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + highp vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + highp vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + highp vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); - float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + highp float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); + highp vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + highp vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _162 = nDotSun > 0.0; - if (_162) + bool _218 = nDotSun > 0.0; + if (_218) { nDotSun = (nDotSun * nDotSun) * nDotSun; fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } void main() { - vec4 tex = texture(uTexture, vTexcoord0); - vec4 tex2 = texture(uTexture2, vTexcoord1); - vec4 tex3 = texture(uTexture3, vTexcoord2); - float uAlphaTest = _212.uAlphaTestv.x; - bool _219 = tex.w < uAlphaTest; - if (_219) + highp vec4 tex = texture(uTexture, vTexcoord0); + highp vec4 tex2 = texture(uTexture2, vTexcoord1); + highp vec4 tex3 = texture(uTexture3, vTexcoord2); + highp float uAlphaTest = _277.uAlphaTestv.x; + bool _284 = tex.w < uAlphaTest; + if (_284) { discard; } - vec4 finalColor = vec4((tex * vColor).xyz, tex.w * vColor.w); - int uNonOptPixelShader = _212.uPixelShaderv.x; - bool _246 = uNonOptPixelShader == 0; - if (_246) + highp vec4 finalColor = vec4((tex * vColor).xyz, tex.w * vColor.w); + int uNonOptPixelShader = _277.uPixelShaderBlendModev.x; + bool _310 = uNonOptPixelShader == 0; + if (_310) { - vec3 matDiffuse = vColor.xyz * tex.xyz; + highp vec3 matDiffuse = vColor.xyz * tex.xyz; finalColor = vec4(matDiffuse, tex.w * vColor.w); } else { - bool _267 = uNonOptPixelShader == 1; - if (_267) + bool _331 = uNonOptPixelShader == 1; + if (_331) { - vec4 textureMod = tex * tex2; - float texAlpha = textureMod.w * tex3.w; - float opacity = texAlpha * vColor.w; - vec3 matDiffuse_1 = vColor.xyz * textureMod.xyz; + highp vec4 textureMod = tex * tex2; + highp float texAlpha = textureMod.w * tex3.w; + highp float opacity = texAlpha * vColor.w; + highp vec3 matDiffuse_1 = vColor.xyz * textureMod.xyz; finalColor = vec4(matDiffuse_1, opacity); } else { - bool _299 = uNonOptPixelShader == 2; - if (_299) + bool _363 = uNonOptPixelShader == 2; + if (_363) { - vec4 textureMod_1 = (tex * tex2) * tex3; - float texAlpha_1 = textureMod_1.w; - float opacity_1 = texAlpha_1 * vColor.w; - vec3 matDiffuse_2 = vColor.xyz * textureMod_1.xyz; + highp vec4 textureMod_1 = (tex * tex2) * tex3; + highp float texAlpha_1 = textureMod_1.w; + highp float opacity_1 = texAlpha_1 * vColor.w; + highp vec3 matDiffuse_2 = vColor.xyz * textureMod_1.xyz; finalColor = vec4(matDiffuse_2, opacity_1); } else { - bool _330 = uNonOptPixelShader == 3; - if (_330) + bool _394 = uNonOptPixelShader == 3; + if (_394) { - vec4 textureMod_2 = (tex * tex2) * tex3; - float texAlpha_2 = textureMod_2.w; - float opacity_2 = texAlpha_2 * vColor.w; - vec3 matDiffuse_3 = vColor.xyz * textureMod_2.xyz; + highp vec4 textureMod_2 = (tex * tex2) * tex3; + highp float texAlpha_2 = textureMod_2.w; + highp float opacity_2 = texAlpha_2 * vColor.w; + highp vec3 matDiffuse_3 = vColor.xyz * textureMod_2.xyz; finalColor = vec4(matDiffuse_3, opacity_2); } else { - bool _361 = uNonOptPixelShader == 4; - if (_361) + bool _425 = uNonOptPixelShader == 4; + if (_425) { + highp float t0_973 = tex.x; + highp float t1_978 = tex2.y; + highp float t2_983 = tex3.z; + highp float textureMod_986 = ((t0_973 * t1_978) * t2_983) * 4.0; + highp float depthScale_991 = 1.0 - clamp(vPosition.z * 0.00999999977648258209228515625, 0.0, 1.0); + highp float textureMod_992 = textureMod_986 * depthScale_991; + highp float height_995 = textureMod_992 * vColor.x; + highp float alpha_997 = textureMod_992 * vColor.w; + finalColor = vec4(height_995, 0.0, 0.0, alpha_997); } } } } } - bool _367 = finalColor.w < uAlphaTest; - if (_367) + bool _474 = finalColor.w < uAlphaTest; + if (_474) { discard; } - vec3 sunDir = _378.scene.extLight.uExteriorDirectColorDir.xyz; + highp vec3 sunDir = _485.scene.extLight.uExteriorDirectColorDir.xyz; PSFog arg; - arg.densityParams = _378.fogData.densityParams; - arg.heightPlane = _378.fogData.heightPlane; - arg.color_and_heightRate = _378.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _378.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _378.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _378.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _378.fogData.sunPercentage; - vec3 param = finalColor.xyz; - vec3 param_1 = vPosition; - vec3 param_2 = sunDir; - vec3 _413 = makeFog(arg, param, param_1, param_2); - finalColor = vec4(_413.x, _413.y, _413.z, finalColor.w); + arg.densityParams = _485.fogData.densityParams; + arg.heightPlane = _485.fogData.heightPlane; + arg.color_and_heightRate = _485.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _485.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _485.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _485.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _485.fogData.sunPercentage; + highp vec4 param = finalColor; + highp vec3 param_1 = vPosition; + highp vec3 param_2 = sunDir; + int param_3 = _277.uPixelShaderBlendModev.y; + finalColor = makeFog(arg, param, param_1, param_2, param_3); outputColor = finalColor; } -#version 330 +#version 300 es struct SceneExteriorLight { @@ -3702,6 +4268,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -3754,111 +4321,118 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; struct SceneExteriorLight { - vec4 uExteriorAmbientColor; - vec4 uExteriorHorizontAmbientColor; - vec4 uExteriorGroundAmbientColor; - vec4 uExteriorDirectColor; - vec4 uExteriorDirectColorDir; + highp vec4 uExteriorAmbientColor; + highp vec4 uExteriorHorizontAmbientColor; + highp vec4 uExteriorGroundAmbientColor; + highp vec4 uExteriorDirectColor; + highp vec4 uExteriorDirectColorDir; + highp vec4 adtSpecMult; }; struct SceneWideParams { - mat4 uLookAtMat; - mat4 uPMatrix; - vec4 uViewUp; - vec4 uInteriorSunDir; + highp mat4 uLookAtMat; + highp mat4 uPMatrix; + highp vec4 uViewUp; + highp vec4 uInteriorSunDir; SceneExteriorLight extLight; }; struct InteriorLightParam { - vec4 uInteriorAmbientColorAndApplyInteriorLight; - vec4 uInteriorDirectColorAndApplyExteriorLight; + highp vec4 uInteriorAmbientColorAndApplyInteriorLight; + highp vec4 uInteriorDirectColorAndApplyExteriorLight; }; struct PSFog { - vec4 densityParams; - vec4 heightPlane; - vec4 color_and_heightRate; - vec4 heightDensity_and_endColor; - vec4 sunAngle_and_sunColor; - vec4 heightColor_and_endFogDistance; - vec4 sunPercentage; + highp vec4 densityParams; + highp vec4 heightPlane; + highp vec4 color_and_heightRate; + highp vec4 heightDensity_and_endColor; + highp vec4 sunAngle_and_sunColor; + highp vec4 heightColor_and_endFogDistance; + highp vec4 sunPercentage; }; +const vec3 _215[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _222[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + struct LocalLight { - vec4 color; - vec4 position; - vec4 attenuation; + highp vec4 color; + highp vec4 position; + highp vec4 attenuation; }; layout(std140) uniform meshWideBlockPS { - ivec4 PixelShader_UnFogged_IsAffectedByLight; - vec4 uFogColorAndAlphaTest; - vec4 uPcColor; -} _409; + ivec4 PixelShader_UnFogged_IsAffectedByLight_blendMode; + highp vec4 uFogColorAndAlphaTest; + highp vec4 uTexSampleAlpha; + highp vec4 uPcColor; +} _472; layout(std140) uniform modelWideBlockPS { InteriorLightParam intLight; LocalLight pc_lights[4]; ivec4 lightCountAndBcHack; - vec4 interiorExteriorBlend; -} _433; + highp vec4 interiorExteriorBlend; +} _495; layout(std140) uniform sceneWideBlockVSPS { SceneWideParams scene; PSFog fogData; -} _472; +} _534; layout(std140) uniform modelWideBlockVS { - mat4 uPlacementMat; - mat4 uBoneMatrixes[220]; -} _480; + highp mat4 uPlacementMat; + highp mat4 uBoneMatrixes[220]; +} _542; -uniform sampler2D uTexture; -uniform sampler2D uTexture2; -uniform sampler2D uTexture3; -uniform sampler2D uTexture4; +uniform highp sampler2D uTexture; +uniform highp sampler2D uTexture2; +uniform highp sampler2D uTexture3; +uniform highp sampler2D uTexture4; -in vec2 vTexCoord; -in vec2 vTexCoord2; -in vec2 vTexCoord3; -in vec4 vDiffuseColor; -in vec3 vPosition; -in vec3 vNormal; -layout(location = 0) out vec4 outputColor; +in highp vec2 vTexCoord; +in highp vec2 vTexCoord2; +in highp vec2 vTexCoord3; +in highp vec4 vDiffuseColor; +in highp vec3 vPosition; +in highp vec3 vNormal; +layout(location = 0) out highp vec4 outputColor; -vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, vec3 accumLight, vec3 precomputedLight, vec3 specular) +highp vec3 calcLight(highp vec3 matDiffuse, highp vec3 vNormal_1, bool applyLight, highp float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, highp vec3 accumLight, highp vec3 precomputedLight, highp vec3 specular) { - vec3 localDiffuse = accumLight; - bool _42 = !applyLight; - if (_42) + highp vec3 localDiffuse = accumLight; + bool _51 = !applyLight; + if (_51) { return matDiffuse; } - vec3 lDiffuse = vec3(0.0); - vec3 normalizedN = normalize(vNormal_1); - bool _59 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - vec3 currColor; - if (_59) + highp vec3 lDiffuse = vec3(0.0); + highp vec3 normalizedN = normalize(vNormal_1); + bool _67 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + highp vec3 currColor; + if (_67) { - float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); - float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); - vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; - vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; - vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; - bool _97 = nDotUp >= 0.0; - if (_97) + highp float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); + highp float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); + highp vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; + highp vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; + highp vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; + bool _104 = nDotUp >= 0.0; + if (_104) { currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); } @@ -3866,19 +4440,19 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE { currColor = mix(adjHorizAmbient, adjGroundAmbient, vec3(-nDotUp)); } - vec3 skyColor = currColor * 1.10000002384185791015625; - vec3 groundColor = currColor * 0.699999988079071044921875; + highp vec3 skyColor = currColor * 1.10000002384185791015625; + highp vec3 groundColor = currColor * 0.699999988079071044921875; lDiffuse = sceneParams.extLight.uExteriorDirectColor.xyz * nDotL; currColor = mix(groundColor, skyColor, vec3(0.5 + (0.5 * nDotL))); } - bool _137 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; - if (_137) + bool _144 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; + if (_144) { - float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); - vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; - vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; - bool _161 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - if (_161) + highp float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); + highp vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; + highp vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; + bool _168 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + if (_168) { lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); @@ -3889,390 +4463,406 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE currColor = interiorAmbient; } } - vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); - vec3 linearDiffTerm = (matDiffuse * matDiffuse) * localDiffuse; - vec3 specTerm = specular; - vec3 emTerm = vec3(0.0); - return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; -} - -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) -{ - vec4 l_densityParams = fogData.densityParams; - vec4 l_heightPlane = fogData.heightPlane; - vec4 l_color_and_heightRate = fogData.color_and_heightRate; - vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; - float start = l_densityParams.x; - float end = l_densityParams.y; - float density = l_densityParams.z; - float bias = l_densityParams.w; - float vLength = length(vertexInViewSpace); - float z = vLength - bias; - float expMax = max(0.0, z - start); - float expFog = 1.0 / exp(expMax * density); - float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); - float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; - float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); - float finalFog = mix(expFog, expFogHeight, heightFog); - float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; - vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; - float end2 = vLength / l_heightColor_and_endFogDistance.w; - float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + highp vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); + highp vec3 linearDiffTerm = (matDiffuse * matDiffuse) * localDiffuse; + highp vec3 specTerm = specular; + highp vec3 emTerm = vec3(0.0); + return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; +} + +highp vec3 validateFogColor(highp vec3 fogColor, int blendMode) +{ + return mix(fogColor, _215[blendMode], vec3(_222[blendMode])); +} + +highp vec4 makeFog(PSFog fogData, highp vec4 final, highp vec3 vertexInViewSpace, highp vec3 sunDirInViewSpace, int blendMode) +{ + highp vec4 l_densityParams = fogData.densityParams; + highp vec4 l_heightPlane = fogData.heightPlane; + highp vec4 l_color_and_heightRate = fogData.color_and_heightRate; + highp vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; + highp float start = l_densityParams.x; + highp float end = l_densityParams.y; + highp float density = l_densityParams.z; + highp float bias = l_densityParams.w; + highp float vLength = length(vertexInViewSpace); + highp float z = vLength - bias; + highp float expMax = max(0.0, z - start); + highp float expFog = 1.0 / exp(expMax * density); + highp float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); + highp float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; + highp float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); + highp float finalFog = mix(expFog, expFogHeight, heightFog); + highp float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); + highp float alpha = 1.0; + bool _310 = blendMode == 13; + if (_310) + { + alpha = min(finalFog, endFadeFog); + } + highp vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + highp vec3 endColor = validateFogColor(param, param_1); + highp vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; + highp float end2 = vLength / l_heightColor_and_endFogDistance.w; + highp float end2_cube = end2 * (end2 * end2); + highp vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + highp vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + highp vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + highp vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); - float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + highp float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); + highp vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + highp vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _334 = nDotSun > 0.0; - if (_334) + bool _388 = nDotSun > 0.0; + if (_388) { nDotSun = (nDotSun * nDotSun) * nDotSun; fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } void main() { - vec2 texCoord = vTexCoord; - vec2 texCoord2 = vTexCoord2; - vec2 texCoord3 = vTexCoord3; - vec4 tex = texture(uTexture, texCoord); - vec4 tex2 = texture(uTexture2, texCoord2); - vec4 tex3 = texture(uTexture3, texCoord3); - vec4 tex2WithTextCoord1 = texture(uTexture2, texCoord); - vec4 tex3WithTextCoord1 = texture(uTexture3, texCoord); - vec4 tex4WithTextCoord2 = texture(uTexture4, texCoord2); - vec4 finalColor = vec4(0.0); - vec4 meshResColor = vDiffuseColor; - bool _413 = _409.PixelShader_UnFogged_IsAffectedByLight.z == 1; - vec3 accumLight; - if (_413) + highp vec2 texCoord = vTexCoord; + highp vec2 texCoord2 = vTexCoord2; + highp vec2 texCoord3 = vTexCoord3; + highp vec4 tex = texture(uTexture, texCoord); + highp vec4 tex2 = texture(uTexture2, texCoord2); + highp vec4 tex3 = texture(uTexture3, texCoord3); + highp vec4 tex2WithTextCoord1 = texture(uTexture2, texCoord); + highp vec4 tex3WithTextCoord1 = texture(uTexture3, texCoord); + highp vec4 tex4WithTextCoord2 = texture(uTexture4, texCoord2); + highp vec4 finalColor = vec4(0.0); + highp vec4 meshResColor = vDiffuseColor; + bool _476 = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.z == 1; + highp vec3 accumLight; + if (_476) { - vec3 vPos3 = vPosition; - vec3 vNormal3 = normalize(vNormal); - vec3 lightColor = vec3(0.0); - int count = int(_433.pc_lights[0].attenuation.w); + highp vec3 vPos3 = vPosition; + highp vec3 vNormal3 = normalize(vNormal); + highp vec3 lightColor = vec3(0.0); + int count = int(_495.pc_lights[0].attenuation.w); LocalLight lightRecord; for (int index = 0; index < 4; index++) { - bool _449 = index >= _433.lightCountAndBcHack.x; - if (_449) + bool _511 = index >= _495.lightCountAndBcHack.x; + if (_511) { break; } - lightRecord.color = _433.pc_lights[index].color; - lightRecord.position = _433.pc_lights[index].position; - lightRecord.attenuation = _433.pc_lights[index].attenuation; - vec3 vectorToLight = (_472.scene.uLookAtMat * (_480.uPlacementMat * lightRecord.position)).xyz - vPos3; - float distanceToLightSqr = dot(vectorToLight, vectorToLight); - float distanceToLightInv = inversesqrt(distanceToLightSqr); - float distanceToLight = distanceToLightSqr * distanceToLightInv; - float diffuseTerm1 = max(dot(vectorToLight, vNormal3) * distanceToLightInv, 0.0); - vec4 attenuationRec = lightRecord.attenuation; - float attenuation = 1.0 - clamp((distanceToLight - attenuationRec.x) * (1.0 / (attenuationRec.z - attenuationRec.x)), 0.0, 1.0); - vec3 attenuatedColor = lightRecord.color.xyz * attenuation; + lightRecord.color = _495.pc_lights[index].color; + lightRecord.position = _495.pc_lights[index].position; + lightRecord.attenuation = _495.pc_lights[index].attenuation; + highp vec3 vectorToLight = (_534.scene.uLookAtMat * (_542.uPlacementMat * lightRecord.position)).xyz - vPos3; + highp float distanceToLightSqr = dot(vectorToLight, vectorToLight); + highp float distanceToLightInv = inversesqrt(distanceToLightSqr); + highp float distanceToLight = distanceToLightSqr * distanceToLightInv; + highp float diffuseTerm1 = max(dot(vectorToLight, vNormal3) * distanceToLightInv, 0.0); + highp vec4 attenuationRec = lightRecord.attenuation; + highp float attenuation = 1.0 - clamp((distanceToLight - attenuationRec.x) * (1.0 / (attenuationRec.z - attenuationRec.x)), 0.0, 1.0); + highp vec3 attenuatedColor = lightRecord.color.xyz * attenuation; lightColor += vec3((attenuatedColor * attenuatedColor) * diffuseTerm1); } - vec3 _547 = clamp(lightColor, vec3(0.0), vec3(1.0)); - meshResColor = vec4(_547.x, _547.y, _547.z, meshResColor.w); - accumLight = mix(lightColor, meshResColor.xyz, vec3(float(_433.lightCountAndBcHack.y))); + highp vec3 _609 = clamp(lightColor, vec3(0.0), vec3(1.0)); + meshResColor = vec4(_609.x, _609.y, _609.z, meshResColor.w); + accumLight = mix(lightColor, meshResColor.xyz, vec3(float(_495.lightCountAndBcHack.y))); } - float finalOpacity = 0.0; - vec3 specular = vec3(0.0); - vec3 visParams = vec3(1.0); - vec4 genericParams[3]; + highp float finalOpacity = 0.0; + highp vec3 specular = vec3(0.0); + highp vec3 visParams = vec3(1.0); + highp vec4 genericParams[3]; genericParams[0] = vec4(1.0); genericParams[1] = vec4(1.0); genericParams[2] = vec4(1.0); - int uPixelShader = _409.PixelShader_UnFogged_IsAffectedByLight.x; - bool _574 = uPixelShader == 0; - vec3 matDiffuse; - float opacity; + bool canDiscard = false; + highp float discardAlpha = 1.0; + int uPixelShader = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.x; + bool _638 = uPixelShader == 0; + highp vec3 matDiffuse; #if (FRAGMENTSHADER == 0) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _595 = uPixelShader == 1; + bool _651 = uPixelShader == 1; #if (FRAGMENTSHADER == 1) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _615 = uPixelShader == 2; + bool _665 = uPixelShader == 2; #if (FRAGMENTSHADER == 2) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = tex2.w * vDiffuseColor.w; - opacity = tex2.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex2.w; + canDiscard = true; #endif - bool _643 = uPixelShader == 3; + bool _681 = uPixelShader == 3; #if (FRAGMENTSHADER == 3) matDiffuse = (((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz) * 2.0; - opacity = (tex2.w * 2.0) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex2.w * 2.0; + canDiscard = true; #endif - bool _668 = uPixelShader == 4; + bool _699 = uPixelShader == 4; #if (FRAGMENTSHADER == 4) matDiffuse = (((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz) * 2.0; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _689 = uPixelShader == 5; + bool _714 = uPixelShader == 5; #if (FRAGMENTSHADER == 5) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _709 = uPixelShader == 6; + bool _728 = uPixelShader == 6; #if (FRAGMENTSHADER == 6) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = (tex.w * tex2.w) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w * tex2.w; + canDiscard = true; #endif - bool _736 = uPixelShader == 7; + bool _748 = uPixelShader == 7; #if (FRAGMENTSHADER == 7) matDiffuse = (((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz) * 2.0; - opacity = ((tex.w * tex2.w) * 2.0) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = (tex.w * tex2.w) * 2.0; + canDiscard = true; #endif - bool _765 = uPixelShader == 8; + bool _770 = uPixelShader == 8; #if (FRAGMENTSHADER == 8) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = (tex.w + tex2.w) * vDiffuseColor.w; + discardAlpha = tex.w + tex2.w; + canDiscard = true; specular = tex2.xyz; - finalOpacity = opacity * visParams.x; #endif - bool _791 = uPixelShader == 9; + bool _789 = uPixelShader == 9; #if (FRAGMENTSHADER == 9) matDiffuse = (((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz) * 2.0; - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _816 = uPixelShader == 10; + bool _807 = uPixelShader == 10; #if (FRAGMENTSHADER == 10) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = tex.w * vDiffuseColor.w; + discardAlpha = tex.w; + canDiscard = true; specular = tex2.xyz; - finalOpacity = opacity * visParams.x; #endif - bool _839 = uPixelShader == 11; + bool _823 = uPixelShader == 11; #if (FRAGMENTSHADER == 11) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _863 = uPixelShader == 12; + bool _840 = uPixelShader == 12; #if (FRAGMENTSHADER == 12) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix((tex.xyz * tex2.xyz) * 2.0, tex.xyz, vec3(tex.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _891 = uPixelShader == 13; + bool _861 = uPixelShader == 13; #if (FRAGMENTSHADER == 13) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; specular = tex2.xyz * tex2.w; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _914 = uPixelShader == 14; + bool _878 = uPixelShader == 14; #if (FRAGMENTSHADER == 14) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; specular = (tex2.xyz * tex2.w) * (1.0 - tex.w); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _941 = uPixelShader == 15; + bool _899 = uPixelShader == 15; #if (FRAGMENTSHADER == 15) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix((tex.xyz * tex2.xyz) * 2.0, tex.xyz, vec3(tex.w)); - specular = (tex3.xyz * tex3.w) * genericParams[0].z; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + specular = (tex3.xyz * tex3.w) * _472.uTexSampleAlpha.z; #endif - bool _977 = uPixelShader == 16; + bool _929 = uPixelShader == 16; #if (FRAGMENTSHADER == 16) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = tex.w * vDiffuseColor.w; + discardAlpha = tex.w; + canDiscard = true; specular = tex2.xyz * tex2.w; - finalOpacity = opacity * visParams.x; #endif - bool _1003 = uPixelShader == 17; + bool _948 = uPixelShader == 17; #if (FRAGMENTSHADER == 17) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = (tex.w + (tex2.w * (((0.300000011920928955078125 * tex2.x) + (0.589999973773956298828125 * tex2.y)) + (0.10999999940395355224609375 * tex2.z)))) * vDiffuseColor.w; + discardAlpha = tex.w + (tex2.w * (((0.300000011920928955078125 * tex2.x) + (0.589999973773956298828125 * tex2.y)) + (0.10999999940395355224609375 * tex2.z))); + canDiscard = true; specular = (tex2.xyz * tex2.w) * (1.0 - tex.w); - finalOpacity = opacity * visParams.x; #endif - bool _1051 = uPixelShader == 18; + bool _989 = uPixelShader == 18; #if (FRAGMENTSHADER == 18) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex.xyz, tex2.xyz, vec3(tex2.w)), tex.xyz, vec3(tex.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1081 = uPixelShader == 19; + bool _1013 = uPixelShader == 19; #if (FRAGMENTSHADER == 19) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix((tex.xyz * tex2.xyz) * 2.0, tex3.xyz, vec3(tex3.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1109 = uPixelShader == 20; + bool _1035 = uPixelShader == 20; #if (FRAGMENTSHADER == 20) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - specular = (tex2.xyz * tex2.w) * genericParams[0].y; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + specular = (tex2.xyz * tex2.w) * _472.uTexSampleAlpha.y; #endif - bool _1135 = uPixelShader == 21; + bool _1055 = uPixelShader == 21; #if (FRAGMENTSHADER == 21) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = (tex.w + tex2.w) * vDiffuseColor.w; + discardAlpha = tex.w + tex2.w; + canDiscard = true; specular = tex2.xyz * (1.0 - tex.w); - finalOpacity = opacity * visParams.x; #endif - bool _1165 = uPixelShader == 22; + bool _1078 = uPixelShader == 22; #if (FRAGMENTSHADER == 22) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz * tex2.xyz, tex.xyz, vec3(tex.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1192 = uPixelShader == 23; + bool _1099 = uPixelShader == 23; #if (FRAGMENTSHADER == 23) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = tex.w * vDiffuseColor.w; - specular = (tex2.xyz * tex2.w) * genericParams[0].y; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; + specular = (tex2.xyz * tex2.w) * _472.uTexSampleAlpha.y; #endif - bool _1221 = uPixelShader == 24; + bool _1121 = uPixelShader == 24; #if (FRAGMENTSHADER == 24) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz, tex2.xyz, vec3(tex2.w)); - specular = (tex.xyz * tex.w) * genericParams[0].x; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + specular = (tex.xyz * tex.w) * _472.uTexSampleAlpha.x; #endif - bool _1253 = uPixelShader == 25; + bool _1147 = uPixelShader == 25; #if (FRAGMENTSHADER == 25) - float glowOpacity = clamp(tex3.w * genericParams[0].z, 0.0, 1.0); + highp float glowOpacity = clamp(tex3.w * _472.uTexSampleAlpha.z, 0.0, 1.0); matDiffuse = ((vDiffuseColor.xyz * 2.0) * mix((tex.xyz * tex2.xyz) * 2.0, tex.xyz, vec3(tex.w))) * (1.0 - glowOpacity); specular = tex3.xyz * glowOpacity; - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1295 = uPixelShader == 26; + bool _1183 = uPixelShader == 26; #if (FRAGMENTSHADER == 26) - matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(genericParams[0].z, 0.0, 1.0))).xyz; - opacity = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(genericParams[0].z, 0.0, 1.0))).w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.z, 0.0, 1.0))).xyz; + discardAlpha = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.z, 0.0, 1.0))).w; + canDiscard = true; #endif - bool _1340 = uPixelShader == 27; + bool _1221 = uPixelShader == 27; #if (FRAGMENTSHADER == 27) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix((tex.xyz * tex2.xyz) * 2.0, tex3.xyz, vec3(tex3.w)), tex.xyz, vec3(tex.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1374 = uPixelShader == 28; + bool _1249 = uPixelShader == 28; #if (FRAGMENTSHADER == 28) - matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(genericParams[0].z, 0.0, 1.0))).xyz; - opacity = (mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(genericParams[0].z, 0.0, 1.0))).w * tex4WithTextCoord2.w) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.z, 0.0, 1.0))).xyz; + discardAlpha = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.y, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(_472.uTexSampleAlpha.z, 0.0, 1.0))).w * tex4WithTextCoord2.w; + canDiscard = true; #endif - bool _1422 = uPixelShader == 29; + bool _1290 = uPixelShader == 29; #if (FRAGMENTSHADER == 29) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz, tex2.xyz, vec3(tex2.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1446 = uPixelShader == 30; + bool _1308 = uPixelShader == 30; #if (FRAGMENTSHADER == 30) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz * mix(genericParams[0].xyz, tex2.xyz * genericParams[1].xyz, vec3(tex2.w)), tex3.xyz * genericParams[2].xyz, vec3(tex3.w)); - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _1491 = uPixelShader == 31; + bool _1346 = uPixelShader == 31; #if (FRAGMENTSHADER == 31) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * mix(genericParams[0].xyz, tex2.xyz * genericParams[1].xyz, vec3(tex2.w)); - opacity = tex.w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _1526 = uPixelShader == 32; + bool _1374 = uPixelShader == 32; #if (FRAGMENTSHADER == 32) matDiffuse = (vDiffuseColor.xyz * 2.0) * mix(tex.xyz * mix(genericParams[0].xyz, tex2.xyz * genericParams[1].xyz, vec3(tex2.w)), tex3.xyz * genericParams[2].xyz, vec3(tex3.w)); - opacity = vDiffuseColor.w; - finalOpacity = opacity * visParams.x; #endif - bool _1568 = uPixelShader == 33; + bool _1410 = uPixelShader == 33; #if (FRAGMENTSHADER == 33) matDiffuse = (vDiffuseColor.xyz * 2.0) * tex.xyz; - opacity = (tex.w * vDiffuseColor.w) * visParams.x; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w; + canDiscard = true; #endif - bool _1592 = uPixelShader == 34; + bool _1424 = uPixelShader == 34; #if (FRAGMENTSHADER == 34) - finalColor = vec4(1.0); + discardAlpha = tex.w; + canDiscard = true; #endif - bool _1598 = uPixelShader == 35; + bool _1432 = uPixelShader == 35; #if (FRAGMENTSHADER == 35) matDiffuse = (vDiffuseColor.xyz * 2.0) * (((tex * tex2) * tex3) * genericParams[0]).xyz; - opacity = (((tex * tex2) * tex3) * genericParams[0]).w * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = (((tex * tex2) * tex3) * genericParams[0]).w; + canDiscard = true; #endif - bool _1633 = uPixelShader == 36; + bool _1460 = uPixelShader == 36; #if (FRAGMENTSHADER == 36) matDiffuse = ((vDiffuseColor.xyz * 2.0) * tex.xyz) * tex2.xyz; - opacity = (tex.w * tex2.w) * vDiffuseColor.w; - finalOpacity = opacity * visParams.x; + discardAlpha = tex.w * tex2.w; + canDiscard = true; #endif - vec3 param = matDiffuse; - vec3 param_1 = vNormal; - bool param_2 = _409.PixelShader_UnFogged_IsAffectedByLight.z > 0; - float param_3 = _433.interiorExteriorBlend.x; + int blendMode = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.w; + bool _1481 = blendMode == 13; + if (_1481) + { + finalOpacity = discardAlpha * vDiffuseColor.w; + } + else + { + bool _1491 = blendMode == 1; + if (_1491) + { + finalOpacity = vDiffuseColor.w; + bool _1500 = canDiscard && (discardAlpha < 0.501960813999176025390625); + if (_1500) + { + discard; + } + finalOpacity = vDiffuseColor.w; + } + else + { + bool _1508 = blendMode == 0; + if (_1508) + { + finalOpacity = vDiffuseColor.w; + } + else + { + finalOpacity = discardAlpha * vDiffuseColor.w; + } + } + } + highp vec3 param = matDiffuse; + highp vec3 param_1 = vNormal; + bool param_2 = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.z > 0; + highp float param_3 = _495.interiorExteriorBlend.x; SceneWideParams param_4; - param_4.uLookAtMat = _472.scene.uLookAtMat; - param_4.uPMatrix = _472.scene.uPMatrix; - param_4.uViewUp = _472.scene.uViewUp; - param_4.uInteriorSunDir = _472.scene.uInteriorSunDir; - param_4.extLight.uExteriorAmbientColor = _472.scene.extLight.uExteriorAmbientColor; - param_4.extLight.uExteriorHorizontAmbientColor = _472.scene.extLight.uExteriorHorizontAmbientColor; - param_4.extLight.uExteriorGroundAmbientColor = _472.scene.extLight.uExteriorGroundAmbientColor; - param_4.extLight.uExteriorDirectColor = _472.scene.extLight.uExteriorDirectColor; - param_4.extLight.uExteriorDirectColorDir = _472.scene.extLight.uExteriorDirectColorDir; + param_4.uLookAtMat = _534.scene.uLookAtMat; + param_4.uPMatrix = _534.scene.uPMatrix; + param_4.uViewUp = _534.scene.uViewUp; + param_4.uInteriorSunDir = _534.scene.uInteriorSunDir; + param_4.extLight.uExteriorAmbientColor = _534.scene.extLight.uExteriorAmbientColor; + param_4.extLight.uExteriorHorizontAmbientColor = _534.scene.extLight.uExteriorHorizontAmbientColor; + param_4.extLight.uExteriorGroundAmbientColor = _534.scene.extLight.uExteriorGroundAmbientColor; + param_4.extLight.uExteriorDirectColor = _534.scene.extLight.uExteriorDirectColor; + param_4.extLight.uExteriorDirectColorDir = _534.scene.extLight.uExteriorDirectColorDir; + param_4.extLight.adtSpecMult = _534.scene.extLight.adtSpecMult; InteriorLightParam param_5; - param_5.uInteriorAmbientColorAndApplyInteriorLight = _433.intLight.uInteriorAmbientColorAndApplyInteriorLight; - param_5.uInteriorDirectColorAndApplyExteriorLight = _433.intLight.uInteriorDirectColorAndApplyExteriorLight; - vec3 param_6 = accumLight; + param_5.uInteriorAmbientColorAndApplyInteriorLight = _495.intLight.uInteriorAmbientColorAndApplyInteriorLight; + param_5.uInteriorDirectColorAndApplyExteriorLight = _495.intLight.uInteriorDirectColorAndApplyExteriorLight; + highp vec3 param_6 = accumLight; finalColor = vec4(calcLight(param, param_1, param_2, param_3, param_4, param_5, param_6, vec3(0.0), specular), finalOpacity); - bool _1715 = finalColor.w < _409.uFogColorAndAlphaTest.w; - if (_1715) - { - discard; - } - int uUnFogged = _409.PixelShader_UnFogged_IsAffectedByLight.y; - bool _1723 = uUnFogged == 0; - if (_1723) + int uUnFogged = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.y; + bool _1578 = uUnFogged == 0; + if (_1578) { - vec3 sunDir = mix(_472.scene.uInteriorSunDir, _472.scene.extLight.uExteriorDirectColorDir, vec4(_433.interiorExteriorBlend.x)).xyz; + highp vec3 sunDir = mix(_534.scene.uInteriorSunDir, _534.scene.extLight.uExteriorDirectColorDir, vec4(_495.interiorExteriorBlend.x)).xyz; PSFog arg; - arg.densityParams = _472.fogData.densityParams; - arg.heightPlane = _472.fogData.heightPlane; - arg.color_and_heightRate = _472.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _472.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _472.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _472.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _472.fogData.sunPercentage; - vec3 param_7 = finalColor.xyz; - vec3 param_8 = vPosition; - vec3 param_9 = sunDir; - vec3 _1765 = makeFog(arg, param_7, param_8, param_9); - finalColor = vec4(_1765.x, _1765.y, _1765.z, finalColor.w); + arg.densityParams = _534.fogData.densityParams; + arg.heightPlane = _534.fogData.heightPlane; + arg.color_and_heightRate = _534.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _534.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _534.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _534.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _534.fogData.sunPercentage; + highp vec4 param_7 = finalColor; + highp vec3 param_8 = vPosition; + highp vec3 param_9 = sunDir; + int param_10 = _472.PixelShader_UnFogged_IsAffectedByLight_blendMode.w; + finalColor = makeFog(arg, param_7, param_8, param_9, param_10); } outputColor = finalColor; } -#version 330 +#version 300 es struct SceneExteriorLight { @@ -4281,6 +4871,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -4507,48 +5098,50 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; layout(std140) uniform meshWideBlockPS { - float gauss_offsets[5]; - float gauss_weights[5]; - vec2 uResolution; + highp float gauss_offsets[5]; + highp float gauss_weights[5]; + highp vec2 uResolution; } _34; -uniform sampler2D u_sampler; -uniform sampler2D u_depth; +uniform highp sampler2D u_sampler; +uniform highp sampler2D u_depth; -in vec2 v_texcoord; -layout(location = 0) out vec4 outputColor; +in highp vec2 v_texcoord; +layout(location = 0) out highp vec4 outputColor; void main() { - float FXAA_SPAN_MAX = 8.0; - float FXAA_REDUCE_MUL = 0.125; - float FXAA_REDUCE_MIN = 0.0078125; - vec3 rgbNW = texture(u_sampler, v_texcoord + (vec2(-1.0) / _34.uResolution)).xyz; - vec3 rgbNE = texture(u_sampler, v_texcoord + (vec2(1.0, -1.0) / _34.uResolution)).xyz; - vec3 rgbSW = texture(u_sampler, v_texcoord + (vec2(-1.0, 1.0) / _34.uResolution)).xyz; - vec3 rgbSE = texture(u_sampler, v_texcoord + (vec2(1.0) / _34.uResolution)).xyz; - vec3 rgbM = texture(u_sampler, v_texcoord).xyz; - vec3 luma = vec3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625); - float lumaNW = dot(rgbNW, luma); - float lumaNE = dot(rgbNE, luma); - float lumaSW = dot(rgbSW, luma); - float lumaSE = dot(rgbSE, luma); - float lumaM = dot(rgbM, luma); - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); - vec2 dir; + highp float FXAA_SPAN_MAX = 8.0; + highp float FXAA_REDUCE_MUL = 0.125; + highp float FXAA_REDUCE_MIN = 0.0078125; + highp vec3 rgbNW = texture(u_sampler, v_texcoord + (vec2(-1.0) / _34.uResolution)).xyz; + highp vec3 rgbNE = texture(u_sampler, v_texcoord + (vec2(1.0, -1.0) / _34.uResolution)).xyz; + highp vec3 rgbSW = texture(u_sampler, v_texcoord + (vec2(-1.0, 1.0) / _34.uResolution)).xyz; + highp vec3 rgbSE = texture(u_sampler, v_texcoord + (vec2(1.0) / _34.uResolution)).xyz; + highp vec3 rgbM = texture(u_sampler, v_texcoord).xyz; + highp vec3 luma = vec3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625); + highp float lumaNW = dot(rgbNW, luma); + highp float lumaNE = dot(rgbNE, luma); + highp float lumaSW = dot(rgbSW, luma); + highp float lumaSE = dot(rgbSE, luma); + highp float lumaM = dot(rgbM, luma); + highp float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + highp float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + highp vec2 dir; dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); dir.y = (lumaNW + lumaSW) - (lumaNE + lumaSE); - float dirReduce = max((((lumaNW + lumaNE) + lumaSW) + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + highp float dirReduce = max((((lumaNW + lumaNE) + lumaSW) + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + highp float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) / _34.uResolution; - vec3 rgbA = (texture(u_sampler, v_texcoord + (dir * (-0.16666667163372039794921875))).xyz + texture(u_sampler, v_texcoord + (dir * 0.16666667163372039794921875)).xyz) * 0.5; - vec3 rgbB = (rgbA * 0.5) + ((texture(u_sampler, v_texcoord + (dir * (-0.5))).xyz + texture(u_sampler, v_texcoord + (dir * 0.5)).xyz) * 0.25); - float lumaB = dot(rgbB, luma); + highp vec3 rgbA = (texture(u_sampler, v_texcoord + (dir * (-0.16666667163372039794921875))).xyz + texture(u_sampler, v_texcoord + (dir * 0.16666667163372039794921875)).xyz) * 0.5; + highp vec3 rgbB = (rgbA * 0.5) + ((texture(u_sampler, v_texcoord + (dir * (-0.5))).xyz + texture(u_sampler, v_texcoord + (dir * 0.5)).xyz) * 0.25); + highp float lumaB = dot(rgbB, luma); bool _240 = (lumaB < lumaMin) || (lumaB > lumaMax); if (_240) { @@ -4561,10 +5154,7 @@ void main() } -#version 330 -#ifdef GL_ARB_shading_language_420pack -#extension GL_ARB_shading_language_420pack : require -#endif +#version 300 es layout(location = 0) in vec4 a_position; out vec2 v_texcoord; @@ -4576,7 +5166,165 @@ void main() } -#version 330 +#version 300 es +precision mediump float; +precision highp int; + +struct PSFog +{ + highp vec4 densityParams; + highp vec4 heightPlane; + highp vec4 color_and_heightRate; + highp vec4 heightDensity_and_endColor; + highp vec4 sunAngle_and_sunColor; + highp vec4 heightColor_and_endFogDistance; + highp vec4 sunPercentage; +}; + +const vec3 _37[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _44[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + +struct SceneExteriorLight +{ + highp vec4 uExteriorAmbientColor; + highp vec4 uExteriorHorizontAmbientColor; + highp vec4 uExteriorGroundAmbientColor; + highp vec4 uExteriorDirectColor; + highp vec4 uExteriorDirectColorDir; + highp vec4 adtSpecMult; +}; + +struct SceneWideParams +{ + highp mat4 uLookAtMat; + highp mat4 uPMatrix; + highp vec4 uViewUp; + highp vec4 uInteriorSunDir; + SceneExteriorLight extLight; +}; + +layout(std140) uniform meshWideBlockPS +{ + highp vec4 uAlphaTestScalev; + ivec4 uPixelShaderv; + highp vec4 uTextureTranslate; +} _256; + +layout(std140) uniform sceneWideBlockVSPS +{ + SceneWideParams scene; + PSFog fogData; +} _304; + +uniform highp sampler2D uTexture; + +in highp vec2 vTexcoord0; +in highp vec4 vColor; +in highp vec3 vPosition; +layout(location = 0) out highp vec4 outputColor; + +highp vec3 validateFogColor(highp vec3 fogColor, int blendMode) +{ + return mix(fogColor, _37[blendMode], vec3(_44[blendMode])); +} + +highp vec4 makeFog(PSFog fogData, highp vec4 final, highp vec3 vertexInViewSpace, highp vec3 sunDirInViewSpace, int blendMode) +{ + highp vec4 l_densityParams = fogData.densityParams; + highp vec4 l_heightPlane = fogData.heightPlane; + highp vec4 l_color_and_heightRate = fogData.color_and_heightRate; + highp vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; + highp float start = l_densityParams.x; + highp float end = l_densityParams.y; + highp float density = l_densityParams.z; + highp float bias = l_densityParams.w; + highp float vLength = length(vertexInViewSpace); + highp float z = vLength - bias; + highp float expMax = max(0.0, z - start); + highp float expFog = 1.0 / exp(expMax * density); + highp float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); + highp float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; + highp float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); + highp float finalFog = mix(expFog, expFogHeight, heightFog); + highp float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); + highp float alpha = 1.0; + bool _139 = blendMode == 13; + if (_139) + { + alpha = min(finalFog, endFadeFog); + } + highp vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + highp vec3 endColor = validateFogColor(param, param_1); + highp vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; + highp float end2 = vLength / l_heightColor_and_endFogDistance.w; + highp float end2_cube = end2 * (end2 * end2); + highp vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + highp vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + highp vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + highp vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); + highp float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); + highp vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + highp vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); + nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); + bool _218 = nDotSun > 0.0; + if (_218) + { + nDotSun = (nDotSun * nDotSun) * nDotSun; + fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); + } + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); +} + +void main() +{ + highp vec2 textCoordScale = _256.uAlphaTestScalev.yz; + highp vec2 texcoord = (vTexcoord0 * textCoordScale) + _256.uTextureTranslate.xy; + highp vec4 tex = texture(uTexture, texcoord); + highp vec4 finalColor = vec4(vColor.xyz * tex.xyz, tex.w * vColor.w); + highp vec3 sunDir = _304.scene.extLight.uExteriorDirectColorDir.xyz; + PSFog arg; + arg.densityParams = _304.fogData.densityParams; + arg.heightPlane = _304.fogData.heightPlane; + arg.color_and_heightRate = _304.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _304.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _304.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _304.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _304.fogData.sunPercentage; + highp vec4 param = finalColor; + highp vec3 param_1 = vPosition; + highp vec3 param_2 = sunDir; + int param_3 = _256.uPixelShaderv.y; + finalColor = makeFog(arg, param, param_1, param_2, param_3); + outputColor = finalColor; +} + + +#version 300 es + +struct SceneExteriorLight +{ + vec4 uExteriorAmbientColor; + vec4 uExteriorHorizontAmbientColor; + vec4 uExteriorGroundAmbientColor; + vec4 uExteriorDirectColor; + vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; +}; + +struct SceneWideParams +{ + mat4 uLookAtMat; + mat4 uPMatrix; + vec4 uViewUp; + vec4 uInteriorSunDir; + SceneExteriorLight extLight; +}; struct PSFog { @@ -4589,6 +5337,45 @@ struct PSFog vec4 sunPercentage; }; +layout(std140) uniform sceneWideBlockVSPS +{ + SceneWideParams scene; + PSFog fogData; +} _37; + +layout(location = 0) in vec3 aPosition; +out vec4 vColor; +layout(location = 1) in vec4 aColor; +out vec2 vTexcoord0; +layout(location = 2) in vec2 aTexcoord0; +out vec3 vPosition; + +void main() +{ + vec4 aPositionVec4 = vec4(aPosition, 1.0); + vColor = aColor; + vTexcoord0 = aTexcoord0; + vec4 vertexViewSpace = _37.scene.uLookAtMat * aPositionVec4; + vPosition = vertexViewSpace.xyz; + gl_Position = _37.scene.uPMatrix * vertexViewSpace; +} + + +#version 300 es +precision mediump float; +precision highp int; + +layout(location = 0) out highp vec4 outputColor; +in highp vec4 vColor; + +void main() +{ + outputColor = vec4(vColor.xyz, vColor.w); +} + + +#version 300 es + struct SceneExteriorLight { vec4 uExteriorAmbientColor; @@ -4596,6 +5383,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -4607,81 +5395,237 @@ struct SceneWideParams SceneExteriorLight extLight; }; +struct PSFog +{ + vec4 densityParams; + vec4 heightPlane; + vec4 color_and_heightRate; + vec4 heightDensity_and_endColor; + vec4 sunAngle_and_sunColor; + vec4 heightColor_and_endFogDistance; + vec4 sunPercentage; +}; + layout(std140) uniform sceneWideBlockVSPS { SceneWideParams scene; PSFog fogData; -} _221; +} _26; -uniform sampler2D uTexture; +layout(std140) uniform meshWideBlockVS +{ + vec4 skyColor[6]; +} _67; + +layout(location = 0) in vec4 aPosition; +out vec4 vColor; + +void main() +{ + vec3 inputPos = aPosition.xyz; + inputPos *= 33.33300018310546875; + vec4 cameraPos = _26.scene.uLookAtMat * vec4(inputPos, 1.0); + vec3 _46 = cameraPos.xyz - _26.scene.uLookAtMat[3].xyz; + cameraPos = vec4(_46.x, _46.y, _46.z, cameraPos.w); + cameraPos.z = cameraPos.z; + vec4 vertPosInNDC = _26.scene.uPMatrix * cameraPos; + vColor = _67.skyColor[int(aPosition.w)]; + gl_Position = _26.scene.uPMatrix * cameraPos; +} -in vec2 vTexcoord0; -in vec4 vColor; -in vec3 vPosition; -layout(location = 0) out vec4 outputColor; -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) +#version 300 es +precision mediump float; +precision highp int; + +struct SceneExteriorLight { - vec4 l_densityParams = fogData.densityParams; - vec4 l_heightPlane = fogData.heightPlane; - vec4 l_color_and_heightRate = fogData.color_and_heightRate; - vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; - float start = l_densityParams.x; - float end = l_densityParams.y; - float density = l_densityParams.z; - float bias = l_densityParams.w; - float vLength = length(vertexInViewSpace); - float z = vLength - bias; - float expMax = max(0.0, z - start); - float expFog = 1.0 / exp(expMax * density); - float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); - float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; - float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); - float finalFog = mix(expFog, expFogHeight, heightFog); - float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; - vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; - float end2 = vLength / l_heightColor_and_endFogDistance.w; - float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); - fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); - float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); - nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _162 = nDotSun > 0.0; - if (_162) + highp vec4 uExteriorAmbientColor; + highp vec4 uExteriorHorizontAmbientColor; + highp vec4 uExteriorGroundAmbientColor; + highp vec4 uExteriorDirectColor; + highp vec4 uExteriorDirectColorDir; + highp vec4 adtSpecMult; +}; + +struct SceneWideParams +{ + highp mat4 uLookAtMat; + highp mat4 uPMatrix; + highp vec4 uViewUp; + highp vec4 uInteriorSunDir; + SceneExteriorLight extLight; +}; + +struct InteriorLightParam +{ + highp vec4 uInteriorAmbientColorAndApplyInteriorLight; + highp vec4 uInteriorDirectColorAndApplyExteriorLight; +}; + +struct PSFog +{ + highp vec4 densityParams; + highp vec4 heightPlane; + highp vec4 color_and_heightRate; + highp vec4 heightDensity_and_endColor; + highp vec4 sunAngle_and_sunColor; + highp vec4 heightColor_and_endFogDistance; + highp vec4 sunPercentage; +}; + +layout(std140) uniform meshWideBlockPS +{ + highp vec4 values0; + highp vec4 values1; + highp vec4 values2; + highp vec4 values3; + highp vec4 values4; + highp vec4 baseColor; +} _219; + +layout(std140) uniform sceneWideBlockVSPS +{ + SceneWideParams scene; + PSFog fogData; +} _487; + +layout(std140) uniform modelWideBlockVS +{ + highp mat4 uPlacementMat; + highp mat4 uBoneMatrixes[220]; +} _570; + +uniform highp sampler2D uNormalTex; +uniform highp sampler2D uNoise; +uniform highp sampler2D uWhiteWater; +uniform highp sampler2D uMask; + +in highp vec2 vTexCoord2_animated; +in highp vec3 vPosition; +in highp vec3 vNormal; +in highp vec2 vTexCoord; +layout(location = 0) out highp vec4 outputColor; +in highp vec2 vTexCoord2; + +highp vec3 PerturbNormal(highp vec3 surf_pos, highp vec3 surf_norm) +{ + highp vec2 dBdUV = ((texture(uNormalTex, vTexCoord2_animated).xy * 2.0) - vec2(1.0)) * (_219.values3.x * 100.0); + highp vec2 duv1 = dFdx(vTexCoord2_animated); + highp vec2 duv2 = dFdy(vTexCoord2_animated); + highp vec3 vSigmaS = dFdx(surf_pos); + highp vec3 vSigmaT = dFdy(surf_pos); + highp vec3 vN = surf_norm; + highp vec3 vR1 = cross(vSigmaT, vN); + highp vec3 vR2 = cross(vN, vSigmaS); + highp float fDet = dot(vSigmaS, vR1); + highp float dBs = (dBdUV.x * duv1.x) + (dBdUV.y * duv1.y); + highp float dBt = (dBdUV.x * duv2.x) + (dBdUV.y * duv2.y); + highp vec3 vSurfGrad = ((vR1 * dBs) + (vR2 * dBt)) * sign(fDet); + return normalize((vN * abs(fDet)) - vSurfGrad); +} + +highp vec3 calcLight(highp vec3 matDiffuse, highp vec3 vNormal_1, bool applyLight, highp float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, highp vec3 accumLight, highp vec3 precomputedLight, highp vec3 specular) +{ + highp vec3 localDiffuse = accumLight; + bool _39 = !applyLight; + if (_39) + { + return matDiffuse; + } + highp vec3 lDiffuse = vec3(0.0); + highp vec3 normalizedN = normalize(vNormal_1); + bool _56 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + highp vec3 currColor; + if (_56) + { + highp float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); + highp float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); + highp vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; + highp vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; + highp vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; + bool _94 = nDotUp >= 0.0; + if (_94) + { + currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); + } + else + { + currColor = mix(adjHorizAmbient, adjGroundAmbient, vec3(-nDotUp)); + } + highp vec3 skyColor = currColor * 1.10000002384185791015625; + highp vec3 groundColor = currColor * 0.699999988079071044921875; + lDiffuse = sceneParams.extLight.uExteriorDirectColor.xyz * nDotL; + currColor = mix(groundColor, skyColor, vec3(0.5 + (0.5 * nDotL))); + } + bool _134 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; + if (_134) { - nDotSun = (nDotSun * nDotSun) * nDotSun; - fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); + highp float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); + highp vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; + highp vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; + bool _158 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + if (_158) + { + lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); + currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); + } + else + { + lDiffuse = lDiffuseInterior; + currColor = interiorAmbient; + } } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + highp vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); + highp vec3 linearDiffTerm = (matDiffuse * matDiffuse) * localDiffuse; + highp vec3 specTerm = specular; + highp vec3 emTerm = vec3(0.0); + return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; } void main() { - vec4 tex = texture(uTexture, vTexcoord0); - vec4 finalColor = vec4(vColor.xyz * tex.xyz, tex.w * vColor.w); - vec3 sunDir = _221.scene.extLight.uExteriorDirectColorDir.xyz; - PSFog arg; - arg.densityParams = _221.fogData.densityParams; - arg.heightPlane = _221.fogData.heightPlane; - arg.color_and_heightRate = _221.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _221.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _221.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _221.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _221.fogData.sunPercentage; - vec3 param = finalColor.xyz; - vec3 param_1 = vPosition; - vec3 param_2 = sunDir; - vec3 _256 = makeFog(arg, param, param_1, param_2); - finalColor = vec4(_256.x, _256.y, _256.z, finalColor.w); + highp vec3 param = vPosition; + highp vec3 param_1 = normalize(vNormal); + highp vec3 perturbedNormal = PerturbNormal(param, param_1); + highp vec2 vTexCoordNorm = vTexCoord / vec2(_219.values1.x); + highp float noise0 = texture(uNoise, vec2(vTexCoordNorm.x - _219.values1.z, (vTexCoordNorm.y - _219.values1.z) - _219.values2.z)).x; + highp float _noise1 = texture(uNoise, vec2((vTexCoordNorm.x - _219.values1.z) + 0.4180000126361846923828125, ((vTexCoordNorm.y + 0.3549999892711639404296875) + _219.values1.z) - _219.values2.z)).x; + highp float _noise2 = texture(uNoise, vec2((vTexCoordNorm.x + _219.values1.z) + 0.8650000095367431640625, ((vTexCoordNorm.y + 0.1480000019073486328125) - _219.values1.z) - _219.values2.z)).x; + highp float _noise3 = texture(uNoise, vec2((vTexCoordNorm.x + _219.values1.z) + 0.65100002288818359375, ((vTexCoordNorm.y + 0.75199997425079345703125) + _219.values1.z) - _219.values2.z)).x; + highp float noise_avr = abs(((noise0 + _noise1) + _noise2) + _noise3) * 0.25; + highp float noiseFinal = clamp(exp((_219.values0.x * log2(noise_avr)) * 2.2000000476837158203125) * _219.values0.y, 0.0, 1.0); + highp vec4 whiteWater_val = texture(uWhiteWater, vTexCoord2_animated); + highp vec4 mask_val_0 = texture(uMask, vTexCoord); + highp vec4 mask_val_1 = texture(uMask, vec2(vTexCoord.x, vTexCoord.y + _219.values3.z)); + highp float mix_alpha = clamp((((((whiteWater_val.w * noiseFinal) - (mask_val_1.y * mask_val_0.x)) * 2.0) + _219.values0.z) * ((_219.values0.w * 2.0) + 1.0)) - _219.values0.w, 0.0, 1.0); + highp vec4 whiteWater_val_baseColor_mix = mix(_219.baseColor, whiteWater_val, vec4(mix_alpha)); + highp vec3 param_2 = whiteWater_val_baseColor_mix.xyz; + highp vec3 param_3 = perturbedNormal; + bool param_4 = true; + highp float param_5 = 0.0; + SceneWideParams param_6; + param_6.uLookAtMat = _487.scene.uLookAtMat; + param_6.uPMatrix = _487.scene.uPMatrix; + param_6.uViewUp = _487.scene.uViewUp; + param_6.uInteriorSunDir = _487.scene.uInteriorSunDir; + param_6.extLight.uExteriorAmbientColor = _487.scene.extLight.uExteriorAmbientColor; + param_6.extLight.uExteriorHorizontAmbientColor = _487.scene.extLight.uExteriorHorizontAmbientColor; + param_6.extLight.uExteriorGroundAmbientColor = _487.scene.extLight.uExteriorGroundAmbientColor; + param_6.extLight.uExteriorDirectColor = _487.scene.extLight.uExteriorDirectColor; + param_6.extLight.uExteriorDirectColorDir = _487.scene.extLight.uExteriorDirectColorDir; + param_6.extLight.adtSpecMult = _487.scene.extLight.adtSpecMult; + InteriorLightParam param_7 = InteriorLightParam(vec4(0.0), vec4(0.0, 0.0, 0.0, 1.0)); + highp vec3 param_8 = vec3(0.0); + highp vec3 colorAfterLight = calcLight(param_2, param_3, param_4, param_5, param_6, param_7, param_8, vec3(0.0), vec3(0.0)); + highp float w_clamped = clamp((1.0 - mask_val_0.w) * _219.values1.w, 0.0, 1.0); + highp float w_alpha_combined = clamp(w_clamped + mix_alpha, 0.0, 1.0); + highp vec4 finalColor = vec4(mix(colorAfterLight, whiteWater_val_baseColor_mix.xyz, vec3(_219.values3.w)), w_alpha_combined); outputColor = finalColor; } -#version 330 +#version 300 es struct SceneExteriorLight { @@ -4690,6 +5634,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams @@ -4712,267 +5657,358 @@ struct PSFog vec4 sunPercentage; }; +layout(std140) uniform meshWideBlockVS +{ + vec4 bumpScale; + mat4 uTextMat[2]; +} _55; + +layout(std140) uniform modelWideBlockVS +{ + mat4 uPlacementMat; + mat4 uBoneMatrixes[220]; +} _104; + layout(std140) uniform sceneWideBlockVSPS { SceneWideParams scene; PSFog fogData; -} _37; +} _199; +uniform highp sampler2D uBumpTexture; + +layout(location = 5) in vec2 aTexCoord2; +layout(location = 1) in vec3 aNormal; layout(location = 0) in vec3 aPosition; -out vec4 vColor; -layout(location = 1) in vec4 aColor; -out vec2 vTexcoord0; -layout(location = 2) in vec2 aTexcoord0; +layout(location = 3) in vec4 boneWeights; +layout(location = 2) in vec4 bones; +out vec3 vNormal; out vec3 vPosition; +out vec2 vTexCoord; +layout(location = 4) in vec2 aTexCoord; +out vec2 vTexCoord2_animated; +out vec2 vTexCoord2; -void main() +mat3 blizzTranspose(mat4 value) { - vec4 aPositionVec4 = vec4(aPosition, 1.0); - vColor = aColor; - vTexcoord0 = aTexcoord0; - vec4 vertexViewSpace = _37.scene.uLookAtMat * aPositionVec4; - vPosition = vertexViewSpace.xyz; - gl_Position = _37.scene.uPMatrix * vertexViewSpace; + return mat3(vec3(value[0].xyz), vec3(value[1].xyz), vec3(value[2].xyz)); } - -#version 330 -#ifdef GL_ARB_shading_language_420pack -#extension GL_ARB_shading_language_420pack : require -#endif - -layout(location = 0) out vec4 outputColor; -in vec4 vColor; - void main() { - outputColor = vec4(vColor.xyz, 1.0); + vec2 texCoord2 = (_55.uTextMat[0] * vec4(aTexCoord2, 0.0, 1.0)).xy; + vec4 bumpValue = textureLod(uBumpTexture, texCoord2, 0.0); + vec3 pos = ((aNormal * _55.bumpScale.x) * bumpValue.z) + aPosition; + mat4 boneTransformMat = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); + mat4 _113 = _104.uBoneMatrixes[bones.x] * boneWeights.x; + boneTransformMat = mat4(boneTransformMat[0] + _113[0], boneTransformMat[1] + _113[1], boneTransformMat[2] + _113[2], boneTransformMat[3] + _113[3]); + mat4 _135 = _104.uBoneMatrixes[bones.y] * boneWeights.y; + boneTransformMat = mat4(boneTransformMat[0] + _135[0], boneTransformMat[1] + _135[1], boneTransformMat[2] + _135[2], boneTransformMat[3] + _135[3]); + mat4 _156 = _104.uBoneMatrixes[bones.z] * boneWeights.z; + boneTransformMat = mat4(boneTransformMat[0] + _156[0], boneTransformMat[1] + _156[1], boneTransformMat[2] + _156[2], boneTransformMat[3] + _156[3]); + mat4 _178 = _104.uBoneMatrixes[bones.w] * boneWeights.w; + boneTransformMat = mat4(boneTransformMat[0] + _178[0], boneTransformMat[1] + _178[1], boneTransformMat[2] + _178[2], boneTransformMat[3] + _178[3]); + mat4 cameraMatrix = (_199.scene.uLookAtMat * _104.uPlacementMat) * boneTransformMat; + vec4 cameraPoint = cameraMatrix * vec4(pos, 1.0); + mat4 param = _199.scene.uLookAtMat; + mat4 param_1 = _104.uPlacementMat; + mat4 param_2 = boneTransformMat; + mat3 viewModelMatTransposed = (blizzTranspose(param) * blizzTranspose(param_1)) * blizzTranspose(param_2); + vNormal = ((_199.scene.uLookAtMat * _104.uPlacementMat) * vec4(aNormal, 0.0)).xyz; + vPosition = pos; + vTexCoord = aTexCoord; + vTexCoord2_animated = texCoord2; + vTexCoord2 = aTexCoord2; + gl_Position = _199.scene.uPMatrix * cameraPoint; } -#version 330 +#version 300 es +precision mediump float; +precision highp int; + +struct PSFog +{ + highp vec4 densityParams; + highp vec4 heightPlane; + highp vec4 color_and_heightRate; + highp vec4 heightDensity_and_endColor; + highp vec4 sunAngle_and_sunColor; + highp vec4 heightColor_and_endFogDistance; + highp vec4 sunPercentage; +}; + +const vec3 _37[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _44[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); struct SceneExteriorLight { - vec4 uExteriorAmbientColor; - vec4 uExteriorHorizontAmbientColor; - vec4 uExteriorGroundAmbientColor; - vec4 uExteriorDirectColor; - vec4 uExteriorDirectColorDir; + highp vec4 uExteriorAmbientColor; + highp vec4 uExteriorHorizontAmbientColor; + highp vec4 uExteriorGroundAmbientColor; + highp vec4 uExteriorDirectColor; + highp vec4 uExteriorDirectColorDir; + highp vec4 adtSpecMult; }; struct SceneWideParams { - mat4 uLookAtMat; - mat4 uPMatrix; - vec4 uViewUp; - vec4 uInteriorSunDir; + highp mat4 uLookAtMat; + highp mat4 uPMatrix; + highp vec4 uViewUp; + highp vec4 uInteriorSunDir; SceneExteriorLight extLight; }; +layout(std140) uniform meshWideBlockPS +{ + highp vec4 color; +} _253; + layout(std140) uniform sceneWideBlockVSPS { SceneWideParams scene; -} _50; + PSFog fogData; +} _277; -layout(std140) uniform meshWideBlockVS -{ - vec4 skyColor[6]; -} _82; +uniform highp sampler2D uTexture; -layout(location = 0) in vec4 aPosition; -out vec4 vColor; +in highp vec2 vTextCoords; +in highp vec3 vPosition; +layout(location = 0) out highp vec4 outputColor; -void main() +highp vec3 validateFogColor(highp vec3 fogColor, int blendMode) { - vec3 inputPos = aPosition.xyz; - vec2 _20 = inputPos.xy / vec2(0.6875); - inputPos = vec3(_20.x, _20.y, inputPos.z); - bool _30 = inputPos.z > 0.0; - float _31; - if (_30) + return mix(fogColor, _37[blendMode], vec3(_44[blendMode])); +} + +highp vec4 makeFog(PSFog fogData, highp vec4 final, highp vec3 vertexInViewSpace, highp vec3 sunDirInViewSpace, int blendMode) +{ + highp vec4 l_densityParams = fogData.densityParams; + highp vec4 l_heightPlane = fogData.heightPlane; + highp vec4 l_color_and_heightRate = fogData.color_and_heightRate; + highp vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; + highp float start = l_densityParams.x; + highp float end = l_densityParams.y; + highp float density = l_densityParams.z; + highp float bias = l_densityParams.w; + highp float vLength = length(vertexInViewSpace); + highp float z = vLength - bias; + highp float expMax = max(0.0, z - start); + highp float expFog = 1.0 / exp(expMax * density); + highp float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); + highp float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; + highp float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); + highp float finalFog = mix(expFog, expFogHeight, heightFog); + highp float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); + highp float alpha = 1.0; + bool _139 = blendMode == 13; + if (_139) { - _31 = inputPos.z / 0.292800009250640869140625; + alpha = min(finalFog, endFadeFog); } - else + highp vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + highp vec3 endColor = validateFogColor(param, param_1); + highp vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; + highp float end2 = vLength / l_heightColor_and_endFogDistance.w; + highp float end2_cube = end2 * (end2 * end2); + highp vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + highp vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + highp vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + highp vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); + highp float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); + highp vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + highp vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); + nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); + bool _218 = nDotSun > 0.0; + if (_218) { - _31 = inputPos.z; + nDotSun = (nDotSun * nDotSun) * nDotSun; + fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - inputPos.z = _31; - vec4 cameraPos = _50.scene.uLookAtMat * vec4(inputPos, 1.0); - vec3 _70 = cameraPos.xyz - _50.scene.uLookAtMat[3].xyz; - cameraPos = vec4(_70.x, _70.y, _70.z, cameraPos.w); - cameraPos.z = cameraPos.z; - vColor = vec4(_82.skyColor[int(aPosition.w)].xyz, 1.0); - gl_Position = _50.scene.uPMatrix * cameraPos; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } - -#version 330 - -layout(std140) uniform meshWideBlockPS +void main() { - ivec4 waterTypeV; -} _12; + highp vec3 finalColor = _253.color.xyz + texture(uTexture, vTextCoords).xyz; + highp vec3 sunDir = _277.scene.extLight.uExteriorDirectColorDir.xyz; + PSFog arg; + arg.densityParams = _277.fogData.densityParams; + arg.heightPlane = _277.fogData.heightPlane; + arg.color_and_heightRate = _277.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _277.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _277.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _277.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _277.fogData.sunPercentage; + highp vec4 param = vec4(finalColor, 1.0); + highp vec3 param_1 = vPosition; + highp vec3 param_2 = sunDir; + int param_3 = 2; + finalColor = makeFog(arg, param, param_1, param_2, param_3).xyz; + outputColor = vec4(finalColor, 0.699999988079071044921875); +} -uniform sampler2D uTexture; -layout(location = 0) out vec4 outputColor; -in vec3 vPosition; +#version 300 es -void main() +struct SceneExteriorLight { - int waterType = _12.waterTypeV.x; - bool _22 = waterType == 13; - if (_22) - { - outputColor = vec4(0.0, 0.0, 0.300000011920928955078125, 0.5); - } - else - { - bool _36 = waterType == 14; - if (_36) - { - outputColor = vec4(0.0, 0.0, 0.800000011920928955078125, 0.800000011920928955078125); - } - else - { - bool _44 = waterType == 19; - if (_44) - { - outputColor = vec4(0.300000011920928955078125, 0.0, 0.0, 0.5); - } - else - { - bool _51 = waterType == 20; - if (_51) - { - outputColor = vec4(0.0, 0.5, 0.0, 0.5); - } - else - { - outputColor = vec4(0.5); - } - } - } - } -} + vec4 uExteriorAmbientColor; + vec4 uExteriorHorizontAmbientColor; + vec4 uExteriorGroundAmbientColor; + vec4 uExteriorDirectColor; + vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; +}; +struct SceneWideParams +{ + mat4 uLookAtMat; + mat4 uPMatrix; + vec4 uViewUp; + vec4 uInteriorSunDir; + SceneExteriorLight extLight; +}; -#version 330 +struct PSFog +{ + vec4 densityParams; + vec4 heightPlane; + vec4 color_and_heightRate; + vec4 heightDensity_and_endColor; + vec4 sunAngle_and_sunColor; + vec4 heightColor_and_endFogDistance; + vec4 sunPercentage; +}; layout(std140) uniform sceneWideBlockVSPS { - mat4 uLookAtMat; - mat4 uPMatrix; -} _24; + SceneWideParams scene; + PSFog fogData; +} _28; layout(std140) uniform modelWideBlockVS { mat4 uPlacementMat; -} _32; +} _36; -layout(location = 0) in vec3 aPosition; +layout(location = 0) in vec4 aPositionTransp; +out vec2 vTextCoords; out vec3 vPosition; +layout(location = 1) in vec2 aTexCoord; void main() { - vec4 aPositionVec4 = vec4(aPosition, 1.0); - mat4 cameraMatrix = _24.uLookAtMat * _32.uPlacementMat; + vec4 aPositionVec4 = vec4(aPositionTransp.xyz, 1.0); + mat4 cameraMatrix = _28.scene.uLookAtMat * _36.uPlacementMat; vec4 cameraPoint = cameraMatrix * aPositionVec4; - gl_Position = _24.uPMatrix * cameraPoint; + vTextCoords = aPositionVec4.xy * 0.02999999932944774627685546875; + gl_Position = _28.scene.uPMatrix * cameraPoint; vPosition = cameraPoint.xyz; } -#version 330 +#version 300 es +precision mediump float; +precision highp int; struct SceneExteriorLight { - vec4 uExteriorAmbientColor; - vec4 uExteriorHorizontAmbientColor; - vec4 uExteriorGroundAmbientColor; - vec4 uExteriorDirectColor; - vec4 uExteriorDirectColorDir; + highp vec4 uExteriorAmbientColor; + highp vec4 uExteriorHorizontAmbientColor; + highp vec4 uExteriorGroundAmbientColor; + highp vec4 uExteriorDirectColor; + highp vec4 uExteriorDirectColorDir; + highp vec4 adtSpecMult; }; struct SceneWideParams { - mat4 uLookAtMat; - mat4 uPMatrix; - vec4 uViewUp; - vec4 uInteriorSunDir; + highp mat4 uLookAtMat; + highp mat4 uPMatrix; + highp vec4 uViewUp; + highp vec4 uInteriorSunDir; SceneExteriorLight extLight; }; struct InteriorLightParam { - vec4 uInteriorAmbientColorAndApplyInteriorLight; - vec4 uInteriorDirectColorAndApplyExteriorLight; + highp vec4 uInteriorAmbientColorAndApplyInteriorLight; + highp vec4 uInteriorDirectColorAndApplyExteriorLight; }; struct PSFog { - vec4 densityParams; - vec4 heightPlane; - vec4 color_and_heightRate; - vec4 heightDensity_and_endColor; - vec4 sunAngle_and_sunColor; - vec4 heightColor_and_endFogDistance; - vec4 sunPercentage; + highp vec4 densityParams; + highp vec4 heightPlane; + highp vec4 color_and_heightRate; + highp vec4 heightDensity_and_endColor; + highp vec4 sunAngle_and_sunColor; + highp vec4 heightColor_and_endFogDistance; + highp vec4 sunPercentage; }; +const vec3 _215[14] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(1.0), vec3(0.5), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.5), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); +const float _222[14] = float[](0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + layout(std140) uniform meshWideBlockPS { - ivec4 UseLitColor_EnableAlpha_PixelShader; - vec4 FogColor_AlphaTest; -} _383; + ivec4 UseLitColor_EnableAlpha_PixelShader_BlendMode; + highp vec4 FogColor_AlphaTest; +} _446; layout(std140) uniform sceneWideBlockVSPS { SceneWideParams scene; PSFog fogData; -} _848; +} _909; layout(std140) uniform modelWideBlockPS { InteriorLightParam intLight; -} _852; - -uniform sampler2D uTexture; -uniform sampler2D uTexture2; -uniform sampler2D uTexture3; - -in vec2 vTexCoord; -in vec2 vTexCoord2; -in vec2 vTexCoord3; -in vec4 vColor; -in vec4 vColor2; -in vec3 vNormal; -in vec4 vPosition; -layout(location = 0) out vec4 outputColor; - -vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, vec3 accumLight, vec3 precomputedLight, vec3 specular) -{ - vec3 localDiffuse = accumLight; - bool _42 = !applyLight; - if (_42) +} _913; + +uniform highp sampler2D uTexture; +uniform highp sampler2D uTexture2; +uniform highp sampler2D uTexture3; + +in highp vec2 vTexCoord; +in highp vec2 vTexCoord2; +in highp vec2 vTexCoord3; +in highp vec4 vColor; +in highp vec4 vColor2; +in highp vec3 vNormal; +in highp vec4 vPosition; +layout(location = 0) out highp vec4 outputColor; + +highp vec3 calcLight(highp vec3 matDiffuse, highp vec3 vNormal_1, bool applyLight, highp float interiorExteriorBlend, SceneWideParams sceneParams, InteriorLightParam intLight, highp vec3 accumLight, highp vec3 precomputedLight, highp vec3 specular) +{ + highp vec3 localDiffuse = accumLight; + bool _51 = !applyLight; + if (_51) { return matDiffuse; } - vec3 lDiffuse = vec3(0.0); - vec3 normalizedN = normalize(vNormal_1); - bool _59 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - vec3 currColor; - if (_59) + highp vec3 lDiffuse = vec3(0.0); + highp vec3 normalizedN = normalize(vNormal_1); + bool _67 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + highp vec3 currColor; + if (_67) { - float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); - float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); - vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; - vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; - vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; - bool _97 = nDotUp >= 0.0; - if (_97) + highp float nDotL = clamp(dot(normalizedN, -sceneParams.extLight.uExteriorDirectColorDir.xyz), 0.0, 1.0); + highp float nDotUp = dot(normalizedN, sceneParams.uViewUp.xyz); + highp vec3 adjAmbient = sceneParams.extLight.uExteriorAmbientColor.xyz + precomputedLight; + highp vec3 adjHorizAmbient = sceneParams.extLight.uExteriorHorizontAmbientColor.xyz + precomputedLight; + highp vec3 adjGroundAmbient = sceneParams.extLight.uExteriorGroundAmbientColor.xyz + precomputedLight; + bool _104 = nDotUp >= 0.0; + if (_104) { currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); } @@ -4980,19 +6016,19 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE { currColor = mix(adjHorizAmbient, adjGroundAmbient, vec3(-nDotUp)); } - vec3 skyColor = currColor * 1.10000002384185791015625; - vec3 groundColor = currColor * 0.699999988079071044921875; + highp vec3 skyColor = currColor * 1.10000002384185791015625; + highp vec3 groundColor = currColor * 0.699999988079071044921875; lDiffuse = sceneParams.extLight.uExteriorDirectColor.xyz * nDotL; currColor = mix(groundColor, skyColor, vec3(0.5 + (0.5 * nDotL))); } - bool _137 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; - if (_137) + bool _144 = intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0.0; + if (_144) { - float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); - vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; - vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; - bool _161 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; - if (_161) + highp float nDotL_1 = clamp(dot(normalizedN, -sceneParams.uInteriorSunDir.xyz), 0.0, 1.0); + highp vec3 lDiffuseInterior = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz * nDotL_1; + highp vec3 interiorAmbient = intLight.uInteriorAmbientColorAndApplyInteriorLight.xyz + precomputedLight; + bool _168 = intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0.0; + if (_168) { lDiffuse = mix(lDiffuseInterior, lDiffuse, vec3(interiorExteriorBlend)); currColor = mix(interiorAmbient, currColor, vec3(interiorExteriorBlend)); @@ -5003,237 +6039,256 @@ vec3 calcLight(vec3 matDiffuse, vec3 vNormal_1, bool applyLight, float interiorE currColor = interiorAmbient; } } - vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); - vec3 linearDiffTerm = (matDiffuse * matDiffuse) * localDiffuse; - vec3 specTerm = specular; - vec3 emTerm = vec3(0.0); + highp vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); + highp vec3 linearDiffTerm = (matDiffuse * matDiffuse) * localDiffuse; + highp vec3 specTerm = specular; + highp vec3 emTerm = vec3(0.0); return (sqrt((gammaDiffTerm * gammaDiffTerm) + linearDiffTerm) + specTerm) + emTerm; } -vec3 makeFog(PSFog fogData, vec3 final, vec3 vertexInViewSpace, vec3 sunDirInViewSpace) +highp vec3 validateFogColor(highp vec3 fogColor, int blendMode) { - vec4 l_densityParams = fogData.densityParams; - vec4 l_heightPlane = fogData.heightPlane; - vec4 l_color_and_heightRate = fogData.color_and_heightRate; - vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; - float start = l_densityParams.x; - float end = l_densityParams.y; - float density = l_densityParams.z; - float bias = l_densityParams.w; - float vLength = length(vertexInViewSpace); - float z = vLength - bias; - float expMax = max(0.0, z - start); - float expFog = 1.0 / exp(expMax * density); - float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); - float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; - float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); - float finalFog = mix(expFog, expFogHeight, heightFog); - float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); - vec3 endColor = l_heightDensity_and_endColor.yzw; - vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; - float end2 = vLength / l_heightColor_and_endFogDistance.w; - float end2_cube = end2 * (end2 * end2); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0.0, 1.0))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0.0, 1.0))); + return mix(fogColor, _215[blendMode], vec3(_222[blendMode])); +} + +highp vec4 makeFog(PSFog fogData, highp vec4 final, highp vec3 vertexInViewSpace, highp vec3 sunDirInViewSpace, int blendMode) +{ + highp vec4 l_densityParams = fogData.densityParams; + highp vec4 l_heightPlane = fogData.heightPlane; + highp vec4 l_color_and_heightRate = fogData.color_and_heightRate; + highp vec4 l_heightDensity_and_endColor = fogData.heightDensity_and_endColor; + highp float start = l_densityParams.x; + highp float end = l_densityParams.y; + highp float density = l_densityParams.z; + highp float bias = l_densityParams.w; + highp float vLength = length(vertexInViewSpace); + highp float z = vLength - bias; + highp float expMax = max(0.0, z - start); + highp float expFog = 1.0 / exp(expMax * density); + highp float expFogHeight = 1.0 / exp(expMax * l_heightDensity_and_endColor.x); + highp float height = dot(l_heightPlane.xyz, vertexInViewSpace) + l_heightPlane.w; + highp float heightFog = clamp(height * l_color_and_heightRate.w, 0.0, 1.0); + highp float finalFog = mix(expFog, expFogHeight, heightFog); + highp float endFadeFog = clamp(1.4285714626312255859375 * (1.0 - (vLength / end)), 0.0, 1.0); + highp float alpha = 1.0; + bool _310 = blendMode == 13; + if (_310) + { + alpha = min(finalFog, endFadeFog); + } + highp vec3 param = l_heightDensity_and_endColor.yzw; + int param_1 = blendMode; + highp vec3 endColor = validateFogColor(param, param_1); + highp vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; + highp float end2 = vLength / l_heightColor_and_endFogDistance.w; + highp float end2_cube = end2 * (end2 * end2); + highp vec3 param_2 = l_heightColor_and_endFogDistance.xyz; + int param_3 = blendMode; + highp vec3 heightColor = mix(validateFogColor(param_2, param_3), endColor, vec3(clamp(end2, 0.0, 1.0))); + highp vec3 param_4 = l_color_and_heightRate.xyz; + int param_5 = blendMode; + highp vec3 fogFinal = mix(validateFogColor(param_4, param_5), endColor, vec3(clamp(end2_cube, 0.0, 1.0))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); - float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + highp float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace); + highp vec3 param_6 = fogData.sunAngle_and_sunColor.yzw; + int param_7 = blendMode; + highp vec3 sunColor = mix(fogFinal, validateFogColor(param_6, param_7), vec3(fogData.sunPercentage.x)); nDotSun = clamp(nDotSun - fogData.sunAngle_and_sunColor.x, 0.0, 1.0); - bool _334 = nDotSun > 0.0; - if (_334) + bool _388 = nDotSun > 0.0; + if (_388) { nDotSun = (nDotSun * nDotSun) * nDotSun; fogFinal = mix(fogFinal, sunColor, vec3(nDotSun)); } - fogFinal = mix(fogFinal, final, vec3(min(finalFog, endFadeFog))); - return fogFinal; + fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); + return vec4(fogFinal, final.w * alpha); } void main() { - vec4 tex = texture(uTexture, vTexCoord); - vec4 tex2 = texture(uTexture2, vTexCoord2); - vec4 tex3 = texture(uTexture3, vTexCoord3); - bool _387 = _383.UseLitColor_EnableAlpha_PixelShader.y == 1; - if (_387) + highp vec4 tex = texture(uTexture, vTexCoord); + highp vec4 tex2 = texture(uTexture2, vTexCoord2); + highp vec4 tex3 = texture(uTexture3, vTexCoord3); + bool _450 = _446.UseLitColor_EnableAlpha_PixelShader_BlendMode.y == 1; + if (_450) { - bool _394 = (tex.w - 0.501960813999176025390625) < 0.0; - if (_394) + bool _457 = (tex.w - 0.501960813999176025390625) < 0.0; + if (_457) { discard; } } - int uPixelShader = _383.UseLitColor_EnableAlpha_PixelShader.z; - vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0); - vec3 matDiffuse = vec3(0.0); - vec3 env = vec3(0.0); - float finalOpacity = 0.0; - bool _409 = uPixelShader == (-1); + int uPixelShader = _446.UseLitColor_EnableAlpha_PixelShader_BlendMode.z; + highp vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0); + highp vec3 matDiffuse = vec3(0.0); + highp vec3 env = vec3(0.0); + highp float finalOpacity = 0.0; + bool _471 = uPixelShader == (-1); #if (FRAGMENTSHADER == (-1)) matDiffuse = (tex.xyz * vColor.xyz) + (tex2.xyz * vColor2.zyx); finalOpacity = tex.w; #endif - bool _430 = uPixelShader == 0; + bool _492 = uPixelShader == 0; #if (FRAGMENTSHADER == 0) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _445 = uPixelShader == 1; + bool _507 = uPixelShader == 1; #if (FRAGMENTSHADER == 1) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _458 = uPixelShader == 2; + bool _520 = uPixelShader == 2; #if (FRAGMENTSHADER == 2) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _471 = uPixelShader == 3; + bool _533 = uPixelShader == 3; #if (FRAGMENTSHADER == 3) matDiffuse = tex.xyz * (vColor.xyz * 2.0); env = tex2.xyz * tex.w; finalOpacity = vColor.w; #endif - bool _489 = uPixelShader == 4; + bool _551 = uPixelShader == 4; #if (FRAGMENTSHADER == 4) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _502 = uPixelShader == 5; + bool _564 = uPixelShader == 5; #if (FRAGMENTSHADER == 5) matDiffuse = tex.xyz * (vColor.xyz * 2.0); env = (tex.xyz * tex.w) * tex2.xyz; finalOpacity = vColor.w; #endif - bool _523 = uPixelShader == 6; + bool _585 = uPixelShader == 6; #if (FRAGMENTSHADER == 6) - vec3 layer1 = tex.xyz; - vec3 layer2 = mix(layer1, tex2.xyz, vec3(tex2.w)); + highp vec3 layer1 = tex.xyz; + highp vec3 layer2 = mix(layer1, tex2.xyz, vec3(tex2.w)); matDiffuse = (vColor.xyz * 2.0) * mix(layer2, layer1, vec3(vColor2.w)); finalOpacity = vColor.w; #endif - bool _552 = uPixelShader == 7; + bool _614 = uPixelShader == 7; #if (FRAGMENTSHADER == 7) - vec4 colorMix = mix(tex2, tex, vec4(vColor2.w)); + highp vec4 colorMix = mix(tex2, tex, vec4(vColor2.w)); matDiffuse = colorMix.xyz * (vColor.xyz * 2.0); env = (colorMix.xyz * colorMix.w) * tex3.xyz; finalOpacity = vColor.w; #endif - bool _581 = uPixelShader == 8; + bool _643 = uPixelShader == 8; #if (FRAGMENTSHADER == 8) - vec3 layer1_1 = tex.xyz; - vec3 layer2_1 = tex2.xyz; + highp vec3 layer1_1 = tex.xyz; + highp vec3 layer2_1 = tex2.xyz; matDiffuse = (vColor.xyz * 2.0) * mix(layer2_1, layer1_1, vec3(vColor2.w)); finalOpacity = vColor.w; #endif - bool _605 = uPixelShader == 9; + bool _667 = uPixelShader == 9; #if (FRAGMENTSHADER == 9) matDiffuse = tex.xyz * (vColor.xyz * 2.0); env = (tex2.xyz * tex2.w) * vColor2.w; finalOpacity = vColor.w; #endif - bool _627 = uPixelShader == 10; + bool _689 = uPixelShader == 10; #if (FRAGMENTSHADER == 10) - float mixFactor = clamp(tex3.w * vColor2.w, 0.0, 1.0); + highp float mixFactor = clamp(tex3.w * vColor2.w, 0.0, 1.0); matDiffuse = (vColor.xyz * 2.0) * mix(mix((tex.xyz * tex2.xyz) * 2.0, tex3.xyz, vec3(mixFactor)), tex.xyz, vec3(tex.w)); finalOpacity = vColor.w; #endif - bool _663 = uPixelShader == 11; + bool _725 = uPixelShader == 11; #if (FRAGMENTSHADER == 11) matDiffuse = tex.xyz * (vColor.xyz * 2.0); env = ((tex.xyz * tex.w) * tex2.xyz) + ((tex3.xyz * tex3.w) * vColor2.w); finalOpacity = vColor.w; #endif - bool _694 = uPixelShader == 12; + bool _756 = uPixelShader == 12; #if (FRAGMENTSHADER == 12) matDiffuse = (vColor.xyz * 2.0) * mix(tex2.xyz, tex.xyz, vec3(vColor2.w)); finalOpacity = vColor.w; #endif - bool _714 = uPixelShader == 13; + bool _775 = uPixelShader == 13; #if (FRAGMENTSHADER == 13) - vec3 t1diffuse = tex2.xyz * (1.0 - tex2.w); + highp vec3 t1diffuse = tex2.xyz * (1.0 - tex2.w); matDiffuse = (vColor.xyz * 2.0) * mix(t1diffuse, tex.xyz, vec3(vColor2.w)); env = (tex2.xyz * tex2.w) * (1.0 - vColor2.w); finalOpacity = vColor.w; #endif - bool _748 = uPixelShader == 13; + bool _809 = uPixelShader == 13; #if (FRAGMENTSHADER == 13) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _762 = uPixelShader == 14; + bool _823 = uPixelShader == 14; #if (FRAGMENTSHADER == 14) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _776 = uPixelShader == 15; + bool _837 = uPixelShader == 15; #if (FRAGMENTSHADER == 15) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _789 = uPixelShader == 16; + bool _850 = uPixelShader == 16; #if (FRAGMENTSHADER == 16) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _802 = uPixelShader == 17; + bool _863 = uPixelShader == 17; #if (FRAGMENTSHADER == 17) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _815 = uPixelShader == 18; + bool _876 = uPixelShader == 18; #if (FRAGMENTSHADER == 18) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - bool _828 = uPixelShader == 19; + bool _889 = uPixelShader == 19; #if (FRAGMENTSHADER == 19) matDiffuse = tex.xyz * (vColor.xyz * 2.0); finalOpacity = vColor.w; #endif - vec3 param = matDiffuse; - vec3 param_1 = vNormal; + highp vec3 param = matDiffuse; + highp vec3 param_1 = vNormal; bool param_2 = true; - float param_3 = vPosition.w; + highp float param_3 = vPosition.w; SceneWideParams param_4; - param_4.uLookAtMat = _848.scene.uLookAtMat; - param_4.uPMatrix = _848.scene.uPMatrix; - param_4.uViewUp = _848.scene.uViewUp; - param_4.uInteriorSunDir = _848.scene.uInteriorSunDir; - param_4.extLight.uExteriorAmbientColor = _848.scene.extLight.uExteriorAmbientColor; - param_4.extLight.uExteriorHorizontAmbientColor = _848.scene.extLight.uExteriorHorizontAmbientColor; - param_4.extLight.uExteriorGroundAmbientColor = _848.scene.extLight.uExteriorGroundAmbientColor; - param_4.extLight.uExteriorDirectColor = _848.scene.extLight.uExteriorDirectColor; - param_4.extLight.uExteriorDirectColorDir = _848.scene.extLight.uExteriorDirectColorDir; + param_4.uLookAtMat = _909.scene.uLookAtMat; + param_4.uPMatrix = _909.scene.uPMatrix; + param_4.uViewUp = _909.scene.uViewUp; + param_4.uInteriorSunDir = _909.scene.uInteriorSunDir; + param_4.extLight.uExteriorAmbientColor = _909.scene.extLight.uExteriorAmbientColor; + param_4.extLight.uExteriorHorizontAmbientColor = _909.scene.extLight.uExteriorHorizontAmbientColor; + param_4.extLight.uExteriorGroundAmbientColor = _909.scene.extLight.uExteriorGroundAmbientColor; + param_4.extLight.uExteriorDirectColor = _909.scene.extLight.uExteriorDirectColor; + param_4.extLight.uExteriorDirectColorDir = _909.scene.extLight.uExteriorDirectColorDir; + param_4.extLight.adtSpecMult = _909.scene.extLight.adtSpecMult; InteriorLightParam param_5; - param_5.uInteriorAmbientColorAndApplyInteriorLight = _852.intLight.uInteriorAmbientColorAndApplyInteriorLight; - param_5.uInteriorDirectColorAndApplyExteriorLight = _852.intLight.uInteriorDirectColorAndApplyExteriorLight; - vec3 param_6 = vec3(0.0); + param_5.uInteriorAmbientColorAndApplyInteriorLight = _913.intLight.uInteriorAmbientColorAndApplyInteriorLight; + param_5.uInteriorDirectColorAndApplyExteriorLight = _913.intLight.uInteriorDirectColorAndApplyExteriorLight; + highp vec3 param_6 = vec3(0.0); finalColor = vec4(calcLight(param, param_1, param_2, param_3, param_4, param_5, param_6, vColor2.xyz, vec3(0.0)), finalOpacity); - bool _909 = finalColor.w < _383.FogColor_AlphaTest.w; - if (_909) + bool _972 = finalColor.w < _446.FogColor_AlphaTest.w; + if (_972) { discard; } PSFog arg; - arg.densityParams = _848.fogData.densityParams; - arg.heightPlane = _848.fogData.heightPlane; - arg.color_and_heightRate = _848.fogData.color_and_heightRate; - arg.heightDensity_and_endColor = _848.fogData.heightDensity_and_endColor; - arg.sunAngle_and_sunColor = _848.fogData.sunAngle_and_sunColor; - arg.heightColor_and_endFogDistance = _848.fogData.heightColor_and_endFogDistance; - arg.sunPercentage = _848.fogData.sunPercentage; - vec3 param_7 = finalColor.xyz; - vec3 param_8 = vPosition.xyz; - vec3 param_9 = _848.scene.extLight.uExteriorDirectColorDir.xyz; - vec3 _945 = makeFog(arg, param_7, param_8, param_9); - finalColor = vec4(_945.x, _945.y, _945.z, finalColor.w); - finalColor.w = 1.0; + arg.densityParams = _909.fogData.densityParams; + arg.heightPlane = _909.fogData.heightPlane; + arg.color_and_heightRate = _909.fogData.color_and_heightRate; + arg.heightDensity_and_endColor = _909.fogData.heightDensity_and_endColor; + arg.sunAngle_and_sunColor = _909.fogData.sunAngle_and_sunColor; + arg.heightColor_and_endFogDistance = _909.fogData.heightColor_and_endFogDistance; + arg.sunPercentage = _909.fogData.sunPercentage; + highp vec4 param_7 = finalColor; + highp vec3 param_8 = vPosition.xyz; + highp vec3 param_9 = _909.scene.extLight.uExteriorDirectColorDir.xyz; + int param_10 = _446.UseLitColor_EnableAlpha_PixelShader_BlendMode.w; + finalColor = makeFog(arg, param_7, param_8, param_9, param_10); outputColor = finalColor; } -#version 330 +#version 300 es struct SceneExteriorLight { @@ -5242,6 +6297,7 @@ struct SceneExteriorLight vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; struct SceneWideParams diff --git a/emscripten_port/project.js b/emscripten_port/project.js index 8f79c81ff..e203e5f40 100644 --- a/emscripten_port/project.js +++ b/emscripten_port/project.js @@ -1,10013 +1 @@ - - -// The Module object: Our interface to the outside world. We import -// and export values on it. There are various ways Module can be used: -// 1. Not defined. We create it here -// 2. A function parameter, function(Module) { ..generated code.. } -// 3. pre-run appended it, var Module = {}; ..generated code.. -// 4. External script tag defines var Module. -// We need to check if Module already exists (e.g. case 3 above). -// Substitution will be replaced with actual code on later stage of the build, -// this way Closure Compiler will not mangle it (e.g. case 4. above). -// Note that if you want to run closure, and also to use Module -// after the generated code, you will need to define var Module = {}; -// before the code. Then that object will be used in the code, and you -// can continue to use Module afterwards as well. -var Module = typeof Module !== 'undefined' ? Module : {}; - - - -// --pre-jses are emitted after the Module integration code, so that they can -// refer to Module (if they choose; they can also define Module) - - if (!Module.expectedDataFileDownloads) { - Module.expectedDataFileDownloads = 0; - } - Module.expectedDataFileDownloads++; - (function() { - var loadPackage = function(metadata) { - - var PACKAGE_PATH; - if (typeof window === 'object') { - PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/'); - } else if (typeof location !== 'undefined') { - // worker - PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/'); - } else { - throw 'using preloaded data can only be done on a web page or in a web worker'; - } - var PACKAGE_NAME = '../project.data'; - var REMOTE_PACKAGE_BASE = 'project.data'; - if (typeof Module['locateFilePackage'] === 'function' && !Module['locateFile']) { - Module['locateFile'] = Module['locateFilePackage']; - err('warning: you defined Module.locateFilePackage, that has been renamed to Module.locateFile (using your locateFilePackage for now)'); - } - var REMOTE_PACKAGE_NAME = Module['locateFile'] ? Module['locateFile'](REMOTE_PACKAGE_BASE, '') : REMOTE_PACKAGE_BASE; - - var REMOTE_PACKAGE_SIZE = metadata['remote_package_size']; - var PACKAGE_UUID = metadata['package_uuid']; - - function fetchRemotePackage(packageName, packageSize, callback, errback) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', packageName, true); - xhr.responseType = 'arraybuffer'; - xhr.onprogress = function(event) { - var url = packageName; - var size = packageSize; - if (event.total) size = event.total; - if (event.loaded) { - if (!xhr.addedTotal) { - xhr.addedTotal = true; - if (!Module.dataFileDownloads) Module.dataFileDownloads = {}; - Module.dataFileDownloads[url] = { - loaded: event.loaded, - total: size - }; - } else { - Module.dataFileDownloads[url].loaded = event.loaded; - } - var total = 0; - var loaded = 0; - var num = 0; - for (var download in Module.dataFileDownloads) { - var data = Module.dataFileDownloads[download]; - total += data.total; - loaded += data.loaded; - num++; - } - total = Math.ceil(total * Module.expectedDataFileDownloads/num); - if (Module['setStatus']) Module['setStatus']('Downloading data... (' + loaded + '/' + total + ')'); - } else if (!Module.dataFileDownloads) { - if (Module['setStatus']) Module['setStatus']('Downloading data...'); - } - }; - xhr.onerror = function(event) { - throw new Error("NetworkError for: " + packageName); - } - xhr.onload = function(event) { - if (xhr.status == 200 || xhr.status == 304 || xhr.status == 206 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 - var packageData = xhr.response; - callback(packageData); - } else { - throw new Error(xhr.statusText + " : " + xhr.responseURL); - } - }; - xhr.send(null); - }; - - function handleError(error) { - console.error('package error:', error); - }; - - var fetchedCallback = null; - var fetched = Module['getPreloadedPackage'] ? Module['getPreloadedPackage'](REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE) : null; - - if (!fetched) fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE, function(data) { - if (fetchedCallback) { - fetchedCallback(data); - fetchedCallback = null; - } else { - fetched = data; - } - }, handleError); - - function runWithFS() { - - function assert(check, msg) { - if (!check) throw msg + new Error().stack; - } - Module['FS_createPath']('/', 'glsl', true, true); -Module['FS_createPath']('/glsl', 'glsl20', true, true); -Module['FS_createPath']('/glsl', 'glsl3.3', true, true); - - /** @constructor */ - function DataRequest(start, end, audio) { - this.start = start; - this.end = end; - this.audio = audio; - } - DataRequest.prototype = { - requests: {}, - open: function(mode, name) { - this.name = name; - this.requests[name] = this; - Module['addRunDependency']('fp ' + this.name); - }, - send: function() {}, - onload: function() { - var byteArray = this.byteArray.subarray(this.start, this.end); - this.finish(byteArray); - }, - finish: function(byteArray) { - var that = this; - - Module['FS_createDataFile'](this.name, null, byteArray, true, true, true); // canOwn this data in the filesystem, it is a slide into the heap that will never change - Module['removeRunDependency']('fp ' + that.name); - - this.requests[this.name] = null; - } - }; - - var files = metadata['files']; - for (var i = 0; i < files.length; ++i) { - new DataRequest(files[i]['start'], files[i]['end'], files[i]['audio']).open('GET', files[i]['filename']); - } - - - function processPackageData(arrayBuffer) { - assert(arrayBuffer, 'Loading data file failed.'); - assert(arrayBuffer instanceof ArrayBuffer, 'bad input to processPackageData'); - var byteArray = new Uint8Array(arrayBuffer); - var curr; - - // Reuse the bytearray from the XHR as the source for file reads. - DataRequest.prototype.byteArray = byteArray; - - var files = metadata['files']; - for (var i = 0; i < files.length; ++i) { - DataRequest.prototype.requests[files[i].filename].onload(); - } - Module['removeRunDependency']('datafile_../project.data'); - - }; - Module['addRunDependency']('datafile_../project.data'); - - if (!Module.preloadResults) Module.preloadResults = {}; - - Module.preloadResults[PACKAGE_NAME] = {fromCache: false}; - if (fetched) { - processPackageData(fetched); - fetched = null; - } else { - fetchedCallback = processPackageData; - } - - } - if (Module['calledRun']) { - runWithFS(); - } else { - if (!Module['preRun']) Module['preRun'] = []; - Module["preRun"].push(runWithFS); // FS is not initialized yet, wait for it - } - - } - loadPackage({"files": [{"filename": "/glsl/glsl20/adtLodShader.frag", "start": 0, "end": 1443, "audio": 0}, {"filename": "/glsl/glsl20/adtLodShader.vert", "start": 1443, "end": 2339, "audio": 0}, {"filename": "/glsl/glsl20/adtShader.frag", "start": 2339, "end": 13106, "audio": 0}, {"filename": "/glsl/glsl20/adtShader.vert", "start": 13106, "end": 14808, "audio": 0}, {"filename": "/glsl/glsl20/adtWater.frag", "start": 14808, "end": 15104, "audio": 0}, {"filename": "/glsl/glsl20/adtWater.vert", "start": 15104, "end": 15997, "audio": 0}, {"filename": "/glsl/glsl20/drawBBShader.frag", "start": 15997, "end": 16237, "audio": 0}, {"filename": "/glsl/glsl20/drawBBShader.vert", "start": 16237, "end": 16811, "audio": 0}, {"filename": "/glsl/glsl20/drawDepthShader.frag", "start": 16811, "end": 17415, "audio": 0}, {"filename": "/glsl/glsl20/drawFrustumShader.frag", "start": 17415, "end": 17613, "audio": 0}, {"filename": "/glsl/glsl20/drawFrustumShader.vert", "start": 17613, "end": 17967, "audio": 0}, {"filename": "/glsl/glsl20/drawLinesShader.frag", "start": 17967, "end": 18158, "audio": 0}, {"filename": "/glsl/glsl20/drawLinesShader.vert", "start": 18158, "end": 18401, "audio": 0}, {"filename": "/glsl/glsl20/drawPoints.frag", "start": 18401, "end": 18575, "audio": 0}, {"filename": "/glsl/glsl20/drawPoints.vert", "start": 18575, "end": 18964, "audio": 0}, {"filename": "/glsl/glsl20/drawPortalShader.frag", "start": 18964, "end": 19141, "audio": 0}, {"filename": "/glsl/glsl20/drawPortalShader.vert", "start": 19141, "end": 19520, "audio": 0}, {"filename": "/glsl/glsl20/drawQuad.vert", "start": 19520, "end": 19941, "audio": 0}, {"filename": "/glsl/glsl20/ffxgauss4.frag", "start": 19941, "end": 20843, "audio": 0}, {"filename": "/glsl/glsl20/ffxglow.frag", "start": 20843, "end": 21313, "audio": 0}, {"filename": "/glsl/glsl20/imguiShader.frag", "start": 21313, "end": 21583, "audio": 0}, {"filename": "/glsl/glsl20/imguiShader.vert", "start": 21583, "end": 21908, "audio": 0}, {"filename": "/glsl/glsl20/m2ParticleShader.frag", "start": 21908, "end": 27847, "audio": 0}, {"filename": "/glsl/glsl20/m2ParticleShader.vert", "start": 27847, "end": 29137, "audio": 0}, {"filename": "/glsl/glsl20/m2Shader.frag", "start": 29137, "end": 50535, "audio": 0}, {"filename": "/glsl/glsl20/m2Shader.vert", "start": 50535, "end": 60222, "audio": 0}, {"filename": "/glsl/glsl20/renderFrameBufferShader.frag", "start": 60222, "end": 62491, "audio": 0}, {"filename": "/glsl/glsl20/renderFrameBufferShader.vert", "start": 62491, "end": 62757, "audio": 0}, {"filename": "/glsl/glsl20/ribbonShader.frag", "start": 62757, "end": 66506, "audio": 0}, {"filename": "/glsl/glsl20/ribbonShader.vert", "start": 66506, "end": 67634, "audio": 0}, {"filename": "/glsl/glsl20/skyConus.frag", "start": 67634, "end": 67831, "audio": 0}, {"filename": "/glsl/glsl20/skyConus.vert", "start": 67831, "end": 69081, "audio": 0}, {"filename": "/glsl/glsl20/waterShader.frag", "start": 69081, "end": 70134, "audio": 0}, {"filename": "/glsl/glsl20/waterShader.vert", "start": 70134, "end": 70646, "audio": 0}, {"filename": "/glsl/glsl20/wmoShader.frag", "start": 70646, "end": 83509, "audio": 0}, {"filename": "/glsl/glsl20/wmoShader.vert", "start": 83509, "end": 87361, "audio": 0}, {"filename": "/glsl/glsl3.3/adtLodShader.frag", "start": 87361, "end": 88815, "audio": 0}, {"filename": "/glsl/glsl3.3/adtLodShader.vert", "start": 88815, "end": 89720, "audio": 0}, {"filename": "/glsl/glsl3.3/adtShader.frag", "start": 89720, "end": 100444, "audio": 0}, {"filename": "/glsl/glsl3.3/adtShader.vert", "start": 100444, "end": 102173, "audio": 0}, {"filename": "/glsl/glsl3.3/adtWater.frag", "start": 102173, "end": 102487, "audio": 0}, {"filename": "/glsl/glsl3.3/adtWater.vert", "start": 102487, "end": 103387, "audio": 0}, {"filename": "/glsl/glsl3.3/drawBBShader.frag", "start": 103387, "end": 103651, "audio": 0}, {"filename": "/glsl/glsl3.3/drawBBShader.vert", "start": 103651, "end": 104215, "audio": 0}, {"filename": "/glsl/glsl3.3/drawDepthShader.frag", "start": 104215, "end": 104836, "audio": 0}, {"filename": "/glsl/glsl3.3/drawFrustumShader.frag", "start": 104836, "end": 105061, "audio": 0}, {"filename": "/glsl/glsl3.3/drawFrustumShader.vert", "start": 105061, "end": 105416, "audio": 0}, {"filename": "/glsl/glsl3.3/drawLinesShader.frag", "start": 105416, "end": 105633, "audio": 0}, {"filename": "/glsl/glsl3.3/drawLinesShader.vert", "start": 105633, "end": 105877, "audio": 0}, {"filename": "/glsl/glsl3.3/drawPoints.frag", "start": 105877, "end": 106071, "audio": 0}, {"filename": "/glsl/glsl3.3/drawPoints.vert", "start": 106071, "end": 106446, "audio": 0}, {"filename": "/glsl/glsl3.3/drawPortalShader.frag", "start": 106446, "end": 106649, "audio": 0}, {"filename": "/glsl/glsl3.3/drawPortalShader.vert", "start": 106649, "end": 107018, "audio": 0}, {"filename": "/glsl/glsl3.3/drawQuad.vert", "start": 107018, "end": 107439, "audio": 0}, {"filename": "/glsl/glsl3.3/ffxgauss4.frag", "start": 107439, "end": 108354, "audio": 0}, {"filename": "/glsl/glsl3.3/ffxglow.frag", "start": 108354, "end": 108841, "audio": 0}, {"filename": "/glsl/glsl3.3/imguiShader.frag", "start": 108841, "end": 109135, "audio": 0}, {"filename": "/glsl/glsl3.3/imguiShader.vert", "start": 109135, "end": 109483, "audio": 0}, {"filename": "/glsl/glsl3.3/m2ParticleShader.frag", "start": 109483, "end": 115408, "audio": 0}, {"filename": "/glsl/glsl3.3/m2ParticleShader.vert", "start": 115408, "end": 116735, "audio": 0}, {"filename": "/glsl/glsl3.3/m2Shader.frag", "start": 116735, "end": 138086, "audio": 0}, {"filename": "/glsl/glsl3.3/m2Shader.vert", "start": 138086, "end": 147799, "audio": 0}, {"filename": "/glsl/glsl3.3/renderFrameBufferShader.frag", "start": 147799, "end": 150066, "audio": 0}, {"filename": "/glsl/glsl3.3/renderFrameBufferShader.vert", "start": 150066, "end": 150342, "audio": 0}, {"filename": "/glsl/glsl3.3/ribbonShader.frag", "start": 150342, "end": 154101, "audio": 0}, {"filename": "/glsl/glsl3.3/ribbonShader.vert", "start": 154101, "end": 155246, "audio": 0}, {"filename": "/glsl/glsl3.3/skyConus.frag", "start": 155246, "end": 155478, "audio": 0}, {"filename": "/glsl/glsl3.3/skyConus.vert", "start": 155478, "end": 156715, "audio": 0}, {"filename": "/glsl/glsl3.3/waterShader.frag", "start": 156715, "end": 157781, "audio": 0}, {"filename": "/glsl/glsl3.3/waterShader.vert", "start": 157781, "end": 158279, "audio": 0}, {"filename": "/glsl/glsl3.3/wmoShader.frag", "start": 158279, "end": 171107, "audio": 0}, {"filename": "/glsl/glsl3.3/wmoShader.vert", "start": 171107, "end": 174995, "audio": 0}], "remote_package_size": 174995, "package_uuid": "277fad44-55a4-43e0-bcd7-1d1898709b76"}); - - })(); - - - // All the pre-js content up to here must remain later on, we need to run - // it. - var necessaryPreJSTasks = Module['preRun'].slice(); - // Module['locateFile'] = function (path, prefix) { -// console.log("path = "+path, path); -// if ((""+path).indexOf("?") <= 0) { -// return prefix+path+"?t="+new Date().getTime(); -// } -// return prefix+path; -// }; -Module['hammerJsAssignControl'] = function () { - var element = Module['canvas']; - - var mleft_pressed = 0; - var m_x = 0, m_y = 0; - - var isPitchGoingOn = false; - var isCameraMoving = false; - - var lastTouchStart = new Date(); - function touchStart(event) { - event.preventDefault(); - if (isPitchGoingOn) return; - mleft_pressed = 1; - - m_x = event.touches[0].pageX; - m_y = event.touches[0].pageY; - - var newTime = new Date(); - if (newTime.getTime() - lastTouchStart.getTime() < 700 && event.touches.length == 1) { - isCameraMoving = true; - Module['_startMovingForward'](); - } - lastTouchStart = newTime; - } - - function touchMove(event) { - event.preventDefault(); - if (isPitchGoingOn) return; - var x = event.touches[0].pageX; - var y = event.touches[0].pageY; - - if (mleft_pressed === 1) { - Module['_addHorizontalViewDir']((x - m_x) / 4.0); - Module['_addVerticalViewDir']((y - m_y) / 4.0); - - m_x = x; - m_y = y; - } - } - function touchEnd(event) { - event.preventDefault(); - if (event.touches.length == 0) { - mleft_pressed = 0; - Module['_stopMovingForward'](); - Module['_stopMovingBackwards'](); - isCameraMoving = false; - } - event.preventDefault(); - // console.log("(end) event.touches.length" + event.touches.length) - } - - element.addEventListener('touchstart', touchStart, false); - element.addEventListener('touchmove', touchMove, false); - element.addEventListener('touchend', touchEnd, false); - - var mc = new Hammer(element); - mc.get('pinch').set({ enable: true }); - var pinchScale = 0; - - var previous_pinch_delta = { - x: 0, - y: 0 - }; - - mc.on("pinchstart pinchin pinchout pinchend pinch", function(ev) { - if (ev.type == 'pinchstart') { - pinchScale = ev.scale; - isPitchGoingOn = true; - // console.log("pinchstart") - if (isCameraMoving) { - Module['_stopMovingForward'](); - Module['_startMovingBackwards'](); - } - } else if (ev.type == 'pinchend') { - pinchScale = 0; - isPitchGoingOn = false; - console.log("pinchend"); - if (isCameraMoving) { - Module['_stopMovingBackwards'](); - Module['_startMovingForward'](); - } - } else if (ev.type == 'pinchin') { - Module["_zoomInFromMouseScroll"]((pinchScale - ev.scale) * 5); - pinchScale = ev.scale; - } else if (ev.type == 'pinchout') { - - Module["_zoomInFromMouseScroll"]((pinchScale - ev.scale) * 5); - pinchScale = ev.scale; - } - - - if (pinchScale < 0.01) pinchScale = 0.01; - - ev.preventDefault(); - }); -} - - if (!Module['preRun']) throw 'Module.preRun should exist because file support used it; did a pre-js delete it?'; - necessaryPreJSTasks.forEach(function(task) { - if (Module['preRun'].indexOf(task) < 0) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?'; - }); - - -// Sometimes an existing Module object exists with properties -// meant to overwrite the default module functionality. Here -// we collect those properties and reapply _after_ we configure -// the current environment's defaults to avoid having to be so -// defensive during initialization. -var moduleOverrides = {}; -var key; -for (key in Module) { - if (Module.hasOwnProperty(key)) { - moduleOverrides[key] = Module[key]; - } -} - -var arguments_ = []; -var thisProgram = './this.program'; -var quit_ = function(status, toThrow) { - throw toThrow; -}; - -// Determine the runtime environment we are in. You can customize this by -// setting the ENVIRONMENT setting at compile time (see settings.js). - -var ENVIRONMENT_IS_WEB = false; -var ENVIRONMENT_IS_WORKER = false; -var ENVIRONMENT_IS_NODE = false; -var ENVIRONMENT_IS_SHELL = false; -ENVIRONMENT_IS_WEB = typeof window === 'object'; -ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; -// N.b. Electron.js environment is simultaneously a NODE-environment, but -// also a web environment. -ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string'; -ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; - -if (Module['ENVIRONMENT']) { - throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)'); -} - - - -// `/` should be present at the end if `scriptDirectory` is not empty -var scriptDirectory = ''; -function locateFile(path) { - if (Module['locateFile']) { - return Module['locateFile'](path, scriptDirectory); - } - return scriptDirectory + path; -} - -// Hooks that are implemented differently in different runtime environments. -var read_, - readAsync, - readBinary, - setWindowTitle; - -var nodeFS; -var nodePath; - -if (ENVIRONMENT_IS_NODE) { - if (ENVIRONMENT_IS_WORKER) { - scriptDirectory = require('path').dirname(scriptDirectory) + '/'; - } else { - scriptDirectory = __dirname + '/'; - } - - - - - read_ = function shell_read(filename, binary) { - if (!nodeFS) nodeFS = require('fs'); - if (!nodePath) nodePath = require('path'); - filename = nodePath['normalize'](filename); - return nodeFS['readFileSync'](filename, binary ? null : 'utf8'); - }; - - readBinary = function readBinary(filename) { - var ret = read_(filename, true); - if (!ret.buffer) { - ret = new Uint8Array(ret); - } - assert(ret.buffer); - return ret; - }; - - - - - if (process['argv'].length > 1) { - thisProgram = process['argv'][1].replace(/\\/g, '/'); - } - - arguments_ = process['argv'].slice(2); - - if (typeof module !== 'undefined') { - module['exports'] = Module; - } - - process['on']('uncaughtException', function(ex) { - // suppress ExitStatus exceptions from showing an error - if (!(ex instanceof ExitStatus)) { - throw ex; - } - }); - - process['on']('unhandledRejection', abort); - - quit_ = function(status) { - process['exit'](status); - }; - - Module['inspect'] = function () { return '[Emscripten Module object]'; }; - - - -} else -if (ENVIRONMENT_IS_SHELL) { - - - if (typeof read != 'undefined') { - read_ = function shell_read(f) { - return read(f); - }; - } - - readBinary = function readBinary(f) { - var data; - if (typeof readbuffer === 'function') { - return new Uint8Array(readbuffer(f)); - } - data = read(f, 'binary'); - assert(typeof data === 'object'); - return data; - }; - - if (typeof scriptArgs != 'undefined') { - arguments_ = scriptArgs; - } else if (typeof arguments != 'undefined') { - arguments_ = arguments; - } - - if (typeof quit === 'function') { - quit_ = function(status) { - quit(status); - }; - } - - if (typeof print !== 'undefined') { - // Prefer to use print/printErr where they exist, as they usually work better. - if (typeof console === 'undefined') console = /** @type{!Console} */({}); - console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); - console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr !== 'undefined' ? printErr : print); - } - - -} else - -// Note that this includes Node.js workers when relevant (pthreads is enabled). -// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and -// ENVIRONMENT_IS_NODE. -if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { - if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled - scriptDirectory = self.location.href; - } else if (document.currentScript) { // web - scriptDirectory = document.currentScript.src; - } - // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. - // otherwise, slice off the final part of the url to find the script directory. - // if scriptDirectory does not contain a slash, lastIndexOf will return -1, - // and scriptDirectory will correctly be replaced with an empty string. - if (scriptDirectory.indexOf('blob:') !== 0) { - scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf('/')+1); - } else { - scriptDirectory = ''; - } - - - // Differentiate the Web Worker from the Node Worker case, as reading must - // be done differently. - { - - - - - read_ = function shell_read(url) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.send(null); - return xhr.responseText; - }; - - if (ENVIRONMENT_IS_WORKER) { - readBinary = function readBinary(url) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - xhr.responseType = 'arraybuffer'; - xhr.send(null); - return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response)); - }; - } - - readAsync = function readAsync(url, onload, onerror) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'arraybuffer'; - xhr.onload = function xhr_onload() { - if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 - onload(xhr.response); - return; - } - onerror(); - }; - xhr.onerror = onerror; - xhr.send(null); - }; - - - - - } - - setWindowTitle = function(title) { document.title = title }; -} else -{ - throw new Error('environment detection error'); -} - - -// Set up the out() and err() hooks, which are how we can print to stdout or -// stderr, respectively. -var out = Module['print'] || console.log.bind(console); -var err = Module['printErr'] || console.warn.bind(console); - -// Merge back in the overrides -for (key in moduleOverrides) { - if (moduleOverrides.hasOwnProperty(key)) { - Module[key] = moduleOverrides[key]; - } -} -// Free the object hierarchy contained in the overrides, this lets the GC -// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. -moduleOverrides = null; - -// Emit code to handle expected values on the Module object. This applies Module.x -// to the proper local x. This has two benefits: first, we only emit it if it is -// expected to arrive, and second, by using a local everywhere else that can be -// minified. -if (Module['arguments']) arguments_ = Module['arguments'];if (!Object.getOwnPropertyDescriptor(Module, 'arguments')) Object.defineProperty(Module, 'arguments', { configurable: true, get: function() { abort('Module.arguments has been replaced with plain arguments_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); -if (Module['thisProgram']) thisProgram = Module['thisProgram'];if (!Object.getOwnPropertyDescriptor(Module, 'thisProgram')) Object.defineProperty(Module, 'thisProgram', { configurable: true, get: function() { abort('Module.thisProgram has been replaced with plain thisProgram (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); -if (Module['quit']) quit_ = Module['quit'];if (!Object.getOwnPropertyDescriptor(Module, 'quit')) Object.defineProperty(Module, 'quit', { configurable: true, get: function() { abort('Module.quit has been replaced with plain quit_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); - -// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message -// Assertions on removed incoming Module JS APIs. -assert(typeof Module['memoryInitializerPrefixURL'] === 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); -assert(typeof Module['pthreadMainPrefixURL'] === 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); -assert(typeof Module['cdInitializerPrefixURL'] === 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); -assert(typeof Module['filePackagePrefixURL'] === 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); -assert(typeof Module['read'] === 'undefined', 'Module.read option was removed (modify read_ in JS)'); -assert(typeof Module['readAsync'] === 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); -assert(typeof Module['readBinary'] === 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); -assert(typeof Module['setWindowTitle'] === 'undefined', 'Module.setWindowTitle option was removed (modify setWindowTitle in JS)'); -assert(typeof Module['TOTAL_MEMORY'] === 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); -if (!Object.getOwnPropertyDescriptor(Module, 'read')) Object.defineProperty(Module, 'read', { configurable: true, get: function() { abort('Module.read has been replaced with plain read_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); -if (!Object.getOwnPropertyDescriptor(Module, 'readAsync')) Object.defineProperty(Module, 'readAsync', { configurable: true, get: function() { abort('Module.readAsync has been replaced with plain readAsync (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); -if (!Object.getOwnPropertyDescriptor(Module, 'readBinary')) Object.defineProperty(Module, 'readBinary', { configurable: true, get: function() { abort('Module.readBinary has been replaced with plain readBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); -if (!Object.getOwnPropertyDescriptor(Module, 'setWindowTitle')) Object.defineProperty(Module, 'setWindowTitle', { configurable: true, get: function() { abort('Module.setWindowTitle has been replaced with plain setWindowTitle (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); -var IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js'; -var PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js'; -var WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js'; -var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js'; - - - - - - -// {{PREAMBLE_ADDITIONS}} - -var STACK_ALIGN = 16; - -function dynamicAlloc(size) { - assert(DYNAMICTOP_PTR); - var ret = HEAP32[DYNAMICTOP_PTR>>2]; - var end = (ret + size + 15) & -16; - assert(end <= HEAP8.length, 'failure to dynamicAlloc - memory growth etc. is not supported there, call malloc/sbrk directly'); - HEAP32[DYNAMICTOP_PTR>>2] = end; - return ret; -} - -function alignMemory(size, factor) { - if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default - return Math.ceil(size / factor) * factor; -} - -function getNativeTypeSize(type) { - switch (type) { - case 'i1': case 'i8': return 1; - case 'i16': return 2; - case 'i32': return 4; - case 'i64': return 8; - case 'float': return 4; - case 'double': return 8; - default: { - if (type[type.length-1] === '*') { - return 4; // A pointer - } else if (type[0] === 'i') { - var bits = Number(type.substr(1)); - assert(bits % 8 === 0, 'getNativeTypeSize invalid bits ' + bits + ', type ' + type); - return bits / 8; - } else { - return 0; - } - } - } -} - -function warnOnce(text) { - if (!warnOnce.shown) warnOnce.shown = {}; - if (!warnOnce.shown[text]) { - warnOnce.shown[text] = 1; - err(text); - } -} - - - - - -// Wraps a JS function as a wasm function with a given signature. -function convertJsFunctionToWasm(func, sig) { - - // If the type reflection proposal is available, use the new - // "WebAssembly.Function" constructor. - // Otherwise, construct a minimal wasm module importing the JS function and - // re-exporting it. - if (typeof WebAssembly.Function === "function") { - var typeNames = { - 'i': 'i32', - 'j': 'i64', - 'f': 'f32', - 'd': 'f64' - }; - var type = { - parameters: [], - results: sig[0] == 'v' ? [] : [typeNames[sig[0]]] - }; - for (var i = 1; i < sig.length; ++i) { - type.parameters.push(typeNames[sig[i]]); - } - return new WebAssembly.Function(type, func); - } - - // The module is static, with the exception of the type section, which is - // generated based on the signature passed in. - var typeSection = [ - 0x01, // id: section, - 0x00, // length: 0 (placeholder) - 0x01, // count: 1 - 0x60, // form: func - ]; - var sigRet = sig.slice(0, 1); - var sigParam = sig.slice(1); - var typeCodes = { - 'i': 0x7f, // i32 - 'j': 0x7e, // i64 - 'f': 0x7d, // f32 - 'd': 0x7c, // f64 - }; - - // Parameters, length + signatures - typeSection.push(sigParam.length); - for (var i = 0; i < sigParam.length; ++i) { - typeSection.push(typeCodes[sigParam[i]]); - } - - // Return values, length + signatures - // With no multi-return in MVP, either 0 (void) or 1 (anything else) - if (sigRet == 'v') { - typeSection.push(0x00); - } else { - typeSection = typeSection.concat([0x01, typeCodes[sigRet]]); - } - - // Write the overall length of the type section back into the section header - // (excepting the 2 bytes for the section id and length) - typeSection[1] = typeSection.length - 2; - - // Rest of the module is static - var bytes = new Uint8Array([ - 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") - 0x01, 0x00, 0x00, 0x00, // version: 1 - ].concat(typeSection, [ - 0x02, 0x07, // import section - // (import "e" "f" (func 0 (type 0))) - 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00, - 0x07, 0x05, // export section - // (export "f" (func 0 (type 0))) - 0x01, 0x01, 0x66, 0x00, 0x00, - ])); - - // We can compile this wasm module synchronously because it is very small. - // This accepts an import (at "e.f"), that it reroutes to an export (at "f") - var module = new WebAssembly.Module(bytes); - var instance = new WebAssembly.Instance(module, { - 'e': { - 'f': func - } - }); - var wrappedFunc = instance.exports['f']; - return wrappedFunc; -} - -var freeTableIndexes = []; - -// Weak map of functions in the table to their indexes, created on first use. -var functionsInTableMap; - -// Add a wasm function to the table. -function addFunctionWasm(func, sig) { - var table = wasmTable; - - // Check if the function is already in the table, to ensure each function - // gets a unique index. First, create the map if this is the first use. - if (!functionsInTableMap) { - functionsInTableMap = new WeakMap(); - for (var i = 0; i < table.length; i++) { - var item = table.get(i); - // Ignore null values. - if (item) { - functionsInTableMap.set(item, i); - } - } - } - if (functionsInTableMap.has(func)) { - return functionsInTableMap.get(func); - } - - // It's not in the table, add it now. - - - var ret; - // Reuse a free index if there is one, otherwise grow. - if (freeTableIndexes.length) { - ret = freeTableIndexes.pop(); - } else { - ret = table.length; - // Grow the table - try { - table.grow(1); - } catch (err) { - if (!(err instanceof RangeError)) { - throw err; - } - throw 'Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'; - } - } - - // Set the new value. - try { - // Attempting to call this with JS function will cause of table.set() to fail - table.set(ret, func); - } catch (err) { - if (!(err instanceof TypeError)) { - throw err; - } - assert(typeof sig !== 'undefined', 'Missing signature argument to addFunction'); - var wrapped = convertJsFunctionToWasm(func, sig); - table.set(ret, wrapped); - } - - functionsInTableMap.set(func, ret); - - return ret; -} - -function removeFunctionWasm(index) { - functionsInTableMap.delete(wasmTable.get(index)); - freeTableIndexes.push(index); -} - -// 'sig' parameter is required for the llvm backend but only when func is not -// already a WebAssembly function. -function addFunction(func, sig) { - assert(typeof func !== 'undefined'); - - return addFunctionWasm(func, sig); -} - -function removeFunction(index) { - removeFunctionWasm(index); -} - - - -var funcWrappers = {}; - -function getFuncWrapper(func, sig) { - if (!func) return; // on null pointer, return undefined - assert(sig); - if (!funcWrappers[sig]) { - funcWrappers[sig] = {}; - } - var sigCache = funcWrappers[sig]; - if (!sigCache[func]) { - // optimize away arguments usage in common cases - if (sig.length === 1) { - sigCache[func] = function dynCall_wrapper() { - return dynCall(sig, func); - }; - } else if (sig.length === 2) { - sigCache[func] = function dynCall_wrapper(arg) { - return dynCall(sig, func, [arg]); - }; - } else { - // general case - sigCache[func] = function dynCall_wrapper() { - return dynCall(sig, func, Array.prototype.slice.call(arguments)); - }; - } - } - return sigCache[func]; -} - - - - - - - -function makeBigInt(low, high, unsigned) { - return unsigned ? ((+((low>>>0)))+((+((high>>>0)))*4294967296.0)) : ((+((low>>>0)))+((+((high|0)))*4294967296.0)); -} - -/** @param {Array=} args */ -function dynCall(sig, ptr, args) { - if (args && args.length) { - // j (64-bit integer) must be passed in as two numbers [low 32, high 32]. - assert(args.length === sig.substring(1).replace(/j/g, '--').length); - assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); - return Module['dynCall_' + sig].apply(null, [ptr].concat(args)); - } else { - assert(sig.length == 1); - assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); - return Module['dynCall_' + sig].call(null, ptr); - } -} - -var tempRet0 = 0; - -var setTempRet0 = function(value) { - tempRet0 = value; -}; - -var getTempRet0 = function() { - return tempRet0; -}; - -function getCompilerSetting(name) { - throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for getCompilerSetting or emscripten_get_compiler_setting to work'; -} - -// The address globals begin at. Very low in memory, for code size and optimization opportunities. -// Above 0 is static memory, starting with globals. -// Then the stack. -// Then 'dynamic' memory for sbrk. -var GLOBAL_BASE = 1024; - - - - - -// === Preamble library stuff === - -// Documentation for the public APIs defined in this file must be updated in: -// site/source/docs/api_reference/preamble.js.rst -// A prebuilt local version of the documentation is available at: -// site/build/text/docs/api_reference/preamble.js.txt -// You can also build docs locally as HTML or other formats in site/ -// An online HTML version (which may be of a different version of Emscripten) -// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html - - -var wasmBinary;if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];if (!Object.getOwnPropertyDescriptor(Module, 'wasmBinary')) Object.defineProperty(Module, 'wasmBinary', { configurable: true, get: function() { abort('Module.wasmBinary has been replaced with plain wasmBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); -var noExitRuntime;if (Module['noExitRuntime']) noExitRuntime = Module['noExitRuntime'];if (!Object.getOwnPropertyDescriptor(Module, 'noExitRuntime')) Object.defineProperty(Module, 'noExitRuntime', { configurable: true, get: function() { abort('Module.noExitRuntime has been replaced with plain noExitRuntime (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); - - -if (typeof WebAssembly !== 'object') { - abort('no native wasm support detected'); -} - - - - -// In MINIMAL_RUNTIME, setValue() and getValue() are only available when building with safe heap enabled, for heap safety checking. -// In traditional runtime, setValue() and getValue() are always available (although their use is highly discouraged due to perf penalties) - -/** @param {number} ptr - @param {number} value - @param {string} type - @param {number|boolean=} noSafe */ -function setValue(ptr, value, type, noSafe) { - type = type || 'i8'; - if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit - switch(type) { - case 'i1': HEAP8[((ptr)>>0)]=value; break; - case 'i8': HEAP8[((ptr)>>0)]=value; break; - case 'i16': HEAP16[((ptr)>>1)]=value; break; - case 'i32': HEAP32[((ptr)>>2)]=value; break; - case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)]=tempI64[0],HEAP32[(((ptr)+(4))>>2)]=tempI64[1]); break; - case 'float': HEAPF32[((ptr)>>2)]=value; break; - case 'double': HEAPF64[((ptr)>>3)]=value; break; - default: abort('invalid type for setValue: ' + type); - } -} - -/** @param {number} ptr - @param {string} type - @param {number|boolean=} noSafe */ -function getValue(ptr, type, noSafe) { - type = type || 'i8'; - if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit - switch(type) { - case 'i1': return HEAP8[((ptr)>>0)]; - case 'i8': return HEAP8[((ptr)>>0)]; - case 'i16': return HEAP16[((ptr)>>1)]; - case 'i32': return HEAP32[((ptr)>>2)]; - case 'i64': return HEAP32[((ptr)>>2)]; - case 'float': return HEAPF32[((ptr)>>2)]; - case 'double': return HEAPF64[((ptr)>>3)]; - default: abort('invalid type for getValue: ' + type); - } - return null; -} - - - - - - -// Wasm globals - -var wasmMemory; - -// In fastcomp asm.js, we don't need a wasm Table at all. -// In the wasm backend, we polyfill the WebAssembly object, -// so this creates a (non-native-wasm) table for us. - -var wasmTable = new WebAssembly.Table({ - 'initial': 2751, - 'maximum': 2751, - 'element': 'anyfunc' -}); - - - - -//======================================== -// Runtime essentials -//======================================== - -// whether we are quitting the application. no code should run after this. -// set in exit() and abort() -var ABORT = false; - -// set by exit() and abort(). Passed to 'onExit' handler. -// NOTE: This is also used as the process return code code in shell environments -// but only when noExitRuntime is false. -var EXITSTATUS = 0; - -/** @type {function(*, string=)} */ -function assert(condition, text) { - if (!condition) { - abort('Assertion failed: ' + text); - } -} - -// Returns the C function with a specified identifier (for C++, you need to do manual name mangling) -function getCFunc(ident) { - var func = Module['_' + ident]; // closure exported function - assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported'); - return func; -} - -// C calling interface. -/** @param {string|null=} returnType - @param {Array=} argTypes - @param {Arguments|Array=} args - @param {Object=} opts */ -function ccall(ident, returnType, argTypes, args, opts) { - // For fast lookup of conversion functions - var toC = { - 'string': function(str) { - var ret = 0; - if (str !== null && str !== undefined && str !== 0) { // null string - // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' - var len = (str.length << 2) + 1; - ret = stackAlloc(len); - stringToUTF8(str, ret, len); - } - return ret; - }, - 'array': function(arr) { - var ret = stackAlloc(arr.length); - writeArrayToMemory(arr, ret); - return ret; - } - }; - - function convertReturnValue(ret) { - if (returnType === 'string') return UTF8ToString(ret); - if (returnType === 'boolean') return Boolean(ret); - return ret; - } - - var func = getCFunc(ident); - var cArgs = []; - var stack = 0; - assert(returnType !== 'array', 'Return type should not be "array".'); - if (args) { - for (var i = 0; i < args.length; i++) { - var converter = toC[argTypes[i]]; - if (converter) { - if (stack === 0) stack = stackSave(); - cArgs[i] = converter(args[i]); - } else { - cArgs[i] = args[i]; - } - } - } - var ret = func.apply(null, cArgs); - - ret = convertReturnValue(ret); - if (stack !== 0) stackRestore(stack); - return ret; -} - -/** @param {string=} returnType - @param {Array=} argTypes - @param {Object=} opts */ -function cwrap(ident, returnType, argTypes, opts) { - return function() { - return ccall(ident, returnType, argTypes, arguments, opts); - } -} - -var ALLOC_NORMAL = 0; // Tries to use _malloc() -var ALLOC_STACK = 1; // Lives for the duration of the current function call -var ALLOC_DYNAMIC = 2; // Cannot be freed except through sbrk -var ALLOC_NONE = 3; // Do not allocate - -// allocate(): This is for internal use. You can use it yourself as well, but the interface -// is a little tricky (see docs right below). The reason is that it is optimized -// for multiple syntaxes to save space in generated code. So you should -// normally not use allocate(), and instead allocate memory using _malloc(), -// initialize it with setValue(), and so forth. -// @slab: An array of data, or a number. If a number, then the size of the block to allocate, -// in *bytes* (note that this is sometimes confusing: the next parameter does not -// affect this!) -// @types: Either an array of types, one for each byte (or 0 if no type at that position), -// or a single type which is used for the entire block. This only matters if there -// is initial data - if @slab is a number, then this does not matter at all and is -// ignored. -// @allocator: How to allocate memory, see ALLOC_* -/** @type {function((TypedArray|Array|number), string, number, number=)} */ -function allocate(slab, types, allocator, ptr) { - var zeroinit, size; - if (typeof slab === 'number') { - zeroinit = true; - size = slab; - } else { - zeroinit = false; - size = slab.length; - } - - var singleType = typeof types === 'string' ? types : null; - - var ret; - if (allocator == ALLOC_NONE) { - ret = ptr; - } else { - ret = [_malloc, - stackAlloc, - dynamicAlloc][allocator](Math.max(size, singleType ? 1 : types.length)); - } - - if (zeroinit) { - var stop; - ptr = ret; - assert((ret & 3) == 0); - stop = ret + (size & ~3); - for (; ptr < stop; ptr += 4) { - HEAP32[((ptr)>>2)]=0; - } - stop = ret + size; - while (ptr < stop) { - HEAP8[((ptr++)>>0)]=0; - } - return ret; - } - - if (singleType === 'i8') { - if (slab.subarray || slab.slice) { - HEAPU8.set(/** @type {!Uint8Array} */ (slab), ret); - } else { - HEAPU8.set(new Uint8Array(slab), ret); - } - return ret; - } - - var i = 0, type, typeSize, previousType; - while (i < size) { - var curr = slab[i]; - - type = singleType || types[i]; - if (type === 0) { - i++; - continue; - } - assert(type, 'Must know what type to store in allocate!'); - - if (type == 'i64') type = 'i32'; // special case: we have one i32 here, and one i32 later - - setValue(ret+i, curr, type); - - // no need to look up size unless type changes, so cache it - if (previousType !== type) { - typeSize = getNativeTypeSize(type); - previousType = type; - } - i += typeSize; - } - - return ret; -} - -// Allocate memory during any stage of startup - static memory early on, dynamic memory later, malloc when ready -function getMemory(size) { - if (!runtimeInitialized) return dynamicAlloc(size); - return _malloc(size); -} - - - - -// runtime_strings.js: Strings related runtime functions that are part of both MINIMAL_RUNTIME and regular runtime. - -// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns -// a copy of that string as a Javascript String object. - -var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined; - -/** - * @param {number} idx - * @param {number=} maxBytesToRead - * @return {string} - */ -function UTF8ArrayToString(heap, idx, maxBytesToRead) { - var endIdx = idx + maxBytesToRead; - var endPtr = idx; - // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. - // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. - // (As a tiny code save trick, compare endPtr against endIdx using a negation, so that undefined means Infinity) - while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr; - - if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) { - return UTF8Decoder.decode(heap.subarray(idx, endPtr)); - } else { - var str = ''; - // If building with TextDecoder, we have already computed the string length above, so test loop end condition against that - while (idx < endPtr) { - // For UTF8 byte structure, see: - // http://en.wikipedia.org/wiki/UTF-8#Description - // https://www.ietf.org/rfc/rfc2279.txt - // https://tools.ietf.org/html/rfc3629 - var u0 = heap[idx++]; - if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } - var u1 = heap[idx++] & 63; - if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } - var u2 = heap[idx++] & 63; - if ((u0 & 0xF0) == 0xE0) { - u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; - } else { - if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte 0x' + u0.toString(16) + ' encountered when deserializing a UTF-8 string on the asm.js/wasm heap to a JS string!'); - u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63); - } - - if (u0 < 0x10000) { - str += String.fromCharCode(u0); - } else { - var ch = u0 - 0x10000; - str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); - } - } - } - return str; -} - -// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns a -// copy of that string as a Javascript String object. -// maxBytesToRead: an optional length that specifies the maximum number of bytes to read. You can omit -// this parameter to scan the string until the first \0 byte. If maxBytesToRead is -// passed, and the string at [ptr, ptr+maxBytesToReadr[ contains a null byte in the -// middle, then the string will cut short at that byte index (i.e. maxBytesToRead will -// not produce a string of exact length [ptr, ptr+maxBytesToRead[) -// N.B. mixing frequent uses of UTF8ToString() with and without maxBytesToRead may -// throw JS JIT optimizations off, so it is worth to consider consistently using one -// style or the other. -/** - * @param {number} ptr - * @param {number=} maxBytesToRead - * @return {string} - */ -function UTF8ToString(ptr, maxBytesToRead) { - return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; -} - -// Copies the given Javascript String object 'str' to the given byte array at address 'outIdx', -// encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP. -// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. -// Parameters: -// str: the Javascript string to copy. -// heap: the array to copy to. Each index in this array is assumed to be one 8-byte element. -// outIdx: The starting offset in the array to begin the copying. -// maxBytesToWrite: The maximum number of bytes this function can write to the array. -// This count should include the null terminator, -// i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else. -// maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator. -// Returns the number of bytes written, EXCLUDING the null terminator. - -function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { - if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes. - return 0; - - var startIdx = outIdx; - var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. - for (var i = 0; i < str.length; ++i) { - // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. - // See http://unicode.org/faq/utf_bom.html#utf16-3 - // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 - var u = str.charCodeAt(i); // possibly a lead surrogate - if (u >= 0xD800 && u <= 0xDFFF) { - var u1 = str.charCodeAt(++i); - u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF); - } - if (u <= 0x7F) { - if (outIdx >= endIdx) break; - heap[outIdx++] = u; - } else if (u <= 0x7FF) { - if (outIdx + 1 >= endIdx) break; - heap[outIdx++] = 0xC0 | (u >> 6); - heap[outIdx++] = 0x80 | (u & 63); - } else if (u <= 0xFFFF) { - if (outIdx + 2 >= endIdx) break; - heap[outIdx++] = 0xE0 | (u >> 12); - heap[outIdx++] = 0x80 | ((u >> 6) & 63); - heap[outIdx++] = 0x80 | (u & 63); - } else { - if (outIdx + 3 >= endIdx) break; - if (u >= 0x200000) warnOnce('Invalid Unicode code point 0x' + u.toString(16) + ' encountered when serializing a JS string to an UTF-8 string on the asm.js/wasm heap! (Valid unicode code points should be in range 0-0x1FFFFF).'); - heap[outIdx++] = 0xF0 | (u >> 18); - heap[outIdx++] = 0x80 | ((u >> 12) & 63); - heap[outIdx++] = 0x80 | ((u >> 6) & 63); - heap[outIdx++] = 0x80 | (u & 63); - } - } - // Null-terminate the pointer to the buffer. - heap[outIdx] = 0; - return outIdx - startIdx; -} - -// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', -// null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP. -// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. -// Returns the number of bytes written, EXCLUDING the null terminator. - -function stringToUTF8(str, outPtr, maxBytesToWrite) { - assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); - return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite); -} - -// Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. -function lengthBytesUTF8(str) { - var len = 0; - for (var i = 0; i < str.length; ++i) { - // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. - // See http://unicode.org/faq/utf_bom.html#utf16-3 - var u = str.charCodeAt(i); // possibly a lead surrogate - if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); - if (u <= 0x7F) ++len; - else if (u <= 0x7FF) len += 2; - else if (u <= 0xFFFF) len += 3; - else len += 4; - } - return len; -} - - - - - -// runtime_strings_extra.js: Strings related runtime functions that are available only in regular runtime. - -// Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns -// a copy of that string as a Javascript String object. - -function AsciiToString(ptr) { - var str = ''; - while (1) { - var ch = HEAPU8[((ptr++)>>0)]; - if (!ch) return str; - str += String.fromCharCode(ch); - } -} - -// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', -// null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP. - -function stringToAscii(str, outPtr) { - return writeAsciiToMemory(str, outPtr, false); -} - -// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns -// a copy of that string as a Javascript String object. - -var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined; - -function UTF16ToString(ptr, maxBytesToRead) { - assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!'); - var endPtr = ptr; - // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. - // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. - var idx = endPtr >> 1; - var maxIdx = idx + maxBytesToRead / 2; - // If maxBytesToRead is not passed explicitly, it will be undefined, and this - // will always evaluate to true. This saves on code size. - while (!(idx >= maxIdx) && HEAPU16[idx]) ++idx; - endPtr = idx << 1; - - if (endPtr - ptr > 32 && UTF16Decoder) { - return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr)); - } else { - var i = 0; - - var str = ''; - while (1) { - var codeUnit = HEAP16[(((ptr)+(i*2))>>1)]; - if (codeUnit == 0 || i == maxBytesToRead / 2) return str; - ++i; - // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through. - str += String.fromCharCode(codeUnit); - } - } -} - -// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', -// null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP. -// Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write. -// Parameters: -// str: the Javascript string to copy. -// outPtr: Byte address in Emscripten HEAP where to write the string to. -// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null -// terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else. -// maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator. -// Returns the number of bytes written, EXCLUDING the null terminator. - -function stringToUTF16(str, outPtr, maxBytesToWrite) { - assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!'); - assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); - // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. - if (maxBytesToWrite === undefined) { - maxBytesToWrite = 0x7FFFFFFF; - } - if (maxBytesToWrite < 2) return 0; - maxBytesToWrite -= 2; // Null terminator. - var startPtr = outPtr; - var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length; - for (var i = 0; i < numCharsToWrite; ++i) { - // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. - var codeUnit = str.charCodeAt(i); // possibly a lead surrogate - HEAP16[((outPtr)>>1)]=codeUnit; - outPtr += 2; - } - // Null-terminate the pointer to the HEAP. - HEAP16[((outPtr)>>1)]=0; - return outPtr - startPtr; -} - -// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. - -function lengthBytesUTF16(str) { - return str.length*2; -} - -function UTF32ToString(ptr, maxBytesToRead) { - assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!'); - var i = 0; - - var str = ''; - // If maxBytesToRead is not passed explicitly, it will be undefined, and this - // will always evaluate to true. This saves on code size. - while (!(i >= maxBytesToRead / 4)) { - var utf32 = HEAP32[(((ptr)+(i*4))>>2)]; - if (utf32 == 0) break; - ++i; - // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing. - // See http://unicode.org/faq/utf_bom.html#utf16-3 - if (utf32 >= 0x10000) { - var ch = utf32 - 0x10000; - str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); - } else { - str += String.fromCharCode(utf32); - } - } - return str; -} - -// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', -// null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP. -// Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write. -// Parameters: -// str: the Javascript string to copy. -// outPtr: Byte address in Emscripten HEAP where to write the string to. -// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null -// terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else. -// maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator. -// Returns the number of bytes written, EXCLUDING the null terminator. - -function stringToUTF32(str, outPtr, maxBytesToWrite) { - assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!'); - assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); - // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. - if (maxBytesToWrite === undefined) { - maxBytesToWrite = 0x7FFFFFFF; - } - if (maxBytesToWrite < 4) return 0; - var startPtr = outPtr; - var endPtr = startPtr + maxBytesToWrite - 4; - for (var i = 0; i < str.length; ++i) { - // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. - // See http://unicode.org/faq/utf_bom.html#utf16-3 - var codeUnit = str.charCodeAt(i); // possibly a lead surrogate - if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) { - var trailSurrogate = str.charCodeAt(++i); - codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF); - } - HEAP32[((outPtr)>>2)]=codeUnit; - outPtr += 4; - if (outPtr + 4 > endPtr) break; - } - // Null-terminate the pointer to the HEAP. - HEAP32[((outPtr)>>2)]=0; - return outPtr - startPtr; -} - -// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. - -function lengthBytesUTF32(str) { - var len = 0; - for (var i = 0; i < str.length; ++i) { - // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. - // See http://unicode.org/faq/utf_bom.html#utf16-3 - var codeUnit = str.charCodeAt(i); - if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate. - len += 4; - } - - return len; -} - -// Allocate heap space for a JS string, and write it there. -// It is the responsibility of the caller to free() that memory. -function allocateUTF8(str) { - var size = lengthBytesUTF8(str) + 1; - var ret = _malloc(size); - if (ret) stringToUTF8Array(str, HEAP8, ret, size); - return ret; -} - -// Allocate stack space for a JS string, and write it there. -function allocateUTF8OnStack(str) { - var size = lengthBytesUTF8(str) + 1; - var ret = stackAlloc(size); - stringToUTF8Array(str, HEAP8, ret, size); - return ret; -} - -// Deprecated: This function should not be called because it is unsafe and does not provide -// a maximum length limit of how many bytes it is allowed to write. Prefer calling the -// function stringToUTF8Array() instead, which takes in a maximum length that can be used -// to be secure from out of bounds writes. -/** @deprecated - @param {boolean=} dontAddNull */ -function writeStringToMemory(string, buffer, dontAddNull) { - warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); - - var /** @type {number} */ lastChar, /** @type {number} */ end; - if (dontAddNull) { - // stringToUTF8Array always appends null. If we don't want to do that, remember the - // character that existed at the location where the null will be placed, and restore - // that after the write (below). - end = buffer + lengthBytesUTF8(string); - lastChar = HEAP8[end]; - } - stringToUTF8(string, buffer, Infinity); - if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. -} - -function writeArrayToMemory(array, buffer) { - assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') - HEAP8.set(array, buffer); -} - -/** @param {boolean=} dontAddNull */ -function writeAsciiToMemory(str, buffer, dontAddNull) { - for (var i = 0; i < str.length; ++i) { - assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff); - HEAP8[((buffer++)>>0)]=str.charCodeAt(i); - } - // Null-terminate the pointer to the HEAP. - if (!dontAddNull) HEAP8[((buffer)>>0)]=0; -} - - - -// Memory management - -var PAGE_SIZE = 16384; -var WASM_PAGE_SIZE = 65536; -var ASMJS_PAGE_SIZE = 16777216; - -function alignUp(x, multiple) { - if (x % multiple > 0) { - x += multiple - (x % multiple); - } - return x; -} - -var HEAP, -/** @type {ArrayBuffer} */ - buffer, -/** @type {Int8Array} */ - HEAP8, -/** @type {Uint8Array} */ - HEAPU8, -/** @type {Int16Array} */ - HEAP16, -/** @type {Uint16Array} */ - HEAPU16, -/** @type {Int32Array} */ - HEAP32, -/** @type {Uint32Array} */ - HEAPU32, -/** @type {Float32Array} */ - HEAPF32, -/** @type {Float64Array} */ - HEAPF64; - -function updateGlobalBufferAndViews(buf) { - buffer = buf; - Module['HEAP8'] = HEAP8 = new Int8Array(buf); - Module['HEAP16'] = HEAP16 = new Int16Array(buf); - Module['HEAP32'] = HEAP32 = new Int32Array(buf); - Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf); - Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf); - Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf); - Module['HEAPF32'] = HEAPF32 = new Float32Array(buf); - Module['HEAPF64'] = HEAPF64 = new Float64Array(buf); -} - -var STATIC_BASE = 1024, - STACK_BASE = 5345152, - STACKTOP = STACK_BASE, - STACK_MAX = 102272, - DYNAMIC_BASE = 5345152, - DYNAMICTOP_PTR = 102080; - -assert(STACK_BASE % 16 === 0, 'stack must start aligned'); -assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned'); - - -var TOTAL_STACK = 5242880; -if (Module['TOTAL_STACK']) assert(TOTAL_STACK === Module['TOTAL_STACK'], 'the stack size can no longer be determined at runtime') - -var INITIAL_INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 41943040;if (!Object.getOwnPropertyDescriptor(Module, 'INITIAL_MEMORY')) Object.defineProperty(Module, 'INITIAL_MEMORY', { configurable: true, get: function() { abort('Module.INITIAL_MEMORY has been replaced with plain INITIAL_INITIAL_MEMORY (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); - -assert(INITIAL_INITIAL_MEMORY >= TOTAL_STACK, 'INITIAL_MEMORY should be larger than TOTAL_STACK, was ' + INITIAL_INITIAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); - -// check for full engine support (use string 'subarray' to avoid closure compiler confusion) -assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray !== undefined && Int32Array.prototype.set !== undefined, - 'JS engine does not provide full typed array support'); - - - - - - - - -// In non-standalone/normal mode, we create the memory here. - - - -// Create the main memory. (Note: this isn't used in STANDALONE_WASM mode since the wasm -// memory is created in the wasm, not in JS.) - - if (Module['wasmMemory']) { - wasmMemory = Module['wasmMemory']; - } else - { - wasmMemory = new WebAssembly.Memory({ - 'initial': INITIAL_INITIAL_MEMORY / WASM_PAGE_SIZE - , - 'maximum': 2147483648 / WASM_PAGE_SIZE - }); - } - - -if (wasmMemory) { - buffer = wasmMemory.buffer; -} - -// If the user provides an incorrect length, just use that length instead rather than providing the user to -// specifically provide the memory length with Module['INITIAL_MEMORY']. -INITIAL_INITIAL_MEMORY = buffer.byteLength; -assert(INITIAL_INITIAL_MEMORY % WASM_PAGE_SIZE === 0); -assert(65536 % WASM_PAGE_SIZE === 0); -updateGlobalBufferAndViews(buffer); - -HEAP32[DYNAMICTOP_PTR>>2] = DYNAMIC_BASE; - - - - - - -// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. -function writeStackCookie() { - assert((STACK_MAX & 3) == 0); - // The stack grows downwards - HEAPU32[(STACK_MAX >> 2)+1] = 0x2135467; - HEAPU32[(STACK_MAX >> 2)+2] = 0x89BACDFE; - // Also test the global address 0 for integrity. - // We don't do this with ASan because ASan does its own checks for this. - HEAP32[0] = 0x63736d65; /* 'emsc' */ -} - -function checkStackCookie() { - var cookie1 = HEAPU32[(STACK_MAX >> 2)+1]; - var cookie2 = HEAPU32[(STACK_MAX >> 2)+2]; - if (cookie1 != 0x2135467 || cookie2 != 0x89BACDFE) { - abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x2135467, but received 0x' + cookie2.toString(16) + ' ' + cookie1.toString(16)); - } - // Also test the global address 0 for integrity. - // We don't do this with ASan because ASan does its own checks for this. - if (HEAP32[0] !== 0x63736d65 /* 'emsc' */) abort('Runtime error: The application has corrupted its heap memory area (address zero)!'); -} - - - - - -// Endianness check (note: assumes compiler arch was little-endian) -(function() { - var h16 = new Int16Array(1); - var h8 = new Int8Array(h16.buffer); - h16[0] = 0x6373; - if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian!'; -})(); - -function abortFnPtrError(ptr, sig) { - abort("Invalid function pointer " + ptr + " called with signature '" + sig + "'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this). Build with ASSERTIONS=2 for more info."); -} - - - -function callRuntimeCallbacks(callbacks) { - while(callbacks.length > 0) { - var callback = callbacks.shift(); - if (typeof callback == 'function') { - callback(Module); // Pass the module as the first argument. - continue; - } - var func = callback.func; - if (typeof func === 'number') { - if (callback.arg === undefined) { - Module['dynCall_v'](func); - } else { - Module['dynCall_vi'](func, callback.arg); - } - } else { - func(callback.arg === undefined ? null : callback.arg); - } - } -} - -var __ATPRERUN__ = []; // functions called before the runtime is initialized -var __ATINIT__ = []; // functions called during startup -var __ATMAIN__ = []; // functions called when main() is to be run -var __ATEXIT__ = []; // functions called during shutdown -var __ATPOSTRUN__ = []; // functions called after the main() is called - -var runtimeInitialized = false; -var runtimeExited = false; - - -function preRun() { - - if (Module['preRun']) { - if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; - while (Module['preRun'].length) { - addOnPreRun(Module['preRun'].shift()); - } - } - - callRuntimeCallbacks(__ATPRERUN__); -} - -function initRuntime() { - checkStackCookie(); - assert(!runtimeInitialized); - runtimeInitialized = true; - if (!Module["noFSInit"] && !FS.init.initialized) FS.init(); -TTY.init(); - callRuntimeCallbacks(__ATINIT__); -} - -function preMain() { - checkStackCookie(); - FS.ignorePermissions = false; - callRuntimeCallbacks(__ATMAIN__); -} - -function exitRuntime() { - checkStackCookie(); - runtimeExited = true; -} - -function postRun() { - checkStackCookie(); - - if (Module['postRun']) { - if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; - while (Module['postRun'].length) { - addOnPostRun(Module['postRun'].shift()); - } - } - - callRuntimeCallbacks(__ATPOSTRUN__); -} - -function addOnPreRun(cb) { - __ATPRERUN__.unshift(cb); -} - -function addOnInit(cb) { - __ATINIT__.unshift(cb); -} - -function addOnPreMain(cb) { - __ATMAIN__.unshift(cb); -} - -function addOnExit(cb) { -} - -function addOnPostRun(cb) { - __ATPOSTRUN__.unshift(cb); -} - -/** @param {number|boolean=} ignore */ -function unSign(value, bits, ignore) { - if (value >= 0) { - return value; - } - return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts - : Math.pow(2, bits) + value; -} -/** @param {number|boolean=} ignore */ -function reSign(value, bits, ignore) { - if (value <= 0) { - return value; - } - var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32 - : Math.pow(2, bits-1); - if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that - // but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors - // TODO: In i64 mode 1, resign the two parts separately and safely - value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts - } - return value; -} - - - - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc - -assert(Math.imul, 'This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); -assert(Math.fround, 'This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); -assert(Math.clz32, 'This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); -assert(Math.trunc, 'This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); - -var Math_abs = Math.abs; -var Math_cos = Math.cos; -var Math_sin = Math.sin; -var Math_tan = Math.tan; -var Math_acos = Math.acos; -var Math_asin = Math.asin; -var Math_atan = Math.atan; -var Math_atan2 = Math.atan2; -var Math_exp = Math.exp; -var Math_log = Math.log; -var Math_sqrt = Math.sqrt; -var Math_ceil = Math.ceil; -var Math_floor = Math.floor; -var Math_pow = Math.pow; -var Math_imul = Math.imul; -var Math_fround = Math.fround; -var Math_round = Math.round; -var Math_min = Math.min; -var Math_max = Math.max; -var Math_clz32 = Math.clz32; -var Math_trunc = Math.trunc; - - - -// A counter of dependencies for calling run(). If we need to -// do asynchronous work before running, increment this and -// decrement it. Incrementing must happen in a place like -// Module.preRun (used by emcc to add file preloading). -// Note that you can add dependencies in preRun, even though -// it happens right before run - run will be postponed until -// the dependencies are met. -var runDependencies = 0; -var runDependencyWatcher = null; -var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled -var runDependencyTracking = {}; - -function getUniqueRunDependency(id) { - var orig = id; - while (1) { - if (!runDependencyTracking[id]) return id; - id = orig + Math.random(); - } -} - -function addRunDependency(id) { - runDependencies++; - - if (Module['monitorRunDependencies']) { - Module['monitorRunDependencies'](runDependencies); - } - - if (id) { - assert(!runDependencyTracking[id]); - runDependencyTracking[id] = 1; - if (runDependencyWatcher === null && typeof setInterval !== 'undefined') { - // Check for missing dependencies every few seconds - runDependencyWatcher = setInterval(function() { - if (ABORT) { - clearInterval(runDependencyWatcher); - runDependencyWatcher = null; - return; - } - var shown = false; - for (var dep in runDependencyTracking) { - if (!shown) { - shown = true; - err('still waiting on run dependencies:'); - } - err('dependency: ' + dep); - } - if (shown) { - err('(end of list)'); - } - }, 10000); - } - } else { - err('warning: run dependency added without ID'); - } -} - -function removeRunDependency(id) { - runDependencies--; - - if (Module['monitorRunDependencies']) { - Module['monitorRunDependencies'](runDependencies); - } - - if (id) { - assert(runDependencyTracking[id]); - delete runDependencyTracking[id]; - } else { - err('warning: run dependency removed without ID'); - } - if (runDependencies == 0) { - if (runDependencyWatcher !== null) { - clearInterval(runDependencyWatcher); - runDependencyWatcher = null; - } - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); // can add another dependenciesFulfilled - } - } -} - -Module["preloadedImages"] = {}; // maps url to image data -Module["preloadedAudios"] = {}; // maps url to audio data - -/** @param {string|number=} what */ -function abort(what) { - if (Module['onAbort']) { - Module['onAbort'](what); - } - - what += ''; - err(what); - - ABORT = true; - EXITSTATUS = 1; - - var output = 'abort(' + what + ') at ' + stackTrace(); - what = output; - - // Use a wasm runtime error, because a JS error might be seen as a foreign - // exception, which means we'd run destructors on it. We need the error to - // simply make the program stop. - var e = new WebAssembly.RuntimeError(what); - - // Throw the error whether or not MODULARIZE is set because abort is used - // in code paths apart from instantiation where an exception is expected - // to be thrown when abort is called. - throw e; -} - - -var memoryInitializer = null; - - - - - - - - - - - -function hasPrefix(str, prefix) { - return String.prototype.startsWith ? - str.startsWith(prefix) : - str.indexOf(prefix) === 0; -} - -// Prefix of data URIs emitted by SINGLE_FILE and related options. -var dataURIPrefix = 'data:application/octet-stream;base64,'; - -// Indicates whether filename is a base64 data URI. -function isDataURI(filename) { - return hasPrefix(filename, dataURIPrefix); -} - -var fileURIPrefix = "file://"; - -// Indicates whether filename is delivered via file protocol (as opposed to http/https) -function isFileURI(filename) { - return hasPrefix(filename, fileURIPrefix); -} - - - -function createExportWrapper(name, fixedasm) { - return function() { - var displayName = name; - var asm = fixedasm; - if (!fixedasm) { - asm = Module['asm']; - } - assert(runtimeInitialized, 'native function `' + displayName + '` called before runtime initialization'); - assert(!runtimeExited, 'native function `' + displayName + '` called after runtime exit (use NO_EXIT_RUNTIME to keep it alive after main() exits)'); - if (!asm[name]) { - assert(asm[name], 'exported native function `' + displayName + '` not found'); - } - return asm[name].apply(null, arguments); - }; -} - -var wasmBinaryFile = 'project.wasm'; -if (!isDataURI(wasmBinaryFile)) { - wasmBinaryFile = locateFile(wasmBinaryFile); -} - -function getBinary() { - try { - if (wasmBinary) { - return new Uint8Array(wasmBinary); - } - - if (readBinary) { - return readBinary(wasmBinaryFile); - } else { - throw "both async and sync fetching of the wasm failed"; - } - } - catch (err) { - abort(err); - } -} - -function getBinaryPromise() { - // If we don't have the binary yet, and have the Fetch api, use that; - // in some environments, like Electron's render process, Fetch api may be present, but have a different context than expected, let's only use it on the Web - if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && typeof fetch === 'function' - // Let's not use fetch to get objects over file:// as it's most likely Cordova which doesn't support fetch for file:// - && !isFileURI(wasmBinaryFile) - ) { - return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { - if (!response['ok']) { - throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; - } - return response['arrayBuffer'](); - }).catch(function () { - return getBinary(); - }); - } - // Otherwise, getBinary should be able to get it synchronously - return Promise.resolve().then(getBinary); -} - - - -// Create the wasm instance. -// Receives the wasm imports, returns the exports. -function createWasm() { - // prepare imports - var info = { - 'env': asmLibraryArg, - 'wasi_snapshot_preview1': asmLibraryArg - }; - // Load the wasm module and create an instance of using native support in the JS engine. - // handle a generated wasm instance, receiving its exports and - // performing other necessary setup - /** @param {WebAssembly.Module=} module*/ - function receiveInstance(instance, module) { - var exports = instance.exports; - Module['asm'] = exports; - removeRunDependency('wasm-instantiate'); - } - // we can't run yet (except in a pthread, where we have a custom sync instantiator) - addRunDependency('wasm-instantiate'); - - - // Async compilation can be confusing when an error on the page overwrites Module - // (for example, if the order of elements is wrong, and the one defining Module is - // later), so we save Module and check it later. - var trueModule = Module; - function receiveInstantiatedSource(output) { - // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance. - // receiveInstance() will swap in the exports (to Module.asm) so they can be called - assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); - trueModule = null; - // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. - // When the regression is fixed, can restore the above USE_PTHREADS-enabled path. - receiveInstance(output['instance']); - } - - - function instantiateArrayBuffer(receiver) { - return getBinaryPromise().then(function(binary) { - return WebAssembly.instantiate(binary, info); - }).then(receiver, function(reason) { - err('failed to asynchronously prepare wasm: ' + reason); - - - abort(reason); - }); - } - - // Prefer streaming instantiation if available. - function instantiateAsync() { - if (!wasmBinary && - typeof WebAssembly.instantiateStreaming === 'function' && - !isDataURI(wasmBinaryFile) && - // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. - !isFileURI(wasmBinaryFile) && - typeof fetch === 'function') { - fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) { - var result = WebAssembly.instantiateStreaming(response, info); - return result.then(receiveInstantiatedSource, function(reason) { - // We expect the most common failure cause to be a bad MIME type for the binary, - // in which case falling back to ArrayBuffer instantiation should work. - err('wasm streaming compile failed: ' + reason); - err('falling back to ArrayBuffer instantiation'); - return instantiateArrayBuffer(receiveInstantiatedSource); - }); - }); - } else { - return instantiateArrayBuffer(receiveInstantiatedSource); - } - } - // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback - // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel - // to any other async startup actions they are performing. - if (Module['instantiateWasm']) { - try { - var exports = Module['instantiateWasm'](info, receiveInstance); - return exports; - } catch(e) { - err('Module.instantiateWasm callback failed with error: ' + e); - return false; - } - } - - instantiateAsync(); - return {}; // no exports yet; we'll fill them in later -} - -// Globals used by JS i64 conversions -var tempDouble; -var tempI64; - -// === Body === - -var ASM_CONSTS = { - -}; - - - - -// STATICTOP = STATIC_BASE + 101248; -/* global initializers */ __ATINIT__.push({ func: function() { ___wasm_call_ctors() } }); - - - - -/* no memory initializer */ -// {{PRE_LIBRARY}} - - - function abortStackOverflow(allocSize) { - abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (STACK_MAX - stackSave() + allocSize) + ' bytes available!'); - } - - function demangle(func) { - warnOnce('warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling'); - return func; - } - - function demangleAll(text) { - var regex = - /\b_Z[\w\d_]+/g; - return text.replace(regex, - function(x) { - var y = demangle(x); - return x === y ? x : (y + ' [' + x + ']'); - }); - } - - function jsStackTrace() { - var err = new Error(); - if (!err.stack) { - // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown, - // so try that as a special-case. - try { - throw new Error(); - } catch(e) { - err = e; - } - if (!err.stack) { - return '(no stack trace available)'; - } - } - return err.stack.toString(); - } - - function stackTrace() { - var js = jsStackTrace(); - if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); - return demangleAll(js); - } - - function ___assert_fail(condition, filename, line, func) { - abort('Assertion failed: ' + UTF8ToString(condition) + ', at: ' + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']); - } - - - var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16};function ___cxa_allocate_exception(size) { - // Thrown object is prepended by exception metadata block - return _malloc(size + ExceptionInfoAttrs.SIZE) + ExceptionInfoAttrs.SIZE; - } - - - function _atexit(func, arg) { - warnOnce('atexit() called, but EXIT_RUNTIME is not set, so atexits() will not be called. set EXIT_RUNTIME to 1 (see the FAQ)'); - - }function ___cxa_atexit(a0,a1 - ) { - return _atexit(a0,a1); - } - - - - function ExceptionInfo(excPtr) { - this.excPtr = excPtr; - this.ptr = excPtr - ExceptionInfoAttrs.SIZE; - - this.set_type = function(type) { - HEAP32[(((this.ptr)+(ExceptionInfoAttrs.TYPE_OFFSET))>>2)]=type; - }; - - this.get_type = function() { - return HEAP32[(((this.ptr)+(ExceptionInfoAttrs.TYPE_OFFSET))>>2)]; - }; - - this.set_destructor = function(destructor) { - HEAP32[(((this.ptr)+(ExceptionInfoAttrs.DESTRUCTOR_OFFSET))>>2)]=destructor; - }; - - this.get_destructor = function() { - return HEAP32[(((this.ptr)+(ExceptionInfoAttrs.DESTRUCTOR_OFFSET))>>2)]; - }; - - this.set_refcount = function(refcount) { - HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)]=refcount; - }; - - this.set_caught = function (caught) { - caught = caught ? 1 : 0; - HEAP8[(((this.ptr)+(ExceptionInfoAttrs.CAUGHT_OFFSET))>>0)]=caught; - }; - - this.get_caught = function () { - return HEAP8[(((this.ptr)+(ExceptionInfoAttrs.CAUGHT_OFFSET))>>0)] != 0; - }; - - this.set_rethrown = function (rethrown) { - rethrown = rethrown ? 1 : 0; - HEAP8[(((this.ptr)+(ExceptionInfoAttrs.RETHROWN_OFFSET))>>0)]=rethrown; - }; - - this.get_rethrown = function () { - return HEAP8[(((this.ptr)+(ExceptionInfoAttrs.RETHROWN_OFFSET))>>0)] != 0; - }; - - // Initialize native structure fields. Should be called once after allocated. - this.init = function(type, destructor) { - this.set_type(type); - this.set_destructor(destructor); - this.set_refcount(0); - this.set_caught(false); - this.set_rethrown(false); - } - - this.add_ref = function() { - var value = HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)]; - HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)]=value + 1; - }; - - // Returns true if last reference released. - this.release_ref = function() { - var prev = HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)]; - HEAP32[(((this.ptr)+(ExceptionInfoAttrs.REFCOUNT_OFFSET))>>2)]=prev - 1; - assert(prev > 0); - return prev === 1; - }; - }function CatchInfo(ptr) { - - this.free = function() { - _free(this.ptr); - this.ptr = 0; - }; - - this.set_base_ptr = function(basePtr) { - HEAP32[((this.ptr)>>2)]=basePtr; - }; - - this.get_base_ptr = function() { - return HEAP32[((this.ptr)>>2)]; - }; - - this.set_adjusted_ptr = function(adjustedPtr) { - var ptrSize = 4; - HEAP32[(((this.ptr)+(ptrSize))>>2)]=adjustedPtr; - }; - - this.get_adjusted_ptr = function() { - var ptrSize = 4; - return HEAP32[(((this.ptr)+(ptrSize))>>2)]; - }; - - // Get pointer which is expected to be received by catch clause in C++ code. It may be adjusted - // when the pointer is casted to some of the exception object base classes (e.g. when virtual - // inheritance is used). When a pointer is thrown this method should return the thrown pointer - // itself. - this.get_exception_ptr = function() { - // Work around a fastcomp bug, this code is still included for some reason in a build without - // exceptions support. - var isPointer = ___cxa_is_pointer_type( - this.get_exception_info().get_type()); - if (isPointer) { - return HEAP32[((this.get_base_ptr())>>2)]; - } - var adjusted = this.get_adjusted_ptr(); - if (adjusted !== 0) return adjusted; - return this.get_base_ptr(); - }; - - this.get_exception_info = function() { - return new ExceptionInfo(this.get_base_ptr()); - }; - - if (ptr === undefined) { - this.ptr = _malloc(8); - this.set_adjusted_ptr(0); - } else { - this.ptr = ptr; - } - } - - var exceptionCaught= []; - - function exception_addRef(info) { - info.add_ref(); - } - - function __ZSt18uncaught_exceptionv() { // std::uncaught_exception() - return __ZSt18uncaught_exceptionv.uncaught_exceptions > 0; - }function ___cxa_begin_catch(ptr) { - var catchInfo = new CatchInfo(ptr); - var info = catchInfo.get_exception_info(); - if (!info.get_caught()) { - info.set_caught(true); - __ZSt18uncaught_exceptionv.uncaught_exceptions--; - } - info.set_rethrown(false); - exceptionCaught.push(catchInfo); - exception_addRef(info); - return catchInfo.get_exception_ptr(); - } - - - - function ___cxa_free_exception(ptr) { - try { - return _free(new ExceptionInfo(ptr).ptr); - } catch(e) { - err('exception during cxa_free_exception: ' + e); - } - }function exception_decRef(info) { - // A rethrown exception can reach refcount 0; it must not be discarded - // Its next handler will clear the rethrown flag and addRef it, prior to - // final decRef and destruction here - if (info.release_ref() && !info.get_rethrown()) { - var destructor = info.get_destructor(); - if (destructor) { - // In Wasm, destructors return 'this' as in ARM - Module['dynCall_ii'](destructor, info.excPtr); - } - ___cxa_free_exception(info.excPtr); - } - }function ___cxa_decrement_exception_refcount(ptr) { - if (!ptr) return; - exception_decRef(new ExceptionInfo(ptr)); - } - - - var exceptionLast=0;function ___cxa_end_catch() { - // Clear state flag. - _setThrew(0); - assert(exceptionCaught.length > 0); - // Call destructor if one is registered then clear it. - var catchInfo = exceptionCaught.pop(); - - exception_decRef(catchInfo.get_exception_info()); - catchInfo.free(); - exceptionLast = 0; // XXX in decRef? - } - - - function ___resumeException(catchInfoPtr) { - var catchInfo = new CatchInfo(catchInfoPtr); - var ptr = catchInfo.get_base_ptr(); - if (!exceptionLast) { exceptionLast = ptr; } - catchInfo.free(); - throw ptr; - }function ___cxa_find_matching_catch_2() { - var thrown = exceptionLast; - if (!thrown) { - // just pass through the null ptr - return ((setTempRet0(0),0)|0); - } - var info = new ExceptionInfo(thrown); - var thrownType = info.get_type(); - var catchInfo = new CatchInfo(); - catchInfo.set_base_ptr(thrown); - if (!thrownType) { - // just pass through the thrown ptr - return ((setTempRet0(0),catchInfo.ptr)|0); - } - var typeArray = Array.prototype.slice.call(arguments); - - // can_catch receives a **, add indirection - var thrownBuf = 102240; - HEAP32[((thrownBuf)>>2)]=thrown; - // The different catch blocks are denoted by different types. - // Due to inheritance, those types may not precisely match the - // type of the thrown object. Find one which matches, and - // return the type of the catch block which should be called. - for (var i = 0; i < typeArray.length; i++) { - var caughtType = typeArray[i]; - if (caughtType === 0 || caughtType === thrownType) { - // Catch all clause matched or exactly the same type is caught - break; - } - if (___cxa_can_catch(caughtType, thrownType, thrownBuf)) { - var adjusted = HEAP32[((thrownBuf)>>2)]; - if (thrown !== adjusted) { - catchInfo.set_adjusted_ptr(adjusted); - } - return ((setTempRet0(caughtType),catchInfo.ptr)|0); - } - } - return ((setTempRet0(thrownType),catchInfo.ptr)|0); - } - - function ___cxa_find_matching_catch_3() { - var thrown = exceptionLast; - if (!thrown) { - // just pass through the null ptr - return ((setTempRet0(0),0)|0); - } - var info = new ExceptionInfo(thrown); - var thrownType = info.get_type(); - var catchInfo = new CatchInfo(); - catchInfo.set_base_ptr(thrown); - if (!thrownType) { - // just pass through the thrown ptr - return ((setTempRet0(0),catchInfo.ptr)|0); - } - var typeArray = Array.prototype.slice.call(arguments); - - // can_catch receives a **, add indirection - var thrownBuf = 102240; - HEAP32[((thrownBuf)>>2)]=thrown; - // The different catch blocks are denoted by different types. - // Due to inheritance, those types may not precisely match the - // type of the thrown object. Find one which matches, and - // return the type of the catch block which should be called. - for (var i = 0; i < typeArray.length; i++) { - var caughtType = typeArray[i]; - if (caughtType === 0 || caughtType === thrownType) { - // Catch all clause matched or exactly the same type is caught - break; - } - if (___cxa_can_catch(caughtType, thrownType, thrownBuf)) { - var adjusted = HEAP32[((thrownBuf)>>2)]; - if (thrown !== adjusted) { - catchInfo.set_adjusted_ptr(adjusted); - } - return ((setTempRet0(caughtType),catchInfo.ptr)|0); - } - } - return ((setTempRet0(thrownType),catchInfo.ptr)|0); - } - - - function ___cxa_increment_exception_refcount(ptr) { - if (!ptr) return; - exception_addRef(new ExceptionInfo(ptr)); - } - - function ___cxa_rethrow() { - var catchInfo = exceptionCaught.pop(); - var info = catchInfo.get_exception_info(); - var ptr = catchInfo.get_base_ptr(); - if (!info.get_rethrown()) { - // Only pop if the corresponding push was through rethrow_primary_exception - exceptionCaught.push(catchInfo); - info.set_rethrown(true); - } else { - catchInfo.free(); - } - exceptionLast = ptr; - throw ptr; - } - - function ___cxa_throw(ptr, type, destructor) { - var info = new ExceptionInfo(ptr); - // Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception. - info.init(type, destructor); - exceptionLast = ptr; - if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { - __ZSt18uncaught_exceptionv.uncaught_exceptions = 1; - } else { - __ZSt18uncaught_exceptionv.uncaught_exceptions++; - } - throw ptr; - } - - function ___cxa_uncaught_exceptions() { - return __ZSt18uncaught_exceptionv.uncaught_exceptions; - } - - - function setErrNo(value) { - HEAP32[((___errno_location())>>2)]=value; - return value; - }function ___map_file(pathname, size) { - setErrNo(63); - return -1; - } - - - - - var PATH={splitPath:function(filename) { - var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; - return splitPathRe.exec(filename).slice(1); - },normalizeArray:function(parts, allowAboveRoot) { - // if the path tries to go above the root, `up` ends up > 0 - var up = 0; - for (var i = parts.length - 1; i >= 0; i--) { - var last = parts[i]; - if (last === '.') { - parts.splice(i, 1); - } else if (last === '..') { - parts.splice(i, 1); - up++; - } else if (up) { - parts.splice(i, 1); - up--; - } - } - // if the path is allowed to go above the root, restore leading ..s - if (allowAboveRoot) { - for (; up; up--) { - parts.unshift('..'); - } - } - return parts; - },normalize:function(path) { - var isAbsolute = path.charAt(0) === '/', - trailingSlash = path.substr(-1) === '/'; - // Normalize the path - path = PATH.normalizeArray(path.split('/').filter(function(p) { - return !!p; - }), !isAbsolute).join('/'); - if (!path && !isAbsolute) { - path = '.'; - } - if (path && trailingSlash) { - path += '/'; - } - return (isAbsolute ? '/' : '') + path; - },dirname:function(path) { - var result = PATH.splitPath(path), - root = result[0], - dir = result[1]; - if (!root && !dir) { - // No dirname whatsoever - return '.'; - } - if (dir) { - // It has a dirname, strip trailing slash - dir = dir.substr(0, dir.length - 1); - } - return root + dir; - },basename:function(path) { - // EMSCRIPTEN return '/'' for '/', not an empty string - if (path === '/') return '/'; - path = PATH.normalize(path); - path = path.replace(/\/$/, ""); - var lastSlash = path.lastIndexOf('/'); - if (lastSlash === -1) return path; - return path.substr(lastSlash+1); - },extname:function(path) { - return PATH.splitPath(path)[3]; - },join:function() { - var paths = Array.prototype.slice.call(arguments, 0); - return PATH.normalize(paths.join('/')); - },join2:function(l, r) { - return PATH.normalize(l + '/' + r); - }}; - - - var PATH_FS={resolve:function() { - var resolvedPath = '', - resolvedAbsolute = false; - for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { - var path = (i >= 0) ? arguments[i] : FS.cwd(); - // Skip empty and invalid entries - if (typeof path !== 'string') { - throw new TypeError('Arguments to path.resolve must be strings'); - } else if (!path) { - return ''; // an invalid portion invalidates the whole thing - } - resolvedPath = path + '/' + resolvedPath; - resolvedAbsolute = path.charAt(0) === '/'; - } - // At this point the path should be resolved to a full absolute path, but - // handle relative paths to be safe (might happen when process.cwd() fails) - resolvedPath = PATH.normalizeArray(resolvedPath.split('/').filter(function(p) { - return !!p; - }), !resolvedAbsolute).join('/'); - return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; - },relative:function(from, to) { - from = PATH_FS.resolve(from).substr(1); - to = PATH_FS.resolve(to).substr(1); - function trim(arr) { - var start = 0; - for (; start < arr.length; start++) { - if (arr[start] !== '') break; - } - var end = arr.length - 1; - for (; end >= 0; end--) { - if (arr[end] !== '') break; - } - if (start > end) return []; - return arr.slice(start, end - start + 1); - } - var fromParts = trim(from.split('/')); - var toParts = trim(to.split('/')); - var length = Math.min(fromParts.length, toParts.length); - var samePartsLength = length; - for (var i = 0; i < length; i++) { - if (fromParts[i] !== toParts[i]) { - samePartsLength = i; - break; - } - } - var outputParts = []; - for (var i = samePartsLength; i < fromParts.length; i++) { - outputParts.push('..'); - } - outputParts = outputParts.concat(toParts.slice(samePartsLength)); - return outputParts.join('/'); - }}; - - var TTY={ttys:[],init:function () { - // https://github.com/emscripten-core/emscripten/pull/1555 - // if (ENVIRONMENT_IS_NODE) { - // // currently, FS.init does not distinguish if process.stdin is a file or TTY - // // device, it always assumes it's a TTY device. because of this, we're forcing - // // process.stdin to UTF8 encoding to at least make stdin reading compatible - // // with text files until FS.init can be refactored. - // process['stdin']['setEncoding']('utf8'); - // } - },shutdown:function() { - // https://github.com/emscripten-core/emscripten/pull/1555 - // if (ENVIRONMENT_IS_NODE) { - // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)? - // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation - // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists? - // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle - // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call - // process['stdin']['pause'](); - // } - },register:function(dev, ops) { - TTY.ttys[dev] = { input: [], output: [], ops: ops }; - FS.registerDevice(dev, TTY.stream_ops); - },stream_ops:{open:function(stream) { - var tty = TTY.ttys[stream.node.rdev]; - if (!tty) { - throw new FS.ErrnoError(43); - } - stream.tty = tty; - stream.seekable = false; - },close:function(stream) { - // flush any pending line data - stream.tty.ops.flush(stream.tty); - },flush:function(stream) { - stream.tty.ops.flush(stream.tty); - },read:function(stream, buffer, offset, length, pos /* ignored */) { - if (!stream.tty || !stream.tty.ops.get_char) { - throw new FS.ErrnoError(60); - } - var bytesRead = 0; - for (var i = 0; i < length; i++) { - var result; - try { - result = stream.tty.ops.get_char(stream.tty); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (result === undefined && bytesRead === 0) { - throw new FS.ErrnoError(6); - } - if (result === null || result === undefined) break; - bytesRead++; - buffer[offset+i] = result; - } - if (bytesRead) { - stream.node.timestamp = Date.now(); - } - return bytesRead; - },write:function(stream, buffer, offset, length, pos) { - if (!stream.tty || !stream.tty.ops.put_char) { - throw new FS.ErrnoError(60); - } - try { - for (var i = 0; i < length; i++) { - stream.tty.ops.put_char(stream.tty, buffer[offset+i]); - } - } catch (e) { - throw new FS.ErrnoError(29); - } - if (length) { - stream.node.timestamp = Date.now(); - } - return i; - }},default_tty_ops:{get_char:function(tty) { - if (!tty.input.length) { - var result = null; - if (ENVIRONMENT_IS_NODE) { - // we will read data by chunks of BUFSIZE - var BUFSIZE = 256; - var buf = Buffer.alloc ? Buffer.alloc(BUFSIZE) : new Buffer(BUFSIZE); - var bytesRead = 0; - - try { - bytesRead = nodeFS.readSync(process.stdin.fd, buf, 0, BUFSIZE, null); - } catch(e) { - // Cross-platform differences: on Windows, reading EOF throws an exception, but on other OSes, - // reading EOF returns 0. Uniformize behavior by treating the EOF exception to return 0. - if (e.toString().indexOf('EOF') != -1) bytesRead = 0; - else throw e; - } - - if (bytesRead > 0) { - result = buf.slice(0, bytesRead).toString('utf-8'); - } else { - result = null; - } - } else - if (typeof window != 'undefined' && - typeof window.prompt == 'function') { - // Browser. - result = window.prompt('Input: '); // returns null on cancel - if (result !== null) { - result += '\n'; - } - } else if (typeof readline == 'function') { - // Command line. - result = readline(); - if (result !== null) { - result += '\n'; - } - } - if (!result) { - return null; - } - tty.input = intArrayFromString(result, true); - } - return tty.input.shift(); - },put_char:function(tty, val) { - if (val === null || val === 10) { - out(UTF8ArrayToString(tty.output, 0)); - tty.output = []; - } else { - if (val != 0) tty.output.push(val); // val == 0 would cut text output off in the middle. - } - },flush:function(tty) { - if (tty.output && tty.output.length > 0) { - out(UTF8ArrayToString(tty.output, 0)); - tty.output = []; - } - }},default_tty1_ops:{put_char:function(tty, val) { - if (val === null || val === 10) { - err(UTF8ArrayToString(tty.output, 0)); - tty.output = []; - } else { - if (val != 0) tty.output.push(val); - } - },flush:function(tty) { - if (tty.output && tty.output.length > 0) { - err(UTF8ArrayToString(tty.output, 0)); - tty.output = []; - } - }}}; - - var MEMFS={ops_table:null,mount:function(mount) { - return MEMFS.createNode(null, '/', 16384 | 511 /* 0777 */, 0); - },createNode:function(parent, name, mode, dev) { - if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { - // no supported - throw new FS.ErrnoError(63); - } - if (!MEMFS.ops_table) { - MEMFS.ops_table = { - dir: { - node: { - getattr: MEMFS.node_ops.getattr, - setattr: MEMFS.node_ops.setattr, - lookup: MEMFS.node_ops.lookup, - mknod: MEMFS.node_ops.mknod, - rename: MEMFS.node_ops.rename, - unlink: MEMFS.node_ops.unlink, - rmdir: MEMFS.node_ops.rmdir, - readdir: MEMFS.node_ops.readdir, - symlink: MEMFS.node_ops.symlink - }, - stream: { - llseek: MEMFS.stream_ops.llseek - } - }, - file: { - node: { - getattr: MEMFS.node_ops.getattr, - setattr: MEMFS.node_ops.setattr - }, - stream: { - llseek: MEMFS.stream_ops.llseek, - read: MEMFS.stream_ops.read, - write: MEMFS.stream_ops.write, - allocate: MEMFS.stream_ops.allocate, - mmap: MEMFS.stream_ops.mmap, - msync: MEMFS.stream_ops.msync - } - }, - link: { - node: { - getattr: MEMFS.node_ops.getattr, - setattr: MEMFS.node_ops.setattr, - readlink: MEMFS.node_ops.readlink - }, - stream: {} - }, - chrdev: { - node: { - getattr: MEMFS.node_ops.getattr, - setattr: MEMFS.node_ops.setattr - }, - stream: FS.chrdev_stream_ops - } - }; - } - var node = FS.createNode(parent, name, mode, dev); - if (FS.isDir(node.mode)) { - node.node_ops = MEMFS.ops_table.dir.node; - node.stream_ops = MEMFS.ops_table.dir.stream; - node.contents = {}; - } else if (FS.isFile(node.mode)) { - node.node_ops = MEMFS.ops_table.file.node; - node.stream_ops = MEMFS.ops_table.file.stream; - node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. - // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred - // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size - // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. - node.contents = null; - } else if (FS.isLink(node.mode)) { - node.node_ops = MEMFS.ops_table.link.node; - node.stream_ops = MEMFS.ops_table.link.stream; - } else if (FS.isChrdev(node.mode)) { - node.node_ops = MEMFS.ops_table.chrdev.node; - node.stream_ops = MEMFS.ops_table.chrdev.stream; - } - node.timestamp = Date.now(); - // add the new node to the parent - if (parent) { - parent.contents[name] = node; - } - return node; - },getFileDataAsRegularArray:function(node) { - if (node.contents && node.contents.subarray) { - var arr = []; - for (var i = 0; i < node.usedBytes; ++i) arr.push(node.contents[i]); - return arr; // Returns a copy of the original data. - } - return node.contents; // No-op, the file contents are already in a JS array. Return as-is. - },getFileDataAsTypedArray:function(node) { - if (!node.contents) return new Uint8Array(0); - if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. - return new Uint8Array(node.contents); - },expandFileStorage:function(node, newCapacity) { - var prevCapacity = node.contents ? node.contents.length : 0; - if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough. - // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. - // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to - // avoid overshooting the allocation cap by a very large margin. - var CAPACITY_DOUBLING_MAX = 1024 * 1024; - newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); - if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. - var oldContents = node.contents; - node.contents = new Uint8Array(newCapacity); // Allocate new storage. - if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage. - return; - },resizeFileStorage:function(node, newSize) { - if (node.usedBytes == newSize) return; - if (newSize == 0) { - node.contents = null; // Fully decommit when requesting a resize to zero. - node.usedBytes = 0; - return; - } - if (!node.contents || node.contents.subarray) { // Resize a typed array if that is being used as the backing store. - var oldContents = node.contents; - node.contents = new Uint8Array(newSize); // Allocate new storage. - if (oldContents) { - node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. - } - node.usedBytes = newSize; - return; - } - // Backing with a JS array. - if (!node.contents) node.contents = []; - if (node.contents.length > newSize) node.contents.length = newSize; - else while (node.contents.length < newSize) node.contents.push(0); - node.usedBytes = newSize; - },node_ops:{getattr:function(node) { - var attr = {}; - // device numbers reuse inode numbers. - attr.dev = FS.isChrdev(node.mode) ? node.id : 1; - attr.ino = node.id; - attr.mode = node.mode; - attr.nlink = 1; - attr.uid = 0; - attr.gid = 0; - attr.rdev = node.rdev; - if (FS.isDir(node.mode)) { - attr.size = 4096; - } else if (FS.isFile(node.mode)) { - attr.size = node.usedBytes; - } else if (FS.isLink(node.mode)) { - attr.size = node.link.length; - } else { - attr.size = 0; - } - attr.atime = new Date(node.timestamp); - attr.mtime = new Date(node.timestamp); - attr.ctime = new Date(node.timestamp); - // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), - // but this is not required by the standard. - attr.blksize = 4096; - attr.blocks = Math.ceil(attr.size / attr.blksize); - return attr; - },setattr:function(node, attr) { - if (attr.mode !== undefined) { - node.mode = attr.mode; - } - if (attr.timestamp !== undefined) { - node.timestamp = attr.timestamp; - } - if (attr.size !== undefined) { - MEMFS.resizeFileStorage(node, attr.size); - } - },lookup:function(parent, name) { - throw FS.genericErrors[44]; - },mknod:function(parent, name, mode, dev) { - return MEMFS.createNode(parent, name, mode, dev); - },rename:function(old_node, new_dir, new_name) { - // if we're overwriting a directory at new_name, make sure it's empty. - if (FS.isDir(old_node.mode)) { - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) { - } - if (new_node) { - for (var i in new_node.contents) { - throw new FS.ErrnoError(55); - } - } - } - // do the internal rewiring - delete old_node.parent.contents[old_node.name]; - old_node.name = new_name; - new_dir.contents[new_name] = old_node; - old_node.parent = new_dir; - },unlink:function(parent, name) { - delete parent.contents[name]; - },rmdir:function(parent, name) { - var node = FS.lookupNode(parent, name); - for (var i in node.contents) { - throw new FS.ErrnoError(55); - } - delete parent.contents[name]; - },readdir:function(node) { - var entries = ['.', '..']; - for (var key in node.contents) { - if (!node.contents.hasOwnProperty(key)) { - continue; - } - entries.push(key); - } - return entries; - },symlink:function(parent, newname, oldpath) { - var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | 40960, 0); - node.link = oldpath; - return node; - },readlink:function(node) { - if (!FS.isLink(node.mode)) { - throw new FS.ErrnoError(28); - } - return node.link; - }},stream_ops:{read:function(stream, buffer, offset, length, position) { - var contents = stream.node.contents; - if (position >= stream.node.usedBytes) return 0; - var size = Math.min(stream.node.usedBytes - position, length); - assert(size >= 0); - if (size > 8 && contents.subarray) { // non-trivial, and typed array - buffer.set(contents.subarray(position, position + size), offset); - } else { - for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; - } - return size; - },write:function(stream, buffer, offset, length, position, canOwn) { - // The data buffer should be a typed array view - assert(!(buffer instanceof ArrayBuffer)); - // If the buffer is located in main memory (HEAP), and if - // memory can grow, we can't hold on to references of the - // memory buffer, as they may get invalidated. That means we - // need to do copy its contents. - if (buffer.buffer === HEAP8.buffer) { - // FIXME: this is inefficient as the file packager may have - // copied the data into memory already - we may want to - // integrate more there and let the file packager loading - // code be able to query if memory growth is on or off. - if (canOwn) { - warnOnce('file packager has copied file data into memory, but in memory growth we are forced to copy it again (see --no-heap-copy)'); - } - canOwn = false; - } - - if (!length) return 0; - var node = stream.node; - node.timestamp = Date.now(); - - if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array? - if (canOwn) { - assert(position === 0, 'canOwn must imply no weird position inside the file'); - node.contents = buffer.subarray(offset, offset + length); - node.usedBytes = length; - return length; - } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. - node.contents = buffer.slice(offset, offset + length); - node.usedBytes = length; - return length; - } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file? - node.contents.set(buffer.subarray(offset, offset + length), position); - return length; - } - } - - // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. - MEMFS.expandFileStorage(node, position+length); - if (node.contents.subarray && buffer.subarray) { - // Use typed array write which is available. - node.contents.set(buffer.subarray(offset, offset + length), position); - } else { - for (var i = 0; i < length; i++) { - node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not. - } - } - node.usedBytes = Math.max(node.usedBytes, position + length); - return length; - },llseek:function(stream, offset, whence) { - var position = offset; - if (whence === 1) { - position += stream.position; - } else if (whence === 2) { - if (FS.isFile(stream.node.mode)) { - position += stream.node.usedBytes; - } - } - if (position < 0) { - throw new FS.ErrnoError(28); - } - return position; - },allocate:function(stream, offset, length) { - MEMFS.expandFileStorage(stream.node, offset + length); - stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length); - },mmap:function(stream, address, length, position, prot, flags) { - // We don't currently support location hints for the address of the mapping - assert(address === 0); - - if (!FS.isFile(stream.node.mode)) { - throw new FS.ErrnoError(43); - } - var ptr; - var allocated; - var contents = stream.node.contents; - // Only make a new copy when MAP_PRIVATE is specified. - if (!(flags & 2) && contents.buffer === buffer) { - // We can't emulate MAP_SHARED when the file is not backed by the buffer - // we're mapping to (e.g. the HEAP buffer). - allocated = false; - ptr = contents.byteOffset; - } else { - // Try to avoid unnecessary slices. - if (position > 0 || position + length < contents.length) { - if (contents.subarray) { - contents = contents.subarray(position, position + length); - } else { - contents = Array.prototype.slice.call(contents, position, position + length); - } - } - allocated = true; - ptr = FS.mmapAlloc(length); - if (!ptr) { - throw new FS.ErrnoError(48); - } - HEAP8.set(contents, ptr); - } - return { ptr: ptr, allocated: allocated }; - },msync:function(stream, buffer, offset, length, mmapFlags) { - if (!FS.isFile(stream.node.mode)) { - throw new FS.ErrnoError(43); - } - if (mmapFlags & 2) { - // MAP_PRIVATE calls need not to be synced back to underlying fs - return 0; - } - - var bytesWritten = MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); - // should we check if bytesWritten and length are the same? - return 0; - }}}; - - var ERRNO_MESSAGES={0:"Success",1:"Arg list too long",2:"Permission denied",3:"Address already in use",4:"Address not available",5:"Address family not supported by protocol family",6:"No more processes",7:"Socket already connected",8:"Bad file number",9:"Trying to read unreadable message",10:"Mount device busy",11:"Operation canceled",12:"No children",13:"Connection aborted",14:"Connection refused",15:"Connection reset by peer",16:"File locking deadlock error",17:"Destination address required",18:"Math arg out of domain of func",19:"Quota exceeded",20:"File exists",21:"Bad address",22:"File too large",23:"Host is unreachable",24:"Identifier removed",25:"Illegal byte sequence",26:"Connection already in progress",27:"Interrupted system call",28:"Invalid argument",29:"I/O error",30:"Socket is already connected",31:"Is a directory",32:"Too many symbolic links",33:"Too many open files",34:"Too many links",35:"Message too long",36:"Multihop attempted",37:"File or path name too long",38:"Network interface is not configured",39:"Connection reset by network",40:"Network is unreachable",41:"Too many open files in system",42:"No buffer space available",43:"No such device",44:"No such file or directory",45:"Exec format error",46:"No record locks available",47:"The link has been severed",48:"Not enough core",49:"No message of desired type",50:"Protocol not available",51:"No space left on device",52:"Function not implemented",53:"Socket is not connected",54:"Not a directory",55:"Directory not empty",56:"State not recoverable",57:"Socket operation on non-socket",59:"Not a typewriter",60:"No such device or address",61:"Value too large for defined data type",62:"Previous owner died",63:"Not super-user",64:"Broken pipe",65:"Protocol error",66:"Unknown protocol",67:"Protocol wrong type for socket",68:"Math result not representable",69:"Read only file system",70:"Illegal seek",71:"No such process",72:"Stale file handle",73:"Connection timed out",74:"Text file busy",75:"Cross-device link",100:"Device not a stream",101:"Bad font file fmt",102:"Invalid slot",103:"Invalid request code",104:"No anode",105:"Block device required",106:"Channel number out of range",107:"Level 3 halted",108:"Level 3 reset",109:"Link number out of range",110:"Protocol driver not attached",111:"No CSI structure available",112:"Level 2 halted",113:"Invalid exchange",114:"Invalid request descriptor",115:"Exchange full",116:"No data (for no delay io)",117:"Timer expired",118:"Out of streams resources",119:"Machine is not on the network",120:"Package not installed",121:"The object is remote",122:"Advertise error",123:"Srmount error",124:"Communication error on send",125:"Cross mount point (not really error)",126:"Given log. name not unique",127:"f.d. invalid for this operation",128:"Remote address changed",129:"Can access a needed shared lib",130:"Accessing a corrupted shared lib",131:".lib section in a.out corrupted",132:"Attempting to link in too many libs",133:"Attempting to exec a shared library",135:"Streams pipe error",136:"Too many users",137:"Socket type not supported",138:"Not supported",139:"Protocol family not supported",140:"Can't send after socket shutdown",141:"Too many references",142:"Host is down",148:"No medium (in tape drive)",156:"Level 2 not synchronized"}; - - var ERRNO_CODES={EPERM:63,ENOENT:44,ESRCH:71,EINTR:27,EIO:29,ENXIO:60,E2BIG:1,ENOEXEC:45,EBADF:8,ECHILD:12,EAGAIN:6,EWOULDBLOCK:6,ENOMEM:48,EACCES:2,EFAULT:21,ENOTBLK:105,EBUSY:10,EEXIST:20,EXDEV:75,ENODEV:43,ENOTDIR:54,EISDIR:31,EINVAL:28,ENFILE:41,EMFILE:33,ENOTTY:59,ETXTBSY:74,EFBIG:22,ENOSPC:51,ESPIPE:70,EROFS:69,EMLINK:34,EPIPE:64,EDOM:18,ERANGE:68,ENOMSG:49,EIDRM:24,ECHRNG:106,EL2NSYNC:156,EL3HLT:107,EL3RST:108,ELNRNG:109,EUNATCH:110,ENOCSI:111,EL2HLT:112,EDEADLK:16,ENOLCK:46,EBADE:113,EBADR:114,EXFULL:115,ENOANO:104,EBADRQC:103,EBADSLT:102,EDEADLOCK:16,EBFONT:101,ENOSTR:100,ENODATA:116,ETIME:117,ENOSR:118,ENONET:119,ENOPKG:120,EREMOTE:121,ENOLINK:47,EADV:122,ESRMNT:123,ECOMM:124,EPROTO:65,EMULTIHOP:36,EDOTDOT:125,EBADMSG:9,ENOTUNIQ:126,EBADFD:127,EREMCHG:128,ELIBACC:129,ELIBBAD:130,ELIBSCN:131,ELIBMAX:132,ELIBEXEC:133,ENOSYS:52,ENOTEMPTY:55,ENAMETOOLONG:37,ELOOP:32,EOPNOTSUPP:138,EPFNOSUPPORT:139,ECONNRESET:15,ENOBUFS:42,EAFNOSUPPORT:5,EPROTOTYPE:67,ENOTSOCK:57,ENOPROTOOPT:50,ESHUTDOWN:140,ECONNREFUSED:14,EADDRINUSE:3,ECONNABORTED:13,ENETUNREACH:40,ENETDOWN:38,ETIMEDOUT:73,EHOSTDOWN:142,EHOSTUNREACH:23,EINPROGRESS:26,EALREADY:7,EDESTADDRREQ:17,EMSGSIZE:35,EPROTONOSUPPORT:66,ESOCKTNOSUPPORT:137,EADDRNOTAVAIL:4,ENETRESET:39,EISCONN:30,ENOTCONN:53,ETOOMANYREFS:141,EUSERS:136,EDQUOT:19,ESTALE:72,ENOTSUP:138,ENOMEDIUM:148,EILSEQ:25,EOVERFLOW:61,ECANCELED:11,ENOTRECOVERABLE:56,EOWNERDEAD:62,ESTRPIPE:135};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,trackingDelegate:{},tracking:{openFlags:{READ:1,WRITE:2}},ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,handleFSError:function(e) { - if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + stackTrace(); - return setErrNo(e.errno); - },lookupPath:function(path, opts) { - path = PATH_FS.resolve(FS.cwd(), path); - opts = opts || {}; - - if (!path) return { path: '', node: null }; - - var defaults = { - follow_mount: true, - recurse_count: 0 - }; - for (var key in defaults) { - if (opts[key] === undefined) { - opts[key] = defaults[key]; - } - } - - if (opts.recurse_count > 8) { // max recursive lookup of 8 - throw new FS.ErrnoError(32); - } - - // split the path - var parts = PATH.normalizeArray(path.split('/').filter(function(p) { - return !!p; - }), false); - - // start at the root - var current = FS.root; - var current_path = '/'; - - for (var i = 0; i < parts.length; i++) { - var islast = (i === parts.length-1); - if (islast && opts.parent) { - // stop resolving - break; - } - - current = FS.lookupNode(current, parts[i]); - current_path = PATH.join2(current_path, parts[i]); - - // jump to the mount's root node if this is a mountpoint - if (FS.isMountpoint(current)) { - if (!islast || (islast && opts.follow_mount)) { - current = current.mounted.root; - } - } - - // by default, lookupPath will not follow a symlink if it is the final path component. - // setting opts.follow = true will override this behavior. - if (!islast || opts.follow) { - var count = 0; - while (FS.isLink(current.mode)) { - var link = FS.readlink(current_path); - current_path = PATH_FS.resolve(PATH.dirname(current_path), link); - - var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); - current = lookup.node; - - if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). - throw new FS.ErrnoError(32); - } - } - } - } - - return { path: current_path, node: current }; - },getPath:function(node) { - var path; - while (true) { - if (FS.isRoot(node)) { - var mount = node.mount.mountpoint; - if (!path) return mount; - return mount[mount.length-1] !== '/' ? mount + '/' + path : mount + path; - } - path = path ? node.name + '/' + path : node.name; - node = node.parent; - } - },hashName:function(parentid, name) { - var hash = 0; - - - for (var i = 0; i < name.length; i++) { - hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; - } - return ((parentid + hash) >>> 0) % FS.nameTable.length; - },hashAddNode:function(node) { - var hash = FS.hashName(node.parent.id, node.name); - node.name_next = FS.nameTable[hash]; - FS.nameTable[hash] = node; - },hashRemoveNode:function(node) { - var hash = FS.hashName(node.parent.id, node.name); - if (FS.nameTable[hash] === node) { - FS.nameTable[hash] = node.name_next; - } else { - var current = FS.nameTable[hash]; - while (current) { - if (current.name_next === node) { - current.name_next = node.name_next; - break; - } - current = current.name_next; - } - } - },lookupNode:function(parent, name) { - var errCode = FS.mayLookup(parent); - if (errCode) { - throw new FS.ErrnoError(errCode, parent); - } - var hash = FS.hashName(parent.id, name); - for (var node = FS.nameTable[hash]; node; node = node.name_next) { - var nodeName = node.name; - if (node.parent.id === parent.id && nodeName === name) { - return node; - } - } - // if we failed to find it in the cache, call into the VFS - return FS.lookup(parent, name); - },createNode:function(parent, name, mode, rdev) { - var node = new FS.FSNode(parent, name, mode, rdev); - - FS.hashAddNode(node); - - return node; - },destroyNode:function(node) { - FS.hashRemoveNode(node); - },isRoot:function(node) { - return node === node.parent; - },isMountpoint:function(node) { - return !!node.mounted; - },isFile:function(mode) { - return (mode & 61440) === 32768; - },isDir:function(mode) { - return (mode & 61440) === 16384; - },isLink:function(mode) { - return (mode & 61440) === 40960; - },isChrdev:function(mode) { - return (mode & 61440) === 8192; - },isBlkdev:function(mode) { - return (mode & 61440) === 24576; - },isFIFO:function(mode) { - return (mode & 61440) === 4096; - },isSocket:function(mode) { - return (mode & 49152) === 49152; - },flagModes:{"r":0,"rs":1052672,"r+":2,"w":577,"wx":705,"xw":705,"w+":578,"wx+":706,"xw+":706,"a":1089,"ax":1217,"xa":1217,"a+":1090,"ax+":1218,"xa+":1218},modeStringToFlags:function(str) { - var flags = FS.flagModes[str]; - if (typeof flags === 'undefined') { - throw new Error('Unknown file open mode: ' + str); - } - return flags; - },flagsToPermissionString:function(flag) { - var perms = ['r', 'w', 'rw'][flag & 3]; - if ((flag & 512)) { - perms += 'w'; - } - return perms; - },nodePermissions:function(node, perms) { - if (FS.ignorePermissions) { - return 0; - } - // return 0 if any user, group or owner bits are set. - if (perms.indexOf('r') !== -1 && !(node.mode & 292)) { - return 2; - } else if (perms.indexOf('w') !== -1 && !(node.mode & 146)) { - return 2; - } else if (perms.indexOf('x') !== -1 && !(node.mode & 73)) { - return 2; - } - return 0; - },mayLookup:function(dir) { - var errCode = FS.nodePermissions(dir, 'x'); - if (errCode) return errCode; - if (!dir.node_ops.lookup) return 2; - return 0; - },mayCreate:function(dir, name) { - try { - var node = FS.lookupNode(dir, name); - return 20; - } catch (e) { - } - return FS.nodePermissions(dir, 'wx'); - },mayDelete:function(dir, name, isdir) { - var node; - try { - node = FS.lookupNode(dir, name); - } catch (e) { - return e.errno; - } - var errCode = FS.nodePermissions(dir, 'wx'); - if (errCode) { - return errCode; - } - if (isdir) { - if (!FS.isDir(node.mode)) { - return 54; - } - if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { - return 10; - } - } else { - if (FS.isDir(node.mode)) { - return 31; - } - } - return 0; - },mayOpen:function(node, flags) { - if (!node) { - return 44; - } - if (FS.isLink(node.mode)) { - return 32; - } else if (FS.isDir(node.mode)) { - if (FS.flagsToPermissionString(flags) !== 'r' || // opening for write - (flags & 512)) { // TODO: check for O_SEARCH? (== search for dir only) - return 31; - } - } - return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); - },MAX_OPEN_FDS:4096,nextfd:function(fd_start, fd_end) { - fd_start = fd_start || 0; - fd_end = fd_end || FS.MAX_OPEN_FDS; - for (var fd = fd_start; fd <= fd_end; fd++) { - if (!FS.streams[fd]) { - return fd; - } - } - throw new FS.ErrnoError(33); - },getStream:function(fd) { - return FS.streams[fd]; - },createStream:function(stream, fd_start, fd_end) { - if (!FS.FSStream) { - FS.FSStream = /** @constructor */ function(){}; - FS.FSStream.prototype = { - object: { - get: function() { return this.node; }, - set: function(val) { this.node = val; } - }, - isRead: { - get: function() { return (this.flags & 2097155) !== 1; } - }, - isWrite: { - get: function() { return (this.flags & 2097155) !== 0; } - }, - isAppend: { - get: function() { return (this.flags & 1024); } - } - }; - } - // clone it, so we can return an instance of FSStream - var newStream = new FS.FSStream(); - for (var p in stream) { - newStream[p] = stream[p]; - } - stream = newStream; - var fd = FS.nextfd(fd_start, fd_end); - stream.fd = fd; - FS.streams[fd] = stream; - return stream; - },closeStream:function(fd) { - FS.streams[fd] = null; - },chrdev_stream_ops:{open:function(stream) { - var device = FS.getDevice(stream.node.rdev); - // override node's stream ops with the device's - stream.stream_ops = device.stream_ops; - // forward the open call - if (stream.stream_ops.open) { - stream.stream_ops.open(stream); - } - },llseek:function() { - throw new FS.ErrnoError(70); - }},major:function(dev) { - return ((dev) >> 8); - },minor:function(dev) { - return ((dev) & 0xff); - },makedev:function(ma, mi) { - return ((ma) << 8 | (mi)); - },registerDevice:function(dev, ops) { - FS.devices[dev] = { stream_ops: ops }; - },getDevice:function(dev) { - return FS.devices[dev]; - },getMounts:function(mount) { - var mounts = []; - var check = [mount]; - - while (check.length) { - var m = check.pop(); - - mounts.push(m); - - check.push.apply(check, m.mounts); - } - - return mounts; - },syncfs:function(populate, callback) { - if (typeof(populate) === 'function') { - callback = populate; - populate = false; - } - - FS.syncFSRequests++; - - if (FS.syncFSRequests > 1) { - err('warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work'); - } - - var mounts = FS.getMounts(FS.root.mount); - var completed = 0; - - function doCallback(errCode) { - assert(FS.syncFSRequests > 0); - FS.syncFSRequests--; - return callback(errCode); - } - - function done(errCode) { - if (errCode) { - if (!done.errored) { - done.errored = true; - return doCallback(errCode); - } - return; - } - if (++completed >= mounts.length) { - doCallback(null); - } - }; - - // sync all mounts - mounts.forEach(function (mount) { - if (!mount.type.syncfs) { - return done(null); - } - mount.type.syncfs(mount, populate, done); - }); - },mount:function(type, opts, mountpoint) { - if (typeof type === 'string') { - // The filesystem was not included, and instead we have an error - // message stored in the variable. - throw type; - } - var root = mountpoint === '/'; - var pseudo = !mountpoint; - var node; - - if (root && FS.root) { - throw new FS.ErrnoError(10); - } else if (!root && !pseudo) { - var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); - - mountpoint = lookup.path; // use the absolute path - node = lookup.node; - - if (FS.isMountpoint(node)) { - throw new FS.ErrnoError(10); - } - - if (!FS.isDir(node.mode)) { - throw new FS.ErrnoError(54); - } - } - - var mount = { - type: type, - opts: opts, - mountpoint: mountpoint, - mounts: [] - }; - - // create a root node for the fs - var mountRoot = type.mount(mount); - mountRoot.mount = mount; - mount.root = mountRoot; - - if (root) { - FS.root = mountRoot; - } else if (node) { - // set as a mountpoint - node.mounted = mount; - - // add the new mount to the current mount's children - if (node.mount) { - node.mount.mounts.push(mount); - } - } - - return mountRoot; - },unmount:function (mountpoint) { - var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); - - if (!FS.isMountpoint(lookup.node)) { - throw new FS.ErrnoError(28); - } - - // destroy the nodes for this mount, and all its child mounts - var node = lookup.node; - var mount = node.mounted; - var mounts = FS.getMounts(mount); - - Object.keys(FS.nameTable).forEach(function (hash) { - var current = FS.nameTable[hash]; - - while (current) { - var next = current.name_next; - - if (mounts.indexOf(current.mount) !== -1) { - FS.destroyNode(current); - } - - current = next; - } - }); - - // no longer a mountpoint - node.mounted = null; - - // remove this mount from the child mounts - var idx = node.mount.mounts.indexOf(mount); - assert(idx !== -1); - node.mount.mounts.splice(idx, 1); - },lookup:function(parent, name) { - return parent.node_ops.lookup(parent, name); - },mknod:function(path, mode, dev) { - var lookup = FS.lookupPath(path, { parent: true }); - var parent = lookup.node; - var name = PATH.basename(path); - if (!name || name === '.' || name === '..') { - throw new FS.ErrnoError(28); - } - var errCode = FS.mayCreate(parent, name); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - if (!parent.node_ops.mknod) { - throw new FS.ErrnoError(63); - } - return parent.node_ops.mknod(parent, name, mode, dev); - },create:function(path, mode) { - mode = mode !== undefined ? mode : 438 /* 0666 */; - mode &= 4095; - mode |= 32768; - return FS.mknod(path, mode, 0); - },mkdir:function(path, mode) { - mode = mode !== undefined ? mode : 511 /* 0777 */; - mode &= 511 | 512; - mode |= 16384; - return FS.mknod(path, mode, 0); - },mkdirTree:function(path, mode) { - var dirs = path.split('/'); - var d = ''; - for (var i = 0; i < dirs.length; ++i) { - if (!dirs[i]) continue; - d += '/' + dirs[i]; - try { - FS.mkdir(d, mode); - } catch(e) { - if (e.errno != 20) throw e; - } - } - },mkdev:function(path, mode, dev) { - if (typeof(dev) === 'undefined') { - dev = mode; - mode = 438 /* 0666 */; - } - mode |= 8192; - return FS.mknod(path, mode, dev); - },symlink:function(oldpath, newpath) { - if (!PATH_FS.resolve(oldpath)) { - throw new FS.ErrnoError(44); - } - var lookup = FS.lookupPath(newpath, { parent: true }); - var parent = lookup.node; - if (!parent) { - throw new FS.ErrnoError(44); - } - var newname = PATH.basename(newpath); - var errCode = FS.mayCreate(parent, newname); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - if (!parent.node_ops.symlink) { - throw new FS.ErrnoError(63); - } - return parent.node_ops.symlink(parent, newname, oldpath); - },rename:function(old_path, new_path) { - var old_dirname = PATH.dirname(old_path); - var new_dirname = PATH.dirname(new_path); - var old_name = PATH.basename(old_path); - var new_name = PATH.basename(new_path); - // parents must exist - var lookup, old_dir, new_dir; - - // let the errors from non existant directories percolate up - lookup = FS.lookupPath(old_path, { parent: true }); - old_dir = lookup.node; - lookup = FS.lookupPath(new_path, { parent: true }); - new_dir = lookup.node; - - if (!old_dir || !new_dir) throw new FS.ErrnoError(44); - // need to be part of the same mount - if (old_dir.mount !== new_dir.mount) { - throw new FS.ErrnoError(75); - } - // source must exist - var old_node = FS.lookupNode(old_dir, old_name); - // old path should not be an ancestor of the new path - var relative = PATH_FS.relative(old_path, new_dirname); - if (relative.charAt(0) !== '.') { - throw new FS.ErrnoError(28); - } - // new path should not be an ancestor of the old path - relative = PATH_FS.relative(new_path, old_dirname); - if (relative.charAt(0) !== '.') { - throw new FS.ErrnoError(55); - } - // see if the new path already exists - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) { - // not fatal - } - // early out if nothing needs to change - if (old_node === new_node) { - return; - } - // we'll need to delete the old entry - var isdir = FS.isDir(old_node.mode); - var errCode = FS.mayDelete(old_dir, old_name, isdir); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - // need delete permissions if we'll be overwriting. - // need create permissions if new doesn't already exist. - errCode = new_node ? - FS.mayDelete(new_dir, new_name, isdir) : - FS.mayCreate(new_dir, new_name); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - if (!old_dir.node_ops.rename) { - throw new FS.ErrnoError(63); - } - if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { - throw new FS.ErrnoError(10); - } - // if we are going to change the parent, check write permissions - if (new_dir !== old_dir) { - errCode = FS.nodePermissions(old_dir, 'w'); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - } - try { - if (FS.trackingDelegate['willMovePath']) { - FS.trackingDelegate['willMovePath'](old_path, new_path); - } - } catch(e) { - err("FS.trackingDelegate['willMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); - } - // remove the node from the lookup hash - FS.hashRemoveNode(old_node); - // do the underlying fs rename - try { - old_dir.node_ops.rename(old_node, new_dir, new_name); - } catch (e) { - throw e; - } finally { - // add the node back to the hash (in case node_ops.rename - // changed its name) - FS.hashAddNode(old_node); - } - try { - if (FS.trackingDelegate['onMovePath']) FS.trackingDelegate['onMovePath'](old_path, new_path); - } catch(e) { - err("FS.trackingDelegate['onMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); - } - },rmdir:function(path) { - var lookup = FS.lookupPath(path, { parent: true }); - var parent = lookup.node; - var name = PATH.basename(path); - var node = FS.lookupNode(parent, name); - var errCode = FS.mayDelete(parent, name, true); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - if (!parent.node_ops.rmdir) { - throw new FS.ErrnoError(63); - } - if (FS.isMountpoint(node)) { - throw new FS.ErrnoError(10); - } - try { - if (FS.trackingDelegate['willDeletePath']) { - FS.trackingDelegate['willDeletePath'](path); - } - } catch(e) { - err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); - } - parent.node_ops.rmdir(parent, name); - FS.destroyNode(node); - try { - if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); - } catch(e) { - err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); - } - },readdir:function(path) { - var lookup = FS.lookupPath(path, { follow: true }); - var node = lookup.node; - if (!node.node_ops.readdir) { - throw new FS.ErrnoError(54); - } - return node.node_ops.readdir(node); - },unlink:function(path) { - var lookup = FS.lookupPath(path, { parent: true }); - var parent = lookup.node; - var name = PATH.basename(path); - var node = FS.lookupNode(parent, name); - var errCode = FS.mayDelete(parent, name, false); - if (errCode) { - // According to POSIX, we should map EISDIR to EPERM, but - // we instead do what Linux does (and we must, as we use - // the musl linux libc). - throw new FS.ErrnoError(errCode); - } - if (!parent.node_ops.unlink) { - throw new FS.ErrnoError(63); - } - if (FS.isMountpoint(node)) { - throw new FS.ErrnoError(10); - } - try { - if (FS.trackingDelegate['willDeletePath']) { - FS.trackingDelegate['willDeletePath'](path); - } - } catch(e) { - err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); - } - parent.node_ops.unlink(parent, name); - FS.destroyNode(node); - try { - if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); - } catch(e) { - err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); - } - },readlink:function(path) { - var lookup = FS.lookupPath(path); - var link = lookup.node; - if (!link) { - throw new FS.ErrnoError(44); - } - if (!link.node_ops.readlink) { - throw new FS.ErrnoError(28); - } - return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link)); - },stat:function(path, dontFollow) { - var lookup = FS.lookupPath(path, { follow: !dontFollow }); - var node = lookup.node; - if (!node) { - throw new FS.ErrnoError(44); - } - if (!node.node_ops.getattr) { - throw new FS.ErrnoError(63); - } - return node.node_ops.getattr(node); - },lstat:function(path) { - return FS.stat(path, true); - },chmod:function(path, mode, dontFollow) { - var node; - if (typeof path === 'string') { - var lookup = FS.lookupPath(path, { follow: !dontFollow }); - node = lookup.node; - } else { - node = path; - } - if (!node.node_ops.setattr) { - throw new FS.ErrnoError(63); - } - node.node_ops.setattr(node, { - mode: (mode & 4095) | (node.mode & ~4095), - timestamp: Date.now() - }); - },lchmod:function(path, mode) { - FS.chmod(path, mode, true); - },fchmod:function(fd, mode) { - var stream = FS.getStream(fd); - if (!stream) { - throw new FS.ErrnoError(8); - } - FS.chmod(stream.node, mode); - },chown:function(path, uid, gid, dontFollow) { - var node; - if (typeof path === 'string') { - var lookup = FS.lookupPath(path, { follow: !dontFollow }); - node = lookup.node; - } else { - node = path; - } - if (!node.node_ops.setattr) { - throw new FS.ErrnoError(63); - } - node.node_ops.setattr(node, { - timestamp: Date.now() - // we ignore the uid / gid for now - }); - },lchown:function(path, uid, gid) { - FS.chown(path, uid, gid, true); - },fchown:function(fd, uid, gid) { - var stream = FS.getStream(fd); - if (!stream) { - throw new FS.ErrnoError(8); - } - FS.chown(stream.node, uid, gid); - },truncate:function(path, len) { - if (len < 0) { - throw new FS.ErrnoError(28); - } - var node; - if (typeof path === 'string') { - var lookup = FS.lookupPath(path, { follow: true }); - node = lookup.node; - } else { - node = path; - } - if (!node.node_ops.setattr) { - throw new FS.ErrnoError(63); - } - if (FS.isDir(node.mode)) { - throw new FS.ErrnoError(31); - } - if (!FS.isFile(node.mode)) { - throw new FS.ErrnoError(28); - } - var errCode = FS.nodePermissions(node, 'w'); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - node.node_ops.setattr(node, { - size: len, - timestamp: Date.now() - }); - },ftruncate:function(fd, len) { - var stream = FS.getStream(fd); - if (!stream) { - throw new FS.ErrnoError(8); - } - if ((stream.flags & 2097155) === 0) { - throw new FS.ErrnoError(28); - } - FS.truncate(stream.node, len); - },utime:function(path, atime, mtime) { - var lookup = FS.lookupPath(path, { follow: true }); - var node = lookup.node; - node.node_ops.setattr(node, { - timestamp: Math.max(atime, mtime) - }); - },open:function(path, flags, mode, fd_start, fd_end) { - if (path === "") { - throw new FS.ErrnoError(44); - } - flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; - mode = typeof mode === 'undefined' ? 438 /* 0666 */ : mode; - if ((flags & 64)) { - mode = (mode & 4095) | 32768; - } else { - mode = 0; - } - var node; - if (typeof path === 'object') { - node = path; - } else { - path = PATH.normalize(path); - try { - var lookup = FS.lookupPath(path, { - follow: !(flags & 131072) - }); - node = lookup.node; - } catch (e) { - // ignore - } - } - // perhaps we need to create the node - var created = false; - if ((flags & 64)) { - if (node) { - // if O_CREAT and O_EXCL are set, error out if the node already exists - if ((flags & 128)) { - throw new FS.ErrnoError(20); - } - } else { - // node doesn't exist, try to create it - node = FS.mknod(path, mode, 0); - created = true; - } - } - if (!node) { - throw new FS.ErrnoError(44); - } - // can't truncate a device - if (FS.isChrdev(node.mode)) { - flags &= ~512; - } - // if asked only for a directory, then this must be one - if ((flags & 65536) && !FS.isDir(node.mode)) { - throw new FS.ErrnoError(54); - } - // check permissions, if this is not a file we just created now (it is ok to - // create and write to a file with read-only permissions; it is read-only - // for later use) - if (!created) { - var errCode = FS.mayOpen(node, flags); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - } - // do truncation if necessary - if ((flags & 512)) { - FS.truncate(node, 0); - } - // we've already handled these, don't pass down to the underlying vfs - flags &= ~(128 | 512 | 131072); - - // register the stream with the filesystem - var stream = FS.createStream({ - node: node, - path: FS.getPath(node), // we want the absolute path to the node - flags: flags, - seekable: true, - position: 0, - stream_ops: node.stream_ops, - // used by the file family libc calls (fopen, fwrite, ferror, etc.) - ungotten: [], - error: false - }, fd_start, fd_end); - // call the new stream's open function - if (stream.stream_ops.open) { - stream.stream_ops.open(stream); - } - if (Module['logReadFiles'] && !(flags & 1)) { - if (!FS.readFiles) FS.readFiles = {}; - if (!(path in FS.readFiles)) { - FS.readFiles[path] = 1; - err("FS.trackingDelegate error on read file: " + path); - } - } - try { - if (FS.trackingDelegate['onOpenFile']) { - var trackingFlags = 0; - if ((flags & 2097155) !== 1) { - trackingFlags |= FS.tracking.openFlags.READ; - } - if ((flags & 2097155) !== 0) { - trackingFlags |= FS.tracking.openFlags.WRITE; - } - FS.trackingDelegate['onOpenFile'](path, trackingFlags); - } - } catch(e) { - err("FS.trackingDelegate['onOpenFile']('"+path+"', flags) threw an exception: " + e.message); - } - return stream; - },close:function(stream) { - if (FS.isClosed(stream)) { - throw new FS.ErrnoError(8); - } - if (stream.getdents) stream.getdents = null; // free readdir state - try { - if (stream.stream_ops.close) { - stream.stream_ops.close(stream); - } - } catch (e) { - throw e; - } finally { - FS.closeStream(stream.fd); - } - stream.fd = null; - },isClosed:function(stream) { - return stream.fd === null; - },llseek:function(stream, offset, whence) { - if (FS.isClosed(stream)) { - throw new FS.ErrnoError(8); - } - if (!stream.seekable || !stream.stream_ops.llseek) { - throw new FS.ErrnoError(70); - } - if (whence != 0 && whence != 1 && whence != 2) { - throw new FS.ErrnoError(28); - } - stream.position = stream.stream_ops.llseek(stream, offset, whence); - stream.ungotten = []; - return stream.position; - },read:function(stream, buffer, offset, length, position) { - if (length < 0 || position < 0) { - throw new FS.ErrnoError(28); - } - if (FS.isClosed(stream)) { - throw new FS.ErrnoError(8); - } - if ((stream.flags & 2097155) === 1) { - throw new FS.ErrnoError(8); - } - if (FS.isDir(stream.node.mode)) { - throw new FS.ErrnoError(31); - } - if (!stream.stream_ops.read) { - throw new FS.ErrnoError(28); - } - var seeking = typeof position !== 'undefined'; - if (!seeking) { - position = stream.position; - } else if (!stream.seekable) { - throw new FS.ErrnoError(70); - } - var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); - if (!seeking) stream.position += bytesRead; - return bytesRead; - },write:function(stream, buffer, offset, length, position, canOwn) { - if (length < 0 || position < 0) { - throw new FS.ErrnoError(28); - } - if (FS.isClosed(stream)) { - throw new FS.ErrnoError(8); - } - if ((stream.flags & 2097155) === 0) { - throw new FS.ErrnoError(8); - } - if (FS.isDir(stream.node.mode)) { - throw new FS.ErrnoError(31); - } - if (!stream.stream_ops.write) { - throw new FS.ErrnoError(28); - } - if (stream.seekable && stream.flags & 1024) { - // seek to the end before writing in append mode - FS.llseek(stream, 0, 2); - } - var seeking = typeof position !== 'undefined'; - if (!seeking) { - position = stream.position; - } else if (!stream.seekable) { - throw new FS.ErrnoError(70); - } - var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); - if (!seeking) stream.position += bytesWritten; - try { - if (stream.path && FS.trackingDelegate['onWriteToFile']) FS.trackingDelegate['onWriteToFile'](stream.path); - } catch(e) { - err("FS.trackingDelegate['onWriteToFile']('"+stream.path+"') threw an exception: " + e.message); - } - return bytesWritten; - },allocate:function(stream, offset, length) { - if (FS.isClosed(stream)) { - throw new FS.ErrnoError(8); - } - if (offset < 0 || length <= 0) { - throw new FS.ErrnoError(28); - } - if ((stream.flags & 2097155) === 0) { - throw new FS.ErrnoError(8); - } - if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { - throw new FS.ErrnoError(43); - } - if (!stream.stream_ops.allocate) { - throw new FS.ErrnoError(138); - } - stream.stream_ops.allocate(stream, offset, length); - },mmap:function(stream, address, length, position, prot, flags) { - // User requests writing to file (prot & PROT_WRITE != 0). - // Checking if we have permissions to write to the file unless - // MAP_PRIVATE flag is set. According to POSIX spec it is possible - // to write to file opened in read-only mode with MAP_PRIVATE flag, - // as all modifications will be visible only in the memory of - // the current process. - if ((prot & 2) !== 0 - && (flags & 2) === 0 - && (stream.flags & 2097155) !== 2) { - throw new FS.ErrnoError(2); - } - if ((stream.flags & 2097155) === 1) { - throw new FS.ErrnoError(2); - } - if (!stream.stream_ops.mmap) { - throw new FS.ErrnoError(43); - } - return stream.stream_ops.mmap(stream, address, length, position, prot, flags); - },msync:function(stream, buffer, offset, length, mmapFlags) { - if (!stream || !stream.stream_ops.msync) { - return 0; - } - return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); - },munmap:function(stream) { - return 0; - },ioctl:function(stream, cmd, arg) { - if (!stream.stream_ops.ioctl) { - throw new FS.ErrnoError(59); - } - return stream.stream_ops.ioctl(stream, cmd, arg); - },readFile:function(path, opts) { - opts = opts || {}; - opts.flags = opts.flags || 'r'; - opts.encoding = opts.encoding || 'binary'; - if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { - throw new Error('Invalid encoding type "' + opts.encoding + '"'); - } - var ret; - var stream = FS.open(path, opts.flags); - var stat = FS.stat(path); - var length = stat.size; - var buf = new Uint8Array(length); - FS.read(stream, buf, 0, length, 0); - if (opts.encoding === 'utf8') { - ret = UTF8ArrayToString(buf, 0); - } else if (opts.encoding === 'binary') { - ret = buf; - } - FS.close(stream); - return ret; - },writeFile:function(path, data, opts) { - opts = opts || {}; - opts.flags = opts.flags || 'w'; - var stream = FS.open(path, opts.flags, opts.mode); - if (typeof data === 'string') { - var buf = new Uint8Array(lengthBytesUTF8(data)+1); - var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length); - FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn); - } else if (ArrayBuffer.isView(data)) { - FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); - } else { - throw new Error('Unsupported data type'); - } - FS.close(stream); - },cwd:function() { - return FS.currentPath; - },chdir:function(path) { - var lookup = FS.lookupPath(path, { follow: true }); - if (lookup.node === null) { - throw new FS.ErrnoError(44); - } - if (!FS.isDir(lookup.node.mode)) { - throw new FS.ErrnoError(54); - } - var errCode = FS.nodePermissions(lookup.node, 'x'); - if (errCode) { - throw new FS.ErrnoError(errCode); - } - FS.currentPath = lookup.path; - },createDefaultDirectories:function() { - FS.mkdir('/tmp'); - FS.mkdir('/home'); - FS.mkdir('/home/web_user'); - },createDefaultDevices:function() { - // create /dev - FS.mkdir('/dev'); - // setup /dev/null - FS.registerDevice(FS.makedev(1, 3), { - read: function() { return 0; }, - write: function(stream, buffer, offset, length, pos) { return length; } - }); - FS.mkdev('/dev/null', FS.makedev(1, 3)); - // setup /dev/tty and /dev/tty1 - // stderr needs to print output using Module['printErr'] - // so we register a second tty just for it. - TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); - TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); - FS.mkdev('/dev/tty', FS.makedev(5, 0)); - FS.mkdev('/dev/tty1', FS.makedev(6, 0)); - // setup /dev/[u]random - var random_device; - if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') { - // for modern web browsers - var randomBuffer = new Uint8Array(1); - random_device = function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; - } else - if (ENVIRONMENT_IS_NODE) { - // for nodejs with or without crypto support included - try { - var crypto_module = require('crypto'); - // nodejs has crypto support - random_device = function() { return crypto_module['randomBytes'](1)[0]; }; - } catch (e) { - // nodejs doesn't have crypto support - } - } else - {} - if (!random_device) { - // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096 - random_device = function() { abort("no cryptographic support found for random_device. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };"); }; - } - FS.createDevice('/dev', 'random', random_device); - FS.createDevice('/dev', 'urandom', random_device); - // we're not going to emulate the actual shm device, - // just create the tmp dirs that reside in it commonly - FS.mkdir('/dev/shm'); - FS.mkdir('/dev/shm/tmp'); - },createSpecialDirectories:function() { - // create /proc/self/fd which allows /proc/self/fd/6 => readlink gives the name of the stream for fd 6 (see test_unistd_ttyname) - FS.mkdir('/proc'); - FS.mkdir('/proc/self'); - FS.mkdir('/proc/self/fd'); - FS.mount({ - mount: function() { - var node = FS.createNode('/proc/self', 'fd', 16384 | 511 /* 0777 */, 73); - node.node_ops = { - lookup: function(parent, name) { - var fd = +name; - var stream = FS.getStream(fd); - if (!stream) throw new FS.ErrnoError(8); - var ret = { - parent: null, - mount: { mountpoint: 'fake' }, - node_ops: { readlink: function() { return stream.path } } - }; - ret.parent = ret; // make it look like a simple root node - return ret; - } - }; - return node; - } - }, {}, '/proc/self/fd'); - },createStandardStreams:function() { - // TODO deprecate the old functionality of a single - // input / output callback and that utilizes FS.createDevice - // and instead require a unique set of stream ops - - // by default, we symlink the standard streams to the - // default tty devices. however, if the standard streams - // have been overwritten we create a unique device for - // them instead. - if (Module['stdin']) { - FS.createDevice('/dev', 'stdin', Module['stdin']); - } else { - FS.symlink('/dev/tty', '/dev/stdin'); - } - if (Module['stdout']) { - FS.createDevice('/dev', 'stdout', null, Module['stdout']); - } else { - FS.symlink('/dev/tty', '/dev/stdout'); - } - if (Module['stderr']) { - FS.createDevice('/dev', 'stderr', null, Module['stderr']); - } else { - FS.symlink('/dev/tty1', '/dev/stderr'); - } - - // open default streams for the stdin, stdout and stderr devices - var stdin = FS.open('/dev/stdin', 'r'); - var stdout = FS.open('/dev/stdout', 'w'); - var stderr = FS.open('/dev/stderr', 'w'); - assert(stdin.fd === 0, 'invalid handle for stdin (' + stdin.fd + ')'); - assert(stdout.fd === 1, 'invalid handle for stdout (' + stdout.fd + ')'); - assert(stderr.fd === 2, 'invalid handle for stderr (' + stderr.fd + ')'); - },ensureErrnoError:function() { - if (FS.ErrnoError) return; - FS.ErrnoError = /** @this{Object} */ function ErrnoError(errno, node) { - this.node = node; - this.setErrno = /** @this{Object} */ function(errno) { - this.errno = errno; - for (var key in ERRNO_CODES) { - if (ERRNO_CODES[key] === errno) { - this.code = key; - break; - } - } - }; - this.setErrno(errno); - this.message = ERRNO_MESSAGES[errno]; - - // Try to get a maximally helpful stack trace. On Node.js, getting Error.stack - // now ensures it shows what we want. - if (this.stack) { - // Define the stack property for Node.js 4, which otherwise errors on the next line. - Object.defineProperty(this, "stack", { value: (new Error).stack, writable: true }); - this.stack = demangleAll(this.stack); - } - }; - FS.ErrnoError.prototype = new Error(); - FS.ErrnoError.prototype.constructor = FS.ErrnoError; - // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info) - [44].forEach(function(code) { - FS.genericErrors[code] = new FS.ErrnoError(code); - FS.genericErrors[code].stack = ''; - }); - },staticInit:function() { - FS.ensureErrnoError(); - - FS.nameTable = new Array(4096); - - FS.mount(MEMFS, {}, '/'); - - FS.createDefaultDirectories(); - FS.createDefaultDevices(); - FS.createSpecialDirectories(); - - FS.filesystems = { - 'MEMFS': MEMFS, - }; - },init:function(input, output, error) { - assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); - FS.init.initialized = true; - - FS.ensureErrnoError(); - - // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here - Module['stdin'] = input || Module['stdin']; - Module['stdout'] = output || Module['stdout']; - Module['stderr'] = error || Module['stderr']; - - FS.createStandardStreams(); - },quit:function() { - FS.init.initialized = false; - // force-flush all streams, so we get musl std streams printed out - var fflush = Module['_fflush']; - if (fflush) fflush(0); - // close all of our streams - for (var i = 0; i < FS.streams.length; i++) { - var stream = FS.streams[i]; - if (!stream) { - continue; - } - FS.close(stream); - } - },getMode:function(canRead, canWrite) { - var mode = 0; - if (canRead) mode |= 292 | 73; - if (canWrite) mode |= 146; - return mode; - },joinPath:function(parts, forceRelative) { - var path = PATH.join.apply(null, parts); - if (forceRelative && path[0] == '/') path = path.substr(1); - return path; - },absolutePath:function(relative, base) { - return PATH_FS.resolve(base, relative); - },standardizePath:function(path) { - return PATH.normalize(path); - },findObject:function(path, dontResolveLastLink) { - var ret = FS.analyzePath(path, dontResolveLastLink); - if (ret.exists) { - return ret.object; - } else { - setErrNo(ret.error); - return null; - } - },analyzePath:function(path, dontResolveLastLink) { - // operate from within the context of the symlink's target - try { - var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); - path = lookup.path; - } catch (e) { - } - var ret = { - isRoot: false, exists: false, error: 0, name: null, path: null, object: null, - parentExists: false, parentPath: null, parentObject: null - }; - try { - var lookup = FS.lookupPath(path, { parent: true }); - ret.parentExists = true; - ret.parentPath = lookup.path; - ret.parentObject = lookup.node; - ret.name = PATH.basename(path); - lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); - ret.exists = true; - ret.path = lookup.path; - ret.object = lookup.node; - ret.name = lookup.node.name; - ret.isRoot = lookup.path === '/'; - } catch (e) { - ret.error = e.errno; - }; - return ret; - },createFolder:function(parent, name, canRead, canWrite) { - var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); - var mode = FS.getMode(canRead, canWrite); - return FS.mkdir(path, mode); - },createPath:function(parent, path, canRead, canWrite) { - parent = typeof parent === 'string' ? parent : FS.getPath(parent); - var parts = path.split('/').reverse(); - while (parts.length) { - var part = parts.pop(); - if (!part) continue; - var current = PATH.join2(parent, part); - try { - FS.mkdir(current); - } catch (e) { - // ignore EEXIST - } - parent = current; - } - return current; - },createFile:function(parent, name, properties, canRead, canWrite) { - var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); - var mode = FS.getMode(canRead, canWrite); - return FS.create(path, mode); - },createDataFile:function(parent, name, data, canRead, canWrite, canOwn) { - var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent; - var mode = FS.getMode(canRead, canWrite); - var node = FS.create(path, mode); - if (data) { - if (typeof data === 'string') { - var arr = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); - data = arr; - } - // make sure we can write to the file - FS.chmod(node, mode | 146); - var stream = FS.open(node, 'w'); - FS.write(stream, data, 0, data.length, 0, canOwn); - FS.close(stream); - FS.chmod(node, mode); - } - return node; - },createDevice:function(parent, name, input, output) { - var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); - var mode = FS.getMode(!!input, !!output); - if (!FS.createDevice.major) FS.createDevice.major = 64; - var dev = FS.makedev(FS.createDevice.major++, 0); - // Create a fake device that a set of stream ops to emulate - // the old behavior. - FS.registerDevice(dev, { - open: function(stream) { - stream.seekable = false; - }, - close: function(stream) { - // flush any pending line data - if (output && output.buffer && output.buffer.length) { - output(10); - } - }, - read: function(stream, buffer, offset, length, pos /* ignored */) { - var bytesRead = 0; - for (var i = 0; i < length; i++) { - var result; - try { - result = input(); - } catch (e) { - throw new FS.ErrnoError(29); - } - if (result === undefined && bytesRead === 0) { - throw new FS.ErrnoError(6); - } - if (result === null || result === undefined) break; - bytesRead++; - buffer[offset+i] = result; - } - if (bytesRead) { - stream.node.timestamp = Date.now(); - } - return bytesRead; - }, - write: function(stream, buffer, offset, length, pos) { - for (var i = 0; i < length; i++) { - try { - output(buffer[offset+i]); - } catch (e) { - throw new FS.ErrnoError(29); - } - } - if (length) { - stream.node.timestamp = Date.now(); - } - return i; - } - }); - return FS.mkdev(path, mode, dev); - },createLink:function(parent, name, target, canRead, canWrite) { - var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); - return FS.symlink(target, path); - },forceLoadFile:function(obj) { - if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; - var success = true; - if (typeof XMLHttpRequest !== 'undefined') { - throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); - } else if (read_) { - // Command-line. - try { - // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as - // read() will try to parse UTF8. - obj.contents = intArrayFromString(read_(obj.url), true); - obj.usedBytes = obj.contents.length; - } catch (e) { - success = false; - } - } else { - throw new Error('Cannot load without read() or XMLHttpRequest.'); - } - if (!success) setErrNo(29); - return success; - },createLazyFile:function(parent, name, url, canRead, canWrite) { - // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. - /** @constructor */ - function LazyUint8Array() { - this.lengthKnown = false; - this.chunks = []; // Loaded chunks. Index is the chunk number - } - LazyUint8Array.prototype.get = /** @this{Object} */ function LazyUint8Array_get(idx) { - if (idx > this.length-1 || idx < 0) { - return undefined; - } - var chunkOffset = idx % this.chunkSize; - var chunkNum = (idx / this.chunkSize)|0; - return this.getter(chunkNum)[chunkOffset]; - }; - LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) { - this.getter = getter; - }; - LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { - // Find length - var xhr = new XMLHttpRequest(); - xhr.open('HEAD', url, false); - xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); - var datalength = Number(xhr.getResponseHeader("Content-length")); - var header; - var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; - var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; - - var chunkSize = 1024*1024; // Chunk size in bytes - - if (!hasByteServing) chunkSize = datalength; - - // Function to get a range from the remote URL. - var doXHR = (function(from, to) { - if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); - - // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); - - // Some hints to the browser that we want binary data. - if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; - if (xhr.overrideMimeType) { - xhr.overrideMimeType('text/plain; charset=x-user-defined'); - } - - xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); - if (xhr.response !== undefined) { - return new Uint8Array(/** @type{Array} */(xhr.response || [])); - } else { - return intArrayFromString(xhr.responseText || '', true); - } - }); - var lazyArray = this; - lazyArray.setDataGetter(function(chunkNum) { - var start = chunkNum * chunkSize; - var end = (chunkNum+1) * chunkSize - 1; // including this byte - end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block - if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { - lazyArray.chunks[chunkNum] = doXHR(start, end); - } - if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); - return lazyArray.chunks[chunkNum]; - }); - - if (usesGzip || !datalength) { - // if the server uses gzip or doesn't supply the length, we have to download the whole file to get the (uncompressed) length - chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file - datalength = this.getter(0).length; - chunkSize = datalength; - out("LazyFiles on gzip forces download of the whole file when length is accessed"); - } - - this._length = datalength; - this._chunkSize = chunkSize; - this.lengthKnown = true; - }; - if (typeof XMLHttpRequest !== 'undefined') { - if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; - var lazyArray = new LazyUint8Array(); - Object.defineProperties(lazyArray, { - length: { - get: /** @this{Object} */ function() { - if(!this.lengthKnown) { - this.cacheLength(); - } - return this._length; - } - }, - chunkSize: { - get: /** @this{Object} */ function() { - if(!this.lengthKnown) { - this.cacheLength(); - } - return this._chunkSize; - } - } - }); - - var properties = { isDevice: false, contents: lazyArray }; - } else { - var properties = { isDevice: false, url: url }; - } - - var node = FS.createFile(parent, name, properties, canRead, canWrite); - // This is a total hack, but I want to get this lazy file code out of the - // core of MEMFS. If we want to keep this lazy file concept I feel it should - // be its own thin LAZYFS proxying calls to MEMFS. - if (properties.contents) { - node.contents = properties.contents; - } else if (properties.url) { - node.contents = null; - node.url = properties.url; - } - // Add a function that defers querying the file size until it is asked the first time. - Object.defineProperties(node, { - usedBytes: { - get: /** @this {FSNode} */ function() { return this.contents.length; } - } - }); - // override each stream op with one that tries to force load the lazy file first - var stream_ops = {}; - var keys = Object.keys(node.stream_ops); - keys.forEach(function(key) { - var fn = node.stream_ops[key]; - stream_ops[key] = function forceLoadLazyFile() { - if (!FS.forceLoadFile(node)) { - throw new FS.ErrnoError(29); - } - return fn.apply(null, arguments); - }; - }); - // use a custom read function - stream_ops.read = function stream_ops_read(stream, buffer, offset, length, position) { - if (!FS.forceLoadFile(node)) { - throw new FS.ErrnoError(29); - } - var contents = stream.node.contents; - if (position >= contents.length) - return 0; - var size = Math.min(contents.length - position, length); - assert(size >= 0); - if (contents.slice) { // normal array - for (var i = 0; i < size; i++) { - buffer[offset + i] = contents[position + i]; - } - } else { - for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR - buffer[offset + i] = contents.get(position + i); - } - } - return size; - }; - node.stream_ops = stream_ops; - return node; - },createPreloadedFile:function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) { - Browser.init(); // XXX perhaps this method should move onto Browser? - // TODO we should allow people to just pass in a complete filename instead - // of parent and name being that we just join them anyways - var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; - var dep = getUniqueRunDependency('cp ' + fullname); // might have several active requests for the same fullname - function processData(byteArray) { - function finish(byteArray) { - if (preFinish) preFinish(); - if (!dontCreateFile) { - FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); - } - if (onload) onload(); - removeRunDependency(dep); - } - var handled = false; - Module['preloadPlugins'].forEach(function(plugin) { - if (handled) return; - if (plugin['canHandle'](fullname)) { - plugin['handle'](byteArray, fullname, finish, function() { - if (onerror) onerror(); - removeRunDependency(dep); - }); - handled = true; - } - }); - if (!handled) finish(byteArray); - } - addRunDependency(dep); - if (typeof url == 'string') { - Browser.asyncLoad(url, function(byteArray) { - processData(byteArray); - }, onerror); - } else { - processData(url); - } - },indexedDB:function() { - return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; - },DB_NAME:function() { - return 'EM_FS_' + window.location.pathname; - },DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:function(paths, onload, onerror) { - onload = onload || function(){}; - onerror = onerror || function(){}; - var indexedDB = FS.indexedDB(); - try { - var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); - } catch (e) { - return onerror(e); - } - openRequest.onupgradeneeded = function openRequest_onupgradeneeded() { - out('creating db'); - var db = openRequest.result; - db.createObjectStore(FS.DB_STORE_NAME); - }; - openRequest.onsuccess = function openRequest_onsuccess() { - var db = openRequest.result; - var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite'); - var files = transaction.objectStore(FS.DB_STORE_NAME); - var ok = 0, fail = 0, total = paths.length; - function finish() { - if (fail == 0) onload(); else onerror(); - } - paths.forEach(function(path) { - var putRequest = files.put(FS.analyzePath(path).object.contents, path); - putRequest.onsuccess = function putRequest_onsuccess() { ok++; if (ok + fail == total) finish() }; - putRequest.onerror = function putRequest_onerror() { fail++; if (ok + fail == total) finish() }; - }); - transaction.onerror = onerror; - }; - openRequest.onerror = onerror; - },loadFilesFromDB:function(paths, onload, onerror) { - onload = onload || function(){}; - onerror = onerror || function(){}; - var indexedDB = FS.indexedDB(); - try { - var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); - } catch (e) { - return onerror(e); - } - openRequest.onupgradeneeded = onerror; // no database to load from - openRequest.onsuccess = function openRequest_onsuccess() { - var db = openRequest.result; - try { - var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly'); - } catch(e) { - onerror(e); - return; - } - var files = transaction.objectStore(FS.DB_STORE_NAME); - var ok = 0, fail = 0, total = paths.length; - function finish() { - if (fail == 0) onload(); else onerror(); - } - paths.forEach(function(path) { - var getRequest = files.get(path); - getRequest.onsuccess = function getRequest_onsuccess() { - if (FS.analyzePath(path).exists) { - FS.unlink(path); - } - FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true); - ok++; - if (ok + fail == total) finish(); - }; - getRequest.onerror = function getRequest_onerror() { fail++; if (ok + fail == total) finish() }; - }); - transaction.onerror = onerror; - }; - openRequest.onerror = onerror; - },mmapAlloc:function(size) { - var alignedSize = alignMemory(size, 16384); - var ptr = _malloc(alignedSize); - while (size < alignedSize) HEAP8[ptr + size++] = 0; - return ptr; - }};var SYSCALLS={mappings:{},DEFAULT_POLLMASK:5,umask:511,calculateAt:function(dirfd, path) { - if (path[0] !== '/') { - // relative path - var dir; - if (dirfd === -100) { - dir = FS.cwd(); - } else { - var dirstream = FS.getStream(dirfd); - if (!dirstream) throw new FS.ErrnoError(8); - dir = dirstream.path; - } - path = PATH.join2(dir, path); - } - return path; - },doStat:function(func, path, buf) { - try { - var stat = func(path); - } catch (e) { - if (e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) { - // an error occurred while trying to look up the path; we should just report ENOTDIR - return -54; - } - throw e; - } - HEAP32[((buf)>>2)]=stat.dev; - HEAP32[(((buf)+(4))>>2)]=0; - HEAP32[(((buf)+(8))>>2)]=stat.ino; - HEAP32[(((buf)+(12))>>2)]=stat.mode; - HEAP32[(((buf)+(16))>>2)]=stat.nlink; - HEAP32[(((buf)+(20))>>2)]=stat.uid; - HEAP32[(((buf)+(24))>>2)]=stat.gid; - HEAP32[(((buf)+(28))>>2)]=stat.rdev; - HEAP32[(((buf)+(32))>>2)]=0; - (tempI64 = [stat.size>>>0,(tempDouble=stat.size,(+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[(((buf)+(40))>>2)]=tempI64[0],HEAP32[(((buf)+(44))>>2)]=tempI64[1]); - HEAP32[(((buf)+(48))>>2)]=4096; - HEAP32[(((buf)+(52))>>2)]=stat.blocks; - HEAP32[(((buf)+(56))>>2)]=(stat.atime.getTime() / 1000)|0; - HEAP32[(((buf)+(60))>>2)]=0; - HEAP32[(((buf)+(64))>>2)]=(stat.mtime.getTime() / 1000)|0; - HEAP32[(((buf)+(68))>>2)]=0; - HEAP32[(((buf)+(72))>>2)]=(stat.ctime.getTime() / 1000)|0; - HEAP32[(((buf)+(76))>>2)]=0; - (tempI64 = [stat.ino>>>0,(tempDouble=stat.ino,(+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[(((buf)+(80))>>2)]=tempI64[0],HEAP32[(((buf)+(84))>>2)]=tempI64[1]); - return 0; - },doMsync:function(addr, stream, len, flags, offset) { - var buffer = HEAPU8.slice(addr, addr + len); - FS.msync(stream, buffer, offset, len, flags); - },doMkdir:function(path, mode) { - // remove a trailing slash, if one - /a/b/ has basename of '', but - // we want to create b in the context of this function - path = PATH.normalize(path); - if (path[path.length-1] === '/') path = path.substr(0, path.length-1); - FS.mkdir(path, mode, 0); - return 0; - },doMknod:function(path, mode, dev) { - // we don't want this in the JS API as it uses mknod to create all nodes. - switch (mode & 61440) { - case 32768: - case 8192: - case 24576: - case 4096: - case 49152: - break; - default: return -28; - } - FS.mknod(path, mode, dev); - return 0; - },doReadlink:function(path, buf, bufsize) { - if (bufsize <= 0) return -28; - var ret = FS.readlink(path); - - var len = Math.min(bufsize, lengthBytesUTF8(ret)); - var endChar = HEAP8[buf+len]; - stringToUTF8(ret, buf, bufsize+1); - // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!) - // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write. - HEAP8[buf+len] = endChar; - - return len; - },doAccess:function(path, amode) { - if (amode & ~7) { - // need a valid mode - return -28; - } - var node; - var lookup = FS.lookupPath(path, { follow: true }); - node = lookup.node; - if (!node) { - return -44; - } - var perms = ''; - if (amode & 4) perms += 'r'; - if (amode & 2) perms += 'w'; - if (amode & 1) perms += 'x'; - if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) { - return -2; - } - return 0; - },doDup:function(path, flags, suggestFD) { - var suggest = FS.getStream(suggestFD); - if (suggest) FS.close(suggest); - return FS.open(path, flags, 0, suggestFD, suggestFD).fd; - },doReadv:function(stream, iov, iovcnt, offset) { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAP32[(((iov)+(i*8))>>2)]; - var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; - var curr = FS.read(stream, HEAP8,ptr, len, offset); - if (curr < 0) return -1; - ret += curr; - if (curr < len) break; // nothing more to read - } - return ret; - },doWritev:function(stream, iov, iovcnt, offset) { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAP32[(((iov)+(i*8))>>2)]; - var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; - var curr = FS.write(stream, HEAP8,ptr, len, offset); - if (curr < 0) return -1; - ret += curr; - } - return ret; - },varargs:undefined,get:function() { - assert(SYSCALLS.varargs != undefined); - SYSCALLS.varargs += 4; - var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; - return ret; - },getStr:function(ptr) { - var ret = UTF8ToString(ptr); - return ret; - },getStreamFromFD:function(fd) { - var stream = FS.getStream(fd); - if (!stream) throw new FS.ErrnoError(8); - return stream; - },get64:function(low, high) { - if (low >= 0) assert(high === 0); - else assert(high === -1); - return low; - }};function ___sys_fcntl64(fd, cmd, varargs) {SYSCALLS.varargs = varargs; - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - switch (cmd) { - case 0: { - var arg = SYSCALLS.get(); - if (arg < 0) { - return -28; - } - var newStream; - newStream = FS.open(stream.path, stream.flags, 0, arg); - return newStream.fd; - } - case 1: - case 2: - return 0; // FD_CLOEXEC makes no sense for a single process. - case 3: - return stream.flags; - case 4: { - var arg = SYSCALLS.get(); - stream.flags |= arg; - return 0; - } - case 12: - /* case 12: Currently in musl F_GETLK64 has same value as F_GETLK, so omitted to avoid duplicate case blocks. If that changes, uncomment this */ { - - var arg = SYSCALLS.get(); - var offset = 0; - // We're always unlocked. - HEAP16[(((arg)+(offset))>>1)]=2; - return 0; - } - case 13: - case 14: - /* case 13: Currently in musl F_SETLK64 has same value as F_SETLK, so omitted to avoid duplicate case blocks. If that changes, uncomment this */ - /* case 14: Currently in musl F_SETLKW64 has same value as F_SETLKW, so omitted to avoid duplicate case blocks. If that changes, uncomment this */ - - - return 0; // Pretend that the locking is successful. - case 16: - case 8: - return -28; // These are for sockets. We don't have them fully implemented yet. - case 9: - // musl trusts getown return values, due to a bug where they must be, as they overlap with errors. just return -1 here, so fnctl() returns that, and we set errno ourselves. - setErrNo(28); - return -1; - default: { - return -28; - } - } - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return -e.errno; - } - } - - function ___sys_ioctl(fd, op, varargs) {SYSCALLS.varargs = varargs; - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - switch (op) { - case 21509: - case 21505: { - if (!stream.tty) return -59; - return 0; - } - case 21510: - case 21511: - case 21512: - case 21506: - case 21507: - case 21508: { - if (!stream.tty) return -59; - return 0; // no-op, not actually adjusting terminal settings - } - case 21519: { - if (!stream.tty) return -59; - var argp = SYSCALLS.get(); - HEAP32[((argp)>>2)]=0; - return 0; - } - case 21520: { - if (!stream.tty) return -59; - return -28; // not supported - } - case 21531: { - var argp = SYSCALLS.get(); - return FS.ioctl(stream, op, argp); - } - case 21523: { - // TODO: in theory we should write to the winsize struct that gets - // passed in, but for now musl doesn't read anything on it - if (!stream.tty) return -59; - return 0; - } - case 21524: { - // TODO: technically, this ioctl call should change the window size. - // but, since emscripten doesn't have any concept of a terminal window - // yet, we'll just silently throw it away as we do TIOCGWINSZ - if (!stream.tty) return -59; - return 0; - } - default: abort('bad ioctl syscall ' + op); - } - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return -e.errno; - } - } - - - function syscallMunmap(addr, len) { - if ((addr | 0) === -1 || len === 0) { - return -28; - } - // TODO: support unmmap'ing parts of allocations - var info = SYSCALLS.mappings[addr]; - if (!info) return 0; - if (len === info.len) { - var stream = FS.getStream(info.fd); - if (info.prot & 2) { - SYSCALLS.doMsync(addr, stream, len, info.flags, info.offset); - } - FS.munmap(stream); - SYSCALLS.mappings[addr] = null; - if (info.allocated) { - _free(info.malloc); - } - } - return 0; - }function ___sys_munmap(addr, len) {try { - - return syscallMunmap(addr, len); - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return -e.errno; - } - } - - function ___sys_open(path, flags, varargs) {SYSCALLS.varargs = varargs; - try { - - var pathname = SYSCALLS.getStr(path); - var mode = SYSCALLS.get(); - var stream = FS.open(pathname, flags, mode); - return stream.fd; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return -e.errno; - } - } - - function __emscripten_fetch_free(id) { - //Note: should just be [id], but indexes off by 1 (see: #8803) - delete Fetch.xhrs[id-1]; - } - - function _abort() { - abort(); - } - - function _emscripten_get_sbrk_ptr() { - return 102080; - } - - function _emscripten_is_main_browser_thread() { - return !ENVIRONMENT_IS_WORKER; - } - - function _emscripten_memcpy_big(dest, src, num) { - HEAPU8.copyWithin(dest, src, src + num); - } - - - function _emscripten_get_heap_size() { - return HEAPU8.length; - } - - function emscripten_realloc_buffer(size) { - try { - // round size grow request up to wasm page size (fixed 64KB per spec) - wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16); // .grow() takes a delta compared to the previous size - updateGlobalBufferAndViews(wasmMemory.buffer); - return 1 /*success*/; - } catch(e) { - console.error('emscripten_realloc_buffer: Attempted to grow heap from ' + buffer.byteLength + ' bytes to ' + size + ' bytes, but got error: ' + e); - } - }function _emscripten_resize_heap(requestedSize) { - requestedSize = requestedSize >>> 0; - var oldSize = _emscripten_get_heap_size(); - // With pthreads, races can happen (another thread might increase the size in between), so return a failure, and let the caller retry. - assert(requestedSize > oldSize); - - - var PAGE_MULTIPLE = 65536; - - // Memory resize rules: - // 1. When resizing, always produce a resized heap that is at least 16MB (to avoid tiny heap sizes receiving lots of repeated resizes at startup) - // 2. Always increase heap size to at least the requested size, rounded up to next page multiple. - // 3a. If MEMORY_GROWTH_LINEAR_STEP == -1, excessively resize the heap geometrically: increase the heap size according to - // MEMORY_GROWTH_GEOMETRIC_STEP factor (default +20%), - // At most overreserve by MEMORY_GROWTH_GEOMETRIC_CAP bytes (default 96MB). - // 3b. If MEMORY_GROWTH_LINEAR_STEP != -1, excessively resize the heap linearly: increase the heap size by at least MEMORY_GROWTH_LINEAR_STEP bytes. - // 4. Max size for the heap is capped at 2048MB-PAGE_MULTIPLE, or by MAXIMUM_MEMORY, or by ASAN limit, depending on which is smallest - // 5. If we were unable to allocate as much memory, it may be due to over-eager decision to excessively reserve due to (3) above. - // Hence if an allocation fails, cut down on the amount of excess growth, in an attempt to succeed to perform a smaller allocation. - - // A limit was set for how much we can grow. We should not exceed that - // (the wasm binary specifies it, so if we tried, we'd fail anyhow). - var maxHeapSize = 2147483648; - if (requestedSize > maxHeapSize) { - err('Cannot enlarge memory, asked to go up to ' + requestedSize + ' bytes, but the limit is ' + maxHeapSize + ' bytes!'); - return false; - } - - var minHeapSize = 16777216; - - // Loop through potential heap size increases. If we attempt a too eager reservation that fails, cut down on the - // attempted size and reserve a smaller bump instead. (max 3 times, chosen somewhat arbitrarily) - for(var cutDown = 1; cutDown <= 4; cutDown *= 2) { - var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown); // ensure geometric growth - // but limit overreserving (default to capping at +96MB overgrowth at most) - overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296 ); - - - var newSize = Math.min(maxHeapSize, alignUp(Math.max(minHeapSize, requestedSize, overGrownHeapSize), PAGE_MULTIPLE)); - - var replacement = emscripten_realloc_buffer(newSize); - if (replacement) { - - return true; - } - } - err('Failed to grow the heap from ' + oldSize + ' bytes to ' + newSize + ' bytes, not enough memory!'); - return false; - } - - function _emscripten_run_script(ptr) { - eval(UTF8ToString(ptr)); - } - - - var Fetch={xhrs:[],setu64:function(addr, val) { - HEAPU32[addr >> 2] = val; - HEAPU32[addr + 4 >> 2] = (val / 4294967296)|0; - },openDatabase:function(dbname, dbversion, onsuccess, onerror) { - try { - var openRequest = indexedDB.open(dbname, dbversion); - } catch (e) { return onerror(e); } - - openRequest.onupgradeneeded = function(event) { - var db = event.target.result; - if (db.objectStoreNames.contains('FILES')) { - db.deleteObjectStore('FILES'); - } - db.createObjectStore('FILES'); - }; - openRequest.onsuccess = function(event) { onsuccess(event.target.result); }; - openRequest.onerror = function(error) { onerror(error); }; - },staticInit:function() { - var isMainThread = true; - - var onsuccess = function(db) { - Fetch.dbInstance = db; - - if (isMainThread) { - removeRunDependency('library_fetch_init'); - } - }; - var onerror = function() { - Fetch.dbInstance = false; - - if (isMainThread) { - removeRunDependency('library_fetch_init'); - } - }; - Fetch.openDatabase('emscripten_filesystem', 1, onsuccess, onerror); - - if (typeof ENVIRONMENT_IS_FETCH_WORKER === 'undefined' || !ENVIRONMENT_IS_FETCH_WORKER) addRunDependency('library_fetch_init'); - }}; - - function __emscripten_fetch_xhr(fetch, onsuccess, onerror, onprogress, onreadystatechange) { - var url = HEAPU32[fetch + 8 >> 2]; - if (!url) { - onerror(fetch, 0, 'no url specified!'); - return; - } - var url_ = UTF8ToString(url); - - var fetch_attr = fetch + 112; - var requestMethod = UTF8ToString(fetch_attr); - if (!requestMethod) requestMethod = 'GET'; - var userData = HEAPU32[fetch + 4 >> 2]; - var fetchAttributes = HEAPU32[fetch_attr + 52 >> 2]; - var timeoutMsecs = HEAPU32[fetch_attr + 56 >> 2]; - var withCredentials = !!HEAPU32[fetch_attr + 60 >> 2]; - var destinationPath = HEAPU32[fetch_attr + 64 >> 2]; - var userName = HEAPU32[fetch_attr + 68 >> 2]; - var password = HEAPU32[fetch_attr + 72 >> 2]; - var requestHeaders = HEAPU32[fetch_attr + 76 >> 2]; - var overriddenMimeType = HEAPU32[fetch_attr + 80 >> 2]; - var dataPtr = HEAPU32[fetch_attr + 84 >> 2]; - var dataLength = HEAPU32[fetch_attr + 88 >> 2]; - - var fetchAttrLoadToMemory = !!(fetchAttributes & 1); - var fetchAttrStreamData = !!(fetchAttributes & 2); - var fetchAttrPersistFile = !!(fetchAttributes & 4); - var fetchAttrAppend = !!(fetchAttributes & 8); - var fetchAttrReplace = !!(fetchAttributes & 16); - var fetchAttrSynchronous = !!(fetchAttributes & 64); - var fetchAttrWaitable = !!(fetchAttributes & 128); - - var userNameStr = userName ? UTF8ToString(userName) : undefined; - var passwordStr = password ? UTF8ToString(password) : undefined; - var overriddenMimeTypeStr = overriddenMimeType ? UTF8ToString(overriddenMimeType) : undefined; - - var xhr = new XMLHttpRequest(); - xhr.withCredentials = withCredentials; - xhr.open(requestMethod, url_, !fetchAttrSynchronous, userNameStr, passwordStr); - if (!fetchAttrSynchronous) xhr.timeout = timeoutMsecs; // XHR timeout field is only accessible in async XHRs, and must be set after .open() but before .send(). - xhr.url_ = url_; // Save the url for debugging purposes (and for comparing to the responseURL that server side advertised) - assert(!fetchAttrStreamData, 'streaming uses moz-chunked-arraybuffer which is no longer supported; TODO: rewrite using fetch()'); - xhr.responseType = 'arraybuffer'; - - if (overriddenMimeType) { - xhr.overrideMimeType(overriddenMimeTypeStr); - } - if (requestHeaders) { - for(;;) { - var key = HEAPU32[requestHeaders >> 2]; - if (!key) break; - var value = HEAPU32[requestHeaders + 4 >> 2]; - if (!value) break; - requestHeaders += 8; - var keyStr = UTF8ToString(key); - var valueStr = UTF8ToString(value); - xhr.setRequestHeader(keyStr, valueStr); - } - } - Fetch.xhrs.push(xhr); - var id = Fetch.xhrs.length; - HEAPU32[fetch + 0 >> 2] = id; - var data = (dataPtr && dataLength) ? HEAPU8.slice(dataPtr, dataPtr + dataLength) : null; - // TODO: Support specifying custom headers to the request. - - // Share the code to save the response, as we need to do so both on success - // and on error (despite an error, there may be a response, like a 404 page). - // This receives a condition, which determines whether to save the xhr's - // response, or just 0. - function saveResponse(condition) { - var ptr = 0; - var ptrLen = 0; - if (condition) { - ptrLen = xhr.response ? xhr.response.byteLength : 0; - // The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is - // freed when emscripten_fetch_close() is called. - ptr = _malloc(ptrLen); - HEAPU8.set(new Uint8Array(xhr.response), ptr); - } - HEAPU32[fetch + 12 >> 2] = ptr; - Fetch.setu64(fetch + 16, ptrLen); - } - - xhr.onload = function(e) { - saveResponse(fetchAttrLoadToMemory && !fetchAttrStreamData); - var len = xhr.response ? xhr.response.byteLength : 0; - Fetch.setu64(fetch + 24, 0); - if (len) { - // If the final XHR.onload handler receives the bytedata to compute total length, report that, - // otherwise don't write anything out here, which will retain the latest byte size reported in - // the most recent XHR.onprogress handler. - Fetch.setu64(fetch + 32, len); - } - HEAPU16[fetch + 40 >> 1] = xhr.readyState; - if (xhr.readyState === 4 && xhr.status === 0) { - if (len > 0) xhr.status = 200; // If loading files from a source that does not give HTTP status code, assume success if we got data bytes. - else xhr.status = 404; // Conversely, no data bytes is 404. - } - HEAPU16[fetch + 42 >> 1] = xhr.status; - if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + 44, 64); - if (xhr.status >= 200 && xhr.status < 300) { - if (onsuccess) onsuccess(fetch, xhr, e); - } else { - if (onerror) onerror(fetch, xhr, e); - } - }; - xhr.onerror = function(e) { - saveResponse(fetchAttrLoadToMemory); - var status = xhr.status; // XXX TODO: Overwriting xhr.status doesn't work here, so don't override anywhere else either. - if (xhr.readyState === 4 && status === 0) status = 404; // If no error recorded, pretend it was 404 Not Found. - Fetch.setu64(fetch + 24, 0); - Fetch.setu64(fetch + 32, xhr.response ? xhr.response.byteLength : 0); - HEAPU16[fetch + 40 >> 1] = xhr.readyState; - HEAPU16[fetch + 42 >> 1] = status; - if (onerror) onerror(fetch, xhr, e); - }; - xhr.ontimeout = function(e) { - if (onerror) onerror(fetch, xhr, e); - }; - xhr.onprogress = function(e) { - var ptrLen = (fetchAttrLoadToMemory && fetchAttrStreamData && xhr.response) ? xhr.response.byteLength : 0; - var ptr = 0; - if (fetchAttrLoadToMemory && fetchAttrStreamData) { - assert(onprogress, 'When doing a streaming fetch, you should have an onprogress handler registered to receive the chunks!'); - // Allocate byte data in Emscripten heap for the streamed memory block (freed immediately after onprogress call) - ptr = _malloc(ptrLen); - HEAPU8.set(new Uint8Array(xhr.response), ptr); - } - HEAPU32[fetch + 12 >> 2] = ptr; - Fetch.setu64(fetch + 16, ptrLen); - Fetch.setu64(fetch + 24, e.loaded - ptrLen); - Fetch.setu64(fetch + 32, e.total); - HEAPU16[fetch + 40 >> 1] = xhr.readyState; - if (xhr.readyState >= 3 && xhr.status === 0 && e.loaded > 0) xhr.status = 200; // If loading files from a source that does not give HTTP status code, assume success if we get data bytes - HEAPU16[fetch + 42 >> 1] = xhr.status; - if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + 44, 64); - if (onprogress) onprogress(fetch, xhr, e); - if (ptr) { - _free(ptr); - } - }; - xhr.onreadystatechange = function(e) { - HEAPU16[fetch + 40 >> 1] = xhr.readyState; - if (xhr.readyState >= 2) { - HEAPU16[fetch + 42 >> 1] = xhr.status; - } - if (onreadystatechange) onreadystatechange(fetch, xhr, e); - }; - try { - xhr.send(data); - } catch(e) { - if (onerror) onerror(fetch, xhr, e); - } - } - - function __emscripten_fetch_cache_data(db, fetch, data, onsuccess, onerror) { - if (!db) { - onerror(fetch, 0, 'IndexedDB not available!'); - return; - } - - var fetch_attr = fetch + 112; - var destinationPath = HEAPU32[fetch_attr + 64 >> 2]; - if (!destinationPath) destinationPath = HEAPU32[fetch + 8 >> 2]; - var destinationPathStr = UTF8ToString(destinationPath); - - try { - var transaction = db.transaction(['FILES'], 'readwrite'); - var packages = transaction.objectStore('FILES'); - var putRequest = packages.put(data, destinationPathStr); - putRequest.onsuccess = function(event) { - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 200; // Mimic XHR HTTP status code 200 "OK" - stringToUTF8("OK", fetch + 44, 64); - onsuccess(fetch, 0, destinationPathStr); - }; - putRequest.onerror = function(error) { - // Most likely we got an error if IndexedDB is unwilling to store any more data for this page. - // TODO: Can we identify and break down different IndexedDB-provided errors and convert those - // to more HTTP status codes for more information? - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 413; // Mimic XHR HTTP status code 413 "Payload Too Large" - stringToUTF8("Payload Too Large", fetch + 44, 64); - onerror(fetch, 0, error); - }; - } catch(e) { - onerror(fetch, 0, e); - } - } - - function __emscripten_fetch_load_cached_data(db, fetch, onsuccess, onerror) { - if (!db) { - onerror(fetch, 0, 'IndexedDB not available!'); - return; - } - - var fetch_attr = fetch + 112; - var path = HEAPU32[fetch_attr + 64 >> 2]; - if (!path) path = HEAPU32[fetch + 8 >> 2]; - var pathStr = UTF8ToString(path); - - try { - var transaction = db.transaction(['FILES'], 'readonly'); - var packages = transaction.objectStore('FILES'); - var getRequest = packages.get(pathStr); - getRequest.onsuccess = function(event) { - if (event.target.result) { - var value = event.target.result; - var len = value.byteLength || value.length; - // The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is - // freed when emscripten_fetch_close() is called. - var ptr = _malloc(len); - HEAPU8.set(new Uint8Array(value), ptr); - HEAPU32[fetch + 12 >> 2] = ptr; - Fetch.setu64(fetch + 16, len); - Fetch.setu64(fetch + 24, 0); - Fetch.setu64(fetch + 32, len); - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 200; // Mimic XHR HTTP status code 200 "OK" - stringToUTF8("OK", fetch + 44, 64); - onsuccess(fetch, 0, value); - } else { - // Succeeded to load, but the load came back with the value of undefined, treat that as an error since we never store undefined in db. - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" - stringToUTF8("Not Found", fetch + 44, 64); - onerror(fetch, 0, 'no data'); - } - }; - getRequest.onerror = function(error) { - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" - stringToUTF8("Not Found", fetch + 44, 64); - onerror(fetch, 0, error); - }; - } catch(e) { - onerror(fetch, 0, e); - } - } - - function __emscripten_fetch_delete_cached_data(db, fetch, onsuccess, onerror) { - if (!db) { - onerror(fetch, 0, 'IndexedDB not available!'); - return; - } - - var fetch_attr = fetch + 112; - var path = HEAPU32[fetch_attr + 64 >> 2]; - if (!path) path = HEAPU32[fetch + 8 >> 2]; - var pathStr = UTF8ToString(path); - - try { - var transaction = db.transaction(['FILES'], 'readwrite'); - var packages = transaction.objectStore('FILES'); - var request = packages.delete(pathStr); - request.onsuccess = function(event) { - var value = event.target.result; - HEAPU32[fetch + 12 >> 2] = 0; - Fetch.setu64(fetch + 16, 0); - Fetch.setu64(fetch + 24, 0); - Fetch.setu64(fetch + 32, 0); - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 200; // Mimic XHR HTTP status code 200 "OK" - stringToUTF8("OK", fetch + 44, 64); - onsuccess(fetch, 0, value); - }; - request.onerror = function(error) { - HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' - HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" - stringToUTF8("Not Found", fetch + 44, 64); - onerror(fetch, 0, error); - }; - } catch(e) { - onerror(fetch, 0, e); - } - } - - - var _fetch_work_queue=102256;function __emscripten_get_fetch_work_queue() { - return _fetch_work_queue; - }function _emscripten_start_fetch(fetch, successcb, errorcb, progresscb, readystatechangecb) { - if (typeof noExitRuntime !== 'undefined') noExitRuntime = true; // If we are the main Emscripten runtime, we should not be closing down. - - var fetch_attr = fetch + 112; - var requestMethod = UTF8ToString(fetch_attr); - var onsuccess = HEAPU32[fetch_attr + 36 >> 2]; - var onerror = HEAPU32[fetch_attr + 40 >> 2]; - var onprogress = HEAPU32[fetch_attr + 44 >> 2]; - var onreadystatechange = HEAPU32[fetch_attr + 48 >> 2]; - var fetchAttributes = HEAPU32[fetch_attr + 52 >> 2]; - var fetchAttrLoadToMemory = !!(fetchAttributes & 1); - var fetchAttrStreamData = !!(fetchAttributes & 2); - var fetchAttrPersistFile = !!(fetchAttributes & 4); - var fetchAttrNoDownload = !!(fetchAttributes & 32); - var fetchAttrAppend = !!(fetchAttributes & 8); - var fetchAttrReplace = !!(fetchAttributes & 16); - - var reportSuccess = function(fetch, xhr, e) { - if (onsuccess) dynCall_vi(onsuccess, fetch); - else if (successcb) successcb(fetch); - }; - - var reportProgress = function(fetch, xhr, e) { - if (onprogress) dynCall_vi(onprogress, fetch); - else if (progresscb) progresscb(fetch); - }; - - var reportError = function(fetch, xhr, e) { - if (onerror) dynCall_vi(onerror, fetch); - else if (errorcb) errorcb(fetch); - }; - - var reportReadyStateChange = function(fetch, xhr, e) { - if (onreadystatechange) dynCall_vi(onreadystatechange, fetch); - else if (readystatechangecb) readystatechangecb(fetch); - }; - - var performUncachedXhr = function(fetch, xhr, e) { - __emscripten_fetch_xhr(fetch, reportSuccess, reportError, reportProgress, reportReadyStateChange); - }; - - var cacheResultAndReportSuccess = function(fetch, xhr, e) { - var storeSuccess = function(fetch, xhr, e) { - if (onsuccess) dynCall_vi(onsuccess, fetch); - else if (successcb) successcb(fetch); - }; - var storeError = function(fetch, xhr, e) { - if (onsuccess) dynCall_vi(onsuccess, fetch); - else if (successcb) successcb(fetch); - }; - __emscripten_fetch_cache_data(Fetch.dbInstance, fetch, xhr.response, storeSuccess, storeError); - }; - - var performCachedXhr = function(fetch, xhr, e) { - __emscripten_fetch_xhr(fetch, cacheResultAndReportSuccess, reportError, reportProgress, reportReadyStateChange); - }; - - if (requestMethod === 'EM_IDB_STORE') { - // TODO(?): Here we perform a clone of the data, because storing shared typed arrays to IndexedDB does not seem to be allowed. - var ptr = HEAPU32[fetch_attr + 84 >> 2]; - __emscripten_fetch_cache_data(Fetch.dbInstance, fetch, HEAPU8.slice(ptr, ptr + HEAPU32[fetch_attr + 88 >> 2]), reportSuccess, reportError); - } else if (requestMethod === 'EM_IDB_DELETE') { - __emscripten_fetch_delete_cached_data(Fetch.dbInstance, fetch, reportSuccess, reportError); - } else if (!fetchAttrReplace) { - __emscripten_fetch_load_cached_data(Fetch.dbInstance, fetch, reportSuccess, fetchAttrNoDownload ? reportError : (fetchAttrPersistFile ? performCachedXhr : performUncachedXhr)); - } else if (!fetchAttrNoDownload) { - __emscripten_fetch_xhr(fetch, fetchAttrPersistFile ? cacheResultAndReportSuccess : reportSuccess, reportError, reportProgress, reportReadyStateChange); - } else { - return 0; // todo: free - } - return fetch; - } - - - - - function __webgl_enable_OES_vertex_array_object(ctx) { - // Extension available in WebGL 1 from Firefox 25 and WebKit 536.28/desktop Safari 6.0.3 onwards. Core feature in WebGL 2. - var ext = ctx.getExtension('OES_vertex_array_object'); - if (ext) { - ctx['createVertexArray'] = function() { return ext['createVertexArrayOES'](); }; - ctx['deleteVertexArray'] = function(vao) { ext['deleteVertexArrayOES'](vao); }; - ctx['bindVertexArray'] = function(vao) { ext['bindVertexArrayOES'](vao); }; - ctx['isVertexArray'] = function(vao) { return ext['isVertexArrayOES'](vao); }; - return 1; - } - } - - function __webgl_enable_WEBGL_draw_buffers(ctx) { - // Extension available in WebGL 1 from Firefox 28 onwards. Core feature in WebGL 2. - var ext = ctx.getExtension('WEBGL_draw_buffers'); - if (ext) { - ctx['drawBuffers'] = function(n, bufs) { ext['drawBuffersWEBGL'](n, bufs); }; - return 1; - } - } - - function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx) { - // Closure is expected to be allowed to minify the '.dibvbi' property, so not accessing it quoted. - return !!(ctx.dibvbi = ctx.getExtension('WEBGL_draw_instanced_base_vertex_base_instance')); - } - - function __webgl_enable_WEBGL_multi_draw(ctx) { - // Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted. - return !!(ctx.multiDrawWebgl = ctx.getExtension('WEBGL_multi_draw')); - }var GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},timerQueriesEXT:[],queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],programInfos:{},stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode) { - if (!GL.lastError) { - GL.lastError = errorCode; - } - },getNewId:function(table) { - var ret = GL.counter++; - for (var i = table.length; i < ret; i++) { - table[i] = null; - } - return ret; - },MAX_TEMP_BUFFER_SIZE:2097152,numTempVertexBuffersPerSize:64,log2ceilLookup:function(i) { - return 32 - Math.clz32(i-1); - },generateTempBuffers:function(quads, context) { - var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE); - context.tempVertexBufferCounters1 = []; - context.tempVertexBufferCounters2 = []; - context.tempVertexBufferCounters1.length = context.tempVertexBufferCounters2.length = largestIndex+1; - context.tempVertexBuffers1 = []; - context.tempVertexBuffers2 = []; - context.tempVertexBuffers1.length = context.tempVertexBuffers2.length = largestIndex+1; - context.tempIndexBuffers = []; - context.tempIndexBuffers.length = largestIndex+1; - for (var i = 0; i <= largestIndex; ++i) { - context.tempIndexBuffers[i] = null; // Created on-demand - context.tempVertexBufferCounters1[i] = context.tempVertexBufferCounters2[i] = 0; - var ringbufferLength = GL.numTempVertexBuffersPerSize; - context.tempVertexBuffers1[i] = []; - context.tempVertexBuffers2[i] = []; - var ringbuffer1 = context.tempVertexBuffers1[i]; - var ringbuffer2 = context.tempVertexBuffers2[i]; - ringbuffer1.length = ringbuffer2.length = ringbufferLength; - for (var j = 0; j < ringbufferLength; ++j) { - ringbuffer1[j] = ringbuffer2[j] = null; // Created on-demand - } - } - - if (quads) { - // GL_QUAD indexes can be precalculated - context.tempQuadIndexBuffer = GLctx.createBuffer(); - context.GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, context.tempQuadIndexBuffer); - var numIndexes = GL.MAX_TEMP_BUFFER_SIZE >> 1; - var quadIndexes = new Uint16Array(numIndexes); - var i = 0, v = 0; - while (1) { - quadIndexes[i++] = v; - if (i >= numIndexes) break; - quadIndexes[i++] = v+1; - if (i >= numIndexes) break; - quadIndexes[i++] = v+2; - if (i >= numIndexes) break; - quadIndexes[i++] = v; - if (i >= numIndexes) break; - quadIndexes[i++] = v+2; - if (i >= numIndexes) break; - quadIndexes[i++] = v+3; - if (i >= numIndexes) break; - v += 4; - } - context.GLctx.bufferData(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, quadIndexes, 0x88E4 /*GL_STATIC_DRAW*/); - context.GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, null); - } - },getTempVertexBuffer:function getTempVertexBuffer(sizeBytes) { - var idx = GL.log2ceilLookup(sizeBytes); - var ringbuffer = GL.currentContext.tempVertexBuffers1[idx]; - var nextFreeBufferIndex = GL.currentContext.tempVertexBufferCounters1[idx]; - GL.currentContext.tempVertexBufferCounters1[idx] = (GL.currentContext.tempVertexBufferCounters1[idx]+1) & (GL.numTempVertexBuffersPerSize-1); - var vbo = ringbuffer[nextFreeBufferIndex]; - if (vbo) { - return vbo; - } - var prevVBO = GLctx.getParameter(0x8894 /*GL_ARRAY_BUFFER_BINDING*/); - ringbuffer[nextFreeBufferIndex] = GLctx.createBuffer(); - GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, ringbuffer[nextFreeBufferIndex]); - GLctx.bufferData(0x8892 /*GL_ARRAY_BUFFER*/, 1 << idx, 0x88E8 /*GL_DYNAMIC_DRAW*/); - GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, prevVBO); - return ringbuffer[nextFreeBufferIndex]; - },getTempIndexBuffer:function getTempIndexBuffer(sizeBytes) { - var idx = GL.log2ceilLookup(sizeBytes); - var ibo = GL.currentContext.tempIndexBuffers[idx]; - if (ibo) { - return ibo; - } - var prevIBO = GLctx.getParameter(0x8895 /*ELEMENT_ARRAY_BUFFER_BINDING*/); - GL.currentContext.tempIndexBuffers[idx] = GLctx.createBuffer(); - GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, GL.currentContext.tempIndexBuffers[idx]); - GLctx.bufferData(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, 1 << idx, 0x88E8 /*GL_DYNAMIC_DRAW*/); - GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, prevIBO); - return GL.currentContext.tempIndexBuffers[idx]; - },newRenderingFrameStarted:function newRenderingFrameStarted() { - if (!GL.currentContext) { - return; - } - var vb = GL.currentContext.tempVertexBuffers1; - GL.currentContext.tempVertexBuffers1 = GL.currentContext.tempVertexBuffers2; - GL.currentContext.tempVertexBuffers2 = vb; - vb = GL.currentContext.tempVertexBufferCounters1; - GL.currentContext.tempVertexBufferCounters1 = GL.currentContext.tempVertexBufferCounters2; - GL.currentContext.tempVertexBufferCounters2 = vb; - var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE); - for (var i = 0; i <= largestIndex; ++i) { - GL.currentContext.tempVertexBufferCounters1[i] = 0; - } - },getSource:function(shader, count, string, length) { - var source = ''; - for (var i = 0; i < count; ++i) { - var len = length ? HEAP32[(((length)+(i*4))>>2)] : -1; - source += UTF8ToString(HEAP32[(((string)+(i*4))>>2)], len < 0 ? undefined : len); - } - return source; - },calcBufLength:function calcBufLength(size, type, stride, count) { - if (stride > 0) { - return count * stride; // XXXvlad this is not exactly correct I don't think - } - var typeSize = GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; - return size * typeSize * count; - },usedTempBuffers:[],preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count) { - GL.resetBufferBinding = false; - - // TODO: initial pass to detect ranges we need to upload, might not need an upload per attrib - for (var i = 0; i < GL.currentContext.maxVertexAttribs; ++i) { - var cb = GL.currentContext.clientBuffers[i]; - if (!cb.clientside || !cb.enabled) continue; - - GL.resetBufferBinding = true; - - var size = GL.calcBufLength(cb.size, cb.type, cb.stride, count); - var buf = GL.getTempVertexBuffer(size); - GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, buf); - GLctx.bufferSubData(0x8892 /*GL_ARRAY_BUFFER*/, - 0, - HEAPU8.subarray(cb.ptr, cb.ptr + size)); - cb.vertexAttribPointerAdaptor.call(GLctx, i, cb.size, cb.type, cb.normalized, cb.stride, 0); - } - },postDrawHandleClientVertexAttribBindings:function postDrawHandleClientVertexAttribBindings() { - if (GL.resetBufferBinding) { - GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, GL.buffers[GLctx.currentArrayBufferBinding]); - } - },createContext:function(canvas, webGLContextAttributes) { - - - - - - var ctx = - (webGLContextAttributes.majorVersion > 1) - ? - canvas.getContext("webgl2", webGLContextAttributes) - : - (canvas.getContext("webgl", webGLContextAttributes) - // https://caniuse.com/#feat=webgl - ); - - - if (!ctx) return 0; - - var handle = GL.registerContext(ctx, webGLContextAttributes); - - - - return handle; - },registerContext:function(ctx, webGLContextAttributes) { - // without pthreads a context is just an integer ID - var handle = GL.getNewId(GL.contexts); - - var context = { - handle: handle, - attributes: webGLContextAttributes, - version: webGLContextAttributes.majorVersion, - GLctx: ctx - }; - - - // Store the created context object so that we can access the context given a canvas without having to pass the parameters again. - if (ctx.canvas) ctx.canvas.GLctxObject = context; - GL.contexts[handle] = context; - if (typeof webGLContextAttributes.enableExtensionsByDefault === 'undefined' || webGLContextAttributes.enableExtensionsByDefault) { - GL.initExtensions(context); - } - - context.maxVertexAttribs = context.GLctx.getParameter(0x8869 /*GL_MAX_VERTEX_ATTRIBS*/); - context.clientBuffers = []; - for (var i = 0; i < context.maxVertexAttribs; i++) { - context.clientBuffers[i] = { enabled: false, clientside: false, size: 0, type: 0, normalized: 0, stride: 0, ptr: 0, vertexAttribPointerAdaptor: null }; - } - - GL.generateTempBuffers(false, context); - - - - return handle; - },makeContextCurrent:function(contextHandle) { - - GL.currentContext = GL.contexts[contextHandle]; // Active Emscripten GL layer context object. - Module.ctx = GLctx = GL.currentContext && GL.currentContext.GLctx; // Active WebGL context object. - return !(contextHandle && !GLctx); - },getContext:function(contextHandle) { - return GL.contexts[contextHandle]; - },deleteContext:function(contextHandle) { - if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null; - if (typeof JSEvents === 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted. - if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises. - GL.contexts[contextHandle] = null; - },initExtensions:function(context) { - // If this function is called without a specific context object, init the extensions of the currently active context. - if (!context) context = GL.currentContext; - - if (context.initExtensionsDone) return; - context.initExtensionsDone = true; - - var GLctx = context.GLctx; - - // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist. - - // Extensions that are only available in WebGL 1 (the calls will be no-ops if called on a WebGL 2 context active) - __webgl_enable_ANGLE_instanced_arrays(GLctx); - __webgl_enable_OES_vertex_array_object(GLctx); - __webgl_enable_WEBGL_draw_buffers(GLctx); - // Extensions that are available from WebGL >= 2 (no-op if called on a WebGL 1 context active) - __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); - - GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); - __webgl_enable_WEBGL_multi_draw(GLctx); - - // These are the 'safe' feature-enabling extensions that don't add any performance impact related to e.g. debugging, and - // should be enabled by default so that client GLES2/GL code will not need to go through extra hoops to get its stuff working. - // As new extensions are ratified at http://www.khronos.org/registry/webgl/extensions/ , feel free to add your new extensions - // here, as long as they don't produce a performance impact for users that might not be using those extensions. - // E.g. debugging-related extensions should probably be off by default. - var automaticallyEnabledExtensions = [ // Khronos ratified WebGL extensions ordered by number (no debug extensions): - "OES_texture_float", "OES_texture_half_float", "OES_standard_derivatives", - "OES_vertex_array_object", "WEBGL_compressed_texture_s3tc", "WEBGL_depth_texture", - "OES_element_index_uint", "EXT_texture_filter_anisotropic", "EXT_frag_depth", - "WEBGL_draw_buffers", "ANGLE_instanced_arrays", "OES_texture_float_linear", - "OES_texture_half_float_linear", "EXT_blend_minmax", "EXT_shader_texture_lod", - "EXT_texture_norm16", - // Community approved WebGL extensions ordered by number: - "WEBGL_compressed_texture_pvrtc", "EXT_color_buffer_half_float", "WEBGL_color_buffer_float", - "EXT_sRGB", "WEBGL_compressed_texture_etc1", "EXT_disjoint_timer_query", - "WEBGL_compressed_texture_etc", "WEBGL_compressed_texture_astc", "EXT_color_buffer_float", - "WEBGL_compressed_texture_s3tc_srgb", "EXT_disjoint_timer_query_webgl2", - // Old style prefixed forms of extensions (but still currently used on e.g. iPhone Xs as - // tested on iOS 12.4.1): - "WEBKIT_WEBGL_compressed_texture_pvrtc"]; - - function shouldEnableAutomatically(extension) { - var ret = false; - automaticallyEnabledExtensions.forEach(function(include) { - if (extension.indexOf(include) != -1) { - ret = true; - } - }); - return ret; - } - - var exts = GLctx.getSupportedExtensions() || []; // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. - exts.forEach(function(ext) { - if (automaticallyEnabledExtensions.indexOf(ext) != -1) { - GLctx.getExtension(ext); // Calling .getExtension enables that extension permanently, no need to store the return value to be enabled. - } - }); - },populateUniformTable:function(program) { - var p = GL.programs[program]; - var ptable = GL.programInfos[program] = { - uniforms: {}, - maxUniformLength: 0, // This is eagerly computed below, since we already enumerate all uniforms anyway. - maxAttributeLength: -1, // This is lazily computed and cached, computed when/if first asked, "-1" meaning not computed yet. - maxUniformBlockNameLength: -1 // Lazily computed as well - }; - - var utable = ptable.uniforms; - // A program's uniform table maps the string name of an uniform to an integer location of that uniform. - // The global GL.uniforms map maps integer locations to WebGLUniformLocations. - var numUniforms = GLctx.getProgramParameter(p, 0x8B86/*GL_ACTIVE_UNIFORMS*/); - for (var i = 0; i < numUniforms; ++i) { - var u = GLctx.getActiveUniform(p, i); - - var name = u.name; - ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length+1); - - // If we are dealing with an array, e.g. vec4 foo[3], strip off the array index part to canonicalize that "foo", "foo[]", - // and "foo[0]" will mean the same. Loop below will populate foo[1] and foo[2]. - if (name.slice(-1) == ']') { - name = name.slice(0, name.lastIndexOf('[')); - } - - // Optimize memory usage slightly: If we have an array of uniforms, e.g. 'vec3 colors[3];', then - // only store the string 'colors' in utable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i. - // Note that for the GL.uniforms table, we still need to fetch the all WebGLUniformLocations for all the indices. - var loc = GLctx.getUniformLocation(p, name); - if (loc) { - var id = GL.getNewId(GL.uniforms); - utable[name] = [u.size, id]; - GL.uniforms[id] = loc; - - for (var j = 1; j < u.size; ++j) { - var n = name + '['+j+']'; - loc = GLctx.getUniformLocation(p, n); - id = GL.getNewId(GL.uniforms); - - GL.uniforms[id] = loc; - } - } - } - }};function __webgl_enable_ANGLE_instanced_arrays(ctx) { - // Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2. - var ext = ctx.getExtension('ANGLE_instanced_arrays'); - if (ext) { - ctx['vertexAttribDivisor'] = function(index, divisor) { ext['vertexAttribDivisorANGLE'](index, divisor); }; - ctx['drawArraysInstanced'] = function(mode, first, count, primcount) { ext['drawArraysInstancedANGLE'](mode, first, count, primcount); }; - ctx['drawElementsInstanced'] = function(mode, count, type, indices, primcount) { ext['drawElementsInstancedANGLE'](mode, count, type, indices, primcount); }; - return 1; - } - }function _emscripten_webgl_enable_extension(contextHandle, extension) { - var context = GL.getContext(contextHandle); - var extString = UTF8ToString(extension); - if (extString.indexOf('GL_') == 0) extString = extString.substr(3); // Allow enabling extensions both with "GL_" prefix and without. - - // Switch-board that pulls in code for all GL extensions, even if those are not used :/ - // Build with -s GL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS = 0 to avoid this. - - // Obtain function entry points to WebGL 1 extension related functions. - if (extString == 'ANGLE_instanced_arrays') __webgl_enable_ANGLE_instanced_arrays(GLctx); - if (extString == 'OES_vertex_array_object') __webgl_enable_OES_vertex_array_object(GLctx); - if (extString == 'WEBGL_draw_buffers') __webgl_enable_WEBGL_draw_buffers(GLctx); - - if (extString == 'WEBGL_draw_instanced_base_vertex_base_instance') __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); - - if (extString == 'WEBGL_multi_draw') __webgl_enable_WEBGL_multi_draw(GLctx); - - - var ext = context.GLctx.getExtension(extString); - return !!ext; - } - - - var __emscripten_webgl_power_preferences=['default', 'low-power', 'high-performance'];function _emscripten_webgl_get_context_attributes(c, a) { - if (!a) return -5; - c = GL.contexts[c]; - if (!c) return -3; - var t = c.GLctx; - if (!t) return -3; - t = t.getContextAttributes(); - - HEAP32[((a)>>2)]=t.alpha; - HEAP32[(((a)+(4))>>2)]=t.depth; - HEAP32[(((a)+(8))>>2)]=t.stencil; - HEAP32[(((a)+(12))>>2)]=t.antialias; - HEAP32[(((a)+(16))>>2)]=t.premultipliedAlpha; - HEAP32[(((a)+(20))>>2)]=t.preserveDrawingBuffer; - var power = t['powerPreference'] && __emscripten_webgl_power_preferences.indexOf(t['powerPreference']); - HEAP32[(((a)+(24))>>2)]=power; - HEAP32[(((a)+(28))>>2)]=t.failIfMajorPerformanceCaveat; - HEAP32[(((a)+(32))>>2)]=c.version; - HEAP32[(((a)+(36))>>2)]=0; - HEAP32[(((a)+(40))>>2)]=c.attributes.enableExtensionsByDefault; - return 0; - } - - - function _emscripten_webgl_do_get_current_context() { - return GL.currentContext ? GL.currentContext.handle : 0; - }function _emscripten_webgl_get_current_context( - ) { - return _emscripten_webgl_do_get_current_context(); - } - - - - var ENV={}; - - function getExecutableName() { - return thisProgram || './this.program'; - }function getEnvStrings() { - if (!getEnvStrings.strings) { - // Default values. - // Browser language detection #8751 - var lang = ((typeof navigator === 'object' && navigator.languages && navigator.languages[0]) || 'C').replace('-', '_') + '.UTF-8'; - var env = { - 'USER': 'web_user', - 'LOGNAME': 'web_user', - 'PATH': '/', - 'PWD': '/', - 'HOME': '/home/web_user', - 'LANG': lang, - '_': getExecutableName() - }; - // Apply the user-provided values, if any. - for (var x in ENV) { - env[x] = ENV[x]; - } - var strings = []; - for (var x in env) { - strings.push(x + '=' + env[x]); - } - getEnvStrings.strings = strings; - } - return getEnvStrings.strings; - }function _environ_get(__environ, environ_buf) { - var bufSize = 0; - getEnvStrings().forEach(function(string, i) { - var ptr = environ_buf + bufSize; - HEAP32[(((__environ)+(i * 4))>>2)]=ptr; - writeAsciiToMemory(string, ptr); - bufSize += string.length + 1; - }); - return 0; - } - - function _environ_sizes_get(penviron_count, penviron_buf_size) { - var strings = getEnvStrings(); - HEAP32[((penviron_count)>>2)]=strings.length; - var bufSize = 0; - strings.forEach(function(string) { - bufSize += string.length + 1; - }); - HEAP32[((penviron_buf_size)>>2)]=bufSize; - return 0; - } - - function _fd_close(fd) {try { - - var stream = SYSCALLS.getStreamFromFD(fd); - FS.close(stream); - return 0; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return e.errno; - } - } - - function _fd_read(fd, iov, iovcnt, pnum) {try { - - var stream = SYSCALLS.getStreamFromFD(fd); - var num = SYSCALLS.doReadv(stream, iov, iovcnt); - HEAP32[((pnum)>>2)]=num - return 0; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return e.errno; - } - } - - function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {try { - - - var stream = SYSCALLS.getStreamFromFD(fd); - var HIGH_OFFSET = 0x100000000; // 2^32 - // use an unsigned operator on low and shift high by 32-bits - var offset = offset_high * HIGH_OFFSET + (offset_low >>> 0); - - var DOUBLE_LIMIT = 0x20000000000000; // 2^53 - // we also check for equality since DOUBLE_LIMIT + 1 == DOUBLE_LIMIT - if (offset <= -DOUBLE_LIMIT || offset >= DOUBLE_LIMIT) { - return -61; - } - - FS.llseek(stream, offset, whence); - (tempI64 = [stream.position>>>0,(tempDouble=stream.position,(+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((newOffset)>>2)]=tempI64[0],HEAP32[(((newOffset)+(4))>>2)]=tempI64[1]); - if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; // reset readdir state - return 0; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return e.errno; - } - } - - function _fd_write(fd, iov, iovcnt, pnum) {try { - - var stream = SYSCALLS.getStreamFromFD(fd); - var num = SYSCALLS.doWritev(stream, iov, iovcnt); - HEAP32[((pnum)>>2)]=num - return 0; - } catch (e) { - if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); - return e.errno; - } - } - - function _getTempRet0() { - return (getTempRet0() | 0); - } - - function _glActiveTexture(x0) { GLctx['activeTexture'](x0) } - - function _glAttachShader(program, shader) { - GLctx.attachShader(GL.programs[program], - GL.shaders[shader]); - } - - function _glBindAttribLocation(program, index, name) { - GLctx.bindAttribLocation(GL.programs[program], index, UTF8ToString(name)); - } - - function _glBindBuffer(target, buffer) { - if (target == 0x8892 /*GL_ARRAY_BUFFER*/) { - GLctx.currentArrayBufferBinding = buffer; - } else if (target == 0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/) { - GLctx.currentElementArrayBufferBinding = buffer; - } - - if (target == 0x88EB /*GL_PIXEL_PACK_BUFFER*/) { - // In WebGL 2 glReadPixels entry point, we need to use a different WebGL 2 API function call when a buffer is bound to - // GL_PIXEL_PACK_BUFFER_BINDING point, so must keep track whether that binding point is non-null to know what is - // the proper API function to call. - GLctx.currentPixelPackBufferBinding = buffer; - } else if (target == 0x88EC /*GL_PIXEL_UNPACK_BUFFER*/) { - // In WebGL 2 gl(Compressed)Tex(Sub)Image[23]D entry points, we need to - // use a different WebGL 2 API function call when a buffer is bound to - // GL_PIXEL_UNPACK_BUFFER_BINDING point, so must keep track whether that - // binding point is non-null to know what is the proper API function to - // call. - GLctx.currentPixelUnpackBufferBinding = buffer; - } - GLctx.bindBuffer(target, GL.buffers[buffer]); - } - - function _glBindBufferRange(target, index, buffer, offset, ptrsize) { - GLctx['bindBufferRange'](target, index, GL.buffers[buffer], offset, ptrsize); - } - - function _glBindFramebuffer(target, framebuffer) { - - GLctx.bindFramebuffer(target, GL.framebuffers[framebuffer]); - - } - - function _glBindRenderbuffer(target, renderbuffer) { - GLctx.bindRenderbuffer(target, GL.renderbuffers[renderbuffer]); - } - - function _glBindTexture(target, texture) { - GLctx.bindTexture(target, GL.textures[texture]); - } - - function _glBindVertexArray(vao) { - GLctx['bindVertexArray'](GL.vaos[vao]); - var ibo = GLctx.getParameter(0x8895 /*ELEMENT_ARRAY_BUFFER_BINDING*/); - GLctx.currentElementArrayBufferBinding = ibo ? (ibo.name | 0) : 0; - } - - function _glBlendFuncSeparate(x0, x1, x2, x3) { GLctx['blendFuncSeparate'](x0, x1, x2, x3) } - - function _glBlitFramebuffer(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) { GLctx['blitFramebuffer'](x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) } - - function _glBufferData(target, size, data, usage) { - - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - if (data) { - GLctx.bufferData(target, HEAPU8, usage, data, size); - } else { - GLctx.bufferData(target, size, usage); - } - } else { - // N.b. here first form specifies a heap subarray, second form an integer size, so the ?: code here is polymorphic. It is advised to avoid - // randomly mixing both uses in calling code, to avoid any potential JS engine JIT issues. - GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage); - } - } - - function _glBufferSubData(target, offset, size, data) { - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - GLctx.bufferSubData(target, offset, HEAPU8, data, size); - return; - } - GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size)); - } - - function _glClear(x0) { GLctx['clear'](x0) } - - function _glClearColor(x0, x1, x2, x3) { GLctx['clearColor'](x0, x1, x2, x3) } - - function _glClearDepthf(x0) { GLctx['clearDepth'](x0) } - - function _glColorMask(red, green, blue, alpha) { - GLctx.colorMask(!!red, !!green, !!blue, !!alpha); - } - - function _glCompileShader(shader) { - GLctx.compileShader(GL.shaders[shader]); - } - - function _glCompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, data) { - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - if (GLctx.currentPixelUnpackBufferBinding) { - GLctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, imageSize, data); - } else { - GLctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, HEAPU8, data, imageSize); - } - return; - } - GLctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, data ? HEAPU8.subarray((data),(data+imageSize)) : null); - } - - function _glCreateProgram() { - var id = GL.getNewId(GL.programs); - var program = GLctx.createProgram(); - program.name = id; - GL.programs[id] = program; - return id; - } - - function _glCreateShader(shaderType) { - var id = GL.getNewId(GL.shaders); - GL.shaders[id] = GLctx.createShader(shaderType); - return id; - } - - function _glDeleteBuffers(n, buffers) { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((buffers)+(i*4))>>2)]; - var buffer = GL.buffers[id]; - - // From spec: "glDeleteBuffers silently ignores 0's and names that do not - // correspond to existing buffer objects." - if (!buffer) continue; - - GLctx.deleteBuffer(buffer); - buffer.name = 0; - GL.buffers[id] = null; - - if (id == GLctx.currentArrayBufferBinding) GLctx.currentArrayBufferBinding = 0; - if (id == GLctx.currentElementArrayBufferBinding) GLctx.currentElementArrayBufferBinding = 0; - if (id == GLctx.currentPixelPackBufferBinding) GLctx.currentPixelPackBufferBinding = 0; - if (id == GLctx.currentPixelUnpackBufferBinding) GLctx.currentPixelUnpackBufferBinding = 0; - } - } - - function _glDeleteFramebuffers(n, framebuffers) { - for (var i = 0; i < n; ++i) { - var id = HEAP32[(((framebuffers)+(i*4))>>2)]; - var framebuffer = GL.framebuffers[id]; - if (!framebuffer) continue; // GL spec: "glDeleteFramebuffers silently ignores 0s and names that do not correspond to existing framebuffer objects". - GLctx.deleteFramebuffer(framebuffer); - framebuffer.name = 0; - GL.framebuffers[id] = null; - } - } - - function _glDeleteRenderbuffers(n, renderbuffers) { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((renderbuffers)+(i*4))>>2)]; - var renderbuffer = GL.renderbuffers[id]; - if (!renderbuffer) continue; // GL spec: "glDeleteRenderbuffers silently ignores 0s and names that do not correspond to existing renderbuffer objects". - GLctx.deleteRenderbuffer(renderbuffer); - renderbuffer.name = 0; - GL.renderbuffers[id] = null; - } - } - - function _glDeleteTextures(n, textures) { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((textures)+(i*4))>>2)]; - var texture = GL.textures[id]; - if (!texture) continue; // GL spec: "glDeleteTextures silently ignores 0s and names that do not correspond to existing textures". - GLctx.deleteTexture(texture); - texture.name = 0; - GL.textures[id] = null; - } - } - - function _glDeleteVertexArrays(n, vaos) { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((vaos)+(i*4))>>2)]; - GLctx['deleteVertexArray'](GL.vaos[id]); - GL.vaos[id] = null; - } - } - - function _glDepthFunc(x0) { GLctx['depthFunc'](x0) } - - function _glDepthMask(flag) { - GLctx.depthMask(!!flag); - } - - function _glDepthRangef(x0, x1) { GLctx['depthRange'](x0, x1) } - - function _glDisable(x0) { GLctx['disable'](x0) } - - function _glDrawElements(mode, count, type, indices) { - var buf; - if (!GLctx.currentElementArrayBufferBinding) { - var size = GL.calcBufLength(1, type, 0, count); - buf = GL.getTempIndexBuffer(size); - GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, buf); - GLctx.bufferSubData(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, - 0, - HEAPU8.subarray(indices, indices + size)); - // the index is now 0 - indices = 0; - } - - // bind any client-side buffers - GL.preDrawHandleClientVertexAttribBindings(count); - - GLctx.drawElements(mode, count, type, indices); - - GL.postDrawHandleClientVertexAttribBindings(count); - - if (!GLctx.currentElementArrayBufferBinding) { - GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, null); - } - } - - function _glEnable(x0) { GLctx['enable'](x0) } - - function _glEnableVertexAttribArray(index) { - var cb = GL.currentContext.clientBuffers[index]; - cb.enabled = true; - GLctx.enableVertexAttribArray(index); - } - - function _glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer) { - GLctx.framebufferRenderbuffer(target, attachment, renderbuffertarget, - GL.renderbuffers[renderbuffer]); - } - - function _glFramebufferTexture2D(target, attachment, textarget, texture, level) { - GLctx.framebufferTexture2D(target, attachment, textarget, - GL.textures[texture], level); - } - - function _glFrontFace(x0) { GLctx['frontFace'](x0) } - - - function __glGenObject(n, buffers, createFunction, objectTable - ) { - for (var i = 0; i < n; i++) { - var buffer = GLctx[createFunction](); - var id = buffer && GL.getNewId(objectTable); - if (buffer) { - buffer.name = id; - objectTable[id] = buffer; - } else { - GL.recordError(0x502 /* GL_INVALID_OPERATION */); - } - HEAP32[(((buffers)+(i*4))>>2)]=id; - } - }function _glGenBuffers(n, buffers) { - __glGenObject(n, buffers, 'createBuffer', GL.buffers - ); - } - - function _glGenFramebuffers(n, ids) { - __glGenObject(n, ids, 'createFramebuffer', GL.framebuffers - ); - } - - function _glGenRenderbuffers(n, renderbuffers) { - __glGenObject(n, renderbuffers, 'createRenderbuffer', GL.renderbuffers - ); - } - - function _glGenTextures(n, textures) { - __glGenObject(n, textures, 'createTexture', GL.textures - ); - } - - function _glGenVertexArrays(n, arrays) { - __glGenObject(n, arrays, 'createVertexArray', GL.vaos - ); - } - - function _glGenerateMipmap(x0) { GLctx['generateMipmap'](x0) } - - - function __glGetActiveAttribOrUniform(funcName, program, index, bufSize, length, size, type, name) { - program = GL.programs[program]; - var info = GLctx[funcName](program, index); - if (info) { // If an error occurs, nothing will be written to length, size and type and name. - var numBytesWrittenExclNull = name && stringToUTF8(info.name, name, bufSize); - if (length) HEAP32[((length)>>2)]=numBytesWrittenExclNull; - if (size) HEAP32[((size)>>2)]=info.size; - if (type) HEAP32[((type)>>2)]=info.type; - } - }function _glGetActiveUniform(program, index, bufSize, length, size, type, name) { - __glGetActiveAttribOrUniform('getActiveUniform', program, index, bufSize, length, size, type, name); - } - - - - - function readI53FromI64(ptr) { - return HEAPU32[ptr>>2] + HEAP32[ptr+4>>2] * 4294967296; - } - - function readI53FromU64(ptr) { - return HEAPU32[ptr>>2] + HEAPU32[ptr+4>>2] * 4294967296; - }function writeI53ToI64(ptr, num) { - HEAPU32[ptr>>2] = num; - HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; - var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr); - if (deserialized != num) warnOnce('writeI53ToI64() out of range: serialized JS Number ' + num + ' to Wasm heap as bytes lo=0x' + HEAPU32[ptr>>2].toString(16) + ', hi=0x' + HEAPU32[ptr+4>>2].toString(16) + ', which deserializes back to ' + deserialized + ' instead!'); - }function emscriptenWebGLGet(name_, p, type) { - // Guard against user passing a null pointer. - // Note that GLES2 spec does not say anything about how passing a null pointer should be treated. - // Testing on desktop core GL 3, the application crashes on glGetIntegerv to a null pointer, but - // better to report an error instead of doing anything random. - if (!p) { - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - var ret = undefined; - switch(name_) { // Handle a few trivial GLES values - case 0x8DFA: // GL_SHADER_COMPILER - ret = 1; - break; - case 0x8DF8: // GL_SHADER_BINARY_FORMATS - if (type != 0 && type != 1) { - GL.recordError(0x500); // GL_INVALID_ENUM - } - return; // Do not write anything to the out pointer, since no binary formats are supported. - case 0x87FE: // GL_NUM_PROGRAM_BINARY_FORMATS - case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS - ret = 0; - break; - case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS - // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length), - // so implement it ourselves to allow C++ GLES2 code get the length. - var formats = GLctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/); - ret = formats ? formats.length : 0; - break; - case 0x821D: // GL_NUM_EXTENSIONS - if (GL.currentContext.version < 2) { - GL.recordError(0x502 /* GL_INVALID_OPERATION */); // Calling GLES3/WebGL2 function with a GLES2/WebGL1 context - return; - } - // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. - var exts = GLctx.getSupportedExtensions() || []; - ret = 2 * exts.length; // each extension is duplicated, first in unprefixed WebGL form, and then a second time with "GL_" prefix. - break; - case 0x821B: // GL_MAJOR_VERSION - case 0x821C: // GL_MINOR_VERSION - if (GL.currentContext.version < 2) { - GL.recordError(0x500); // GL_INVALID_ENUM - return; - } - ret = name_ == 0x821B ? 3 : 0; // return version 3.0 - break; - } - - if (ret === undefined) { - var result = GLctx.getParameter(name_); - switch (typeof(result)) { - case "number": - ret = result; - break; - case "boolean": - ret = result ? 1 : 0; - break; - case "string": - GL.recordError(0x500); // GL_INVALID_ENUM - return; - case "object": - if (result === null) { - // null is a valid result for some (e.g., which buffer is bound - perhaps nothing is bound), but otherwise - // can mean an invalid name_, which we need to report as an error - switch(name_) { - case 0x8894: // ARRAY_BUFFER_BINDING - case 0x8B8D: // CURRENT_PROGRAM - case 0x8895: // ELEMENT_ARRAY_BUFFER_BINDING - case 0x8CA6: // FRAMEBUFFER_BINDING or DRAW_FRAMEBUFFER_BINDING - case 0x8CA7: // RENDERBUFFER_BINDING - case 0x8069: // TEXTURE_BINDING_2D - case 0x85B5: // WebGL 2 GL_VERTEX_ARRAY_BINDING, or WebGL 1 extension OES_vertex_array_object GL_VERTEX_ARRAY_BINDING_OES - case 0x8F36: // COPY_READ_BUFFER_BINDING or COPY_READ_BUFFER - case 0x8F37: // COPY_WRITE_BUFFER_BINDING or COPY_WRITE_BUFFER - case 0x88ED: // PIXEL_PACK_BUFFER_BINDING - case 0x88EF: // PIXEL_UNPACK_BUFFER_BINDING - case 0x8CAA: // READ_FRAMEBUFFER_BINDING - case 0x8919: // SAMPLER_BINDING - case 0x8C1D: // TEXTURE_BINDING_2D_ARRAY - case 0x806A: // TEXTURE_BINDING_3D - case 0x8E25: // TRANSFORM_FEEDBACK_BINDING - case 0x8C8F: // TRANSFORM_FEEDBACK_BUFFER_BINDING - case 0x8A28: // UNIFORM_BUFFER_BINDING - case 0x8514: { // TEXTURE_BINDING_CUBE_MAP - ret = 0; - break; - } - default: { - GL.recordError(0x500); // GL_INVALID_ENUM - return; - } - } - } else if (result instanceof Float32Array || - result instanceof Uint32Array || - result instanceof Int32Array || - result instanceof Array) { - for (var i = 0; i < result.length; ++i) { - switch (type) { - case 0: HEAP32[(((p)+(i*4))>>2)]=result[i]; break; - case 2: HEAPF32[(((p)+(i*4))>>2)]=result[i]; break; - case 4: HEAP8[(((p)+(i))>>0)]=result[i] ? 1 : 0; break; - } - } - return; - } else { - try { - ret = result.name | 0; - } catch(e) { - GL.recordError(0x500); // GL_INVALID_ENUM - err('GL_INVALID_ENUM in glGet' + type + 'v: Unknown object returned from WebGL getParameter(' + name_ + ')! (error: ' + e + ')'); - return; - } - } - break; - default: - GL.recordError(0x500); // GL_INVALID_ENUM - err('GL_INVALID_ENUM in glGet' + type + 'v: Native code calling glGet' + type + 'v(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); - return; - } - } - - switch (type) { - case 1: writeI53ToI64(p, ret); break; - case 0: HEAP32[((p)>>2)]=ret; break; - case 2: HEAPF32[((p)>>2)]=ret; break; - case 4: HEAP8[((p)>>0)]=ret ? 1 : 0; break; - } - }function _glGetFloatv(name_, p) { - emscriptenWebGLGet(name_, p, 2); - } - - function _glGetIntegerv(name_, p) { - emscriptenWebGLGet(name_, p, 0); - } - - function _glGetProgramInfoLog(program, maxLength, length, infoLog) { - var log = GLctx.getProgramInfoLog(GL.programs[program]); - if (log === null) log = '(unknown error)'; - var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; - if (length) HEAP32[((length)>>2)]=numBytesWrittenExclNull; - } - - function _glGetProgramiv(program, pname, p) { - if (!p) { - // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense - // if p == null, issue a GL error to notify user about it. - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - - if (program >= GL.counter) { - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - - var ptable = GL.programInfos[program]; - if (!ptable) { - GL.recordError(0x502 /* GL_INVALID_OPERATION */); - return; - } - - if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH - var log = GLctx.getProgramInfoLog(GL.programs[program]); - if (log === null) log = '(unknown error)'; - HEAP32[((p)>>2)]=log.length + 1; - } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) { - HEAP32[((p)>>2)]=ptable.maxUniformLength; - } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { - if (ptable.maxAttributeLength == -1) { - program = GL.programs[program]; - var numAttribs = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); - ptable.maxAttributeLength = 0; // Spec says if there are no active attribs, 0 must be returned. - for (var i = 0; i < numAttribs; ++i) { - var activeAttrib = GLctx.getActiveAttrib(program, i); - ptable.maxAttributeLength = Math.max(ptable.maxAttributeLength, activeAttrib.name.length+1); - } - } - HEAP32[((p)>>2)]=ptable.maxAttributeLength; - } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) { - if (ptable.maxUniformBlockNameLength == -1) { - program = GL.programs[program]; - var numBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); - ptable.maxUniformBlockNameLength = 0; - for (var i = 0; i < numBlocks; ++i) { - var activeBlockName = GLctx.getActiveUniformBlockName(program, i); - ptable.maxUniformBlockNameLength = Math.max(ptable.maxUniformBlockNameLength, activeBlockName.length+1); - } - } - HEAP32[((p)>>2)]=ptable.maxUniformBlockNameLength; - } else { - HEAP32[((p)>>2)]=GLctx.getProgramParameter(GL.programs[program], pname); - } - } - - function _glGetShaderInfoLog(shader, maxLength, length, infoLog) { - var log = GLctx.getShaderInfoLog(GL.shaders[shader]); - if (log === null) log = '(unknown error)'; - var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; - if (length) HEAP32[((length)>>2)]=numBytesWrittenExclNull; - } - - function _glGetShaderiv(shader, pname, p) { - if (!p) { - // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense - // if p == null, issue a GL error to notify user about it. - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH - var log = GLctx.getShaderInfoLog(GL.shaders[shader]); - if (log === null) log = '(unknown error)'; - // The GLES2 specification says that if the shader has an empty info log, - // a value of 0 is returned. Otherwise the log has a null char appended. - // (An empty string is falsey, so we can just check that instead of - // looking at log.length.) - var logLength = log ? log.length + 1 : 0; - HEAP32[((p)>>2)]=logLength; - } else if (pname == 0x8B88) { // GL_SHADER_SOURCE_LENGTH - var source = GLctx.getShaderSource(GL.shaders[shader]); - // source may be a null, or the empty string, both of which are falsey - // values that we report a 0 length for. - var sourceLength = source ? source.length + 1 : 0; - HEAP32[((p)>>2)]=sourceLength; - } else { - HEAP32[((p)>>2)]=GLctx.getShaderParameter(GL.shaders[shader], pname); - } - } - - function _glGetUniformBlockIndex(program, uniformBlockName) { - return GLctx['getUniformBlockIndex'](GL.programs[program], UTF8ToString(uniformBlockName)); - } - - - /** @suppress {checkTypes} */ - function jstoi_q(str) { - return parseInt(str); - }function _glGetUniformLocation(program, name) { - name = UTF8ToString(name); - - var arrayIndex = 0; - // If user passed an array accessor "[index]", parse the array index off the accessor. - if (name[name.length - 1] == ']') { - var leftBrace = name.lastIndexOf('['); - arrayIndex = name[leftBrace+1] != ']' ? jstoi_q(name.slice(leftBrace + 1)) : 0; // "index]", parseInt will ignore the ']' at the end; but treat "foo[]" as "foo[0]" - name = name.slice(0, leftBrace); - } - - var uniformInfo = GL.programInfos[program] && GL.programInfos[program].uniforms[name]; // returns pair [ dimension_of_uniform_array, uniform_location ] - if (uniformInfo && arrayIndex >= 0 && arrayIndex < uniformInfo[0]) { // Check if user asked for an out-of-bounds element, i.e. for 'vec4 colors[3];' user could ask for 'colors[10]' which should return -1. - return uniformInfo[1] + arrayIndex; - } else { - return -1; - } - } - - function _glLinkProgram(program) { - GLctx.linkProgram(GL.programs[program]); - GL.populateUniformTable(program); - } - - function _glReadBuffer(x0) { GLctx['readBuffer'](x0) } - - - - function computeUnpackAlignedImageSize(width, height, sizePerPixel, alignment) { - function roundedToNextMultipleOf(x, y) { - return (x + y - 1) & -y; - } - var plainRowSize = width * sizePerPixel; - var alignedRowSize = roundedToNextMultipleOf(plainRowSize, alignment); - return height * alignedRowSize; - } - - function __colorChannelsInGlTextureFormat(format) { - // Micro-optimizations for size: map format to size by subtracting smallest enum value (0x1902) from all values first. - // Also omit the most common size value (1) from the list, which is assumed by formats not on the list. - var colorChannels = { - // 0x1902 /* GL_DEPTH_COMPONENT */ - 0x1902: 1, - // 0x1906 /* GL_ALPHA */ - 0x1902: 1, - 5: 3, - 6: 4, - // 0x1909 /* GL_LUMINANCE */ - 0x1902: 1, - 8: 2, - 29502: 3, - 29504: 4, - // 0x1903 /* GL_RED */ - 0x1902: 1, - 26917: 2, - 26918: 2, - // 0x8D94 /* GL_RED_INTEGER */ - 0x1902: 1, - 29846: 3, - 29847: 4 - }; - return colorChannels[format - 0x1902]||1; - } - - function heapObjectForWebGLType(type) { - // Micro-optimization for size: Subtract lowest GL enum number (0x1400/* GL_BYTE */) from type to compare - // smaller values for the heap, for shorter generated code size. - // Also the type HEAPU16 is not tested for explicitly, but any unrecognized type will return out HEAPU16. - // (since most types are HEAPU16) - type -= 0x1400; - if (type == 0) return HEAP8; - - if (type == 1) return HEAPU8; - - if (type == 2) return HEAP16; - - if (type == 4) return HEAP32; - - if (type == 6) return HEAPF32; - - if (type == 5 - || type == 28922 - || type == 28520 - || type == 30779 - || type == 30782 - ) - return HEAPU32; - - return HEAPU16; - } - - function heapAccessShiftForWebGLHeap(heap) { - return 31 - Math.clz32(heap.BYTES_PER_ELEMENT); - }function emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) { - var heap = heapObjectForWebGLType(type); - var shift = heapAccessShiftForWebGLHeap(heap); - var byteSize = 1<> shift, pixels + bytes >> shift); - }function _glReadPixels(x, y, width, height, format, type, pixels) { - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - if (GLctx.currentPixelPackBufferBinding) { - GLctx.readPixels(x, y, width, height, format, type, pixels); - } else { - var heap = heapObjectForWebGLType(type); - GLctx.readPixels(x, y, width, height, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); - } - return; - } - var pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, format); - if (!pixelData) { - GL.recordError(0x500/*GL_INVALID_ENUM*/); - return; - } - GLctx.readPixels(x, y, width, height, format, type, pixelData); - } - - function _glRenderbufferStorageMultisample(x0, x1, x2, x3, x4) { GLctx['renderbufferStorageMultisample'](x0, x1, x2, x3, x4) } - - function _glScissor(x0, x1, x2, x3) { GLctx['scissor'](x0, x1, x2, x3) } - - function _glShaderSource(shader, count, string, length) { - var source = GL.getSource(shader, count, string, length); - - - GLctx.shaderSource(GL.shaders[shader], source); - } - - function _glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels) { - if (GL.currentContext.version >= 2) { - // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - if (GLctx.currentPixelUnpackBufferBinding) { - GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels); - } else if (pixels) { - var heap = heapObjectForWebGLType(type); - GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); - } else { - GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, null); - } - return; - } - GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null); - } - - function _glTexParameterf(x0, x1, x2) { GLctx['texParameterf'](x0, x1, x2) } - - function _glTexParameteri(x0, x1, x2) { GLctx['texParameteri'](x0, x1, x2) } - - function _glUniform1i(location, v0) { - GLctx.uniform1i(GL.uniforms[location], v0); - } - - - var miniTempWebGLFloatBuffers=[];function _glUniform4fv(location, count, value) { - - - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - GLctx.uniform4fv(GL.uniforms[location], HEAPF32, value>>2, count*4); - return; - } - - if (count <= 72) { - // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLFloatBuffers[4*count-1]; - // hoist the heap out of the loop for size and for pthreads+growth. - var heap = HEAPF32; - value >>= 2; - for (var i = 0; i < 4 * count; i += 4) { - var dst = value + i; - view[i] = heap[dst]; - view[i + 1] = heap[dst + 1]; - view[i + 2] = heap[dst + 2]; - view[i + 3] = heap[dst + 3]; - } - } else - { - var view = HEAPF32.subarray((value)>>2,(value+count*16)>>2); - } - GLctx.uniform4fv(GL.uniforms[location], view); - } - - - var __miniTempWebGLIntBuffers=[];function _glUniform4iv(location, count, value) { - - - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - GLctx.uniform4iv(GL.uniforms[location], HEAP32, value>>2, count*4); - return; - } - - if (count <= 72) { - // avoid allocation when uploading few enough uniforms - var view = __miniTempWebGLIntBuffers[4*count-1]; - for (var i = 0; i < 4*count; i += 4) { - view[i] = HEAP32[(((value)+(4*i))>>2)]; - view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; - view[i+2] = HEAP32[(((value)+(4*i+8))>>2)]; - view[i+3] = HEAP32[(((value)+(4*i+12))>>2)]; - } - } else - { - var view = HEAP32.subarray((value)>>2,(value+count*16)>>2); - } - GLctx.uniform4iv(GL.uniforms[location], view); - } - - function _glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding) { - program = GL.programs[program]; - - GLctx['uniformBlockBinding'](program, uniformBlockIndex, uniformBlockBinding); - } - - function _glUniformMatrix2fv(location, count, transpose, value) { - - - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - GLctx.uniformMatrix2fv(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*4); - return; - } - - if (count <= 72) { - // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLFloatBuffers[4*count-1]; - for (var i = 0; i < 4*count; i += 4) { - view[i] = HEAPF32[(((value)+(4*i))>>2)]; - view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; - view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; - view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)]; - } - } else - { - var view = HEAPF32.subarray((value)>>2,(value+count*16)>>2); - } - GLctx.uniformMatrix2fv(GL.uniforms[location], !!transpose, view); - } - - function _glUniformMatrix3fv(location, count, transpose, value) { - - - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - GLctx.uniformMatrix3fv(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*9); - return; - } - - if (count <= 32) { - // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLFloatBuffers[9*count-1]; - for (var i = 0; i < 9*count; i += 9) { - view[i] = HEAPF32[(((value)+(4*i))>>2)]; - view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; - view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; - view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)]; - view[i+4] = HEAPF32[(((value)+(4*i+16))>>2)]; - view[i+5] = HEAPF32[(((value)+(4*i+20))>>2)]; - view[i+6] = HEAPF32[(((value)+(4*i+24))>>2)]; - view[i+7] = HEAPF32[(((value)+(4*i+28))>>2)]; - view[i+8] = HEAPF32[(((value)+(4*i+32))>>2)]; - } - } else - { - var view = HEAPF32.subarray((value)>>2,(value+count*36)>>2); - } - GLctx.uniformMatrix3fv(GL.uniforms[location], !!transpose, view); - } - - function _glUniformMatrix4fv(location, count, transpose, value) { - - - if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*16); - return; - } - - if (count <= 18) { - // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLFloatBuffers[16*count-1]; - // hoist the heap out of the loop for size and for pthreads+growth. - var heap = HEAPF32; - value >>= 2; - for (var i = 0; i < 16 * count; i += 16) { - var dst = value + i; - view[i] = heap[dst]; - view[i + 1] = heap[dst + 1]; - view[i + 2] = heap[dst + 2]; - view[i + 3] = heap[dst + 3]; - view[i + 4] = heap[dst + 4]; - view[i + 5] = heap[dst + 5]; - view[i + 6] = heap[dst + 6]; - view[i + 7] = heap[dst + 7]; - view[i + 8] = heap[dst + 8]; - view[i + 9] = heap[dst + 9]; - view[i + 10] = heap[dst + 10]; - view[i + 11] = heap[dst + 11]; - view[i + 12] = heap[dst + 12]; - view[i + 13] = heap[dst + 13]; - view[i + 14] = heap[dst + 14]; - view[i + 15] = heap[dst + 15]; - } - } else - { - var view = HEAPF32.subarray((value)>>2,(value+count*64)>>2); - } - GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, view); - } - - function _glUseProgram(program) { - GLctx.useProgram(GL.programs[program]); - } - - function _glVertexAttribPointer(index, size, type, normalized, stride, ptr) { - var cb = GL.currentContext.clientBuffers[index]; - if (!GLctx.currentArrayBufferBinding) { - cb.size = size; - cb.type = type; - cb.normalized = normalized; - cb.stride = stride; - cb.ptr = ptr; - cb.clientside = true; - cb.vertexAttribPointerAdaptor = function(index, size, type, normalized, stride, ptr) { - this.vertexAttribPointer(index, size, type, normalized, stride, ptr); - }; - return; - } - cb.clientside = false; - GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); - } - - function _glViewport(x0, x1, x2, x3) { GLctx['viewport'](x0, x1, x2, x3) } - - - - var _emscripten_get_now;if (ENVIRONMENT_IS_NODE) { - _emscripten_get_now = function() { - var t = process['hrtime'](); - return t[0] * 1e3 + t[1] / 1e6; - }; - } else if (typeof dateNow !== 'undefined') { - _emscripten_get_now = dateNow; - } else _emscripten_get_now = function() { return performance.now(); } - ; - - - - function _emscripten_set_main_loop_timing(mode, value) { - Browser.mainLoop.timingMode = mode; - Browser.mainLoop.timingValue = value; - - if (!Browser.mainLoop.func) { - console.error('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.'); - return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. - } - - if (mode == 0 /*EM_TIMING_SETTIMEOUT*/) { - Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setTimeout() { - var timeUntilNextTick = Math.max(0, Browser.mainLoop.tickStartTime + value - _emscripten_get_now())|0; - setTimeout(Browser.mainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop - }; - Browser.mainLoop.method = 'timeout'; - } else if (mode == 1 /*EM_TIMING_RAF*/) { - Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_rAF() { - Browser.requestAnimationFrame(Browser.mainLoop.runner); - }; - Browser.mainLoop.method = 'rAF'; - } else if (mode == 2 /*EM_TIMING_SETIMMEDIATE*/) { - if (typeof setImmediate === 'undefined') { - // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) - var setImmediates = []; - var emscriptenMainLoopMessageId = 'setimmediate'; - var Browser_setImmediate_messageHandler = function(event) { - // When called in current thread or Worker, the main loop ID is structured slightly different to accommodate for --proxy-to-worker runtime listening to Worker events, - // so check for both cases. - if (event.data === emscriptenMainLoopMessageId || event.data.target === emscriptenMainLoopMessageId) { - event.stopPropagation(); - setImmediates.shift()(); - } - } - addEventListener("message", Browser_setImmediate_messageHandler, true); - setImmediate = /** @type{function(function(): ?, ...?): number} */(function Browser_emulated_setImmediate(func) { - setImmediates.push(func); - if (ENVIRONMENT_IS_WORKER) { - if (Module['setImmediates'] === undefined) Module['setImmediates'] = []; - Module['setImmediates'].push(func); - postMessage({target: emscriptenMainLoopMessageId}); // In --proxy-to-worker, route the message via proxyClient.js - } else postMessage(emscriptenMainLoopMessageId, "*"); // On the main thread, can just send the message to itself. - }) - } - Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setImmediate() { - setImmediate(Browser.mainLoop.runner); - }; - Browser.mainLoop.method = 'immediate'; - } - return 0; - }/** @param {number|boolean=} noSetTiming */ - function _emscripten_set_main_loop(func, fps, simulateInfiniteLoop, arg, noSetTiming) { - noExitRuntime = true; - - assert(!Browser.mainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.'); - - Browser.mainLoop.func = func; - Browser.mainLoop.arg = arg; - - var browserIterationFunc; - if (typeof arg !== 'undefined') { - browserIterationFunc = function() { - Module['dynCall_vi'](func, arg); - }; - } else { - browserIterationFunc = function() { - Module['dynCall_v'](func); - }; - } - - var thisMainLoopId = Browser.mainLoop.currentlyRunningMainloop; - - Browser.mainLoop.runner = function Browser_mainLoop_runner() { - if (ABORT) return; - if (Browser.mainLoop.queue.length > 0) { - var start = Date.now(); - var blocker = Browser.mainLoop.queue.shift(); - blocker.func(blocker.arg); - if (Browser.mainLoop.remainingBlockers) { - var remaining = Browser.mainLoop.remainingBlockers; - var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining); - if (blocker.counted) { - Browser.mainLoop.remainingBlockers = next; - } else { - // not counted, but move the progress along a tiny bit - next = next + 0.5; // do not steal all the next one's progress - Browser.mainLoop.remainingBlockers = (8*remaining + next)/9; - } - } - console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers); - Browser.mainLoop.updateStatus(); - - // catches pause/resume main loop from blocker execution - if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; - - setTimeout(Browser.mainLoop.runner, 0); - return; - } - - // catch pauses from non-main loop sources - if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; - - // Implement very basic swap interval control - Browser.mainLoop.currentFrameNumber = Browser.mainLoop.currentFrameNumber + 1 | 0; - if (Browser.mainLoop.timingMode == 1/*EM_TIMING_RAF*/ && Browser.mainLoop.timingValue > 1 && Browser.mainLoop.currentFrameNumber % Browser.mainLoop.timingValue != 0) { - // Not the scheduled time to render this frame - skip. - Browser.mainLoop.scheduler(); - return; - } else if (Browser.mainLoop.timingMode == 0/*EM_TIMING_SETTIMEOUT*/) { - Browser.mainLoop.tickStartTime = _emscripten_get_now(); - } - - // Signal GL rendering layer that processing of a new frame is about to start. This helps it optimize - // VBO double-buffering and reduce GPU stalls. - GL.newRenderingFrameStarted(); - - - - if (Browser.mainLoop.method === 'timeout' && Module.ctx) { - warnOnce('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); - Browser.mainLoop.method = ''; // just warn once per call to set main loop - } - - Browser.mainLoop.runIter(browserIterationFunc); - - checkStackCookie(); - - // catch pauses from the main loop itself - if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; - - // Queue new audio data. This is important to be right after the main loop invocation, so that we will immediately be able - // to queue the newest produced audio samples. - // TODO: Consider adding pre- and post- rAF callbacks so that GL.newRenderingFrameStarted() and SDL.audio.queueNewAudioData() - // do not need to be hardcoded into this function, but can be more generic. - if (typeof SDL === 'object' && SDL.audio && SDL.audio.queueNewAudioData) SDL.audio.queueNewAudioData(); - - Browser.mainLoop.scheduler(); - } - - if (!noSetTiming) { - if (fps && fps > 0) _emscripten_set_main_loop_timing(0/*EM_TIMING_SETTIMEOUT*/, 1000.0 / fps); - else _emscripten_set_main_loop_timing(1/*EM_TIMING_RAF*/, 1); // Do rAF by rendering each frame (no decimating) - - Browser.mainLoop.scheduler(); - } - - if (simulateInfiniteLoop) { - throw 'unwind'; - } - }var Browser={mainLoop:{scheduler:null,method:"",currentlyRunningMainloop:0,func:null,arg:0,timingMode:0,timingValue:0,currentFrameNumber:0,queue:[],pause:function() { - Browser.mainLoop.scheduler = null; - Browser.mainLoop.currentlyRunningMainloop++; // Incrementing this signals the previous main loop that it's now become old, and it must return. - },resume:function() { - Browser.mainLoop.currentlyRunningMainloop++; - var timingMode = Browser.mainLoop.timingMode; - var timingValue = Browser.mainLoop.timingValue; - var func = Browser.mainLoop.func; - Browser.mainLoop.func = null; - _emscripten_set_main_loop(func, 0, false, Browser.mainLoop.arg, true /* do not set timing and call scheduler, we will do it on the next lines */); - _emscripten_set_main_loop_timing(timingMode, timingValue); - Browser.mainLoop.scheduler(); - },updateStatus:function() { - if (Module['setStatus']) { - var message = Module['statusMessage'] || 'Please wait...'; - var remaining = Browser.mainLoop.remainingBlockers; - var expected = Browser.mainLoop.expectedBlockers; - if (remaining) { - if (remaining < expected) { - Module['setStatus'](message + ' (' + (expected - remaining) + '/' + expected + ')'); - } else { - Module['setStatus'](message); - } - } else { - Module['setStatus'](''); - } - } - },runIter:function(func) { - if (ABORT) return; - if (Module['preMainLoop']) { - var preRet = Module['preMainLoop'](); - if (preRet === false) { - return; // |return false| skips a frame - } - } - try { - func(); - } catch (e) { - if (e instanceof ExitStatus) { - return; - } else if (e == 'unwind') { - return; - } else { - if (e && typeof e === 'object' && e.stack) err('exception thrown: ' + [e, e.stack]); - throw e; - } - } - if (Module['postMainLoop']) Module['postMainLoop'](); - }},isFullscreen:false,pointerLock:false,moduleContextCreatedCallbacks:[],workers:[],init:function() { - if (!Module["preloadPlugins"]) Module["preloadPlugins"] = []; // needs to exist even in workers - - if (Browser.initted) return; - Browser.initted = true; - - try { - new Blob(); - Browser.hasBlobConstructor = true; - } catch(e) { - Browser.hasBlobConstructor = false; - console.log("warning: no blob constructor, cannot create blobs with mimetypes"); - } - Browser.BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : (!Browser.hasBlobConstructor ? console.log("warning: no BlobBuilder") : null)); - Browser.URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : undefined; - if (!Module.noImageDecoding && typeof Browser.URLObject === 'undefined') { - console.log("warning: Browser does not support creating object URLs. Built-in browser image decoding will not be available."); - Module.noImageDecoding = true; - } - - // Support for plugins that can process preloaded files. You can add more of these to - // your app by creating and appending to Module.preloadPlugins. - // - // Each plugin is asked if it can handle a file based on the file's name. If it can, - // it is given the file's raw data. When it is done, it calls a callback with the file's - // (possibly modified) data. For example, a plugin might decompress a file, or it - // might create some side data structure for use later (like an Image element, etc.). - - var imagePlugin = {}; - imagePlugin['canHandle'] = function imagePlugin_canHandle(name) { - return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name); - }; - imagePlugin['handle'] = function imagePlugin_handle(byteArray, name, onload, onerror) { - var b = null; - if (Browser.hasBlobConstructor) { - try { - b = new Blob([byteArray], { type: Browser.getMimetype(name) }); - if (b.size !== byteArray.length) { // Safari bug #118630 - // Safari's Blob can only take an ArrayBuffer - b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) }); - } - } catch(e) { - warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder'); - } - } - if (!b) { - var bb = new Browser.BlobBuilder(); - bb.append((new Uint8Array(byteArray)).buffer); // we need to pass a buffer, and must copy the array to get the right data range - b = bb.getBlob(); - } - var url = Browser.URLObject.createObjectURL(b); - assert(typeof url == 'string', 'createObjectURL must return a url as a string'); - var img = new Image(); - img.onload = function img_onload() { - assert(img.complete, 'Image ' + name + ' could not be decoded'); - var canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - Module["preloadedImages"][name] = canvas; - Browser.URLObject.revokeObjectURL(url); - if (onload) onload(byteArray); - }; - img.onerror = function img_onerror(event) { - console.log('Image ' + url + ' could not be decoded'); - if (onerror) onerror(); - }; - img.src = url; - }; - Module['preloadPlugins'].push(imagePlugin); - - var audioPlugin = {}; - audioPlugin['canHandle'] = function audioPlugin_canHandle(name) { - return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 }; - }; - audioPlugin['handle'] = function audioPlugin_handle(byteArray, name, onload, onerror) { - var done = false; - function finish(audio) { - if (done) return; - done = true; - Module["preloadedAudios"][name] = audio; - if (onload) onload(byteArray); - } - function fail() { - if (done) return; - done = true; - Module["preloadedAudios"][name] = new Audio(); // empty shim - if (onerror) onerror(); - } - if (Browser.hasBlobConstructor) { - try { - var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); - } catch(e) { - return fail(); - } - var url = Browser.URLObject.createObjectURL(b); // XXX we never revoke this! - assert(typeof url == 'string', 'createObjectURL must return a url as a string'); - var audio = new Audio(); - audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926 - audio.onerror = function audio_onerror(event) { - if (done) return; - console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach'); - function encode64(data) { - var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; - var PAD = '='; - var ret = ''; - var leftchar = 0; - var leftbits = 0; - for (var i = 0; i < data.length; i++) { - leftchar = (leftchar << 8) | data[i]; - leftbits += 8; - while (leftbits >= 6) { - var curr = (leftchar >> (leftbits-6)) & 0x3f; - leftbits -= 6; - ret += BASE[curr]; - } - } - if (leftbits == 2) { - ret += BASE[(leftchar&3) << 4]; - ret += PAD + PAD; - } else if (leftbits == 4) { - ret += BASE[(leftchar&0xf) << 2]; - ret += PAD; - } - return ret; - } - audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray); - finish(audio); // we don't wait for confirmation this worked - but it's worth trying - }; - audio.src = url; - // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror - Browser.safeSetTimeout(function() { - finish(audio); // try to use it even though it is not necessarily ready to play - }, 10000); - } else { - return fail(); - } - }; - Module['preloadPlugins'].push(audioPlugin); - - - // Canvas event setup - - function pointerLockChange() { - Browser.pointerLock = document['pointerLockElement'] === Module['canvas'] || - document['mozPointerLockElement'] === Module['canvas'] || - document['webkitPointerLockElement'] === Module['canvas'] || - document['msPointerLockElement'] === Module['canvas']; - } - var canvas = Module['canvas']; - if (canvas) { - // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module - // Module['forcedAspectRatio'] = 4 / 3; - - canvas.requestPointerLock = canvas['requestPointerLock'] || - canvas['mozRequestPointerLock'] || - canvas['webkitRequestPointerLock'] || - canvas['msRequestPointerLock'] || - function(){}; - canvas.exitPointerLock = document['exitPointerLock'] || - document['mozExitPointerLock'] || - document['webkitExitPointerLock'] || - document['msExitPointerLock'] || - function(){}; // no-op if function does not exist - canvas.exitPointerLock = canvas.exitPointerLock.bind(document); - - document.addEventListener('pointerlockchange', pointerLockChange, false); - document.addEventListener('mozpointerlockchange', pointerLockChange, false); - document.addEventListener('webkitpointerlockchange', pointerLockChange, false); - document.addEventListener('mspointerlockchange', pointerLockChange, false); - - if (Module['elementPointerLock']) { - canvas.addEventListener("click", function(ev) { - if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { - Module['canvas'].requestPointerLock(); - ev.preventDefault(); - } - }, false); - } - } - },createContext:function(canvas, useWebGL, setInModule, webGLContextAttributes) { - if (useWebGL && Module.ctx && canvas == Module.canvas) return Module.ctx; // no need to recreate GL context if it's already been created for this canvas. - - var ctx; - var contextHandle; - if (useWebGL) { - // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults. - var contextAttributes = { - antialias: false, - alpha: false, - majorVersion: (typeof WebGL2RenderingContext !== 'undefined') ? 2 : 1, - }; - - if (webGLContextAttributes) { - for (var attribute in webGLContextAttributes) { - contextAttributes[attribute] = webGLContextAttributes[attribute]; - } - } - - // This check of existence of GL is here to satisfy Closure compiler, which yells if variable GL is referenced below but GL object is not - // actually compiled in because application is not doing any GL operations. TODO: Ideally if GL is not being used, this function - // Browser.createContext() should not even be emitted. - if (typeof GL !== 'undefined') { - contextHandle = GL.createContext(canvas, contextAttributes); - if (contextHandle) { - ctx = GL.getContext(contextHandle).GLctx; - } - } - } else { - ctx = canvas.getContext('2d'); - } - - if (!ctx) return null; - - if (setInModule) { - if (!useWebGL) assert(typeof GLctx === 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it'); - - Module.ctx = ctx; - if (useWebGL) GL.makeContextCurrent(contextHandle); - Module.useWebGL = useWebGL; - Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() }); - Browser.init(); - } - return ctx; - },destroyContext:function(canvas, useWebGL, setInModule) {},fullscreenHandlersInstalled:false,lockPointer:undefined,resizeCanvas:undefined,requestFullscreen:function(lockPointer, resizeCanvas) { - Browser.lockPointer = lockPointer; - Browser.resizeCanvas = resizeCanvas; - if (typeof Browser.lockPointer === 'undefined') Browser.lockPointer = true; - if (typeof Browser.resizeCanvas === 'undefined') Browser.resizeCanvas = false; - - var canvas = Module['canvas']; - function fullscreenChange() { - Browser.isFullscreen = false; - var canvasContainer = canvas.parentNode; - if ((document['fullscreenElement'] || document['mozFullScreenElement'] || - document['msFullscreenElement'] || document['webkitFullscreenElement'] || - document['webkitCurrentFullScreenElement']) === canvasContainer) { - canvas.exitFullscreen = Browser.exitFullscreen; - if (Browser.lockPointer) canvas.requestPointerLock(); - Browser.isFullscreen = true; - if (Browser.resizeCanvas) { - Browser.setFullscreenCanvasSize(); - } else { - Browser.updateCanvasDimensions(canvas); - } - } else { - // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen - canvasContainer.parentNode.insertBefore(canvas, canvasContainer); - canvasContainer.parentNode.removeChild(canvasContainer); - - if (Browser.resizeCanvas) { - Browser.setWindowedCanvasSize(); - } else { - Browser.updateCanvasDimensions(canvas); - } - } - if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullscreen); - if (Module['onFullscreen']) Module['onFullscreen'](Browser.isFullscreen); - } - - if (!Browser.fullscreenHandlersInstalled) { - Browser.fullscreenHandlersInstalled = true; - document.addEventListener('fullscreenchange', fullscreenChange, false); - document.addEventListener('mozfullscreenchange', fullscreenChange, false); - document.addEventListener('webkitfullscreenchange', fullscreenChange, false); - document.addEventListener('MSFullscreenChange', fullscreenChange, false); - } - - // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root - var canvasContainer = document.createElement("div"); - canvas.parentNode.insertBefore(canvasContainer, canvas); - canvasContainer.appendChild(canvas); - - // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size) - canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] || - canvasContainer['mozRequestFullScreen'] || - canvasContainer['msRequestFullscreen'] || - (canvasContainer['webkitRequestFullscreen'] ? function() { canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null) || - (canvasContainer['webkitRequestFullScreen'] ? function() { canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null); - - canvasContainer.requestFullscreen(); - },requestFullScreen:function() { - abort('Module.requestFullScreen has been replaced by Module.requestFullscreen (without a capital S)'); - },exitFullscreen:function() { - // This is workaround for chrome. Trying to exit from fullscreen - // not in fullscreen state will cause "TypeError: Document not active" - // in chrome. See https://github.com/emscripten-core/emscripten/pull/8236 - if (!Browser.isFullscreen) { - return false; - } - - var CFS = document['exitFullscreen'] || - document['cancelFullScreen'] || - document['mozCancelFullScreen'] || - document['msExitFullscreen'] || - document['webkitCancelFullScreen'] || - (function() {}); - CFS.apply(document, []); - return true; - },nextRAF:0,fakeRequestAnimationFrame:function(func) { - // try to keep 60fps between calls to here - var now = Date.now(); - if (Browser.nextRAF === 0) { - Browser.nextRAF = now + 1000/60; - } else { - while (now + 2 >= Browser.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0 - Browser.nextRAF += 1000/60; - } - } - var delay = Math.max(Browser.nextRAF - now, 0); - setTimeout(func, delay); - },requestAnimationFrame:function(func) { - if (typeof requestAnimationFrame === 'function') { - requestAnimationFrame(func); - return; - } - var RAF = Browser.fakeRequestAnimationFrame; - RAF(func); - },safeCallback:function(func) { - return function() { - if (!ABORT) return func.apply(null, arguments); - }; - },allowAsyncCallbacks:true,queuedAsyncCallbacks:[],pauseAsyncCallbacks:function() { - Browser.allowAsyncCallbacks = false; - },resumeAsyncCallbacks:function() { // marks future callbacks as ok to execute, and synchronously runs any remaining ones right now - Browser.allowAsyncCallbacks = true; - if (Browser.queuedAsyncCallbacks.length > 0) { - var callbacks = Browser.queuedAsyncCallbacks; - Browser.queuedAsyncCallbacks = []; - callbacks.forEach(function(func) { - func(); - }); - } - },safeRequestAnimationFrame:function(func) { - return Browser.requestAnimationFrame(function() { - if (ABORT) return; - if (Browser.allowAsyncCallbacks) { - func(); - } else { - Browser.queuedAsyncCallbacks.push(func); - } - }); - },safeSetTimeout:function(func, timeout) { - noExitRuntime = true; - return setTimeout(function() { - if (ABORT) return; - if (Browser.allowAsyncCallbacks) { - func(); - } else { - Browser.queuedAsyncCallbacks.push(func); - } - }, timeout); - },safeSetInterval:function(func, timeout) { - noExitRuntime = true; - return setInterval(function() { - if (ABORT) return; - if (Browser.allowAsyncCallbacks) { - func(); - } // drop it on the floor otherwise, next interval will kick in - }, timeout); - },getMimetype:function(name) { - return { - 'jpg': 'image/jpeg', - 'jpeg': 'image/jpeg', - 'png': 'image/png', - 'bmp': 'image/bmp', - 'ogg': 'audio/ogg', - 'wav': 'audio/wav', - 'mp3': 'audio/mpeg' - }[name.substr(name.lastIndexOf('.')+1)]; - },getUserMedia:function(func) { - if(!window.getUserMedia) { - window.getUserMedia = navigator['getUserMedia'] || - navigator['mozGetUserMedia']; - } - window.getUserMedia(func); - },getMovementX:function(event) { - return event['movementX'] || - event['mozMovementX'] || - event['webkitMovementX'] || - 0; - },getMovementY:function(event) { - return event['movementY'] || - event['mozMovementY'] || - event['webkitMovementY'] || - 0; - },getMouseWheelDelta:function(event) { - var delta = 0; - switch (event.type) { - case 'DOMMouseScroll': - // 3 lines make up a step - delta = event.detail / 3; - break; - case 'mousewheel': - // 120 units make up a step - delta = event.wheelDelta / 120; - break; - case 'wheel': - delta = event.deltaY - switch(event.deltaMode) { - case 0: - // DOM_DELTA_PIXEL: 100 pixels make up a step - delta /= 100; - break; - case 1: - // DOM_DELTA_LINE: 3 lines make up a step - delta /= 3; - break; - case 2: - // DOM_DELTA_PAGE: A page makes up 80 steps - delta *= 80; - break; - default: - throw 'unrecognized mouse wheel delta mode: ' + event.deltaMode; - } - break; - default: - throw 'unrecognized mouse wheel event: ' + event.type; - } - return delta; - },mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,touches:{},lastTouches:{},calculateMouseEvent:function(event) { // event should be mousemove, mousedown or mouseup - if (Browser.pointerLock) { - // When the pointer is locked, calculate the coordinates - // based on the movement of the mouse. - // Workaround for Firefox bug 764498 - if (event.type != 'mousemove' && - ('mozMovementX' in event)) { - Browser.mouseMovementX = Browser.mouseMovementY = 0; - } else { - Browser.mouseMovementX = Browser.getMovementX(event); - Browser.mouseMovementY = Browser.getMovementY(event); - } - - // check if SDL is available - if (typeof SDL != "undefined") { - Browser.mouseX = SDL.mouseX + Browser.mouseMovementX; - Browser.mouseY = SDL.mouseY + Browser.mouseMovementY; - } else { - // just add the mouse delta to the current absolut mouse position - // FIXME: ideally this should be clamped against the canvas size and zero - Browser.mouseX += Browser.mouseMovementX; - Browser.mouseY += Browser.mouseMovementY; - } - } else { - // Otherwise, calculate the movement based on the changes - // in the coordinates. - var rect = Module["canvas"].getBoundingClientRect(); - var cw = Module["canvas"].width; - var ch = Module["canvas"].height; - - // Neither .scrollX or .pageXOffset are defined in a spec, but - // we prefer .scrollX because it is currently in a spec draft. - // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/) - var scrollX = ((typeof window.scrollX !== 'undefined') ? window.scrollX : window.pageXOffset); - var scrollY = ((typeof window.scrollY !== 'undefined') ? window.scrollY : window.pageYOffset); - // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset - // and we have no viable fallback. - assert((typeof scrollX !== 'undefined') && (typeof scrollY !== 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.'); - - if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') { - var touch = event.touch; - if (touch === undefined) { - return; // the "touch" property is only defined in SDL - - } - var adjustedX = touch.pageX - (scrollX + rect.left); - var adjustedY = touch.pageY - (scrollY + rect.top); - - adjustedX = adjustedX * (cw / rect.width); - adjustedY = adjustedY * (ch / rect.height); - - var coords = { x: adjustedX, y: adjustedY }; - - if (event.type === 'touchstart') { - Browser.lastTouches[touch.identifier] = coords; - Browser.touches[touch.identifier] = coords; - } else if (event.type === 'touchend' || event.type === 'touchmove') { - var last = Browser.touches[touch.identifier]; - if (!last) last = coords; - Browser.lastTouches[touch.identifier] = last; - Browser.touches[touch.identifier] = coords; - } - return; - } - - var x = event.pageX - (scrollX + rect.left); - var y = event.pageY - (scrollY + rect.top); - - // the canvas might be CSS-scaled compared to its backbuffer; - // SDL-using content will want mouse coordinates in terms - // of backbuffer units. - x = x * (cw / rect.width); - y = y * (ch / rect.height); - - Browser.mouseMovementX = x - Browser.mouseX; - Browser.mouseMovementY = y - Browser.mouseY; - Browser.mouseX = x; - Browser.mouseY = y; - } - },asyncLoad:function(url, onload, onerror, noRunDep) { - var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : ''; - readAsync(url, function(arrayBuffer) { - assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); - onload(new Uint8Array(arrayBuffer)); - if (dep) removeRunDependency(dep); - }, function(event) { - if (onerror) { - onerror(); - } else { - throw 'Loading data file "' + url + '" failed.'; - } - }); - if (dep) addRunDependency(dep); - },resizeListeners:[],updateResizeListeners:function() { - var canvas = Module['canvas']; - Browser.resizeListeners.forEach(function(listener) { - listener(canvas.width, canvas.height); - }); - },setCanvasSize:function(width, height, noUpdates) { - var canvas = Module['canvas']; - Browser.updateCanvasDimensions(canvas, width, height); - if (!noUpdates) Browser.updateResizeListeners(); - },windowedWidth:0,windowedHeight:0,setFullscreenCanvasSize:function() { - // check if SDL is available - if (typeof SDL != "undefined") { - var flags = HEAPU32[((SDL.screen)>>2)]; - flags = flags | 0x00800000; // set SDL_FULLSCREEN flag - HEAP32[((SDL.screen)>>2)]=flags - } - Browser.updateCanvasDimensions(Module['canvas']); - Browser.updateResizeListeners(); - },setWindowedCanvasSize:function() { - // check if SDL is available - if (typeof SDL != "undefined") { - var flags = HEAPU32[((SDL.screen)>>2)]; - flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag - HEAP32[((SDL.screen)>>2)]=flags - } - Browser.updateCanvasDimensions(Module['canvas']); - Browser.updateResizeListeners(); - },updateCanvasDimensions:function(canvas, wNative, hNative) { - if (wNative && hNative) { - canvas.widthNative = wNative; - canvas.heightNative = hNative; - } else { - wNative = canvas.widthNative; - hNative = canvas.heightNative; - } - var w = wNative; - var h = hNative; - if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) { - if (w/h < Module['forcedAspectRatio']) { - w = Math.round(h * Module['forcedAspectRatio']); - } else { - h = Math.round(w / Module['forcedAspectRatio']); - } - } - if (((document['fullscreenElement'] || document['mozFullScreenElement'] || - document['msFullscreenElement'] || document['webkitFullscreenElement'] || - document['webkitCurrentFullScreenElement']) === canvas.parentNode) && (typeof screen != 'undefined')) { - var factor = Math.min(screen.width / w, screen.height / h); - w = Math.round(w * factor); - h = Math.round(h * factor); - } - if (Browser.resizeCanvas) { - if (canvas.width != w) canvas.width = w; - if (canvas.height != h) canvas.height = h; - if (typeof canvas.style != 'undefined') { - canvas.style.removeProperty( "width"); - canvas.style.removeProperty("height"); - } - } else { - if (canvas.width != wNative) canvas.width = wNative; - if (canvas.height != hNative) canvas.height = hNative; - if (typeof canvas.style != 'undefined') { - if (w != wNative || h != hNative) { - canvas.style.setProperty( "width", w + "px", "important"); - canvas.style.setProperty("height", h + "px", "important"); - } else { - canvas.style.removeProperty( "width"); - canvas.style.removeProperty("height"); - } - } - } - },wgetRequests:{},nextWgetRequestHandle:0,getNextWgetRequestHandle:function() { - var handle = Browser.nextWgetRequestHandle; - Browser.nextWgetRequestHandle++; - return handle; - }}; - - /** @constructor */ - function GLFW_Window(id, width, height, title, monitor, share) { - this.id = id; - this.x = 0; - this.y = 0; - this.fullscreen = false; // Used to determine if app in fullscreen mode - this.storedX = 0; // Used to store X before fullscreen - this.storedY = 0; // Used to store Y before fullscreen - this.width = width; - this.height = height; - this.storedWidth = width; // Used to store width before fullscreen - this.storedHeight = height; // Used to store height before fullscreen - this.title = title; - this.monitor = monitor; - this.share = share; - this.attributes = GLFW.hints; - this.inputModes = { - 0x00033001:0x00034001, // GLFW_CURSOR (GLFW_CURSOR_NORMAL) - 0x00033002:0, // GLFW_STICKY_KEYS - 0x00033003:0, // GLFW_STICKY_MOUSE_BUTTONS - }; - this.buttons = 0; - this.keys = new Array(); - this.domKeys = new Array(); - this.shouldClose = 0; - this.title = null; - this.windowPosFunc = null; // GLFWwindowposfun - this.windowSizeFunc = null; // GLFWwindowsizefun - this.windowCloseFunc = null; // GLFWwindowclosefun - this.windowRefreshFunc = null; // GLFWwindowrefreshfun - this.windowFocusFunc = null; // GLFWwindowfocusfun - this.windowIconifyFunc = null; // GLFWwindowiconifyfun - this.framebufferSizeFunc = null; // GLFWframebuffersizefun - this.mouseButtonFunc = null; // GLFWmousebuttonfun - this.cursorPosFunc = null; // GLFWcursorposfun - this.cursorEnterFunc = null; // GLFWcursorenterfun - this.scrollFunc = null; // GLFWscrollfun - this.dropFunc = null; // GLFWdropfun - this.keyFunc = null; // GLFWkeyfun - this.charFunc = null; // GLFWcharfun - this.userptr = null; - }var GLFW={WindowFromId:function(id) { - if (id <= 0 || !GLFW.windows) return null; - return GLFW.windows[id - 1]; - },joystickFunc:null,errorFunc:null,monitorFunc:null,active:null,windows:null,monitors:null,monitorString:null,versionString:null,initialTime:null,extensions:null,hints:null,defaultHints:{131073:0,131074:0,131075:1,131076:1,131077:1,135169:8,135170:8,135171:8,135172:8,135173:24,135174:8,135175:0,135176:0,135177:0,135178:0,135179:0,135180:0,135181:0,135182:0,135183:0,139265:196609,139266:1,139267:0,139268:0,139269:0,139270:0,139271:0,139272:0},DOMToGLFWKeyCode:function(keycode) { - switch (keycode) { - // these keycodes are only defined for GLFW3, assume they are the same for GLFW2 - case 0x20:return 32; // DOM_VK_SPACE -> GLFW_KEY_SPACE - case 0xDE:return 39; // DOM_VK_QUOTE -> GLFW_KEY_APOSTROPHE - case 0xBC:return 44; // DOM_VK_COMMA -> GLFW_KEY_COMMA - case 0xAD:return 45; // DOM_VK_HYPHEN_MINUS -> GLFW_KEY_MINUS - case 0xBD:return 45; // DOM_VK_MINUS -> GLFW_KEY_MINUS - case 0xBE:return 46; // DOM_VK_PERIOD -> GLFW_KEY_PERIOD - case 0xBF:return 47; // DOM_VK_SLASH -> GLFW_KEY_SLASH - case 0x30:return 48; // DOM_VK_0 -> GLFW_KEY_0 - case 0x31:return 49; // DOM_VK_1 -> GLFW_KEY_1 - case 0x32:return 50; // DOM_VK_2 -> GLFW_KEY_2 - case 0x33:return 51; // DOM_VK_3 -> GLFW_KEY_3 - case 0x34:return 52; // DOM_VK_4 -> GLFW_KEY_4 - case 0x35:return 53; // DOM_VK_5 -> GLFW_KEY_5 - case 0x36:return 54; // DOM_VK_6 -> GLFW_KEY_6 - case 0x37:return 55; // DOM_VK_7 -> GLFW_KEY_7 - case 0x38:return 56; // DOM_VK_8 -> GLFW_KEY_8 - case 0x39:return 57; // DOM_VK_9 -> GLFW_KEY_9 - case 0x3B:return 59; // DOM_VK_SEMICOLON -> GLFW_KEY_SEMICOLON - case 0x3D:return 61; // DOM_VK_EQUALS -> GLFW_KEY_EQUAL - case 0xBB:return 61; // DOM_VK_EQUALS -> GLFW_KEY_EQUAL - case 0x41:return 65; // DOM_VK_A -> GLFW_KEY_A - case 0x42:return 66; // DOM_VK_B -> GLFW_KEY_B - case 0x43:return 67; // DOM_VK_C -> GLFW_KEY_C - case 0x44:return 68; // DOM_VK_D -> GLFW_KEY_D - case 0x45:return 69; // DOM_VK_E -> GLFW_KEY_E - case 0x46:return 70; // DOM_VK_F -> GLFW_KEY_F - case 0x47:return 71; // DOM_VK_G -> GLFW_KEY_G - case 0x48:return 72; // DOM_VK_H -> GLFW_KEY_H - case 0x49:return 73; // DOM_VK_I -> GLFW_KEY_I - case 0x4A:return 74; // DOM_VK_J -> GLFW_KEY_J - case 0x4B:return 75; // DOM_VK_K -> GLFW_KEY_K - case 0x4C:return 76; // DOM_VK_L -> GLFW_KEY_L - case 0x4D:return 77; // DOM_VK_M -> GLFW_KEY_M - case 0x4E:return 78; // DOM_VK_N -> GLFW_KEY_N - case 0x4F:return 79; // DOM_VK_O -> GLFW_KEY_O - case 0x50:return 80; // DOM_VK_P -> GLFW_KEY_P - case 0x51:return 81; // DOM_VK_Q -> GLFW_KEY_Q - case 0x52:return 82; // DOM_VK_R -> GLFW_KEY_R - case 0x53:return 83; // DOM_VK_S -> GLFW_KEY_S - case 0x54:return 84; // DOM_VK_T -> GLFW_KEY_T - case 0x55:return 85; // DOM_VK_U -> GLFW_KEY_U - case 0x56:return 86; // DOM_VK_V -> GLFW_KEY_V - case 0x57:return 87; // DOM_VK_W -> GLFW_KEY_W - case 0x58:return 88; // DOM_VK_X -> GLFW_KEY_X - case 0x59:return 89; // DOM_VK_Y -> GLFW_KEY_Y - case 0x5a:return 90; // DOM_VK_Z -> GLFW_KEY_Z - case 0xDB:return 91; // DOM_VK_OPEN_BRACKET -> GLFW_KEY_LEFT_BRACKET - case 0xDC:return 92; // DOM_VK_BACKSLASH -> GLFW_KEY_BACKSLASH - case 0xDD:return 93; // DOM_VK_CLOSE_BRACKET -> GLFW_KEY_RIGHT_BRACKET - case 0xC0:return 94; // DOM_VK_BACK_QUOTE -> GLFW_KEY_GRAVE_ACCENT - - - case 0x1B:return 256; // DOM_VK_ESCAPE -> GLFW_KEY_ESCAPE - case 0x0D:return 257; // DOM_VK_RETURN -> GLFW_KEY_ENTER - case 0x09:return 258; // DOM_VK_TAB -> GLFW_KEY_TAB - case 0x08:return 259; // DOM_VK_BACK -> GLFW_KEY_BACKSPACE - case 0x2D:return 260; // DOM_VK_INSERT -> GLFW_KEY_INSERT - case 0x2E:return 261; // DOM_VK_DELETE -> GLFW_KEY_DELETE - case 0x27:return 262; // DOM_VK_RIGHT -> GLFW_KEY_RIGHT - case 0x25:return 263; // DOM_VK_LEFT -> GLFW_KEY_LEFT - case 0x28:return 264; // DOM_VK_DOWN -> GLFW_KEY_DOWN - case 0x26:return 265; // DOM_VK_UP -> GLFW_KEY_UP - case 0x21:return 266; // DOM_VK_PAGE_UP -> GLFW_KEY_PAGE_UP - case 0x22:return 267; // DOM_VK_PAGE_DOWN -> GLFW_KEY_PAGE_DOWN - case 0x24:return 268; // DOM_VK_HOME -> GLFW_KEY_HOME - case 0x23:return 269; // DOM_VK_END -> GLFW_KEY_END - case 0x14:return 280; // DOM_VK_CAPS_LOCK -> GLFW_KEY_CAPS_LOCK - case 0x91:return 281; // DOM_VK_SCROLL_LOCK -> GLFW_KEY_SCROLL_LOCK - case 0x90:return 282; // DOM_VK_NUM_LOCK -> GLFW_KEY_NUM_LOCK - case 0x2C:return 283; // DOM_VK_SNAPSHOT -> GLFW_KEY_PRINT_SCREEN - case 0x13:return 284; // DOM_VK_PAUSE -> GLFW_KEY_PAUSE - case 0x70:return 290; // DOM_VK_F1 -> GLFW_KEY_F1 - case 0x71:return 291; // DOM_VK_F2 -> GLFW_KEY_F2 - case 0x72:return 292; // DOM_VK_F3 -> GLFW_KEY_F3 - case 0x73:return 293; // DOM_VK_F4 -> GLFW_KEY_F4 - case 0x74:return 294; // DOM_VK_F5 -> GLFW_KEY_F5 - case 0x75:return 295; // DOM_VK_F6 -> GLFW_KEY_F6 - case 0x76:return 296; // DOM_VK_F7 -> GLFW_KEY_F7 - case 0x77:return 297; // DOM_VK_F8 -> GLFW_KEY_F8 - case 0x78:return 298; // DOM_VK_F9 -> GLFW_KEY_F9 - case 0x79:return 299; // DOM_VK_F10 -> GLFW_KEY_F10 - case 0x7A:return 300; // DOM_VK_F11 -> GLFW_KEY_F11 - case 0x7B:return 301; // DOM_VK_F12 -> GLFW_KEY_F12 - case 0x7C:return 302; // DOM_VK_F13 -> GLFW_KEY_F13 - case 0x7D:return 303; // DOM_VK_F14 -> GLFW_KEY_F14 - case 0x7E:return 304; // DOM_VK_F15 -> GLFW_KEY_F15 - case 0x7F:return 305; // DOM_VK_F16 -> GLFW_KEY_F16 - case 0x80:return 306; // DOM_VK_F17 -> GLFW_KEY_F17 - case 0x81:return 307; // DOM_VK_F18 -> GLFW_KEY_F18 - case 0x82:return 308; // DOM_VK_F19 -> GLFW_KEY_F19 - case 0x83:return 309; // DOM_VK_F20 -> GLFW_KEY_F20 - case 0x84:return 310; // DOM_VK_F21 -> GLFW_KEY_F21 - case 0x85:return 311; // DOM_VK_F22 -> GLFW_KEY_F22 - case 0x86:return 312; // DOM_VK_F23 -> GLFW_KEY_F23 - case 0x87:return 313; // DOM_VK_F24 -> GLFW_KEY_F24 - case 0x88:return 314; // 0x88 (not used?) -> GLFW_KEY_F25 - case 0x60:return 320; // DOM_VK_NUMPAD0 -> GLFW_KEY_KP_0 - case 0x61:return 321; // DOM_VK_NUMPAD1 -> GLFW_KEY_KP_1 - case 0x62:return 322; // DOM_VK_NUMPAD2 -> GLFW_KEY_KP_2 - case 0x63:return 323; // DOM_VK_NUMPAD3 -> GLFW_KEY_KP_3 - case 0x64:return 324; // DOM_VK_NUMPAD4 -> GLFW_KEY_KP_4 - case 0x65:return 325; // DOM_VK_NUMPAD5 -> GLFW_KEY_KP_5 - case 0x66:return 326; // DOM_VK_NUMPAD6 -> GLFW_KEY_KP_6 - case 0x67:return 327; // DOM_VK_NUMPAD7 -> GLFW_KEY_KP_7 - case 0x68:return 328; // DOM_VK_NUMPAD8 -> GLFW_KEY_KP_8 - case 0x69:return 329; // DOM_VK_NUMPAD9 -> GLFW_KEY_KP_9 - case 0x6E:return 330; // DOM_VK_DECIMAL -> GLFW_KEY_KP_DECIMAL - case 0x6F:return 331; // DOM_VK_DIVIDE -> GLFW_KEY_KP_DIVIDE - case 0x6A:return 332; // DOM_VK_MULTIPLY -> GLFW_KEY_KP_MULTIPLY - case 0x6D:return 333; // DOM_VK_SUBTRACT -> GLFW_KEY_KP_SUBTRACT - case 0x6B:return 334; // DOM_VK_ADD -> GLFW_KEY_KP_ADD - // case 0x0D:return 335; // DOM_VK_RETURN -> GLFW_KEY_KP_ENTER (DOM_KEY_LOCATION_RIGHT) - // case 0x61:return 336; // DOM_VK_EQUALS -> GLFW_KEY_KP_EQUAL (DOM_KEY_LOCATION_RIGHT) - case 0x10:return 340; // DOM_VK_SHIFT -> GLFW_KEY_LEFT_SHIFT - case 0x11:return 341; // DOM_VK_CONTROL -> GLFW_KEY_LEFT_CONTROL - case 0x12:return 342; // DOM_VK_ALT -> GLFW_KEY_LEFT_ALT - case 0x5B:return 343; // DOM_VK_WIN -> GLFW_KEY_LEFT_SUPER - // case 0x10:return 344; // DOM_VK_SHIFT -> GLFW_KEY_RIGHT_SHIFT (DOM_KEY_LOCATION_RIGHT) - // case 0x11:return 345; // DOM_VK_CONTROL -> GLFW_KEY_RIGHT_CONTROL (DOM_KEY_LOCATION_RIGHT) - // case 0x12:return 346; // DOM_VK_ALT -> GLFW_KEY_RIGHT_ALT (DOM_KEY_LOCATION_RIGHT) - // case 0x5B:return 347; // DOM_VK_WIN -> GLFW_KEY_RIGHT_SUPER (DOM_KEY_LOCATION_RIGHT) - case 0x5D:return 348; // DOM_VK_CONTEXT_MENU -> GLFW_KEY_MENU - // XXX: GLFW_KEY_WORLD_1, GLFW_KEY_WORLD_2 what are these? - default:return -1; // GLFW_KEY_UNKNOWN - }; - },getModBits:function(win) { - var mod = 0; - if (win.keys[340]) mod |= 0x0001; // GLFW_MOD_SHIFT - if (win.keys[341]) mod |= 0x0002; // GLFW_MOD_CONTROL - if (win.keys[342]) mod |= 0x0004; // GLFW_MOD_ALT - if (win.keys[343]) mod |= 0x0008; // GLFW_MOD_SUPER - return mod; - },onKeyPress:function(event) { - if (!GLFW.active || !GLFW.active.charFunc) return; - if (event.ctrlKey || event.metaKey) return; - - // correct unicode charCode is only available with onKeyPress event - var charCode = event.charCode; - if (charCode == 0 || (charCode >= 0x00 && charCode <= 0x1F)) return; - - - dynCall_vii(GLFW.active.charFunc, GLFW.active.id, charCode); - },onKeyChanged:function(keyCode, status) { - if (!GLFW.active) return; - - var key = GLFW.DOMToGLFWKeyCode(keyCode); - if (key == -1) return; - - var repeat = status && GLFW.active.keys[key]; - GLFW.active.keys[key] = status; - GLFW.active.domKeys[keyCode] = status; - if (!GLFW.active.keyFunc) return; - - - if (repeat) status = 2; // GLFW_REPEAT - dynCall_viiiii(GLFW.active.keyFunc, GLFW.active.id, key, keyCode, status, GLFW.getModBits(GLFW.active)); - },onGamepadConnected:function(event) { - GLFW.refreshJoysticks(); - },onGamepadDisconnected:function(event) { - GLFW.refreshJoysticks(); - },onKeydown:function(event) { - GLFW.onKeyChanged(event.keyCode, 1); // GLFW_PRESS or GLFW_REPEAT - - // This logic comes directly from the sdl implementation. We cannot - // call preventDefault on all keydown events otherwise onKeyPress will - // not get called - if (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */) { - event.preventDefault(); - } - },onKeyup:function(event) { - GLFW.onKeyChanged(event.keyCode, 0); // GLFW_RELEASE - },onBlur:function(event) { - if (!GLFW.active) return; - - for (var i = 0; i < GLFW.active.domKeys.length; ++i) { - if (GLFW.active.domKeys[i]) { - GLFW.onKeyChanged(i, 0); // GLFW_RELEASE - } - } - },onMousemove:function(event) { - if (!GLFW.active) return; - - Browser.calculateMouseEvent(event); - - if (event.target != Module["canvas"] || !GLFW.active.cursorPosFunc) return; - - - dynCall_vidd(GLFW.active.cursorPosFunc, GLFW.active.id, Browser.mouseX, Browser.mouseY); - },DOMToGLFWMouseButton:function(event) { - // DOM and glfw have different button codes. - // See http://www.w3schools.com/jsref/event_button.asp. - var eventButton = event['button']; - if (eventButton > 0) { - if (eventButton == 1) { - eventButton = 2; - } else { - eventButton = 1; - } - } - return eventButton; - },onMouseenter:function(event) { - if (!GLFW.active) return; - - if (event.target != Module["canvas"] || !GLFW.active.cursorEnterFunc) return; - - dynCall_vii(GLFW.active.cursorEnterFunc, GLFW.active.id, 1); - },onMouseleave:function(event) { - if (!GLFW.active) return; - - if (event.target != Module["canvas"] || !GLFW.active.cursorEnterFunc) return; - - dynCall_vii(GLFW.active.cursorEnterFunc, GLFW.active.id, 0); - },onMouseButtonChanged:function(event, status) { - if (!GLFW.active) return; - - Browser.calculateMouseEvent(event); - - if (event.target != Module["canvas"]) return; - - var eventButton = GLFW.DOMToGLFWMouseButton(event); - - if (status == 1) { // GLFW_PRESS - GLFW.active.buttons |= (1 << eventButton); - try { - event.target.setCapture(); - } catch (e) {} - } else { // GLFW_RELEASE - GLFW.active.buttons &= ~(1 << eventButton); - } - - if (!GLFW.active.mouseButtonFunc) return; - - - dynCall_viiii(GLFW.active.mouseButtonFunc, GLFW.active.id, eventButton, status, GLFW.getModBits(GLFW.active)); - },onMouseButtonDown:function(event) { - if (!GLFW.active) return; - GLFW.onMouseButtonChanged(event, 1); // GLFW_PRESS - },onMouseButtonUp:function(event) { - if (!GLFW.active) return; - GLFW.onMouseButtonChanged(event, 0); // GLFW_RELEASE - },onMouseWheel:function(event) { - // Note the minus sign that flips browser wheel direction (positive direction scrolls page down) to native wheel direction (positive direction is mouse wheel up) - var delta = -Browser.getMouseWheelDelta(event); - delta = (delta == 0) ? 0 : (delta > 0 ? Math.max(delta, 1) : Math.min(delta, -1)); // Quantize to integer so that minimum scroll is at least +/- 1. - GLFW.wheelPos += delta; - - if (!GLFW.active || !GLFW.active.scrollFunc || event.target != Module['canvas']) return; - - - var sx = 0; - var sy = 0; - if (event.type == 'mousewheel') { - sx = event.wheelDeltaX; - sy = event.wheelDeltaY; - } else { - sx = event.deltaX; - sy = event.deltaY; - } - - dynCall_vidd(GLFW.active.scrollFunc, GLFW.active.id, sx, sy); - - event.preventDefault(); - },onCanvasResize:function(width, height) { - if (!GLFW.active) return; - - var resizeNeeded = true; - - // If the client is requesting fullscreen mode - if (document["fullscreen"] || document["fullScreen"] || document["mozFullScreen"] || document["webkitIsFullScreen"]) { - GLFW.active.storedX = GLFW.active.x; - GLFW.active.storedY = GLFW.active.y; - GLFW.active.storedWidth = GLFW.active.width; - GLFW.active.storedHeight = GLFW.active.height; - GLFW.active.x = GLFW.active.y = 0; - GLFW.active.width = screen.width; - GLFW.active.height = screen.height; - GLFW.active.fullscreen = true; - - // If the client is reverting from fullscreen mode - } else if (GLFW.active.fullscreen == true) { - GLFW.active.x = GLFW.active.storedX; - GLFW.active.y = GLFW.active.storedY; - GLFW.active.width = GLFW.active.storedWidth; - GLFW.active.height = GLFW.active.storedHeight; - GLFW.active.fullscreen = false; - - // If the width/height values do not match current active window sizes - } else if (GLFW.active.width != width || GLFW.active.height != height) { - GLFW.active.width = width; - GLFW.active.height = height; - } else { - resizeNeeded = false; - } - - // If any of the above conditions were true, we need to resize the canvas - if (resizeNeeded) { - // resets the canvas size to counter the aspect preservation of Browser.updateCanvasDimensions - Browser.setCanvasSize(GLFW.active.width, GLFW.active.height, true); - // TODO: Client dimensions (clientWidth/clientHeight) vs pixel dimensions (width/height) of - // the canvas should drive window and framebuffer size respectfully. - GLFW.onWindowSizeChanged(); - GLFW.onFramebufferSizeChanged(); - } - },onWindowSizeChanged:function() { - if (!GLFW.active) return; - - if (!GLFW.active.windowSizeFunc) return; - - - dynCall_viii(GLFW.active.windowSizeFunc, GLFW.active.id, GLFW.active.width, GLFW.active.height); - },onFramebufferSizeChanged:function() { - if (!GLFW.active) return; - - if (!GLFW.active.framebufferSizeFunc) return; - - dynCall_viii(GLFW.active.framebufferSizeFunc, GLFW.active.id, GLFW.active.width, GLFW.active.height); - },getTime:function() { - return _emscripten_get_now() / 1000; - },setWindowTitle:function(winid, title) { - var win = GLFW.WindowFromId(winid); - if (!win) return; - - win.title = UTF8ToString(title); - if (GLFW.active.id == win.id) { - document.title = win.title; - } - },setJoystickCallback:function(cbfun) { - GLFW.joystickFunc = cbfun; - GLFW.refreshJoysticks(); - },joys:{},lastGamepadState:null,lastGamepadStateFrame:null,refreshJoysticks:function() { - // Produce a new Gamepad API sample if we are ticking a new game frame, or if not using emscripten_set_main_loop() at all to drive animation. - if (Browser.mainLoop.currentFrameNumber !== GLFW.lastGamepadStateFrame || !Browser.mainLoop.currentFrameNumber) { - GLFW.lastGamepadState = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : null); - GLFW.lastGamepadStateFrame = Browser.mainLoop.currentFrameNumber; - - for (var joy = 0; joy < GLFW.lastGamepadState.length; ++joy) { - var gamepad = GLFW.lastGamepadState[joy]; - - if (gamepad) { - if (!GLFW.joys[joy]) { - console.log('glfw joystick connected:',joy); - GLFW.joys[joy] = { - id: allocate(intArrayFromString(gamepad.id), 'i8', ALLOC_NORMAL), - buttonsCount: gamepad.buttons.length, - axesCount: gamepad.axes.length, - buttons: allocate(new Array(gamepad.buttons.length), 'i8', ALLOC_NORMAL), - axes: allocate(new Array(gamepad.axes.length*4), 'float', ALLOC_NORMAL) - }; - - if (GLFW.joystickFunc) { - dynCall_vii(GLFW.joystickFunc, joy, 0x00040001); // GLFW_CONNECTED - } - } - - var data = GLFW.joys[joy]; - - for (var i = 0; i < gamepad.buttons.length; ++i) { - setValue(data.buttons + i, gamepad.buttons[i].pressed, 'i8'); - } - - for (var i = 0; i < gamepad.axes.length; ++i) { - setValue(data.axes + i*4, gamepad.axes[i], 'float'); - } - } else { - if (GLFW.joys[joy]) { - console.log('glfw joystick disconnected',joy); - - if (GLFW.joystickFunc) { - dynCall_vii(GLFW.joystickFunc, joy, 0x00040002); // GLFW_DISCONNECTED - } - - _free(GLFW.joys[joy].id); - _free(GLFW.joys[joy].buttons); - _free(GLFW.joys[joy].axes); - - delete GLFW.joys[joy]; - } - } - } - } - },setKeyCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.keyFunc; - win.keyFunc = cbfun; - return prevcbfun; - },setCharCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.charFunc; - win.charFunc = cbfun; - return prevcbfun; - },setMouseButtonCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.mouseButtonFunc; - win.mouseButtonFunc = cbfun; - return prevcbfun; - },setCursorPosCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.cursorPosFunc; - win.cursorPosFunc = cbfun; - return prevcbfun; - },setScrollCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.scrollFunc; - win.scrollFunc = cbfun; - return prevcbfun; - },setDropCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.dropFunc; - win.dropFunc = cbfun; - return prevcbfun; - },onDrop:function(event) { - if (!GLFW.active || !GLFW.active.dropFunc) return; - if (!event.dataTransfer || !event.dataTransfer.files || event.dataTransfer.files.length == 0) return; - - event.preventDefault(); - - var filenames = allocate(new Array(event.dataTransfer.files.length*4), 'i8*', ALLOC_NORMAL); - var filenamesArray = []; - var count = event.dataTransfer.files.length; - - // Read and save the files to emscripten's FS - var written = 0; - var drop_dir = '.glfw_dropped_files'; - FS.createPath('/', drop_dir); - - function save(file) { - var path = '/' + drop_dir + '/' + file.name.replace(/\//g, '_'); - var reader = new FileReader(); - reader.onloadend = function(e) { - if (reader.readyState != 2) { // not DONE - ++written; - console.log('failed to read dropped file: '+file.name+': '+reader.error); - return; - } - - var data = e.target.result; - FS.writeFile(path, new Uint8Array(data)); - if (++written === count) { - dynCall_viii(GLFW.active.dropFunc, GLFW.active.id, count, filenames); - - for (var i = 0; i < filenamesArray.length; ++i) { - _free(filenamesArray[i]); - } - _free(filenames); - } - }; - reader.readAsArrayBuffer(file); - - var filename = allocate(intArrayFromString(path), 'i8', ALLOC_NORMAL); - filenamesArray.push(filename); - setValue(filenames + i*4, filename, 'i8*'); - } - - for (var i = 0; i < count; ++i) { - save(event.dataTransfer.files[i]); - } - - return false; - },onDragover:function(event) { - if (!GLFW.active || !GLFW.active.dropFunc) return; - - event.preventDefault(); - return false; - },setWindowSizeCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.windowSizeFunc; - win.windowSizeFunc = cbfun; - - - return prevcbfun; - },setWindowCloseCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.windowCloseFunc; - win.windowCloseFunc = cbfun; - return prevcbfun; - },setWindowRefreshCallback:function(winid, cbfun) { - var win = GLFW.WindowFromId(winid); - if (!win) return null; - var prevcbfun = win.windowRefreshFunc; - win.windowRefreshFunc = cbfun; - return prevcbfun; - },onClickRequestPointerLock:function(e) { - if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { - Module['canvas'].requestPointerLock(); - e.preventDefault(); - } - },setInputMode:function(winid, mode, value) { - var win = GLFW.WindowFromId(winid); - if (!win) return; - - switch(mode) { - case 0x00033001: { // GLFW_CURSOR - switch(value) { - case 0x00034001: { // GLFW_CURSOR_NORMAL - win.inputModes[mode] = value; - Module['canvas'].removeEventListener('click', GLFW.onClickRequestPointerLock, true); - Module['canvas'].exitPointerLock(); - break; - } - case 0x00034002: { // GLFW_CURSOR_HIDDEN - console.log("glfwSetInputMode called with GLFW_CURSOR_HIDDEN value not implemented."); - break; - } - case 0x00034003: { // GLFW_CURSOR_DISABLED - win.inputModes[mode] = value; - Module['canvas'].addEventListener('click', GLFW.onClickRequestPointerLock, true); - Module['canvas'].requestPointerLock(); - break; - } - default: { - console.log("glfwSetInputMode called with unknown value parameter value: " + value + "."); - break; - } - } - break; - } - case 0x00033002: { // GLFW_STICKY_KEYS - console.log("glfwSetInputMode called with GLFW_STICKY_KEYS mode not implemented."); - break; - } - case 0x00033003: { // GLFW_STICKY_MOUSE_BUTTONS - console.log("glfwSetInputMode called with GLFW_STICKY_MOUSE_BUTTONS mode not implemented."); - break; - } - default: { - console.log("glfwSetInputMode called with unknown mode parameter value: " + mode + "."); - break; - } - } - },getKey:function(winid, key) { - var win = GLFW.WindowFromId(winid); - if (!win) return 0; - return win.keys[key]; - },getMouseButton:function(winid, button) { - var win = GLFW.WindowFromId(winid); - if (!win) return 0; - return (win.buttons & (1 << button)) > 0; - },getCursorPos:function(winid, x, y) { - setValue(x, Browser.mouseX, 'double'); - setValue(y, Browser.mouseY, 'double'); - },getMousePos:function(winid, x, y) { - setValue(x, Browser.mouseX, 'i32'); - setValue(y, Browser.mouseY, 'i32'); - },setCursorPos:function(winid, x, y) { - },getWindowPos:function(winid, x, y) { - var wx = 0; - var wy = 0; - - var win = GLFW.WindowFromId(winid); - if (win) { - wx = win.x; - wy = win.y; - } - - if (x) { - setValue(x, wx, 'i32'); - } - - if (y) { - setValue(y, wy, 'i32'); - } - },setWindowPos:function(winid, x, y) { - var win = GLFW.WindowFromId(winid); - if (!win) return; - win.x = x; - win.y = y; - },getWindowSize:function(winid, width, height) { - var ww = 0; - var wh = 0; - - var win = GLFW.WindowFromId(winid); - if (win) { - ww = win.width; - wh = win.height; - } - - if (width) { - setValue(width, ww, 'i32'); - } - - if (height) { - setValue(height, wh, 'i32'); - } - },setWindowSize:function(winid, width, height) { - var win = GLFW.WindowFromId(winid); - if (!win) return; - - if (GLFW.active.id == win.id) { - if (width == screen.width && height == screen.height) { - Browser.requestFullscreen(); - } else { - Browser.exitFullscreen(); - Browser.setCanvasSize(width, height); - win.width = width; - win.height = height; - } - } - - if (!win.windowSizeFunc) return; - - - dynCall_viii(win.windowSizeFunc, win.id, width, height); - },createWindow:function(width, height, title, monitor, share) { - var i, id; - for (i = 0; i < GLFW.windows.length && GLFW.windows[i] !== null; i++) { - // no-op - } - if (i > 0) throw "glfwCreateWindow only supports one window at time currently"; - - // id for window - id = i + 1; - - // not valid - if (width <= 0 || height <= 0) return 0; - - if (monitor) { - Browser.requestFullscreen(); - } else { - Browser.setCanvasSize(width, height); - } - - // Create context when there are no existing alive windows - for (i = 0; i < GLFW.windows.length && GLFW.windows[i] == null; i++) { - // no-op - } - if (i == GLFW.windows.length) { - var contextAttributes = { - antialias: (GLFW.hints[0x0002100D] > 1), // GLFW_SAMPLES - depth: (GLFW.hints[0x00021005] > 0), // GLFW_DEPTH_BITS - stencil: (GLFW.hints[0x00021006] > 0), // GLFW_STENCIL_BITS - alpha: (GLFW.hints[0x00021004] > 0) // GLFW_ALPHA_BITS - } - Module.ctx = Browser.createContext(Module['canvas'], true, true, contextAttributes); - } - - // If context creation failed, do not return a valid window - if (!Module.ctx) return 0; - - // Get non alive id - var win = new GLFW_Window(id, width, height, title, monitor, share); - - // Set window to array - if (id - 1 == GLFW.windows.length) { - GLFW.windows.push(win); - } else { - GLFW.windows[id - 1] = win; - } - - GLFW.active = win; - return win.id; - },destroyWindow:function(winid) { - var win = GLFW.WindowFromId(winid); - if (!win) return; - - if (win.windowCloseFunc) - dynCall_vi(win.windowCloseFunc, win.id); - - GLFW.windows[win.id - 1] = null; - if (GLFW.active.id == win.id) - GLFW.active = null; - - // Destroy context when no alive windows - for (var i = 0; i < GLFW.windows.length; i++) - if (GLFW.windows[i] !== null) return; - - Module.ctx = Browser.destroyContext(Module['canvas'], true, true); - },swapBuffers:function(winid) { - },GLFW2ParamToGLFW3Param:function(param) { - var table = { - 0x00030001:0, // GLFW_MOUSE_CURSOR - 0x00030002:0, // GLFW_STICKY_KEYS - 0x00030003:0, // GLFW_STICKY_MOUSE_BUTTONS - 0x00030004:0, // GLFW_SYSTEM_KEYS - 0x00030005:0, // GLFW_KEY_REPEAT - 0x00030006:0, // GLFW_AUTO_POLL_EVENTS - 0x00020001:0, // GLFW_OPENED - 0x00020002:0, // GLFW_ACTIVE - 0x00020003:0, // GLFW_ICONIFIED - 0x00020004:0, // GLFW_ACCELERATED - 0x00020005:0x00021001, // GLFW_RED_BITS - 0x00020006:0x00021002, // GLFW_GREEN_BITS - 0x00020007:0x00021003, // GLFW_BLUE_BITS - 0x00020008:0x00021004, // GLFW_ALPHA_BITS - 0x00020009:0x00021005, // GLFW_DEPTH_BITS - 0x0002000A:0x00021006, // GLFW_STENCIL_BITS - 0x0002000B:0x0002100F, // GLFW_REFRESH_RATE - 0x0002000C:0x00021007, // GLFW_ACCUM_RED_BITS - 0x0002000D:0x00021008, // GLFW_ACCUM_GREEN_BITS - 0x0002000E:0x00021009, // GLFW_ACCUM_BLUE_BITS - 0x0002000F:0x0002100A, // GLFW_ACCUM_ALPHA_BITS - 0x00020010:0x0002100B, // GLFW_AUX_BUFFERS - 0x00020011:0x0002100C, // GLFW_STEREO - 0x00020012:0, // GLFW_WINDOW_NO_RESIZE - 0x00020013:0x0002100D, // GLFW_FSAA_SAMPLES - 0x00020014:0x00022002, // GLFW_OPENGL_VERSION_MAJOR - 0x00020015:0x00022003, // GLFW_OPENGL_VERSION_MINOR - 0x00020016:0x00022006, // GLFW_OPENGL_FORWARD_COMPAT - 0x00020017:0x00022007, // GLFW_OPENGL_DEBUG_CONTEXT - 0x00020018:0x00022008, // GLFW_OPENGL_PROFILE - }; - return table[param]; - }};function _glfwCreateWindow(width, height, title, monitor, share) { - return GLFW.createWindow(width, height, title, monitor, share); - } - - function _glfwGetCursorPos(winid, x, y) { - GLFW.getCursorPos(winid, x, y); - } - - function _glfwInit() { - if (GLFW.windows) return 1; // GL_TRUE - - GLFW.initialTime = GLFW.getTime(); - GLFW.hints = GLFW.defaultHints; - GLFW.windows = new Array() - GLFW.active = null; - - window.addEventListener("gamepadconnected", GLFW.onGamepadConnected, true); - window.addEventListener("gamepaddisconnected", GLFW.onGamepadDisconnected, true); - window.addEventListener("keydown", GLFW.onKeydown, true); - window.addEventListener("keypress", GLFW.onKeyPress, true); - window.addEventListener("keyup", GLFW.onKeyup, true); - window.addEventListener("blur", GLFW.onBlur, true); - Module["canvas"].addEventListener("touchmove", GLFW.onMousemove, true); - Module["canvas"].addEventListener("touchstart", GLFW.onMouseButtonDown, true); - Module["canvas"].addEventListener("touchcancel", GLFW.onMouseButtonUp, true); - Module["canvas"].addEventListener("touchend", GLFW.onMouseButtonUp, true); - Module["canvas"].addEventListener("mousemove", GLFW.onMousemove, true); - Module["canvas"].addEventListener("mousedown", GLFW.onMouseButtonDown, true); - Module["canvas"].addEventListener("mouseup", GLFW.onMouseButtonUp, true); - Module["canvas"].addEventListener('wheel', GLFW.onMouseWheel, true); - Module["canvas"].addEventListener('mousewheel', GLFW.onMouseWheel, true); - Module["canvas"].addEventListener('mouseenter', GLFW.onMouseenter, true); - Module["canvas"].addEventListener('mouseleave', GLFW.onMouseleave, true); - Module["canvas"].addEventListener('drop', GLFW.onDrop, true); - Module["canvas"].addEventListener('dragover', GLFW.onDragover, true); - - Browser.resizeListeners.push(function(width, height) { - GLFW.onCanvasResize(width, height); - }); - return 1; // GL_TRUE - } - - function _glfwMakeContextCurrent(winid) {} - - function _glfwPollEvents() {} - - function _glfwSetCursorPosCallback(winid, cbfun) { - return GLFW.setCursorPosCallback(winid, cbfun); - } - - function _glfwSetInputMode(winid, mode, value) { - GLFW.setInputMode(winid, mode, value); - } - - function _glfwSetKeyCallback(winid, cbfun) { - return GLFW.setKeyCallback(winid, cbfun); - } - - function _glfwSetMouseButtonCallback(winid, cbfun) { - return GLFW.setMouseButtonCallback(winid, cbfun); - } - - function _glfwSetScrollCallback(winid, cbfun) { - return GLFW.setScrollCallback(winid, cbfun); - } - - function _glfwSetWindowSizeCallback(winid, cbfun) { - return GLFW.setWindowSizeCallback(winid, cbfun); - } - - function _glfwSetWindowUserPointer(winid, ptr) { - var win = GLFW.WindowFromId(winid); - if (!win) return; - win.userptr = ptr; - } - - function _glfwSwapBuffers(winid) { - GLFW.swapBuffers(winid); - } - - function _glfwTerminate() { - window.removeEventListener("gamepadconnected", GLFW.onGamepadConnected, true); - window.removeEventListener("gamepaddisconnected", GLFW.onGamepadDisconnected, true); - window.removeEventListener("keydown", GLFW.onKeydown, true); - window.removeEventListener("keypress", GLFW.onKeyPress, true); - window.removeEventListener("keyup", GLFW.onKeyup, true); - window.removeEventListener("blur", GLFW.onBlur, true); - Module["canvas"].removeEventListener("touchmove", GLFW.onMousemove, true); - Module["canvas"].removeEventListener("touchstart", GLFW.onMouseButtonDown, true); - Module["canvas"].removeEventListener("touchcancel", GLFW.onMouseButtonUp, true); - Module["canvas"].removeEventListener("touchend", GLFW.onMouseButtonUp, true); - Module["canvas"].removeEventListener("mousemove", GLFW.onMousemove, true); - Module["canvas"].removeEventListener("mousedown", GLFW.onMouseButtonDown, true); - Module["canvas"].removeEventListener("mouseup", GLFW.onMouseButtonUp, true); - Module["canvas"].removeEventListener('wheel', GLFW.onMouseWheel, true); - Module["canvas"].removeEventListener('mousewheel', GLFW.onMouseWheel, true); - Module["canvas"].removeEventListener('mouseenter', GLFW.onMouseenter, true); - Module["canvas"].removeEventListener('mouseleave', GLFW.onMouseleave, true); - Module["canvas"].removeEventListener('drop', GLFW.onDrop, true); - Module["canvas"].removeEventListener('dragover', GLFW.onDragover, true); - - - Module["canvas"].width = Module["canvas"].height = 1; - GLFW.windows = null; - GLFW.active = null; - } - - function _glfwWindowHint(target, hint) { - GLFW.hints[target] = hint; - } - - - function _usleep(useconds) { - // int usleep(useconds_t useconds); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/usleep.html - // We're single-threaded, so use a busy loop. Super-ugly. - var start = _emscripten_get_now(); - while (_emscripten_get_now() - start < useconds / 1000) { - // Do nothing. - } - }function _nanosleep(rqtp, rmtp) { - // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); - if (rqtp === 0) { - setErrNo(28); - return -1; - } - var seconds = HEAP32[((rqtp)>>2)]; - var nanoseconds = HEAP32[(((rqtp)+(4))>>2)]; - if (nanoseconds < 0 || nanoseconds > 999999999 || seconds < 0) { - setErrNo(28); - return -1; - } - if (rmtp !== 0) { - HEAP32[((rmtp)>>2)]=0; - HEAP32[(((rmtp)+(4))>>2)]=0; - } - return _usleep((seconds * 1e6) + (nanoseconds / 1000)); - } - - function _pthread_create() { - return 6; - } - - function _setTempRet0($i) { - setTempRet0(($i) | 0); - } - - - - function __isLeapYear(year) { - return year%4 === 0 && (year%100 !== 0 || year%400 === 0); - } - - function __arraySum(array, index) { - var sum = 0; - for (var i = 0; i <= index; sum += array[i++]) { - // no-op - } - return sum; - } - - - var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31]; - - var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date, days) { - var newDate = new Date(date.getTime()); - while(days > 0) { - var leap = __isLeapYear(newDate.getFullYear()); - var currentMonth = newDate.getMonth(); - var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth]; - - if (days > daysInCurrentMonth-newDate.getDate()) { - // we spill over to next month - days -= (daysInCurrentMonth-newDate.getDate()+1); - newDate.setDate(1); - if (currentMonth < 11) { - newDate.setMonth(currentMonth+1) - } else { - newDate.setMonth(0); - newDate.setFullYear(newDate.getFullYear()+1); - } - } else { - // we stay in current month - newDate.setDate(newDate.getDate()+days); - return newDate; - } - } - - return newDate; - }function _strftime(s, maxsize, format, tm) { - // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html - - var tm_zone = HEAP32[(((tm)+(40))>>2)]; - - var date = { - tm_sec: HEAP32[((tm)>>2)], - tm_min: HEAP32[(((tm)+(4))>>2)], - tm_hour: HEAP32[(((tm)+(8))>>2)], - tm_mday: HEAP32[(((tm)+(12))>>2)], - tm_mon: HEAP32[(((tm)+(16))>>2)], - tm_year: HEAP32[(((tm)+(20))>>2)], - tm_wday: HEAP32[(((tm)+(24))>>2)], - tm_yday: HEAP32[(((tm)+(28))>>2)], - tm_isdst: HEAP32[(((tm)+(32))>>2)], - tm_gmtoff: HEAP32[(((tm)+(36))>>2)], - tm_zone: tm_zone ? UTF8ToString(tm_zone) : '' - }; - - var pattern = UTF8ToString(format); - - // expand format - var EXPANSION_RULES_1 = { - '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013 - '%D': '%m/%d/%y', // Equivalent to %m / %d / %y - '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d - '%h': '%b', // Equivalent to %b - '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation - '%R': '%H:%M', // Replaced by the time in 24-hour notation - '%T': '%H:%M:%S', // Replaced by the time - '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation - '%X': '%H:%M:%S', // Replaced by the locale's appropriate time representation - // Modified Conversion Specifiers - '%Ec': '%c', // Replaced by the locale's alternative appropriate date and time representation. - '%EC': '%C', // Replaced by the name of the base year (period) in the locale's alternative representation. - '%Ex': '%m/%d/%y', // Replaced by the locale's alternative date representation. - '%EX': '%H:%M:%S', // Replaced by the locale's alternative time representation. - '%Ey': '%y', // Replaced by the offset from %EC (year only) in the locale's alternative representation. - '%EY': '%Y', // Replaced by the full alternative year representation. - '%Od': '%d', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading zeros if there is any alternative symbol for zero; otherwise, with leading characters. - '%Oe': '%e', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading characters. - '%OH': '%H', // Replaced by the hour (24-hour clock) using the locale's alternative numeric symbols. - '%OI': '%I', // Replaced by the hour (12-hour clock) using the locale's alternative numeric symbols. - '%Om': '%m', // Replaced by the month using the locale's alternative numeric symbols. - '%OM': '%M', // Replaced by the minutes using the locale's alternative numeric symbols. - '%OS': '%S', // Replaced by the seconds using the locale's alternative numeric symbols. - '%Ou': '%u', // Replaced by the weekday as a number in the locale's alternative representation (Monday=1). - '%OU': '%U', // Replaced by the week number of the year (Sunday as the first day of the week, rules corresponding to %U ) using the locale's alternative numeric symbols. - '%OV': '%V', // Replaced by the week number of the year (Monday as the first day of the week, rules corresponding to %V ) using the locale's alternative numeric symbols. - '%Ow': '%w', // Replaced by the number of the weekday (Sunday=0) using the locale's alternative numeric symbols. - '%OW': '%W', // Replaced by the week number of the year (Monday as the first day of the week) using the locale's alternative numeric symbols. - '%Oy': '%y', // Replaced by the year (offset from %C ) using the locale's alternative numeric symbols. - }; - for (var rule in EXPANSION_RULES_1) { - pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); - } - - var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - - function leadingSomething(value, digits, character) { - var str = typeof value === 'number' ? value.toString() : (value || ''); - while (str.length < digits) { - str = character[0]+str; - } - return str; - } - - function leadingNulls(value, digits) { - return leadingSomething(value, digits, '0'); - } - - function compareByDay(date1, date2) { - function sgn(value) { - return value < 0 ? -1 : (value > 0 ? 1 : 0); - } - - var compare; - if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) { - if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) { - compare = sgn(date1.getDate()-date2.getDate()); - } - } - return compare; - } - - function getFirstWeekStartDate(janFourth) { - switch (janFourth.getDay()) { - case 0: // Sunday - return new Date(janFourth.getFullYear()-1, 11, 29); - case 1: // Monday - return janFourth; - case 2: // Tuesday - return new Date(janFourth.getFullYear(), 0, 3); - case 3: // Wednesday - return new Date(janFourth.getFullYear(), 0, 2); - case 4: // Thursday - return new Date(janFourth.getFullYear(), 0, 1); - case 5: // Friday - return new Date(janFourth.getFullYear()-1, 11, 31); - case 6: // Saturday - return new Date(janFourth.getFullYear()-1, 11, 30); - } - } - - function getWeekBasedYear(date) { - var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); - - var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); - var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4); - - var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); - var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); - - if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { - // this date is after the start of the first week of this year - if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { - return thisDate.getFullYear()+1; - } else { - return thisDate.getFullYear(); - } - } else { - return thisDate.getFullYear()-1; - } - } - - var EXPANSION_RULES_2 = { - '%a': function(date) { - return WEEKDAYS[date.tm_wday].substring(0,3); - }, - '%A': function(date) { - return WEEKDAYS[date.tm_wday]; - }, - '%b': function(date) { - return MONTHS[date.tm_mon].substring(0,3); - }, - '%B': function(date) { - return MONTHS[date.tm_mon]; - }, - '%C': function(date) { - var year = date.tm_year+1900; - return leadingNulls((year/100)|0,2); - }, - '%d': function(date) { - return leadingNulls(date.tm_mday, 2); - }, - '%e': function(date) { - return leadingSomething(date.tm_mday, 2, ' '); - }, - '%g': function(date) { - // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. - // In this system, weeks begin on a Monday and week 1 of the year is the week that includes - // January 4th, which is also the week that includes the first Thursday of the year, and - // is also the first week that contains at least four days in the year. - // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of - // the last week of the preceding year; thus, for Saturday 2nd January 1999, - // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, - // or 31st is a Monday, it and any following days are part of week 1 of the following year. - // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. - - return getWeekBasedYear(date).toString().substring(2); - }, - '%G': function(date) { - return getWeekBasedYear(date); - }, - '%H': function(date) { - return leadingNulls(date.tm_hour, 2); - }, - '%I': function(date) { - var twelveHour = date.tm_hour; - if (twelveHour == 0) twelveHour = 12; - else if (twelveHour > 12) twelveHour -= 12; - return leadingNulls(twelveHour, 2); - }, - '%j': function(date) { - // Day of the year (001-366) - return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3); - }, - '%m': function(date) { - return leadingNulls(date.tm_mon+1, 2); - }, - '%M': function(date) { - return leadingNulls(date.tm_min, 2); - }, - '%n': function() { - return '\n'; - }, - '%p': function(date) { - if (date.tm_hour >= 0 && date.tm_hour < 12) { - return 'AM'; - } else { - return 'PM'; - } - }, - '%S': function(date) { - return leadingNulls(date.tm_sec, 2); - }, - '%t': function() { - return '\t'; - }, - '%u': function(date) { - return date.tm_wday || 7; - }, - '%U': function(date) { - // Replaced by the week number of the year as a decimal number [00,53]. - // The first Sunday of January is the first day of week 1; - // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] - var janFirst = new Date(date.tm_year+1900, 0, 1); - var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay()); - var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); - - // is target date after the first Sunday? - if (compareByDay(firstSunday, endDate) < 0) { - // calculate difference in days between first Sunday and endDate - var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; - var firstSundayUntilEndJanuary = 31-firstSunday.getDate(); - var days = firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); - return leadingNulls(Math.ceil(days/7), 2); - } - - return compareByDay(firstSunday, janFirst) === 0 ? '01': '00'; - }, - '%V': function(date) { - // Replaced by the week number of the year (Monday as the first day of the week) - // as a decimal number [01,53]. If the week containing 1 January has four - // or more days in the new year, then it is considered week 1. - // Otherwise, it is the last week of the previous year, and the next week is week 1. - // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] - var janFourthThisYear = new Date(date.tm_year+1900, 0, 4); - var janFourthNextYear = new Date(date.tm_year+1901, 0, 4); - - var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); - var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); - - var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); - - if (compareByDay(endDate, firstWeekStartThisYear) < 0) { - // if given date is before this years first week, then it belongs to the 53rd week of last year - return '53'; - } - - if (compareByDay(firstWeekStartNextYear, endDate) <= 0) { - // if given date is after next years first week, then it belongs to the 01th week of next year - return '01'; - } - - // given date is in between CW 01..53 of this calendar year - var daysDifference; - if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) { - // first CW of this year starts last year - daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate() - } else { - // first CW of this year starts this year - daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate(); - } - return leadingNulls(Math.ceil(daysDifference/7), 2); - }, - '%w': function(date) { - return date.tm_wday; - }, - '%W': function(date) { - // Replaced by the week number of the year as a decimal number [00,53]. - // The first Monday of January is the first day of week 1; - // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] - var janFirst = new Date(date.tm_year, 0, 1); - var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1); - var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); - - // is target date after the first Monday? - if (compareByDay(firstMonday, endDate) < 0) { - var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; - var firstMondayUntilEndJanuary = 31-firstMonday.getDate(); - var days = firstMondayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); - return leadingNulls(Math.ceil(days/7), 2); - } - return compareByDay(firstMonday, janFirst) === 0 ? '01': '00'; - }, - '%y': function(date) { - // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] - return (date.tm_year+1900).toString().substring(2); - }, - '%Y': function(date) { - // Replaced by the year as a decimal number (for example, 1997). [ tm_year] - return date.tm_year+1900; - }, - '%z': function(date) { - // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ). - // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). - var off = date.tm_gmtoff; - var ahead = off >= 0; - off = Math.abs(off) / 60; - // convert from minutes into hhmm format (which means 60 minutes = 100 units) - off = (off / 60)*100 + (off % 60); - return (ahead ? '+' : '-') + String("0000" + off).slice(-4); - }, - '%Z': function(date) { - return date.tm_zone; - }, - '%%': function() { - return '%'; - } - }; - for (var rule in EXPANSION_RULES_2) { - if (pattern.indexOf(rule) >= 0) { - pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); - } - } - - var bytes = intArrayFromString(pattern, false); - if (bytes.length > maxsize) { - return 0; - } - - writeArrayToMemory(bytes, s); - return bytes.length-1; - }function _strftime_l(s, maxsize, format, tm) { - return _strftime(s, maxsize, format, tm); // no locale support yet - } - - function _supplyPointer(arrPtr, length) { - var animationIdArr = Module.HEAP32.subarray(arrPtr / 4, arrPtr / 4 + length); - console.log(animationIdArr); - - Module['animationArrayCallback'](animationIdArr); - } - -var FSNode = /** @constructor */ function(parent, name, mode, rdev) { - if (!parent) { - parent = this; // root node sets parent to itself - } - this.parent = parent; - this.mount = parent.mount; - this.mounted = null; - this.id = FS.nextInode++; - this.name = name; - this.mode = mode; - this.node_ops = {}; - this.stream_ops = {}; - this.rdev = rdev; - }; - var readMode = 292/*292*/ | 73/*73*/; - var writeMode = 146/*146*/; - Object.defineProperties(FSNode.prototype, { - read: { - get: /** @this{FSNode} */function() { - return (this.mode & readMode) === readMode; - }, - set: /** @this{FSNode} */function(val) { - val ? this.mode |= readMode : this.mode &= ~readMode; - } - }, - write: { - get: /** @this{FSNode} */function() { - return (this.mode & writeMode) === writeMode; - }, - set: /** @this{FSNode} */function(val) { - val ? this.mode |= writeMode : this.mode &= ~writeMode; - } - }, - isFolder: { - get: /** @this{FSNode} */function() { - return FS.isDir(this.mode); - } - }, - isDevice: { - get: /** @this{FSNode} */function() { - return FS.isChrdev(this.mode); - } - } - }); - FS.FSNode = FSNode; - FS.staticInit();Module["FS_createFolder"] = FS.createFolder;Module["FS_createPath"] = FS.createPath;Module["FS_createDataFile"] = FS.createDataFile;Module["FS_createPreloadedFile"] = FS.createPreloadedFile;Module["FS_createLazyFile"] = FS.createLazyFile;Module["FS_createLink"] = FS.createLink;Module["FS_createDevice"] = FS.createDevice;Module["FS_unlink"] = FS.unlink;; -Fetch.staticInit();; -var GLctx;; -var miniTempWebGLFloatBuffersStorage = new Float32Array(288); - for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { - miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i+1); - } - ; -var __miniTempWebGLIntBuffersStorage = new Int32Array(288); - for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { - __miniTempWebGLIntBuffers[i] = __miniTempWebGLIntBuffersStorage.subarray(0, i+1); - } - ; -Module["requestFullscreen"] = function Module_requestFullscreen(lockPointer, resizeCanvas) { Browser.requestFullscreen(lockPointer, resizeCanvas) }; - Module["requestFullScreen"] = function Module_requestFullScreen() { Browser.requestFullScreen() }; - Module["requestAnimationFrame"] = function Module_requestAnimationFrame(func) { Browser.requestAnimationFrame(func) }; - Module["setCanvasSize"] = function Module_setCanvasSize(width, height, noUpdates) { Browser.setCanvasSize(width, height, noUpdates) }; - Module["pauseMainLoop"] = function Module_pauseMainLoop() { Browser.mainLoop.pause() }; - Module["resumeMainLoop"] = function Module_resumeMainLoop() { Browser.mainLoop.resume() }; - Module["getUserMedia"] = function Module_getUserMedia() { Browser.getUserMedia() } - Module["createContext"] = function Module_createContext(canvas, useWebGL, setInModule, webGLContextAttributes) { return Browser.createContext(canvas, useWebGL, setInModule, webGLContextAttributes) }; -var ASSERTIONS = true; - - - -/** @type {function(string, boolean=, number=)} */ -function intArrayFromString(stringy, dontAddNull, length) { - var len = length > 0 ? length : lengthBytesUTF8(stringy)+1; - var u8array = new Array(len); - var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); - if (dontAddNull) u8array.length = numBytesWritten; - return u8array; -} - -function intArrayToString(array) { - var ret = []; - for (var i = 0; i < array.length; i++) { - var chr = array[i]; - if (chr > 0xFF) { - if (ASSERTIONS) { - assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.'); - } - chr &= 0xFF; - } - ret.push(String.fromCharCode(chr)); - } - return ret.join(''); -} - - -var asmGlobalArg = {}; -var asmLibraryArg = { "__assert_fail": ___assert_fail, "__cxa_allocate_exception": ___cxa_allocate_exception, "__cxa_atexit": ___cxa_atexit, "__cxa_begin_catch": ___cxa_begin_catch, "__cxa_decrement_exception_refcount": ___cxa_decrement_exception_refcount, "__cxa_end_catch": ___cxa_end_catch, "__cxa_find_matching_catch_2": ___cxa_find_matching_catch_2, "__cxa_find_matching_catch_3": ___cxa_find_matching_catch_3, "__cxa_free_exception": ___cxa_free_exception, "__cxa_increment_exception_refcount": ___cxa_increment_exception_refcount, "__cxa_rethrow": ___cxa_rethrow, "__cxa_throw": ___cxa_throw, "__cxa_uncaught_exceptions": ___cxa_uncaught_exceptions, "__map_file": ___map_file, "__resumeException": ___resumeException, "__sys_fcntl64": ___sys_fcntl64, "__sys_ioctl": ___sys_ioctl, "__sys_munmap": ___sys_munmap, "__sys_open": ___sys_open, "_emscripten_fetch_free": __emscripten_fetch_free, "abort": _abort, "emscripten_get_sbrk_ptr": _emscripten_get_sbrk_ptr, "emscripten_is_main_browser_thread": _emscripten_is_main_browser_thread, "emscripten_memcpy_big": _emscripten_memcpy_big, "emscripten_resize_heap": _emscripten_resize_heap, "emscripten_run_script": _emscripten_run_script, "emscripten_start_fetch": _emscripten_start_fetch, "emscripten_webgl_enable_extension": _emscripten_webgl_enable_extension, "emscripten_webgl_get_context_attributes": _emscripten_webgl_get_context_attributes, "emscripten_webgl_get_current_context": _emscripten_webgl_get_current_context, "environ_get": _environ_get, "environ_sizes_get": _environ_sizes_get, "fd_close": _fd_close, "fd_read": _fd_read, "fd_seek": _fd_seek, "fd_write": _fd_write, "getTempRet0": _getTempRet0, "glActiveTexture": _glActiveTexture, "glAttachShader": _glAttachShader, "glBindAttribLocation": _glBindAttribLocation, "glBindBuffer": _glBindBuffer, "glBindBufferRange": _glBindBufferRange, "glBindFramebuffer": _glBindFramebuffer, "glBindRenderbuffer": _glBindRenderbuffer, "glBindTexture": _glBindTexture, "glBindVertexArray": _glBindVertexArray, "glBlendFuncSeparate": _glBlendFuncSeparate, "glBlitFramebuffer": _glBlitFramebuffer, "glBufferData": _glBufferData, "glBufferSubData": _glBufferSubData, "glClear": _glClear, "glClearColor": _glClearColor, "glClearDepthf": _glClearDepthf, "glColorMask": _glColorMask, "glCompileShader": _glCompileShader, "glCompressedTexImage2D": _glCompressedTexImage2D, "glCreateProgram": _glCreateProgram, "glCreateShader": _glCreateShader, "glDeleteBuffers": _glDeleteBuffers, "glDeleteFramebuffers": _glDeleteFramebuffers, "glDeleteRenderbuffers": _glDeleteRenderbuffers, "glDeleteTextures": _glDeleteTextures, "glDeleteVertexArrays": _glDeleteVertexArrays, "glDepthFunc": _glDepthFunc, "glDepthMask": _glDepthMask, "glDepthRangef": _glDepthRangef, "glDisable": _glDisable, "glDrawElements": _glDrawElements, "glEnable": _glEnable, "glEnableVertexAttribArray": _glEnableVertexAttribArray, "glFramebufferRenderbuffer": _glFramebufferRenderbuffer, "glFramebufferTexture2D": _glFramebufferTexture2D, "glFrontFace": _glFrontFace, "glGenBuffers": _glGenBuffers, "glGenFramebuffers": _glGenFramebuffers, "glGenRenderbuffers": _glGenRenderbuffers, "glGenTextures": _glGenTextures, "glGenVertexArrays": _glGenVertexArrays, "glGenerateMipmap": _glGenerateMipmap, "glGetActiveUniform": _glGetActiveUniform, "glGetFloatv": _glGetFloatv, "glGetIntegerv": _glGetIntegerv, "glGetProgramInfoLog": _glGetProgramInfoLog, "glGetProgramiv": _glGetProgramiv, "glGetShaderInfoLog": _glGetShaderInfoLog, "glGetShaderiv": _glGetShaderiv, "glGetUniformBlockIndex": _glGetUniformBlockIndex, "glGetUniformLocation": _glGetUniformLocation, "glLinkProgram": _glLinkProgram, "glReadBuffer": _glReadBuffer, "glReadPixels": _glReadPixels, "glRenderbufferStorageMultisample": _glRenderbufferStorageMultisample, "glScissor": _glScissor, "glShaderSource": _glShaderSource, "glTexImage2D": _glTexImage2D, "glTexParameterf": _glTexParameterf, "glTexParameteri": _glTexParameteri, "glUniform1i": _glUniform1i, "glUniform4fv": _glUniform4fv, "glUniform4iv": _glUniform4iv, "glUniformBlockBinding": _glUniformBlockBinding, "glUniformMatrix2fv": _glUniformMatrix2fv, "glUniformMatrix3fv": _glUniformMatrix3fv, "glUniformMatrix4fv": _glUniformMatrix4fv, "glUseProgram": _glUseProgram, "glVertexAttribPointer": _glVertexAttribPointer, "glViewport": _glViewport, "glfwCreateWindow": _glfwCreateWindow, "glfwGetCursorPos": _glfwGetCursorPos, "glfwInit": _glfwInit, "glfwMakeContextCurrent": _glfwMakeContextCurrent, "glfwPollEvents": _glfwPollEvents, "glfwSetCursorPosCallback": _glfwSetCursorPosCallback, "glfwSetInputMode": _glfwSetInputMode, "glfwSetKeyCallback": _glfwSetKeyCallback, "glfwSetMouseButtonCallback": _glfwSetMouseButtonCallback, "glfwSetScrollCallback": _glfwSetScrollCallback, "glfwSetWindowSizeCallback": _glfwSetWindowSizeCallback, "glfwSetWindowUserPointer": _glfwSetWindowUserPointer, "glfwSwapBuffers": _glfwSwapBuffers, "glfwTerminate": _glfwTerminate, "glfwWindowHint": _glfwWindowHint, "invoke_diii": invoke_diii, "invoke_fiii": invoke_fiii, "invoke_i": invoke_i, "invoke_ii": invoke_ii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_iiiii": invoke_iiiii, "invoke_iiiiid": invoke_iiiiid, "invoke_iiiiii": invoke_iiiiii, "invoke_iiiiiii": invoke_iiiiiii, "invoke_iiiiiiii": invoke_iiiiiiii, "invoke_iiiiiiiiiii": invoke_iiiiiiiiiii, "invoke_iiiiiiiiiiii": invoke_iiiiiiiiiiii, "invoke_iiiiiiiiiiiii": invoke_iiiiiiiiiiiii, "invoke_jiiii": invoke_jiiii, "invoke_v": invoke_v, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viii": invoke_viii, "invoke_viiii": invoke_viiii, "invoke_viiiiiii": invoke_viiiiiii, "invoke_viiiiiiiiii": invoke_viiiiiiiiii, "invoke_viiiiiiiiiiiiiii": invoke_viiiiiiiiiiiiiii, "memory": wasmMemory, "nanosleep": _nanosleep, "pthread_create": _pthread_create, "setTempRet0": _setTempRet0, "strftime_l": _strftime_l, "supplyPointer": _supplyPointer, "table": wasmTable }; -var asm = createWasm(); -/** @type {function(...*):?} */ -var ___wasm_call_ctors = Module["___wasm_call_ctors"] = createExportWrapper("__wasm_call_ctors"); - -/** @type {function(...*):?} */ -var _createWebJsScene = Module["_createWebJsScene"] = createExportWrapper("createWebJsScene"); - -/** @type {function(...*):?} */ -var _setNewUrls = Module["_setNewUrls"] = createExportWrapper("setNewUrls"); - -/** @type {function(...*):?} */ -var _setScene = Module["_setScene"] = createExportWrapper("setScene"); - -/** @type {function(...*):?} */ -var _setMap = Module["_setMap"] = createExportWrapper("setMap"); - -/** @type {function(...*):?} */ -var _setSceneFileDataId = Module["_setSceneFileDataId"] = createExportWrapper("setSceneFileDataId"); - -/** @type {function(...*):?} */ -var _setSceneSize = Module["_setSceneSize"] = createExportWrapper("setSceneSize"); - -/** @type {function(...*):?} */ -var _addHorizontalViewDir = Module["_addHorizontalViewDir"] = createExportWrapper("addHorizontalViewDir"); - -/** @type {function(...*):?} */ -var _addVerticalViewDir = Module["_addVerticalViewDir"] = createExportWrapper("addVerticalViewDir"); - -/** @type {function(...*):?} */ -var _zoomInFromMouseScroll = Module["_zoomInFromMouseScroll"] = createExportWrapper("zoomInFromMouseScroll"); - -/** @type {function(...*):?} */ -var _addCameraViewOffset = Module["_addCameraViewOffset"] = createExportWrapper("addCameraViewOffset"); - -/** @type {function(...*):?} */ -var _startMovingForward = Module["_startMovingForward"] = createExportWrapper("startMovingForward"); - -/** @type {function(...*):?} */ -var _startMovingBackwards = Module["_startMovingBackwards"] = createExportWrapper("startMovingBackwards"); - -/** @type {function(...*):?} */ -var _stopMovingForward = Module["_stopMovingForward"] = createExportWrapper("stopMovingForward"); - -/** @type {function(...*):?} */ -var _stopMovingBackwards = Module["_stopMovingBackwards"] = createExportWrapper("stopMovingBackwards"); - -/** @type {function(...*):?} */ -var _setClearColor = Module["_setClearColor"] = createExportWrapper("setClearColor"); - -/** @type {function(...*):?} */ -var _setFarPlane = Module["_setFarPlane"] = createExportWrapper("setFarPlane"); - -/** @type {function(...*):?} */ -var _setFarPlaneForCulling = Module["_setFarPlaneForCulling"] = createExportWrapper("setFarPlaneForCulling"); - -/** @type {function(...*):?} */ -var _setAnimationId = Module["_setAnimationId"] = createExportWrapper("setAnimationId"); - -/** @type {function(...*):?} */ -var _setReplaceParticleColors = Module["_setReplaceParticleColors"] = createExportWrapper("setReplaceParticleColors"); - -/** @type {function(...*):?} */ -var _resetReplaceParticleColor = Module["_resetReplaceParticleColor"] = createExportWrapper("resetReplaceParticleColor"); - -/** @type {function(...*):?} */ -var _setTextures = Module["_setTextures"] = createExportWrapper("setTextures"); - -/** @type {function(...*):?} */ -var _setScenePos = Module["_setScenePos"] = createExportWrapper("setScenePos"); - -/** @type {function(...*):?} */ -var _gameloop = Module["_gameloop"] = createExportWrapper("gameloop"); - -/** @type {function(...*):?} */ -var _main = Module["_main"] = createExportWrapper("main"); - -/** @type {function(...*):?} */ -var _fflush = Module["_fflush"] = createExportWrapper("fflush"); - -/** @type {function(...*):?} */ -var _malloc = Module["_malloc"] = createExportWrapper("malloc"); - -/** @type {function(...*):?} */ -var _free = Module["_free"] = createExportWrapper("free"); - -/** @type {function(...*):?} */ -var ___errno_location = Module["___errno_location"] = createExportWrapper("__errno_location"); - -/** @type {function(...*):?} */ -var _setThrew = Module["_setThrew"] = createExportWrapper("setThrew"); - -/** @type {function(...*):?} */ -var stackSave = Module["stackSave"] = createExportWrapper("stackSave"); - -/** @type {function(...*):?} */ -var stackRestore = Module["stackRestore"] = createExportWrapper("stackRestore"); - -/** @type {function(...*):?} */ -var stackAlloc = Module["stackAlloc"] = createExportWrapper("stackAlloc"); - -/** @type {function(...*):?} */ -var _emscripten_main_thread_process_queued_calls = Module["_emscripten_main_thread_process_queued_calls"] = createExportWrapper("emscripten_main_thread_process_queued_calls"); - -/** @type {function(...*):?} */ -var dynCall_v = Module["dynCall_v"] = createExportWrapper("dynCall_v"); - -/** @type {function(...*):?} */ -var dynCall_vi = Module["dynCall_vi"] = createExportWrapper("dynCall_vi"); - -/** @type {function(...*):?} */ -var dynCall_vii = Module["dynCall_vii"] = createExportWrapper("dynCall_vii"); - -/** @type {function(...*):?} */ -var dynCall_viii = Module["dynCall_viii"] = createExportWrapper("dynCall_viii"); - -/** @type {function(...*):?} */ -var dynCall_viiii = Module["dynCall_viiii"] = createExportWrapper("dynCall_viiii"); - -/** @type {function(...*):?} */ -var dynCall_viiiiiii = Module["dynCall_viiiiiii"] = createExportWrapper("dynCall_viiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_viiiiiiiiii = Module["dynCall_viiiiiiiiii"] = createExportWrapper("dynCall_viiiiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_viiiiiiiiiiiiiii = Module["dynCall_viiiiiiiiiiiiiii"] = createExportWrapper("dynCall_viiiiiiiiiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_i = Module["dynCall_i"] = createExportWrapper("dynCall_i"); - -/** @type {function(...*):?} */ -var dynCall_ii = Module["dynCall_ii"] = createExportWrapper("dynCall_ii"); - -/** @type {function(...*):?} */ -var dynCall_iii = Module["dynCall_iii"] = createExportWrapper("dynCall_iii"); - -/** @type {function(...*):?} */ -var dynCall_iiii = Module["dynCall_iiii"] = createExportWrapper("dynCall_iiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiii = Module["dynCall_iiiii"] = createExportWrapper("dynCall_iiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiii = Module["dynCall_iiiiii"] = createExportWrapper("dynCall_iiiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiiii = Module["dynCall_iiiiiii"] = createExportWrapper("dynCall_iiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiiiii = Module["dynCall_iiiiiiii"] = createExportWrapper("dynCall_iiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiiiiiiii = Module["dynCall_iiiiiiiiiii"] = createExportWrapper("dynCall_iiiiiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiiiiiiiii = Module["dynCall_iiiiiiiiiiii"] = createExportWrapper("dynCall_iiiiiiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiiiiiiiiii = Module["dynCall_iiiiiiiiiiiii"] = createExportWrapper("dynCall_iiiiiiiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiid = Module["dynCall_iiiiid"] = createExportWrapper("dynCall_iiiiid"); - -/** @type {function(...*):?} */ -var dynCall_jiiii = Module["dynCall_jiiii"] = createExportWrapper("dynCall_jiiii"); - -/** @type {function(...*):?} */ -var dynCall_fiii = Module["dynCall_fiii"] = createExportWrapper("dynCall_fiii"); - -/** @type {function(...*):?} */ -var dynCall_diii = Module["dynCall_diii"] = createExportWrapper("dynCall_diii"); - -/** @type {function(...*):?} */ -var dynCall_vidd = Module["dynCall_vidd"] = createExportWrapper("dynCall_vidd"); - -/** @type {function(...*):?} */ -var dynCall_viiiii = Module["dynCall_viiiii"] = createExportWrapper("dynCall_viiiii"); - -/** @type {function(...*):?} */ -var dynCall_vifff = Module["dynCall_vifff"] = createExportWrapper("dynCall_vifff"); - -/** @type {function(...*):?} */ -var dynCall_viijii = Module["dynCall_viijii"] = createExportWrapper("dynCall_viijii"); - -/** @type {function(...*):?} */ -var dynCall_vif = Module["dynCall_vif"] = createExportWrapper("dynCall_vif"); - -/** @type {function(...*):?} */ -var dynCall_viff = Module["dynCall_viff"] = createExportWrapper("dynCall_viff"); - -/** @type {function(...*):?} */ -var dynCall_viiffff = Module["dynCall_viiffff"] = createExportWrapper("dynCall_viiffff"); - -/** @type {function(...*):?} */ -var dynCall_vid = Module["dynCall_vid"] = createExportWrapper("dynCall_vid"); - -/** @type {function(...*):?} */ -var dynCall_di = Module["dynCall_di"] = createExportWrapper("dynCall_di"); - -/** @type {function(...*):?} */ -var dynCall_viiiiiiiii = Module["dynCall_viiiiiiiii"] = createExportWrapper("dynCall_viiiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_viiiiiiii = Module["dynCall_viiiiiiii"] = createExportWrapper("dynCall_viiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_fi = Module["dynCall_fi"] = createExportWrapper("dynCall_fi"); - -/** @type {function(...*):?} */ -var dynCall_vidi = Module["dynCall_vidi"] = createExportWrapper("dynCall_vidi"); - -/** @type {function(...*):?} */ -var dynCall_viid = Module["dynCall_viid"] = createExportWrapper("dynCall_viid"); - -/** @type {function(...*):?} */ -var dynCall_viffff = Module["dynCall_viffff"] = createExportWrapper("dynCall_viffff"); - -/** @type {function(...*):?} */ -var dynCall_viiiiii = Module["dynCall_viiiiii"] = createExportWrapper("dynCall_viiiiii"); - -/** @type {function(...*):?} */ -var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji"); - -/** @type {function(...*):?} */ -var dynCall_iidiiii = Module["dynCall_iidiiii"] = createExportWrapper("dynCall_iidiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiiiiii = Module["dynCall_iiiiiiiii"] = createExportWrapper("dynCall_iiiiiiiii"); - -/** @type {function(...*):?} */ -var dynCall_iiiiij = Module["dynCall_iiiiij"] = createExportWrapper("dynCall_iiiiij"); - -/** @type {function(...*):?} */ -var dynCall_iiiiijj = Module["dynCall_iiiiijj"] = createExportWrapper("dynCall_iiiiijj"); - -/** @type {function(...*):?} */ -var dynCall_iiiiiijj = Module["dynCall_iiiiiijj"] = createExportWrapper("dynCall_iiiiiijj"); - -/** @type {function(...*):?} */ -var __growWasmMemory = Module["__growWasmMemory"] = createExportWrapper("__growWasmMemory"); - - -function invoke_ii(index,a1) { - var sp = stackSave(); - try { - return dynCall_ii(index,a1); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_vii(index,a1,a2) { - var sp = stackSave(); - try { - dynCall_vii(index,a1,a2); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iii(index,a1,a2) { - var sp = stackSave(); - try { - return dynCall_iii(index,a1,a2); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_vi(index,a1) { - var sp = stackSave(); - try { - dynCall_vi(index,a1); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_v(index) { - var sp = stackSave(); - try { - dynCall_v(index); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiii(index,a1,a2,a3) { - var sp = stackSave(); - try { - return dynCall_iiii(index,a1,a2,a3); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6) { - var sp = stackSave(); - try { - return dynCall_iiiiiii(index,a1,a2,a3,a4,a5,a6); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiiiii(index,a1,a2,a3,a4,a5) { - var sp = stackSave(); - try { - return dynCall_iiiiii(index,a1,a2,a3,a4,a5); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiiiid(index,a1,a2,a3,a4,a5) { - var sp = stackSave(); - try { - return dynCall_iiiiid(index,a1,a2,a3,a4,a5); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7) { - var sp = stackSave(); - try { - return dynCall_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) { - var sp = stackSave(); - try { - return dynCall_iiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiiii(index,a1,a2,a3,a4) { - var sp = stackSave(); - try { - return dynCall_iiiii(index,a1,a2,a3,a4); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_viiii(index,a1,a2,a3,a4) { - var sp = stackSave(); - try { - dynCall_viiii(index,a1,a2,a3,a4); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12) { - var sp = stackSave(); - try { - return dynCall_iiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_fiii(index,a1,a2,a3) { - var sp = stackSave(); - try { - return dynCall_fiii(index,a1,a2,a3); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_diii(index,a1,a2,a3) { - var sp = stackSave(); - try { - return dynCall_diii(index,a1,a2,a3); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_i(index) { - var sp = stackSave(); - try { - return dynCall_i(index); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7) { - var sp = stackSave(); - try { - dynCall_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_iiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) { - var sp = stackSave(); - try { - return dynCall_iiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_viiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) { - var sp = stackSave(); - try { - dynCall_viiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_viii(index,a1,a2,a3) { - var sp = stackSave(); - try { - dynCall_viii(index,a1,a2,a3); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_viiiiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15) { - var sp = stackSave(); - try { - dynCall_viiiiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - -function invoke_jiiii(index,a1,a2,a3,a4) { - var sp = stackSave(); - try { - return dynCall_jiiii(index,a1,a2,a3,a4); - } catch(e) { - stackRestore(sp); - if (e !== e+0 && e !== 'longjmp') throw e; - _setThrew(1, 0); - } -} - - - - -// === Auto-generated postamble setup entry stuff === - -if (!Object.getOwnPropertyDescriptor(Module, "intArrayFromString")) Module["intArrayFromString"] = function() { abort("'intArrayFromString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "intArrayToString")) Module["intArrayToString"] = function() { abort("'intArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "ccall")) Module["ccall"] = function() { abort("'ccall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "cwrap")) Module["cwrap"] = function() { abort("'cwrap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "setValue")) Module["setValue"] = function() { abort("'setValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "getValue")) Module["getValue"] = function() { abort("'getValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "allocate")) Module["allocate"] = function() { abort("'allocate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -Module["getMemory"] = getMemory; -if (!Object.getOwnPropertyDescriptor(Module, "UTF8ArrayToString")) Module["UTF8ArrayToString"] = function() { abort("'UTF8ArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "UTF8ToString")) Module["UTF8ToString"] = function() { abort("'UTF8ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF8Array")) Module["stringToUTF8Array"] = function() { abort("'stringToUTF8Array' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF8")) Module["stringToUTF8"] = function() { abort("'stringToUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF8")) Module["lengthBytesUTF8"] = function() { abort("'lengthBytesUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stackTrace")) Module["stackTrace"] = function() { abort("'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "addOnPreRun")) Module["addOnPreRun"] = function() { abort("'addOnPreRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "addOnInit")) Module["addOnInit"] = function() { abort("'addOnInit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "addOnPreMain")) Module["addOnPreMain"] = function() { abort("'addOnPreMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "addOnExit")) Module["addOnExit"] = function() { abort("'addOnExit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "addOnPostRun")) Module["addOnPostRun"] = function() { abort("'addOnPostRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeStringToMemory")) Module["writeStringToMemory"] = function() { abort("'writeStringToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeArrayToMemory")) Module["writeArrayToMemory"] = function() { abort("'writeArrayToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeAsciiToMemory")) Module["writeAsciiToMemory"] = function() { abort("'writeAsciiToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -Module["addRunDependency"] = addRunDependency; -Module["removeRunDependency"] = removeRunDependency; -Module["FS_createFolder"] = FS.createFolder; -Module["FS_createPath"] = FS.createPath; -Module["FS_createDataFile"] = FS.createDataFile; -Module["FS_createPreloadedFile"] = FS.createPreloadedFile; -Module["FS_createLazyFile"] = FS.createLazyFile; -Module["FS_createLink"] = FS.createLink; -Module["FS_createDevice"] = FS.createDevice; -Module["FS_unlink"] = FS.unlink; -if (!Object.getOwnPropertyDescriptor(Module, "dynamicAlloc")) Module["dynamicAlloc"] = function() { abort("'dynamicAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "loadDynamicLibrary")) Module["loadDynamicLibrary"] = function() { abort("'loadDynamicLibrary' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "loadWebAssemblyModule")) Module["loadWebAssemblyModule"] = function() { abort("'loadWebAssemblyModule' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "getLEB")) Module["getLEB"] = function() { abort("'getLEB' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "getFunctionTables")) Module["getFunctionTables"] = function() { abort("'getFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "alignFunctionTables")) Module["alignFunctionTables"] = function() { abort("'alignFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "registerFunctions")) Module["registerFunctions"] = function() { abort("'registerFunctions' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "addFunction")) Module["addFunction"] = function() { abort("'addFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "removeFunction")) Module["removeFunction"] = function() { abort("'removeFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "getFuncWrapper")) Module["getFuncWrapper"] = function() { abort("'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "prettyPrint")) Module["prettyPrint"] = function() { abort("'prettyPrint' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "makeBigInt")) Module["makeBigInt"] = function() { abort("'makeBigInt' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "dynCall")) Module["dynCall"] = function() { abort("'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "getCompilerSetting")) Module["getCompilerSetting"] = function() { abort("'getCompilerSetting' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "print")) Module["print"] = function() { abort("'print' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "printErr")) Module["printErr"] = function() { abort("'printErr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "getTempRet0")) Module["getTempRet0"] = function() { abort("'getTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "setTempRet0")) Module["setTempRet0"] = function() { abort("'setTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "callMain")) Module["callMain"] = function() { abort("'callMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "abort")) Module["abort"] = function() { abort("'abort' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stringToNewUTF8")) Module["stringToNewUTF8"] = function() { abort("'stringToNewUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "emscripten_realloc_buffer")) Module["emscripten_realloc_buffer"] = function() { abort("'emscripten_realloc_buffer' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "ENV")) Module["ENV"] = function() { abort("'ENV' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "ERRNO_CODES")) Module["ERRNO_CODES"] = function() { abort("'ERRNO_CODES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "ERRNO_MESSAGES")) Module["ERRNO_MESSAGES"] = function() { abort("'ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "setErrNo")) Module["setErrNo"] = function() { abort("'setErrNo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "DNS")) Module["DNS"] = function() { abort("'DNS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "GAI_ERRNO_MESSAGES")) Module["GAI_ERRNO_MESSAGES"] = function() { abort("'GAI_ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "Protocols")) Module["Protocols"] = function() { abort("'Protocols' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "Sockets")) Module["Sockets"] = function() { abort("'Sockets' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "traverseStack")) Module["traverseStack"] = function() { abort("'traverseStack' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "UNWIND_CACHE")) Module["UNWIND_CACHE"] = function() { abort("'UNWIND_CACHE' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "withBuiltinMalloc")) Module["withBuiltinMalloc"] = function() { abort("'withBuiltinMalloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "readAsmConstArgsArray")) Module["readAsmConstArgsArray"] = function() { abort("'readAsmConstArgsArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "readAsmConstArgs")) Module["readAsmConstArgs"] = function() { abort("'readAsmConstArgs' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "mainThreadEM_ASM")) Module["mainThreadEM_ASM"] = function() { abort("'mainThreadEM_ASM' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "jstoi_q")) Module["jstoi_q"] = function() { abort("'jstoi_q' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "jstoi_s")) Module["jstoi_s"] = function() { abort("'jstoi_s' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "getExecutableName")) Module["getExecutableName"] = function() { abort("'getExecutableName' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "listenOnce")) Module["listenOnce"] = function() { abort("'listenOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "autoResumeAudioContext")) Module["autoResumeAudioContext"] = function() { abort("'autoResumeAudioContext' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "abortStackOverflow")) Module["abortStackOverflow"] = function() { abort("'abortStackOverflow' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "reallyNegative")) Module["reallyNegative"] = function() { abort("'reallyNegative' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "formatString")) Module["formatString"] = function() { abort("'formatString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "PATH")) Module["PATH"] = function() { abort("'PATH' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "PATH_FS")) Module["PATH_FS"] = function() { abort("'PATH_FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "SYSCALLS")) Module["SYSCALLS"] = function() { abort("'SYSCALLS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "syscallMmap2")) Module["syscallMmap2"] = function() { abort("'syscallMmap2' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "syscallMunmap")) Module["syscallMunmap"] = function() { abort("'syscallMunmap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "JSEvents")) Module["JSEvents"] = function() { abort("'JSEvents' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "specialHTMLTargets")) Module["specialHTMLTargets"] = function() { abort("'specialHTMLTargets' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "maybeCStringToJsString")) Module["maybeCStringToJsString"] = function() { abort("'maybeCStringToJsString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "findEventTarget")) Module["findEventTarget"] = function() { abort("'findEventTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "findCanvasEventTarget")) Module["findCanvasEventTarget"] = function() { abort("'findCanvasEventTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "polyfillSetImmediate")) Module["polyfillSetImmediate"] = function() { abort("'polyfillSetImmediate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "demangle")) Module["demangle"] = function() { abort("'demangle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "demangleAll")) Module["demangleAll"] = function() { abort("'demangleAll' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "jsStackTrace")) Module["jsStackTrace"] = function() { abort("'jsStackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stackTrace")) Module["stackTrace"] = function() { abort("'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "getEnvStrings")) Module["getEnvStrings"] = function() { abort("'getEnvStrings' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "checkWasiClock")) Module["checkWasiClock"] = function() { abort("'checkWasiClock' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64")) Module["writeI53ToI64"] = function() { abort("'writeI53ToI64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64Clamped")) Module["writeI53ToI64Clamped"] = function() { abort("'writeI53ToI64Clamped' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64Signaling")) Module["writeI53ToI64Signaling"] = function() { abort("'writeI53ToI64Signaling' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToU64Clamped")) Module["writeI53ToU64Clamped"] = function() { abort("'writeI53ToU64Clamped' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToU64Signaling")) Module["writeI53ToU64Signaling"] = function() { abort("'writeI53ToU64Signaling' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "readI53FromI64")) Module["readI53FromI64"] = function() { abort("'readI53FromI64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "readI53FromU64")) Module["readI53FromU64"] = function() { abort("'readI53FromU64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "convertI32PairToI53")) Module["convertI32PairToI53"] = function() { abort("'convertI32PairToI53' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "convertU32PairToI53")) Module["convertU32PairToI53"] = function() { abort("'convertU32PairToI53' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "exceptionLast")) Module["exceptionLast"] = function() { abort("'exceptionLast' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "exceptionCaught")) Module["exceptionCaught"] = function() { abort("'exceptionCaught' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "ExceptionInfoAttrs")) Module["ExceptionInfoAttrs"] = function() { abort("'ExceptionInfoAttrs' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "ExceptionInfo")) Module["ExceptionInfo"] = function() { abort("'ExceptionInfo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "CatchInfo")) Module["CatchInfo"] = function() { abort("'CatchInfo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "exception_addRef")) Module["exception_addRef"] = function() { abort("'exception_addRef' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "exception_decRef")) Module["exception_decRef"] = function() { abort("'exception_decRef' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "Browser")) Module["Browser"] = function() { abort("'Browser' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "FS")) Module["FS"] = function() { abort("'FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "MEMFS")) Module["MEMFS"] = function() { abort("'MEMFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "TTY")) Module["TTY"] = function() { abort("'TTY' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "PIPEFS")) Module["PIPEFS"] = function() { abort("'PIPEFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "SOCKFS")) Module["SOCKFS"] = function() { abort("'SOCKFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "tempFixedLengthArray")) Module["tempFixedLengthArray"] = function() { abort("'tempFixedLengthArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "miniTempWebGLFloatBuffers")) Module["miniTempWebGLFloatBuffers"] = function() { abort("'miniTempWebGLFloatBuffers' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "heapObjectForWebGLType")) Module["heapObjectForWebGLType"] = function() { abort("'heapObjectForWebGLType' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "heapAccessShiftForWebGLHeap")) Module["heapAccessShiftForWebGLHeap"] = function() { abort("'heapAccessShiftForWebGLHeap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "GL")) Module["GL"] = function() { abort("'GL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGet")) Module["emscriptenWebGLGet"] = function() { abort("'emscriptenWebGLGet' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "computeUnpackAlignedImageSize")) Module["computeUnpackAlignedImageSize"] = function() { abort("'computeUnpackAlignedImageSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetTexPixelData")) Module["emscriptenWebGLGetTexPixelData"] = function() { abort("'emscriptenWebGLGetTexPixelData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetUniform")) Module["emscriptenWebGLGetUniform"] = function() { abort("'emscriptenWebGLGetUniform' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetVertexAttrib")) Module["emscriptenWebGLGetVertexAttrib"] = function() { abort("'emscriptenWebGLGetVertexAttrib' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetBufferBinding")) Module["emscriptenWebGLGetBufferBinding"] = function() { abort("'emscriptenWebGLGetBufferBinding' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLValidateMapBufferTarget")) Module["emscriptenWebGLValidateMapBufferTarget"] = function() { abort("'emscriptenWebGLValidateMapBufferTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "writeGLArray")) Module["writeGLArray"] = function() { abort("'writeGLArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "AL")) Module["AL"] = function() { abort("'AL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "SDL_unicode")) Module["SDL_unicode"] = function() { abort("'SDL_unicode' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "SDL_ttfContext")) Module["SDL_ttfContext"] = function() { abort("'SDL_ttfContext' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "SDL_audio")) Module["SDL_audio"] = function() { abort("'SDL_audio' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "SDL")) Module["SDL"] = function() { abort("'SDL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "SDL_gfx")) Module["SDL_gfx"] = function() { abort("'SDL_gfx' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "GLUT")) Module["GLUT"] = function() { abort("'GLUT' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "EGL")) Module["EGL"] = function() { abort("'EGL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "GLFW_Window")) Module["GLFW_Window"] = function() { abort("'GLFW_Window' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "GLFW")) Module["GLFW"] = function() { abort("'GLFW' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "GLEW")) Module["GLEW"] = function() { abort("'GLEW' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "IDBStore")) Module["IDBStore"] = function() { abort("'IDBStore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "runAndAbortIfError")) Module["runAndAbortIfError"] = function() { abort("'runAndAbortIfError' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "Fetch")) Module["Fetch"] = function() { abort("'Fetch' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "__emscripten_fetch_delete_cached_data")) Module["__emscripten_fetch_delete_cached_data"] = function() { abort("'__emscripten_fetch_delete_cached_data' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "__emscripten_fetch_load_cached_data")) Module["__emscripten_fetch_load_cached_data"] = function() { abort("'__emscripten_fetch_load_cached_data' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "__emscripten_fetch_cache_data")) Module["__emscripten_fetch_cache_data"] = function() { abort("'__emscripten_fetch_cache_data' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "__emscripten_fetch_xhr")) Module["__emscripten_fetch_xhr"] = function() { abort("'__emscripten_fetch_xhr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetIndexed")) Module["emscriptenWebGLGetIndexed"] = function() { abort("'emscriptenWebGLGetIndexed' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "warnOnce")) Module["warnOnce"] = function() { abort("'warnOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stackSave")) Module["stackSave"] = function() { abort("'stackSave' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stackRestore")) Module["stackRestore"] = function() { abort("'stackRestore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stackAlloc")) Module["stackAlloc"] = function() { abort("'stackAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "AsciiToString")) Module["AsciiToString"] = function() { abort("'AsciiToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stringToAscii")) Module["stringToAscii"] = function() { abort("'stringToAscii' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "UTF16ToString")) Module["UTF16ToString"] = function() { abort("'UTF16ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF16")) Module["stringToUTF16"] = function() { abort("'stringToUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF16")) Module["lengthBytesUTF16"] = function() { abort("'lengthBytesUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "UTF32ToString")) Module["UTF32ToString"] = function() { abort("'UTF32ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF32")) Module["stringToUTF32"] = function() { abort("'stringToUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF32")) Module["lengthBytesUTF32"] = function() { abort("'lengthBytesUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "allocateUTF8")) Module["allocateUTF8"] = function() { abort("'allocateUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "allocateUTF8OnStack")) Module["allocateUTF8OnStack"] = function() { abort("'allocateUTF8OnStack' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -Module["writeStackCookie"] = writeStackCookie; -Module["checkStackCookie"] = checkStackCookie;if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_NORMAL")) Object.defineProperty(Module, "ALLOC_NORMAL", { configurable: true, get: function() { abort("'ALLOC_NORMAL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } }); -if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_STACK")) Object.defineProperty(Module, "ALLOC_STACK", { configurable: true, get: function() { abort("'ALLOC_STACK' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } }); -if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_DYNAMIC")) Object.defineProperty(Module, "ALLOC_DYNAMIC", { configurable: true, get: function() { abort("'ALLOC_DYNAMIC' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } }); -if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_NONE")) Object.defineProperty(Module, "ALLOC_NONE", { configurable: true, get: function() { abort("'ALLOC_NONE' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } }); - - -var calledRun; - -/** - * @constructor - * @this {ExitStatus} - */ -function ExitStatus(status) { - this.name = "ExitStatus"; - this.message = "Program terminated with exit(" + status + ")"; - this.status = status; -} - -var calledMain = false; - - -dependenciesFulfilled = function runCaller() { - // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) - if (!calledRun) run(); - if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled -}; - -function callMain(args) { - assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); - assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); - - var entryFunction = Module['_main']; - - - args = args || []; - - var argc = args.length+1; - var argv = stackAlloc((argc + 1) * 4); - HEAP32[argv >> 2] = allocateUTF8OnStack(thisProgram); - for (var i = 1; i < argc; i++) { - HEAP32[(argv >> 2) + i] = allocateUTF8OnStack(args[i - 1]); - } - HEAP32[(argv >> 2) + argc] = 0; - - try { - - - var ret = entryFunction(argc, argv); - - - // In PROXY_TO_PTHREAD builds, we should never exit the runtime below, as execution is asynchronously handed - // off to a pthread. - // if we're not running an evented main loop, it's time to exit - exit(ret, /* implicit = */ true); - } - catch(e) { - if (e instanceof ExitStatus) { - // exit() throws this once it's done to make sure execution - // has been stopped completely - return; - } else if (e == 'unwind') { - // running an evented main loop, don't immediately exit - noExitRuntime = true; - return; - } else { - var toLog = e; - if (e && typeof e === 'object' && e.stack) { - toLog = [e, e.stack]; - } - err('exception thrown: ' + toLog); - quit_(1, e); - } - } finally { - calledMain = true; - } -} - - - - -/** @type {function(Array=)} */ -function run(args) { - args = args || arguments_; - - if (runDependencies > 0) { - return; - } - - writeStackCookie(); - - preRun(); - - if (runDependencies > 0) return; // a preRun added a dependency, run will be called later - - function doRun() { - // run may have just been called through dependencies being fulfilled just in this very frame, - // or while the async setStatus time below was happening - if (calledRun) return; - calledRun = true; - Module['calledRun'] = true; - - if (ABORT) return; - - initRuntime(); - - preMain(); - - if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); - - if (shouldRunNow) callMain(args); - - postRun(); - } - - if (Module['setStatus']) { - Module['setStatus']('Running...'); - setTimeout(function() { - setTimeout(function() { - Module['setStatus'](''); - }, 1); - doRun(); - }, 1); - } else - { - doRun(); - } - checkStackCookie(); -} -Module['run'] = run; - -function checkUnflushedContent() { - // Compiler settings do not allow exiting the runtime, so flushing - // the streams is not possible. but in ASSERTIONS mode we check - // if there was something to flush, and if so tell the user they - // should request that the runtime be exitable. - // Normally we would not even include flush() at all, but in ASSERTIONS - // builds we do so just for this check, and here we see if there is any - // content to flush, that is, we check if there would have been - // something a non-ASSERTIONS build would have not seen. - // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0 - // mode (which has its own special function for this; otherwise, all - // the code is inside libc) - var print = out; - var printErr = err; - var has = false; - out = err = function(x) { - has = true; - } - try { // it doesn't matter if it fails - var flush = Module['_fflush']; - if (flush) flush(0); - // also flush in the JS FS layer - ['stdout', 'stderr'].forEach(function(name) { - var info = FS.analyzePath('/dev/' + name); - if (!info) return; - var stream = info.object; - var rdev = stream.rdev; - var tty = TTY.ttys[rdev]; - if (tty && tty.output && tty.output.length) { - has = true; - } - }); - } catch(e) {} - out = print; - err = printErr; - if (has) { - warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline when you printf etc.'); - } -} - -/** @param {boolean|number=} implicit */ -function exit(status, implicit) { - checkUnflushedContent(); - - // if this is just main exit-ing implicitly, and the status is 0, then we - // don't need to do anything here and can just leave. if the status is - // non-zero, though, then we need to report it. - // (we may have warned about this earlier, if a situation justifies doing so) - if (implicit && noExitRuntime && status === 0) { - return; - } - - if (noExitRuntime) { - // if exit() was called, we may warn the user if the runtime isn't actually being shut down - if (!implicit) { - var msg = 'program exited (with status: ' + status + '), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)'; - err(msg); - } - } else { - - ABORT = true; - EXITSTATUS = status; - - exitRuntime(); - - if (Module['onExit']) Module['onExit'](status); - } - - quit_(status, new ExitStatus(status)); -} - -if (Module['preInit']) { - if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; - while (Module['preInit'].length > 0) { - Module['preInit'].pop()(); - } -} - -// shouldRunNow refers to calling main(), not run(). -var shouldRunNow = true; - -if (Module['noInitialRun']) shouldRunNow = false; - - - noExitRuntime = true; - -run(); - - - - - - -// {{MODULE_ADDITIONS}} - - - +var Module=typeof Module!=="undefined"?Module:{};if(!Module.expectedDataFileDownloads){Module.expectedDataFileDownloads=0}Module.expectedDataFileDownloads++;(function(){var loadPackage=function(metadata){var PACKAGE_PATH;if(typeof window==="object"){PACKAGE_PATH=window["encodeURIComponent"](window.location.pathname.toString().substring(0,window.location.pathname.toString().lastIndexOf("/"))+"/")}else if(typeof location!=="undefined"){PACKAGE_PATH=encodeURIComponent(location.pathname.toString().substring(0,location.pathname.toString().lastIndexOf("/"))+"/")}else{throw"using preloaded data can only be done on a web page or in a web worker"}var PACKAGE_NAME="../project.data";var REMOTE_PACKAGE_BASE="project.data";if(typeof Module["locateFilePackage"]==="function"&&!Module["locateFile"]){Module["locateFile"]=Module["locateFilePackage"];err("warning: you defined Module.locateFilePackage, that has been renamed to Module.locateFile (using your locateFilePackage for now)")}var REMOTE_PACKAGE_NAME=Module["locateFile"]?Module["locateFile"](REMOTE_PACKAGE_BASE,""):REMOTE_PACKAGE_BASE;var REMOTE_PACKAGE_SIZE=metadata["remote_package_size"];var PACKAGE_UUID=metadata["package_uuid"];function fetchRemotePackage(packageName,packageSize,callback,errback){var xhr=new XMLHttpRequest;xhr.open("GET",packageName,true);xhr.responseType="arraybuffer";xhr.onprogress=function(event){var url=packageName;var size=packageSize;if(event.total)size=event.total;if(event.loaded){if(!xhr.addedTotal){xhr.addedTotal=true;if(!Module.dataFileDownloads)Module.dataFileDownloads={};Module.dataFileDownloads[url]={loaded:event.loaded,total:size}}else{Module.dataFileDownloads[url].loaded=event.loaded}var total=0;var loaded=0;var num=0;for(var download in Module.dataFileDownloads){var data=Module.dataFileDownloads[download];total+=data.total;loaded+=data.loaded;num++}total=Math.ceil(total*Module.expectedDataFileDownloads/num);if(Module["setStatus"])Module["setStatus"]("Downloading data... ("+loaded+"/"+total+")")}else if(!Module.dataFileDownloads){if(Module["setStatus"])Module["setStatus"]("Downloading data...")}};xhr.onerror=function(event){throw new Error("NetworkError for: "+packageName)};xhr.onload=function(event){if(xhr.status==200||xhr.status==304||xhr.status==206||xhr.status==0&&xhr.response){var packageData=xhr.response;callback(packageData)}else{throw new Error(xhr.statusText+" : "+xhr.responseURL)}};xhr.send(null)}function handleError(error){console.error("package error:",error)}var fetchedCallback=null;var fetched=Module["getPreloadedPackage"]?Module["getPreloadedPackage"](REMOTE_PACKAGE_NAME,REMOTE_PACKAGE_SIZE):null;if(!fetched)fetchRemotePackage(REMOTE_PACKAGE_NAME,REMOTE_PACKAGE_SIZE,function(data){if(fetchedCallback){fetchedCallback(data);fetchedCallback=null}else{fetched=data}},handleError);function runWithFS(){function assert(check,msg){if(!check)throw msg+(new Error).stack}Module["FS_createPath"]("/","glsl",true,true);Module["FS_createPath"]("/glsl","glsl20",true,true);Module["FS_createPath"]("/glsl","glsl3.3",true,true);function DataRequest(start,end,audio){this.start=start;this.end=end;this.audio=audio}DataRequest.prototype={requests:{},open:function(mode,name){this.name=name;this.requests[name]=this;Module["addRunDependency"]("fp "+this.name)},send:function(){},onload:function(){var byteArray=this.byteArray.subarray(this.start,this.end);this.finish(byteArray)},finish:function(byteArray){var that=this;Module["FS_createDataFile"](this.name,null,byteArray,true,true,true);Module["removeRunDependency"]("fp "+that.name);this.requests[this.name]=null}};var files=metadata["files"];for(var i=0;i>2];var end=ret+size+15&-16;HEAP32[DYNAMICTOP_PTR>>2]=end;return ret}function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}function getNativeTypeSize(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return 4}else if(type[0]==="i"){var bits=Number(type.substr(1));assert(bits%8===0,"getNativeTypeSize invalid bits "+bits+", type "+type);return bits/8}else{return 0}}}}function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}}function convertJsFunctionToWasm(func,sig){if(typeof WebAssembly.Function==="function"){var typeNames={"i":"i32","j":"i64","f":"f32","d":"f64"};var type={parameters:[],results:sig[0]=="v"?[]:[typeNames[sig[0]]]};for(var i=1;i>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":2915,"maximum":2915,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i>2]=0}stop=ret+size;while(ptr>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}function AsciiToString(ptr){var str="";while(1){var ch=HEAPU8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var i=0;var str="";while(1){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0||i==maxBytesToRead/2)return str;++i;str+=String.fromCharCode(codeUnit)}}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var STACK_BASE=5383760,DYNAMIC_BASE=5383760,DYNAMICTOP_PTR=140688;var INITIAL_INITIAL_MEMORY=Module["INITIAL_MEMORY"]||41943040;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE,"maximum":2147483648/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_abs=Math.abs;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_min=Math.min;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";var wasmBinaryFile="project.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"env":asmLibraryArg,"wasi_snapshot_preview1":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function demangle(func){return func}function demangleAll(text){var regex=/\b_Z[\w\d_]+/g;return text.replace(regex,function(x){var y=demangle(x);return x===y?x:y+" ["+x+"]"})}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){var js=jsStackTrace();if(Module["extraStackTrace"])js+="\n"+Module["extraStackTrace"]();return demangleAll(js)}var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16};function ___cxa_allocate_exception(size){return _malloc(size+ExceptionInfoAttrs.SIZE)+ExceptionInfoAttrs.SIZE}function _atexit(func,arg){}function ___cxa_atexit(a0,a1){return _atexit(a0,a1)}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-ExceptionInfoAttrs.SIZE;this.set_type=function(type){HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]=type};this.get_type=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=prev-1;return prev===1}}function CatchInfo(ptr){this.free=function(){_free(this.ptr);this.ptr=0};this.set_base_ptr=function(basePtr){HEAP32[this.ptr>>2]=basePtr};this.get_base_ptr=function(){return HEAP32[this.ptr>>2]};this.set_adjusted_ptr=function(adjustedPtr){var ptrSize=4;HEAP32[this.ptr+ptrSize>>2]=adjustedPtr};this.get_adjusted_ptr=function(){var ptrSize=4;return HEAP32[this.ptr+ptrSize>>2]};this.get_exception_ptr=function(){var isPointer=___cxa_is_pointer_type(this.get_exception_info().get_type());if(isPointer){return HEAP32[this.get_base_ptr()>>2]}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.get_base_ptr()};this.get_exception_info=function(){return new ExceptionInfo(this.get_base_ptr())};if(ptr===undefined){this.ptr=_malloc(8);this.set_adjusted_ptr(0)}else{this.ptr=ptr}}var exceptionCaught=[];function exception_addRef(info){info.add_ref()}function ___cxa_begin_catch(ptr){var catchInfo=new CatchInfo(ptr);var info=catchInfo.get_exception_info();if(!info.get_caught()){info.set_caught(true);__ZSt18uncaught_exceptionv.uncaught_exceptions--}info.set_rethrown(false);exceptionCaught.push(catchInfo);exception_addRef(info);return catchInfo.get_exception_ptr()}function ___cxa_call_unexpected(exception){err("Unexpected exception thrown, this is not properly supported - aborting");ABORT=true;throw exception}function ___cxa_current_primary_exception(){if(!exceptionCaught.length){return 0}var catchInfo=exceptionCaught[exceptionCaught.length-1];exception_addRef(catchInfo.get_exception_info());return catchInfo.get_base_ptr()}function ___cxa_free_exception(ptr){return _free(new ExceptionInfo(ptr).ptr)}function exception_decRef(info){if(info.release_ref()&&!info.get_rethrown()){var destructor=info.get_destructor();if(destructor){Module["dynCall_ii"](destructor,info.excPtr)}___cxa_free_exception(info.excPtr)}}function ___cxa_decrement_exception_refcount(ptr){if(!ptr)return;exception_decRef(new ExceptionInfo(ptr))}var exceptionLast=0;function ___cxa_end_catch(){_setThrew(0);var catchInfo=exceptionCaught.pop();exception_decRef(catchInfo.get_exception_info());catchInfo.free();exceptionLast=0}function ___resumeException(catchInfoPtr){var catchInfo=new CatchInfo(catchInfoPtr);var ptr=catchInfo.get_base_ptr();if(!exceptionLast){exceptionLast=ptr}catchInfo.free();throw ptr}function ___cxa_find_matching_catch_2(){var thrown=exceptionLast;if(!thrown){return(setTempRet0(0),0)|0}var info=new ExceptionInfo(thrown);var thrownType=info.get_type();var catchInfo=new CatchInfo;catchInfo.set_base_ptr(thrown);if(!thrownType){return(setTempRet0(0),catchInfo.ptr)|0}var typeArray=Array.prototype.slice.call(arguments);var thrownBuf=140848;HEAP32[thrownBuf>>2]=thrown;for(var i=0;i>2];if(thrown!==adjusted){catchInfo.set_adjusted_ptr(adjusted)}return(setTempRet0(caughtType),catchInfo.ptr)|0}}return(setTempRet0(thrownType),catchInfo.ptr)|0}function ___cxa_find_matching_catch_3(){var thrown=exceptionLast;if(!thrown){return(setTempRet0(0),0)|0}var info=new ExceptionInfo(thrown);var thrownType=info.get_type();var catchInfo=new CatchInfo;catchInfo.set_base_ptr(thrown);if(!thrownType){return(setTempRet0(0),catchInfo.ptr)|0}var typeArray=Array.prototype.slice.call(arguments);var thrownBuf=140848;HEAP32[thrownBuf>>2]=thrown;for(var i=0;i>2];if(thrown!==adjusted){catchInfo.set_adjusted_ptr(adjusted)}return(setTempRet0(caughtType),catchInfo.ptr)|0}}return(setTempRet0(thrownType),catchInfo.ptr)|0}function ___cxa_find_matching_catch_5(){var thrown=exceptionLast;if(!thrown){return(setTempRet0(0),0)|0}var info=new ExceptionInfo(thrown);var thrownType=info.get_type();var catchInfo=new CatchInfo;catchInfo.set_base_ptr(thrown);if(!thrownType){return(setTempRet0(0),catchInfo.ptr)|0}var typeArray=Array.prototype.slice.call(arguments);var thrownBuf=140848;HEAP32[thrownBuf>>2]=thrown;for(var i=0;i>2];if(thrown!==adjusted){catchInfo.set_adjusted_ptr(adjusted)}return(setTempRet0(caughtType),catchInfo.ptr)|0}}return(setTempRet0(thrownType),catchInfo.ptr)|0}function ___cxa_increment_exception_refcount(ptr){if(!ptr)return;exception_addRef(new ExceptionInfo(ptr))}function ___cxa_rethrow(){var catchInfo=exceptionCaught.pop();var info=catchInfo.get_exception_info();var ptr=catchInfo.get_base_ptr();if(!info.get_rethrown()){exceptionCaught.push(catchInfo);info.set_rethrown(true)}else{catchInfo.free()}exceptionLast=ptr;throw ptr}function ___cxa_rethrow_primary_exception(ptr){if(!ptr)return;var catchInfo=new CatchInfo;catchInfo.set_base_ptr(ptr);var info=catchInfo.get_exception_info();exceptionCaught.push(catchInfo);info.set_rethrown(true);___cxa_rethrow()}function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;if(!("uncaught_exception"in __ZSt18uncaught_exceptionv)){__ZSt18uncaught_exceptionv.uncaught_exceptions=1}else{__ZSt18uncaught_exceptionv.uncaught_exceptions++}throw ptr}function ___cxa_uncaught_exceptions(){return __ZSt18uncaught_exceptionv.uncaught_exceptions}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}function ___map_file(pathname,size){setErrNo(63);return-1}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node}return node},getFileDataAsRegularArray:function(node){if(node.contents&&node.contents.subarray){var arr=[];for(var i=0;i=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);return},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;return}if(!node.contents||node.contents.subarray){var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize;return}if(!node.contents)node.contents=[];if(node.contents.length>newSize)node.contents.length=newSize;else while(node.contents.length=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"rs":1052672,"r+":2,"w":577,"wx":705,"xw":705,"w+":578,"wx+":706,"xw+":706,"a":1089,"ax":1217,"xa":1217,"a+":1090,"ax+":1218,"xa+":1218},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){if(!FS.forceLoadFile(node)){throw new FS.ErrnoError(29)}return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){if(!FS.forceLoadFile(node)){throw new FS.ErrnoError(29)}var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_getdents64(fd,dirp,count){try{var stream=SYSCALLS.getStreamFromFD(fd);if(!stream.getdents){stream.getdents=FS.readdir(stream.path)}var struct_size=280;var pos=0;var off=FS.llseek(stream,0,1);var idx=Math.floor(off/struct_size);while(idx>>0,(tempDouble=id,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[dirp+pos>>2]=tempI64[0],HEAP32[dirp+pos+4>>2]=tempI64[1];tempI64=[(idx+1)*struct_size>>>0,(tempDouble=(idx+1)*struct_size,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[dirp+pos+8>>2]=tempI64[0],HEAP32[dirp+pos+12>>2]=tempI64[1];HEAP16[dirp+pos+16>>1]=280;HEAP8[dirp+pos+18>>0]=type;stringToUTF8(name,dirp+pos+19,256);pos+=struct_size;idx+=1}FS.llseek(stream,idx*struct_size,0);return pos}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_lstat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.lstat,path,buf)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_mkdir(path,mode){try{path=SYSCALLS.getStr(path);return SYSCALLS.doMkdir(path,mode)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function syscallMunmap(addr,len){if((addr|0)===-1||len===0){return-28}var info=SYSCALLS.mappings[addr];if(!info)return 0;if(len===info.len){var stream=FS.getStream(info.fd);if(info.prot&2){SYSCALLS.doMsync(addr,stream,len,info.flags,info.offset)}FS.munmap(stream);SYSCALLS.mappings[addr]=null;if(info.allocated){_free(info.malloc)}}return 0}function ___sys_munmap(addr,len){try{return syscallMunmap(addr,len)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=SYSCALLS.get();var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_rmdir(path){try{path=SYSCALLS.getStr(path);FS.rmdir(path);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_unlink(path){try{path=SYSCALLS.getStr(path);FS.unlink(path);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>2])}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function __emscripten_fetch_free(id){delete Fetch.xhrs[id-1]}function _abort(){abort()}var _emscripten_get_now;_emscripten_get_now=function(){return performance.now()};var _emscripten_get_now_is_monotonic=true;function _clock_gettime(clk_id,tp){var now;if(clk_id===0){now=Date.now()}else if((clk_id===1||clk_id===4)&&_emscripten_get_now_is_monotonic){now=_emscripten_get_now()}else{setErrNo(28);return-1}HEAP32[tp>>2]=now/1e3|0;HEAP32[tp+4>>2]=now%1e3*1e3*1e3|0;return 0}function _emscripten_get_sbrk_ptr(){return 140688}function _emscripten_is_main_browser_thread(){return!ENVIRONMENT_IS_WORKER}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var PAGE_MULTIPLE=65536;var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),PAGE_MULTIPLE));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}function _emscripten_run_script(ptr){eval(UTF8ToString(ptr))}var Fetch={xhrs:[],setu64:function(addr,val){HEAPU32[addr>>2]=val;HEAPU32[addr+4>>2]=val/4294967296|0},openDatabase:function(dbname,dbversion,onsuccess,onerror){try{var openRequest=indexedDB.open(dbname,dbversion)}catch(e){return onerror(e)}openRequest.onupgradeneeded=function(event){var db=event.target.result;if(db.objectStoreNames.contains("FILES")){db.deleteObjectStore("FILES")}db.createObjectStore("FILES")};openRequest.onsuccess=function(event){onsuccess(event.target.result)};openRequest.onerror=function(error){onerror(error)}},staticInit:function(){var isMainThread=true;var onsuccess=function(db){Fetch.dbInstance=db;if(isMainThread){removeRunDependency("library_fetch_init")}};var onerror=function(){Fetch.dbInstance=false;if(isMainThread){removeRunDependency("library_fetch_init")}};Fetch.openDatabase("emscripten_filesystem",1,onsuccess,onerror);if(typeof ENVIRONMENT_IS_FETCH_WORKER==="undefined"||!ENVIRONMENT_IS_FETCH_WORKER)addRunDependency("library_fetch_init")}};function __emscripten_fetch_xhr(fetch,onsuccess,onerror,onprogress,onreadystatechange){var url=HEAPU32[fetch+8>>2];if(!url){onerror(fetch,0,"no url specified!");return}var url_=UTF8ToString(url);var fetch_attr=fetch+112;var requestMethod=UTF8ToString(fetch_attr);if(!requestMethod)requestMethod="GET";var userData=HEAPU32[fetch+4>>2];var fetchAttributes=HEAPU32[fetch_attr+52>>2];var timeoutMsecs=HEAPU32[fetch_attr+56>>2];var withCredentials=!!HEAPU32[fetch_attr+60>>2];var destinationPath=HEAPU32[fetch_attr+64>>2];var userName=HEAPU32[fetch_attr+68>>2];var password=HEAPU32[fetch_attr+72>>2];var requestHeaders=HEAPU32[fetch_attr+76>>2];var overriddenMimeType=HEAPU32[fetch_attr+80>>2];var dataPtr=HEAPU32[fetch_attr+84>>2];var dataLength=HEAPU32[fetch_attr+88>>2];var fetchAttrLoadToMemory=!!(fetchAttributes&1);var fetchAttrStreamData=!!(fetchAttributes&2);var fetchAttrSynchronous=!!(fetchAttributes&64);var userNameStr=userName?UTF8ToString(userName):undefined;var passwordStr=password?UTF8ToString(password):undefined;var overriddenMimeTypeStr=overriddenMimeType?UTF8ToString(overriddenMimeType):undefined;var xhr=new XMLHttpRequest;xhr.withCredentials=withCredentials;xhr.open(requestMethod,url_,!fetchAttrSynchronous,userNameStr,passwordStr);if(!fetchAttrSynchronous)xhr.timeout=timeoutMsecs;xhr.url_=url_;xhr.responseType="arraybuffer";if(overriddenMimeType){xhr.overrideMimeType(overriddenMimeTypeStr)}if(requestHeaders){for(;;){var key=HEAPU32[requestHeaders>>2];if(!key)break;var value=HEAPU32[requestHeaders+4>>2];if(!value)break;requestHeaders+=8;var keyStr=UTF8ToString(key);var valueStr=UTF8ToString(value);xhr.setRequestHeader(keyStr,valueStr)}}Fetch.xhrs.push(xhr);var id=Fetch.xhrs.length;HEAPU32[fetch+0>>2]=id;var data=dataPtr&&dataLength?HEAPU8.slice(dataPtr,dataPtr+dataLength):null;function saveResponse(condition){var ptr=0;var ptrLen=0;if(condition){ptrLen=xhr.response?xhr.response.byteLength:0;ptr=_malloc(ptrLen);HEAPU8.set(new Uint8Array(xhr.response),ptr)}HEAPU32[fetch+12>>2]=ptr;Fetch.setu64(fetch+16,ptrLen)}xhr.onload=function(e){saveResponse(fetchAttrLoadToMemory&&!fetchAttrStreamData);var len=xhr.response?xhr.response.byteLength:0;Fetch.setu64(fetch+24,0);if(len){Fetch.setu64(fetch+32,len)}HEAPU16[fetch+40>>1]=xhr.readyState;if(xhr.readyState===4&&xhr.status===0){if(len>0)xhr.status=200;else xhr.status=404}HEAPU16[fetch+42>>1]=xhr.status;if(xhr.statusText)stringToUTF8(xhr.statusText,fetch+44,64);if(xhr.status>=200&&xhr.status<300){if(onsuccess)onsuccess(fetch,xhr,e)}else{if(onerror)onerror(fetch,xhr,e)}};xhr.onerror=function(e){saveResponse(fetchAttrLoadToMemory);var status=xhr.status;if(xhr.readyState===4&&status===0)status=404;Fetch.setu64(fetch+24,0);Fetch.setu64(fetch+32,xhr.response?xhr.response.byteLength:0);HEAPU16[fetch+40>>1]=xhr.readyState;HEAPU16[fetch+42>>1]=status;if(onerror)onerror(fetch,xhr,e)};xhr.ontimeout=function(e){if(onerror)onerror(fetch,xhr,e)};xhr.onprogress=function(e){var ptrLen=fetchAttrLoadToMemory&&fetchAttrStreamData&&xhr.response?xhr.response.byteLength:0;var ptr=0;if(fetchAttrLoadToMemory&&fetchAttrStreamData){ptr=_malloc(ptrLen);HEAPU8.set(new Uint8Array(xhr.response),ptr)}HEAPU32[fetch+12>>2]=ptr;Fetch.setu64(fetch+16,ptrLen);Fetch.setu64(fetch+24,e.loaded-ptrLen);Fetch.setu64(fetch+32,e.total);HEAPU16[fetch+40>>1]=xhr.readyState;if(xhr.readyState>=3&&xhr.status===0&&e.loaded>0)xhr.status=200;HEAPU16[fetch+42>>1]=xhr.status;if(xhr.statusText)stringToUTF8(xhr.statusText,fetch+44,64);if(onprogress)onprogress(fetch,xhr,e);if(ptr){_free(ptr)}};xhr.onreadystatechange=function(e){HEAPU16[fetch+40>>1]=xhr.readyState;if(xhr.readyState>=2){HEAPU16[fetch+42>>1]=xhr.status}if(onreadystatechange)onreadystatechange(fetch,xhr,e)};try{xhr.send(data)}catch(e){if(onerror)onerror(fetch,xhr,e)}}function __emscripten_fetch_cache_data(db,fetch,data,onsuccess,onerror){if(!db){onerror(fetch,0,"IndexedDB not available!");return}var fetch_attr=fetch+112;var destinationPath=HEAPU32[fetch_attr+64>>2];if(!destinationPath)destinationPath=HEAPU32[fetch+8>>2];var destinationPathStr=UTF8ToString(destinationPath);try{var transaction=db.transaction(["FILES"],"readwrite");var packages=transaction.objectStore("FILES");var putRequest=packages.put(data,destinationPathStr);putRequest.onsuccess=function(event){HEAPU16[fetch+40>>1]=4;HEAPU16[fetch+42>>1]=200;stringToUTF8("OK",fetch+44,64);onsuccess(fetch,0,destinationPathStr)};putRequest.onerror=function(error){HEAPU16[fetch+40>>1]=4;HEAPU16[fetch+42>>1]=413;stringToUTF8("Payload Too Large",fetch+44,64);onerror(fetch,0,error)}}catch(e){onerror(fetch,0,e)}}function __emscripten_fetch_load_cached_data(db,fetch,onsuccess,onerror){if(!db){onerror(fetch,0,"IndexedDB not available!");return}var fetch_attr=fetch+112;var path=HEAPU32[fetch_attr+64>>2];if(!path)path=HEAPU32[fetch+8>>2];var pathStr=UTF8ToString(path);try{var transaction=db.transaction(["FILES"],"readonly");var packages=transaction.objectStore("FILES");var getRequest=packages.get(pathStr);getRequest.onsuccess=function(event){if(event.target.result){var value=event.target.result;var len=value.byteLength||value.length;var ptr=_malloc(len);HEAPU8.set(new Uint8Array(value),ptr);HEAPU32[fetch+12>>2]=ptr;Fetch.setu64(fetch+16,len);Fetch.setu64(fetch+24,0);Fetch.setu64(fetch+32,len);HEAPU16[fetch+40>>1]=4;HEAPU16[fetch+42>>1]=200;stringToUTF8("OK",fetch+44,64);onsuccess(fetch,0,value)}else{HEAPU16[fetch+40>>1]=4;HEAPU16[fetch+42>>1]=404;stringToUTF8("Not Found",fetch+44,64);onerror(fetch,0,"no data")}};getRequest.onerror=function(error){HEAPU16[fetch+40>>1]=4;HEAPU16[fetch+42>>1]=404;stringToUTF8("Not Found",fetch+44,64);onerror(fetch,0,error)}}catch(e){onerror(fetch,0,e)}}function __emscripten_fetch_delete_cached_data(db,fetch,onsuccess,onerror){if(!db){onerror(fetch,0,"IndexedDB not available!");return}var fetch_attr=fetch+112;var path=HEAPU32[fetch_attr+64>>2];if(!path)path=HEAPU32[fetch+8>>2];var pathStr=UTF8ToString(path);try{var transaction=db.transaction(["FILES"],"readwrite");var packages=transaction.objectStore("FILES");var request=packages.delete(pathStr);request.onsuccess=function(event){var value=event.target.result;HEAPU32[fetch+12>>2]=0;Fetch.setu64(fetch+16,0);Fetch.setu64(fetch+24,0);Fetch.setu64(fetch+32,0);HEAPU16[fetch+40>>1]=4;HEAPU16[fetch+42>>1]=200;stringToUTF8("OK",fetch+44,64);onsuccess(fetch,0,value)};request.onerror=function(error){HEAPU16[fetch+40>>1]=4;HEAPU16[fetch+42>>1]=404;stringToUTF8("Not Found",fetch+44,64);onerror(fetch,0,error)}}catch(e){onerror(fetch,0,e)}}var _fetch_work_queue=140864;function _emscripten_start_fetch(fetch,successcb,errorcb,progresscb,readystatechangecb){if(typeof noExitRuntime!=="undefined")noExitRuntime=true;var fetch_attr=fetch+112;var requestMethod=UTF8ToString(fetch_attr);var onsuccess=HEAPU32[fetch_attr+36>>2];var onerror=HEAPU32[fetch_attr+40>>2];var onprogress=HEAPU32[fetch_attr+44>>2];var onreadystatechange=HEAPU32[fetch_attr+48>>2];var fetchAttributes=HEAPU32[fetch_attr+52>>2];var fetchAttrPersistFile=!!(fetchAttributes&4);var fetchAttrNoDownload=!!(fetchAttributes&32);var fetchAttrReplace=!!(fetchAttributes&16);var reportSuccess=function(fetch,xhr,e){if(onsuccess)dynCall_vi(onsuccess,fetch);else if(successcb)successcb(fetch)};var reportProgress=function(fetch,xhr,e){if(onprogress)dynCall_vi(onprogress,fetch);else if(progresscb)progresscb(fetch)};var reportError=function(fetch,xhr,e){if(onerror)dynCall_vi(onerror,fetch);else if(errorcb)errorcb(fetch)};var reportReadyStateChange=function(fetch,xhr,e){if(onreadystatechange)dynCall_vi(onreadystatechange,fetch);else if(readystatechangecb)readystatechangecb(fetch)};var performUncachedXhr=function(fetch,xhr,e){__emscripten_fetch_xhr(fetch,reportSuccess,reportError,reportProgress,reportReadyStateChange)};var cacheResultAndReportSuccess=function(fetch,xhr,e){var storeSuccess=function(fetch,xhr,e){if(onsuccess)dynCall_vi(onsuccess,fetch);else if(successcb)successcb(fetch)};var storeError=function(fetch,xhr,e){if(onsuccess)dynCall_vi(onsuccess,fetch);else if(successcb)successcb(fetch)};__emscripten_fetch_cache_data(Fetch.dbInstance,fetch,xhr.response,storeSuccess,storeError)};var performCachedXhr=function(fetch,xhr,e){__emscripten_fetch_xhr(fetch,cacheResultAndReportSuccess,reportError,reportProgress,reportReadyStateChange)};if(requestMethod==="EM_IDB_STORE"){var ptr=HEAPU32[fetch_attr+84>>2];__emscripten_fetch_cache_data(Fetch.dbInstance,fetch,HEAPU8.slice(ptr,ptr+HEAPU32[fetch_attr+88>>2]),reportSuccess,reportError)}else if(requestMethod==="EM_IDB_DELETE"){__emscripten_fetch_delete_cached_data(Fetch.dbInstance,fetch,reportSuccess,reportError)}else if(!fetchAttrReplace){__emscripten_fetch_load_cached_data(Fetch.dbInstance,fetch,reportSuccess,fetchAttrNoDownload?reportError:fetchAttrPersistFile?performCachedXhr:performUncachedXhr)}else if(!fetchAttrNoDownload){__emscripten_fetch_xhr(fetch,fetchAttrPersistFile?cacheResultAndReportSuccess:reportSuccess,reportError,reportProgress,reportReadyStateChange)}else{return 0}return fetch}function __webgl_enable_OES_vertex_array_object(ctx){var ext=ctx.getExtension("OES_vertex_array_object");if(ext){ctx["createVertexArray"]=function(){return ext["createVertexArrayOES"]()};ctx["deleteVertexArray"]=function(vao){ext["deleteVertexArrayOES"](vao)};ctx["bindVertexArray"]=function(vao){ext["bindVertexArrayOES"](vao)};ctx["isVertexArray"]=function(vao){return ext["isVertexArrayOES"](vao)};return 1}}function __webgl_enable_WEBGL_draw_buffers(ctx){var ext=ctx.getExtension("WEBGL_draw_buffers");if(ext){ctx["drawBuffers"]=function(n,bufs){ext["drawBuffersWEBGL"](n,bufs)};return 1}}function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw(ctx){return!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"))}var GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},timerQueriesEXT:[],queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],programInfos:{},stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode){if(!GL.lastError){GL.lastError=errorCode}},getNewId:function(table){var ret=GL.counter++;for(var i=table.length;i>1;var quadIndexes=new Uint16Array(numIndexes);var i=0,v=0;while(1){quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+1;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v+3;if(i>=numIndexes)break;v+=4}context.GLctx.bufferData(34963,quadIndexes,35044);context.GLctx.bindBuffer(34963,null)}},getTempVertexBuffer:function getTempVertexBuffer(sizeBytes){var idx=GL.log2ceilLookup(sizeBytes);var ringbuffer=GL.currentContext.tempVertexBuffers1[idx];var nextFreeBufferIndex=GL.currentContext.tempVertexBufferCounters1[idx];GL.currentContext.tempVertexBufferCounters1[idx]=GL.currentContext.tempVertexBufferCounters1[idx]+1&GL.numTempVertexBuffersPerSize-1;var vbo=ringbuffer[nextFreeBufferIndex];if(vbo){return vbo}var prevVBO=GLctx.getParameter(34964);ringbuffer[nextFreeBufferIndex]=GLctx.createBuffer();GLctx.bindBuffer(34962,ringbuffer[nextFreeBufferIndex]);GLctx.bufferData(34962,1<>2]:-1;source+=UTF8ToString(HEAP32[string+i*4>>2],len<0?undefined:len)}return source},calcBufLength:function calcBufLength(size,type,stride,count){if(stride>0){return count*stride}var typeSize=GL.byteSizeByType[type-GL.byteSizeByTypeRoot];return size*typeSize*count},usedTempBuffers:[],preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count){GL.resetBufferBinding=false;for(var i=0;i1?canvas.getContext("webgl2",webGLContextAttributes):canvas.getContext("webgl",webGLContextAttributes);if(!ctx)return 0;var handle=GL.registerContext(ctx,webGLContextAttributes);return handle},registerContext:function(ctx,webGLContextAttributes){var handle=GL.getNewId(GL.contexts);var context={handle:handle,attributes:webGLContextAttributes,version:webGLContextAttributes.majorVersion,GLctx:ctx};if(ctx.canvas)ctx.canvas.GLctxObject=context;GL.contexts[handle]=context;if(typeof webGLContextAttributes.enableExtensionsByDefault==="undefined"||webGLContextAttributes.enableExtensionsByDefault){GL.initExtensions(context)}context.maxVertexAttribs=context.GLctx.getParameter(34921);context.clientBuffers=[];for(var i=0;i>2]=t.alpha;HEAP32[a+4>>2]=t.depth;HEAP32[a+8>>2]=t.stencil;HEAP32[a+12>>2]=t.antialias;HEAP32[a+16>>2]=t.premultipliedAlpha;HEAP32[a+20>>2]=t.preserveDrawingBuffer;var power=t["powerPreference"]&&__emscripten_webgl_power_preferences.indexOf(t["powerPreference"]);HEAP32[a+24>>2]=power;HEAP32[a+28>>2]=t.failIfMajorPerformanceCaveat;HEAP32[a+32>>2]=c.version;HEAP32[a+36>>2]=0;HEAP32[a+40>>2]=c.attributes.enableExtensionsByDefault;return 0}function _emscripten_webgl_do_get_current_context(){return GL.currentContext?GL.currentContext.handle:0}function _emscripten_webgl_get_current_context(){return _emscripten_webgl_do_get_current_context()}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator==="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}function _exit(status){exit(status)}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _getTempRet0(){return getTempRet0()|0}function _glActiveTexture(x0){GLctx["activeTexture"](x0)}function _glAttachShader(program,shader){GLctx.attachShader(GL.programs[program],GL.shaders[shader])}function _glBindAttribLocation(program,index,name){GLctx.bindAttribLocation(GL.programs[program],index,UTF8ToString(name))}function _glBindBuffer(target,buffer){if(target==34962){GLctx.currentArrayBufferBinding=buffer}else if(target==34963){GLctx.currentElementArrayBufferBinding=buffer}if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])}function _glBindBufferRange(target,index,buffer,offset,ptrsize){GLctx["bindBufferRange"](target,index,GL.buffers[buffer],offset,ptrsize)}function _glBindFramebuffer(target,framebuffer){GLctx.bindFramebuffer(target,GL.framebuffers[framebuffer])}function _glBindRenderbuffer(target,renderbuffer){GLctx.bindRenderbuffer(target,GL.renderbuffers[renderbuffer])}function _glBindTexture(target,texture){GLctx.bindTexture(target,GL.textures[texture])}function _glBindVertexArray(vao){GLctx["bindVertexArray"](GL.vaos[vao]);var ibo=GLctx.getParameter(34965);GLctx.currentElementArrayBufferBinding=ibo?ibo.name|0:0}function _glBlendFuncSeparate(x0,x1,x2,x3){GLctx["blendFuncSeparate"](x0,x1,x2,x3)}function _glBlitFramebuffer(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9){GLctx["blitFramebuffer"](x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)}function _glBufferData(target,size,data,usage){if(GL.currentContext.version>=2){if(data){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}}else{GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)}}function _glBufferSubData(target,offset,size,data){if(GL.currentContext.version>=2){GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))}function _glClear(x0){GLctx["clear"](x0)}function _glClearColor(x0,x1,x2,x3){GLctx["clearColor"](x0,x1,x2,x3)}function _glClearDepthf(x0){GLctx["clearDepth"](x0)}function _glColorMask(red,green,blue,alpha){GLctx.colorMask(!!red,!!green,!!blue,!!alpha)}function _glCompileShader(shader){GLctx.compileShader(GL.shaders[shader])}function _glCompressedTexImage2D(target,level,internalFormat,width,height,border,imageSize,data){if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,imageSize,data)}else{GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,HEAPU8,data,imageSize)}return}GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,data?HEAPU8.subarray(data,data+imageSize):null)}function _glCreateProgram(){var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;GL.programs[id]=program;return id}function _glCreateShader(shaderType){var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id}function _glDeleteBuffers(n,buffers){for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentArrayBufferBinding)GLctx.currentArrayBufferBinding=0;if(id==GLctx.currentElementArrayBufferBinding)GLctx.currentElementArrayBufferBinding=0;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}}function _glDeleteFramebuffers(n,framebuffers){for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}}function _glDeleteRenderbuffers(n,renderbuffers){for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}}function _glDeleteTextures(n,textures){for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}}function _glDeleteVertexArrays(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _glDepthFunc(x0){GLctx["depthFunc"](x0)}function _glDepthMask(flag){GLctx.depthMask(!!flag)}function _glDepthRangef(x0,x1){GLctx["depthRange"](x0,x1)}function _glDisable(x0){GLctx["disable"](x0)}function _glDrawElements(mode,count,type,indices){var buf;if(!GLctx.currentElementArrayBufferBinding){var size=GL.calcBufLength(1,type,0,count);buf=GL.getTempIndexBuffer(size);GLctx.bindBuffer(34963,buf);GLctx.bufferSubData(34963,0,HEAPU8.subarray(indices,indices+size));indices=0}GL.preDrawHandleClientVertexAttribBindings(count);GLctx.drawElements(mode,count,type,indices);GL.postDrawHandleClientVertexAttribBindings(count);if(!GLctx.currentElementArrayBufferBinding){GLctx.bindBuffer(34963,null)}}function _glEnable(x0){GLctx["enable"](x0)}function _glEnableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=true;GLctx.enableVertexAttribArray(index)}function _glFramebufferRenderbuffer(target,attachment,renderbuffertarget,renderbuffer){GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])}function _glFramebufferTexture2D(target,attachment,textarget,texture,level){GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)}function _glFrontFace(x0){GLctx["frontFace"](x0)}function __glGenObject(n,buffers,createFunction,objectTable){for(var i=0;i>2]=id}}function _glGenBuffers(n,buffers){__glGenObject(n,buffers,"createBuffer",GL.buffers)}function _glGenFramebuffers(n,ids){__glGenObject(n,ids,"createFramebuffer",GL.framebuffers)}function _glGenRenderbuffers(n,renderbuffers){__glGenObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)}function _glGenTextures(n,textures){__glGenObject(n,textures,"createTexture",GL.textures)}function _glGenVertexArrays(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _glGenerateMipmap(x0){GLctx["generateMipmap"](x0)}function __glGetActiveAttribOrUniform(funcName,program,index,bufSize,length,size,type,name){program=GL.programs[program];var info=GLctx[funcName](program,index);if(info){var numBytesWrittenExclNull=name&&stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull;if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type}}function _glGetActiveUniform(program,index,bufSize,length,size,type,name){__glGetActiveAttribOrUniform("getActiveUniform",program,index,bufSize,length,size,type,name)}function writeI53ToI64(ptr,num){HEAPU32[ptr>>2]=num;HEAPU32[ptr+4>>2]=(num-HEAPU32[ptr>>2])/4294967296}function emscriptenWebGLGet(name_,p,type){if(!p){GL.recordError(1281);return}var ret=undefined;switch(name_){case 36346:ret=1;break;case 36344:if(type!=0&&type!=1){GL.recordError(1280)}return;case 34814:case 36345:ret=0;break;case 34466:var formats=GLctx.getParameter(34467);ret=formats?formats.length:0;break;case 33309:if(GL.currentContext.version<2){GL.recordError(1282);return}var exts=GLctx.getSupportedExtensions()||[];ret=2*exts.length;break;case 33307:case 33308:if(GL.currentContext.version<2){GL.recordError(1280);return}ret=name_==33307?3:0;break}if(ret===undefined){var result=GLctx.getParameter(name_);switch(typeof result){case"number":ret=result;break;case"boolean":ret=result?1:0;break;case"string":GL.recordError(1280);return;case"object":if(result===null){switch(name_){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:{ret=0;break}default:{GL.recordError(1280);return}}}else if(result instanceof Float32Array||result instanceof Uint32Array||result instanceof Int32Array||result instanceof Array){for(var i=0;i>2]=result[i];break;case 2:HEAPF32[p+i*4>>2]=result[i];break;case 4:HEAP8[p+i>>0]=result[i]?1:0;break}}return}else{try{ret=result.name|0}catch(e){GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Unknown object returned from WebGL getParameter("+name_+")! (error: "+e+")");return}}break;default:GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Native code calling glGet"+type+"v("+name_+") and it returns "+result+" of type "+typeof result+"!");return}}switch(type){case 1:writeI53ToI64(p,ret);break;case 0:HEAP32[p>>2]=ret;break;case 2:HEAPF32[p>>2]=ret;break;case 4:HEAP8[p>>0]=ret?1:0;break}}function _glGetFloatv(name_,p){emscriptenWebGLGet(name_,p,2)}function _glGetIntegerv(name_,p){emscriptenWebGLGet(name_,p,0)}function _glGetInternalformativ(target,internalformat,pname,bufSize,params){if(bufSize<0){GL.recordError(1281);return}if(!params){GL.recordError(1281);return}var ret=GLctx["getInternalformatParameter"](target,internalformat,pname);if(ret===null)return;for(var i=0;i>2]=ret[i]}}function _glGetProgramInfoLog(program,maxLength,length,infoLog){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetProgramiv(program,pname,p){if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}var ptable=GL.programInfos[program];if(!ptable){GL.recordError(1282);return}if(pname==35716){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){HEAP32[p>>2]=ptable.maxUniformLength}else if(pname==35722){if(ptable.maxAttributeLength==-1){program=GL.programs[program];var numAttribs=GLctx.getProgramParameter(program,35721);ptable.maxAttributeLength=0;for(var i=0;i>2]=ptable.maxAttributeLength}else if(pname==35381){if(ptable.maxUniformBlockNameLength==-1){program=GL.programs[program];var numBlocks=GLctx.getProgramParameter(program,35382);ptable.maxUniformBlockNameLength=0;for(var i=0;i>2]=ptable.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(GL.programs[program],pname)}}function _glGetShaderInfoLog(shader,maxLength,length,infoLog){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetShaderiv(shader,pname,p){if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}}function _glGetUniformBlockIndex(program,uniformBlockName){return GLctx["getUniformBlockIndex"](GL.programs[program],UTF8ToString(uniformBlockName))}function jstoi_q(str){return parseInt(str)}function _glGetUniformLocation(program,name){name=UTF8ToString(name);var arrayIndex=0;if(name[name.length-1]=="]"){var leftBrace=name.lastIndexOf("[");arrayIndex=name[leftBrace+1]!="]"?jstoi_q(name.slice(leftBrace+1)):0;name=name.slice(0,leftBrace)}var uniformInfo=GL.programInfos[program]&&GL.programInfos[program].uniforms[name];if(uniformInfo&&arrayIndex>=0&&arrayIndex>shift,pixels+bytes>>shift)}function _glReadPixels(x,y,width,height,format,type,pixels){if(GL.currentContext.version>=2){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels)}else{var heap=heapObjectForWebGLType(type);GLctx.readPixels(x,y,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)}function _glRenderbufferStorageMultisample(x0,x1,x2,x3,x4){GLctx["renderbufferStorageMultisample"](x0,x1,x2,x3,x4)}function _glScissor(x0,x1,x2,x3){GLctx["scissor"](x0,x1,x2,x3)}function _glShaderSource(shader,count,string,length){var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)}function _glTexImage2D(target,level,internalFormat,width,height,border,format,type,pixels){if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,null)}return}GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat):null)}function _glTexParameterf(x0,x1,x2){GLctx["texParameterf"](x0,x1,x2)}function _glTexParameteri(x0,x1,x2){GLctx["texParameteri"](x0,x1,x2)}function _glUniform1i(location,v0){GLctx.uniform1i(GL.uniforms[location],v0)}var miniTempWebGLFloatBuffers=[];function _glUniform4fv(location,count,value){if(GL.currentContext.version>=2){GLctx.uniform4fv(GL.uniforms[location],HEAPF32,value>>2,count*4);return}if(count<=72){var view=miniTempWebGLFloatBuffers[4*count-1];var heap=HEAPF32;value>>=2;for(var i=0;i<4*count;i+=4){var dst=value+i;view[i]=heap[dst];view[i+1]=heap[dst+1];view[i+2]=heap[dst+2];view[i+3]=heap[dst+3]}}else{var view=HEAPF32.subarray(value>>2,value+count*16>>2)}GLctx.uniform4fv(GL.uniforms[location],view)}var __miniTempWebGLIntBuffers=[];function _glUniform4iv(location,count,value){if(GL.currentContext.version>=2){GLctx.uniform4iv(GL.uniforms[location],HEAP32,value>>2,count*4);return}if(count<=72){var view=__miniTempWebGLIntBuffers[4*count-1];for(var i=0;i<4*count;i+=4){view[i]=HEAP32[value+4*i>>2];view[i+1]=HEAP32[value+(4*i+4)>>2];view[i+2]=HEAP32[value+(4*i+8)>>2];view[i+3]=HEAP32[value+(4*i+12)>>2]}}else{var view=HEAP32.subarray(value>>2,value+count*16>>2)}GLctx.uniform4iv(GL.uniforms[location],view)}function _glUniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding){program=GL.programs[program];GLctx["uniformBlockBinding"](program,uniformBlockIndex,uniformBlockBinding)}function _glUniformMatrix2fv(location,count,transpose,value){if(GL.currentContext.version>=2){GLctx.uniformMatrix2fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*4);return}if(count<=72){var view=miniTempWebGLFloatBuffers[4*count-1];for(var i=0;i<4*count;i+=4){view[i]=HEAPF32[value+4*i>>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2];view[i+3]=HEAPF32[value+(4*i+12)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*16>>2)}GLctx.uniformMatrix2fv(GL.uniforms[location],!!transpose,view)}function _glUniformMatrix3fv(location,count,transpose,value){if(GL.currentContext.version>=2){GLctx.uniformMatrix3fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*9);return}if(count<=32){var view=miniTempWebGLFloatBuffers[9*count-1];for(var i=0;i<9*count;i+=9){view[i]=HEAPF32[value+4*i>>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2];view[i+3]=HEAPF32[value+(4*i+12)>>2];view[i+4]=HEAPF32[value+(4*i+16)>>2];view[i+5]=HEAPF32[value+(4*i+20)>>2];view[i+6]=HEAPF32[value+(4*i+24)>>2];view[i+7]=HEAPF32[value+(4*i+28)>>2];view[i+8]=HEAPF32[value+(4*i+32)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*36>>2)}GLctx.uniformMatrix3fv(GL.uniforms[location],!!transpose,view)}function _glUniformMatrix4fv(location,count,transpose,value){if(GL.currentContext.version>=2){GLctx.uniformMatrix4fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*16);return}if(count<=18){var view=miniTempWebGLFloatBuffers[16*count-1];var heap=HEAPF32;value>>=2;for(var i=0;i<16*count;i+=16){var dst=value+i;view[i]=heap[dst];view[i+1]=heap[dst+1];view[i+2]=heap[dst+2];view[i+3]=heap[dst+3];view[i+4]=heap[dst+4];view[i+5]=heap[dst+5];view[i+6]=heap[dst+6];view[i+7]=heap[dst+7];view[i+8]=heap[dst+8];view[i+9]=heap[dst+9];view[i+10]=heap[dst+10];view[i+11]=heap[dst+11];view[i+12]=heap[dst+12];view[i+13]=heap[dst+13];view[i+14]=heap[dst+14];view[i+15]=heap[dst+15]}}else{var view=HEAPF32.subarray(value>>2,value+count*64>>2)}GLctx.uniformMatrix4fv(GL.uniforms[location],!!transpose,view)}function _glUseProgram(program){GLctx.useProgram(GL.programs[program])}function _glVertexAttribPointer(index,size,type,normalized,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=normalized;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribPointer(index,size,type,normalized,stride,ptr)};return}cb.clientside=false;GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)}function _glViewport(x0,x1,x2,x3){GLctx["viewport"](x0,x1,x2,x3)}function _emscripten_set_main_loop_timing(mode,value){Browser.mainLoop.timingMode=mode;Browser.mainLoop.timingValue=value;if(!Browser.mainLoop.func){return 1}if(mode==0){Browser.mainLoop.scheduler=function Browser_mainLoop_scheduler_setTimeout(){var timeUntilNextTick=Math.max(0,Browser.mainLoop.tickStartTime+value-_emscripten_get_now())|0;setTimeout(Browser.mainLoop.runner,timeUntilNextTick)};Browser.mainLoop.method="timeout"}else if(mode==1){Browser.mainLoop.scheduler=function Browser_mainLoop_scheduler_rAF(){Browser.requestAnimationFrame(Browser.mainLoop.runner)};Browser.mainLoop.method="rAF"}else if(mode==2){if(typeof setImmediate==="undefined"){var setImmediates=[];var emscriptenMainLoopMessageId="setimmediate";var Browser_setImmediate_messageHandler=function(event){if(event.data===emscriptenMainLoopMessageId||event.data.target===emscriptenMainLoopMessageId){event.stopPropagation();setImmediates.shift()()}};addEventListener("message",Browser_setImmediate_messageHandler,true);setImmediate=function Browser_emulated_setImmediate(func){setImmediates.push(func);if(ENVIRONMENT_IS_WORKER){if(Module["setImmediates"]===undefined)Module["setImmediates"]=[];Module["setImmediates"].push(func);postMessage({target:emscriptenMainLoopMessageId})}else postMessage(emscriptenMainLoopMessageId,"*")}}Browser.mainLoop.scheduler=function Browser_mainLoop_scheduler_setImmediate(){setImmediate(Browser.mainLoop.runner)};Browser.mainLoop.method="immediate"}return 0}function _emscripten_set_main_loop(func,fps,simulateInfiniteLoop,arg,noSetTiming){noExitRuntime=true;assert(!Browser.mainLoop.func,"emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.");Browser.mainLoop.func=func;Browser.mainLoop.arg=arg;var browserIterationFunc;if(typeof arg!=="undefined"){browserIterationFunc=function(){Module["dynCall_vi"](func,arg)}}else{browserIterationFunc=function(){Module["dynCall_v"](func)}}var thisMainLoopId=Browser.mainLoop.currentlyRunningMainloop;Browser.mainLoop.runner=function Browser_mainLoop_runner(){if(ABORT)return;if(Browser.mainLoop.queue.length>0){var start=Date.now();var blocker=Browser.mainLoop.queue.shift();blocker.func(blocker.arg);if(Browser.mainLoop.remainingBlockers){var remaining=Browser.mainLoop.remainingBlockers;var next=remaining%1==0?remaining-1:Math.floor(remaining);if(blocker.counted){Browser.mainLoop.remainingBlockers=next}else{next=next+.5;Browser.mainLoop.remainingBlockers=(8*remaining+next)/9}}console.log('main loop blocker "'+blocker.name+'" took '+(Date.now()-start)+" ms");Browser.mainLoop.updateStatus();if(thisMainLoopId1&&Browser.mainLoop.currentFrameNumber%Browser.mainLoop.timingValue!=0){Browser.mainLoop.scheduler();return}else if(Browser.mainLoop.timingMode==0){Browser.mainLoop.tickStartTime=_emscripten_get_now()}GL.newRenderingFrameStarted();Browser.mainLoop.runIter(browserIterationFunc);if(thisMainLoopId0)_emscripten_set_main_loop_timing(0,1e3/fps);else _emscripten_set_main_loop_timing(1,1);Browser.mainLoop.scheduler()}if(simulateInfiniteLoop){throw"unwind"}}var Browser={mainLoop:{scheduler:null,method:"",currentlyRunningMainloop:0,func:null,arg:0,timingMode:0,timingValue:0,currentFrameNumber:0,queue:[],pause:function(){Browser.mainLoop.scheduler=null;Browser.mainLoop.currentlyRunningMainloop++},resume:function(){Browser.mainLoop.currentlyRunningMainloop++;var timingMode=Browser.mainLoop.timingMode;var timingValue=Browser.mainLoop.timingValue;var func=Browser.mainLoop.func;Browser.mainLoop.func=null;_emscripten_set_main_loop(func,0,false,Browser.mainLoop.arg,true);_emscripten_set_main_loop_timing(timingMode,timingValue);Browser.mainLoop.scheduler()},updateStatus:function(){if(Module["setStatus"]){var message=Module["statusMessage"]||"Please wait...";var remaining=Browser.mainLoop.remainingBlockers;var expected=Browser.mainLoop.expectedBlockers;if(remaining){if(remaining=6){var curr=leftchar>>leftbits-6&63;leftbits-=6;ret+=BASE[curr]}}if(leftbits==2){ret+=BASE[(leftchar&3)<<4];ret+=PAD+PAD}else if(leftbits==4){ret+=BASE[(leftchar&15)<<2];ret+=PAD}return ret}audio.src="data:audio/x-"+name.substr(-3)+";base64,"+encode64(byteArray);finish(audio)};audio.src=url;Browser.safeSetTimeout(function(){finish(audio)},1e4)}else{return fail()}};Module["preloadPlugins"].push(audioPlugin);function pointerLockChange(){Browser.pointerLock=document["pointerLockElement"]===Module["canvas"]||document["mozPointerLockElement"]===Module["canvas"]||document["webkitPointerLockElement"]===Module["canvas"]||document["msPointerLockElement"]===Module["canvas"]}var canvas=Module["canvas"];if(canvas){canvas.requestPointerLock=canvas["requestPointerLock"]||canvas["mozRequestPointerLock"]||canvas["webkitRequestPointerLock"]||canvas["msRequestPointerLock"]||function(){};canvas.exitPointerLock=document["exitPointerLock"]||document["mozExitPointerLock"]||document["webkitExitPointerLock"]||document["msExitPointerLock"]||function(){};canvas.exitPointerLock=canvas.exitPointerLock.bind(document);document.addEventListener("pointerlockchange",pointerLockChange,false);document.addEventListener("mozpointerlockchange",pointerLockChange,false);document.addEventListener("webkitpointerlockchange",pointerLockChange,false);document.addEventListener("mspointerlockchange",pointerLockChange,false);if(Module["elementPointerLock"]){canvas.addEventListener("click",function(ev){if(!Browser.pointerLock&&Module["canvas"].requestPointerLock){Module["canvas"].requestPointerLock();ev.preventDefault()}},false)}}},createContext:function(canvas,useWebGL,setInModule,webGLContextAttributes){if(useWebGL&&Module.ctx&&canvas==Module.canvas)return Module.ctx;var ctx;var contextHandle;if(useWebGL){var contextAttributes={antialias:false,alpha:false,majorVersion:typeof WebGL2RenderingContext!=="undefined"?2:1};if(webGLContextAttributes){for(var attribute in webGLContextAttributes){contextAttributes[attribute]=webGLContextAttributes[attribute]}}if(typeof GL!=="undefined"){contextHandle=GL.createContext(canvas,contextAttributes);if(contextHandle){ctx=GL.getContext(contextHandle).GLctx}}}else{ctx=canvas.getContext("2d")}if(!ctx)return null;if(setInModule){if(!useWebGL)assert(typeof GLctx==="undefined","cannot set in module if GLctx is used, but we are a non-GL context that would replace it");Module.ctx=ctx;if(useWebGL)GL.makeContextCurrent(contextHandle);Module.useWebGL=useWebGL;Browser.moduleContextCreatedCallbacks.forEach(function(callback){callback()});Browser.init()}return ctx},destroyContext:function(canvas,useWebGL,setInModule){},fullscreenHandlersInstalled:false,lockPointer:undefined,resizeCanvas:undefined,requestFullscreen:function(lockPointer,resizeCanvas){Browser.lockPointer=lockPointer;Browser.resizeCanvas=resizeCanvas;if(typeof Browser.lockPointer==="undefined")Browser.lockPointer=true;if(typeof Browser.resizeCanvas==="undefined")Browser.resizeCanvas=false;var canvas=Module["canvas"];function fullscreenChange(){Browser.isFullscreen=false;var canvasContainer=canvas.parentNode;if((document["fullscreenElement"]||document["mozFullScreenElement"]||document["msFullscreenElement"]||document["webkitFullscreenElement"]||document["webkitCurrentFullScreenElement"])===canvasContainer){canvas.exitFullscreen=Browser.exitFullscreen;if(Browser.lockPointer)canvas.requestPointerLock();Browser.isFullscreen=true;if(Browser.resizeCanvas){Browser.setFullscreenCanvasSize()}else{Browser.updateCanvasDimensions(canvas)}}else{canvasContainer.parentNode.insertBefore(canvas,canvasContainer);canvasContainer.parentNode.removeChild(canvasContainer);if(Browser.resizeCanvas){Browser.setWindowedCanvasSize()}else{Browser.updateCanvasDimensions(canvas)}}if(Module["onFullScreen"])Module["onFullScreen"](Browser.isFullscreen);if(Module["onFullscreen"])Module["onFullscreen"](Browser.isFullscreen)}if(!Browser.fullscreenHandlersInstalled){Browser.fullscreenHandlersInstalled=true;document.addEventListener("fullscreenchange",fullscreenChange,false);document.addEventListener("mozfullscreenchange",fullscreenChange,false);document.addEventListener("webkitfullscreenchange",fullscreenChange,false);document.addEventListener("MSFullscreenChange",fullscreenChange,false)}var canvasContainer=document.createElement("div");canvas.parentNode.insertBefore(canvasContainer,canvas);canvasContainer.appendChild(canvas);canvasContainer.requestFullscreen=canvasContainer["requestFullscreen"]||canvasContainer["mozRequestFullScreen"]||canvasContainer["msRequestFullscreen"]||(canvasContainer["webkitRequestFullscreen"]?function(){canvasContainer["webkitRequestFullscreen"](Element["ALLOW_KEYBOARD_INPUT"])}:null)||(canvasContainer["webkitRequestFullScreen"]?function(){canvasContainer["webkitRequestFullScreen"](Element["ALLOW_KEYBOARD_INPUT"])}:null);canvasContainer.requestFullscreen()},exitFullscreen:function(){if(!Browser.isFullscreen){return false}var CFS=document["exitFullscreen"]||document["cancelFullScreen"]||document["mozCancelFullScreen"]||document["msExitFullscreen"]||document["webkitCancelFullScreen"]||function(){};CFS.apply(document,[]);return true},nextRAF:0,fakeRequestAnimationFrame:function(func){var now=Date.now();if(Browser.nextRAF===0){Browser.nextRAF=now+1e3/60}else{while(now+2>=Browser.nextRAF){Browser.nextRAF+=1e3/60}}var delay=Math.max(Browser.nextRAF-now,0);setTimeout(func,delay)},requestAnimationFrame:function(func){if(typeof requestAnimationFrame==="function"){requestAnimationFrame(func);return}var RAF=Browser.fakeRequestAnimationFrame;RAF(func)},safeCallback:function(func){return function(){if(!ABORT)return func.apply(null,arguments)}},allowAsyncCallbacks:true,queuedAsyncCallbacks:[],pauseAsyncCallbacks:function(){Browser.allowAsyncCallbacks=false},resumeAsyncCallbacks:function(){Browser.allowAsyncCallbacks=true;if(Browser.queuedAsyncCallbacks.length>0){var callbacks=Browser.queuedAsyncCallbacks;Browser.queuedAsyncCallbacks=[];callbacks.forEach(function(func){func()})}},safeRequestAnimationFrame:function(func){return Browser.requestAnimationFrame(function(){if(ABORT)return;if(Browser.allowAsyncCallbacks){func()}else{Browser.queuedAsyncCallbacks.push(func)}})},safeSetTimeout:function(func,timeout){noExitRuntime=true;return setTimeout(function(){if(ABORT)return;if(Browser.allowAsyncCallbacks){func()}else{Browser.queuedAsyncCallbacks.push(func)}},timeout)},safeSetInterval:function(func,timeout){noExitRuntime=true;return setInterval(function(){if(ABORT)return;if(Browser.allowAsyncCallbacks){func()}},timeout)},getMimetype:function(name){return{"jpg":"image/jpeg","jpeg":"image/jpeg","png":"image/png","bmp":"image/bmp","ogg":"audio/ogg","wav":"audio/wav","mp3":"audio/mpeg"}[name.substr(name.lastIndexOf(".")+1)]},getUserMedia:function(func){if(!window.getUserMedia){window.getUserMedia=navigator["getUserMedia"]||navigator["mozGetUserMedia"]}window.getUserMedia(func)},getMovementX:function(event){return event["movementX"]||event["mozMovementX"]||event["webkitMovementX"]||0},getMovementY:function(event){return event["movementY"]||event["mozMovementY"]||event["webkitMovementY"]||0},getMouseWheelDelta:function(event){var delta=0;switch(event.type){case"DOMMouseScroll":delta=event.detail/3;break;case"mousewheel":delta=event.wheelDelta/120;break;case"wheel":delta=event.deltaY;switch(event.deltaMode){case 0:delta/=100;break;case 1:delta/=3;break;case 2:delta*=80;break;default:throw"unrecognized mouse wheel delta mode: "+event.deltaMode}break;default:throw"unrecognized mouse wheel event: "+event.type}return delta},mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,touches:{},lastTouches:{},calculateMouseEvent:function(event){if(Browser.pointerLock){if(event.type!="mousemove"&&"mozMovementX"in event){Browser.mouseMovementX=Browser.mouseMovementY=0}else{Browser.mouseMovementX=Browser.getMovementX(event);Browser.mouseMovementY=Browser.getMovementY(event)}if(typeof SDL!="undefined"){Browser.mouseX=SDL.mouseX+Browser.mouseMovementX;Browser.mouseY=SDL.mouseY+Browser.mouseMovementY}else{Browser.mouseX+=Browser.mouseMovementX;Browser.mouseY+=Browser.mouseMovementY}}else{var rect=Module["canvas"].getBoundingClientRect();var cw=Module["canvas"].width;var ch=Module["canvas"].height;var scrollX=typeof window.scrollX!=="undefined"?window.scrollX:window.pageXOffset;var scrollY=typeof window.scrollY!=="undefined"?window.scrollY:window.pageYOffset;if(event.type==="touchstart"||event.type==="touchend"||event.type==="touchmove"){var touch=event.touch;if(touch===undefined){return}var adjustedX=touch.pageX-(scrollX+rect.left);var adjustedY=touch.pageY-(scrollY+rect.top);adjustedX=adjustedX*(cw/rect.width);adjustedY=adjustedY*(ch/rect.height);var coords={x:adjustedX,y:adjustedY};if(event.type==="touchstart"){Browser.lastTouches[touch.identifier]=coords;Browser.touches[touch.identifier]=coords}else if(event.type==="touchend"||event.type==="touchmove"){var last=Browser.touches[touch.identifier];if(!last)last=coords;Browser.lastTouches[touch.identifier]=last;Browser.touches[touch.identifier]=coords}return}var x=event.pageX-(scrollX+rect.left);var y=event.pageY-(scrollY+rect.top);x=x*(cw/rect.width);y=y*(ch/rect.height);Browser.mouseMovementX=x-Browser.mouseX;Browser.mouseMovementY=y-Browser.mouseY;Browser.mouseX=x;Browser.mouseY=y}},asyncLoad:function(url,onload,onerror,noRunDep){var dep=!noRunDep?getUniqueRunDependency("al "+url):"";readAsync(url,function(arrayBuffer){assert(arrayBuffer,'Loading data file "'+url+'" failed (no arrayBuffer).');onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},function(event){if(onerror){onerror()}else{throw'Loading data file "'+url+'" failed.'}});if(dep)addRunDependency(dep)},resizeListeners:[],updateResizeListeners:function(){var canvas=Module["canvas"];Browser.resizeListeners.forEach(function(listener){listener(canvas.width,canvas.height)})},setCanvasSize:function(width,height,noUpdates){var canvas=Module["canvas"];Browser.updateCanvasDimensions(canvas,width,height);if(!noUpdates)Browser.updateResizeListeners()},windowedWidth:0,windowedHeight:0,setFullscreenCanvasSize:function(){if(typeof SDL!="undefined"){var flags=HEAPU32[SDL.screen>>2];flags=flags|8388608;HEAP32[SDL.screen>>2]=flags}Browser.updateCanvasDimensions(Module["canvas"]);Browser.updateResizeListeners()},setWindowedCanvasSize:function(){if(typeof SDL!="undefined"){var flags=HEAPU32[SDL.screen>>2];flags=flags&~8388608;HEAP32[SDL.screen>>2]=flags}Browser.updateCanvasDimensions(Module["canvas"]);Browser.updateResizeListeners()},updateCanvasDimensions:function(canvas,wNative,hNative){if(wNative&&hNative){canvas.widthNative=wNative;canvas.heightNative=hNative}else{wNative=canvas.widthNative;hNative=canvas.heightNative}var w=wNative;var h=hNative;if(Module["forcedAspectRatio"]&&Module["forcedAspectRatio"]>0){if(w/h=0&&charCode<=31)return;dynCall_vii(GLFW.active.charFunc,GLFW.active.id,charCode)},onKeyChanged:function(keyCode,status){if(!GLFW.active)return;var key=GLFW.DOMToGLFWKeyCode(keyCode);if(key==-1)return;var repeat=status&&GLFW.active.keys[key];GLFW.active.keys[key]=status;GLFW.active.domKeys[keyCode]=status;if(!GLFW.active.keyFunc)return;if(repeat)status=2;dynCall_viiiii(GLFW.active.keyFunc,GLFW.active.id,key,keyCode,status,GLFW.getModBits(GLFW.active))},onGamepadConnected:function(event){GLFW.refreshJoysticks()},onGamepadDisconnected:function(event){GLFW.refreshJoysticks()},onKeydown:function(event){GLFW.onKeyChanged(event.keyCode,1);if(event.keyCode===8||event.keyCode===9){event.preventDefault()}},onKeyup:function(event){GLFW.onKeyChanged(event.keyCode,0)},onBlur:function(event){if(!GLFW.active)return;for(var i=0;i0){if(eventButton==1){eventButton=2}else{eventButton=1}}return eventButton},onMouseenter:function(event){if(!GLFW.active)return;if(event.target!=Module["canvas"]||!GLFW.active.cursorEnterFunc)return;dynCall_vii(GLFW.active.cursorEnterFunc,GLFW.active.id,1)},onMouseleave:function(event){if(!GLFW.active)return;if(event.target!=Module["canvas"]||!GLFW.active.cursorEnterFunc)return;dynCall_vii(GLFW.active.cursorEnterFunc,GLFW.active.id,0)},onMouseButtonChanged:function(event,status){if(!GLFW.active)return;Browser.calculateMouseEvent(event);if(event.target!=Module["canvas"])return;var eventButton=GLFW.DOMToGLFWMouseButton(event);if(status==1){GLFW.active.buttons|=1<0?Math.max(delta,1):Math.min(delta,-1);GLFW.wheelPos+=delta;if(!GLFW.active||!GLFW.active.scrollFunc||event.target!=Module["canvas"])return;var sx=0;var sy=0;if(event.type=="mousewheel"){sx=event.wheelDeltaX;sy=event.wheelDeltaY}else{sx=event.deltaX;sy=event.deltaY}dynCall_vidd(GLFW.active.scrollFunc,GLFW.active.id,sx,sy);event.preventDefault()},onCanvasResize:function(width,height){if(!GLFW.active)return;var resizeNeeded=true;if(document["fullscreen"]||document["fullScreen"]||document["mozFullScreen"]||document["webkitIsFullScreen"]){GLFW.active.storedX=GLFW.active.x;GLFW.active.storedY=GLFW.active.y;GLFW.active.storedWidth=GLFW.active.width;GLFW.active.storedHeight=GLFW.active.height;GLFW.active.x=GLFW.active.y=0;GLFW.active.width=screen.width;GLFW.active.height=screen.height;GLFW.active.fullscreen=true}else if(GLFW.active.fullscreen==true){GLFW.active.x=GLFW.active.storedX;GLFW.active.y=GLFW.active.storedY;GLFW.active.width=GLFW.active.storedWidth;GLFW.active.height=GLFW.active.storedHeight;GLFW.active.fullscreen=false}else if(GLFW.active.width!=width||GLFW.active.height!=height){GLFW.active.width=width;GLFW.active.height=height}else{resizeNeeded=false}if(resizeNeeded){Browser.setCanvasSize(GLFW.active.width,GLFW.active.height,true);GLFW.onWindowSizeChanged();GLFW.onFramebufferSizeChanged()}},onWindowSizeChanged:function(){if(!GLFW.active)return;if(!GLFW.active.windowSizeFunc)return;dynCall_viii(GLFW.active.windowSizeFunc,GLFW.active.id,GLFW.active.width,GLFW.active.height)},onFramebufferSizeChanged:function(){if(!GLFW.active)return;if(!GLFW.active.framebufferSizeFunc)return;dynCall_viii(GLFW.active.framebufferSizeFunc,GLFW.active.id,GLFW.active.width,GLFW.active.height)},getTime:function(){return _emscripten_get_now()/1e3},setWindowTitle:function(winid,title){var win=GLFW.WindowFromId(winid);if(!win)return;win.title=UTF8ToString(title);if(GLFW.active.id==win.id){document.title=win.title}},setJoystickCallback:function(cbfun){GLFW.joystickFunc=cbfun;GLFW.refreshJoysticks()},joys:{},lastGamepadState:null,lastGamepadStateFrame:null,refreshJoysticks:function(){if(Browser.mainLoop.currentFrameNumber!==GLFW.lastGamepadStateFrame||!Browser.mainLoop.currentFrameNumber){GLFW.lastGamepadState=navigator.getGamepads?navigator.getGamepads():navigator.webkitGetGamepads?navigator.webkitGetGamepads:null;GLFW.lastGamepadStateFrame=Browser.mainLoop.currentFrameNumber;for(var joy=0;joy0},getCursorPos:function(winid,x,y){setValue(x,Browser.mouseX,"double");setValue(y,Browser.mouseY,"double")},getMousePos:function(winid,x,y){setValue(x,Browser.mouseX,"i32");setValue(y,Browser.mouseY,"i32")},setCursorPos:function(winid,x,y){},getWindowPos:function(winid,x,y){var wx=0;var wy=0;var win=GLFW.WindowFromId(winid);if(win){wx=win.x;wy=win.y}if(x){setValue(x,wx,"i32")}if(y){setValue(y,wy,"i32")}},setWindowPos:function(winid,x,y){var win=GLFW.WindowFromId(winid);if(!win)return;win.x=x;win.y=y},getWindowSize:function(winid,width,height){var ww=0;var wh=0;var win=GLFW.WindowFromId(winid);if(win){ww=win.width;wh=win.height}if(width){setValue(width,ww,"i32")}if(height){setValue(height,wh,"i32")}},setWindowSize:function(winid,width,height){var win=GLFW.WindowFromId(winid);if(!win)return;if(GLFW.active.id==win.id){if(width==screen.width&&height==screen.height){Browser.requestFullscreen()}else{Browser.exitFullscreen();Browser.setCanvasSize(width,height);win.width=width;win.height=height}}if(!win.windowSizeFunc)return;dynCall_viii(win.windowSizeFunc,win.id,width,height)},createWindow:function(width,height,title,monitor,share){var i,id;for(i=0;i0)throw"glfwCreateWindow only supports one window at time currently";id=i+1;if(width<=0||height<=0)return 0;if(monitor){Browser.requestFullscreen()}else{Browser.setCanvasSize(width,height)}for(i=0;i1,depth:GLFW.hints[135173]>0,stencil:GLFW.hints[135174]>0,alpha:GLFW.hints[135172]>0};Module.ctx=Browser.createContext(Module["canvas"],true,true,contextAttributes)}if(!Module.ctx)return 0;var win=new GLFW_Window(id,width,height,title,monitor,share);if(id-1==GLFW.windows.length){GLFW.windows.push(win)}else{GLFW.windows[id-1]=win}GLFW.active=win;return win.id},destroyWindow:function(winid){var win=GLFW.WindowFromId(winid);if(!win)return;if(win.windowCloseFunc)dynCall_vi(win.windowCloseFunc,win.id);GLFW.windows[win.id-1]=null;if(GLFW.active.id==win.id)GLFW.active=null;for(var i=0;i>2];var nanoseconds=HEAP32[rqtp+4>>2];if(nanoseconds<0||nanoseconds>999999999||seconds<0){setErrNo(28);return-1}if(rmtp!==0){HEAP32[rmtp>>2]=0;HEAP32[rmtp+4>>2]=0}return _usleep(seconds*1e6+nanoseconds/1e3)}function _offerFileAsDownload(filename_ptr,filename_len){let mime="application/octet-stream";let filename=AsciiToString(filename_ptr);let content=Module.FS.readFile(filename);console.log(`Offering download of "${filename}", with ${content.length} bytes...`);var a=document.createElement("a");a.download=filename;a.href=URL.createObjectURL(new Blob([content],{type:mime}));a.style.display="none";document.body.appendChild(a);a.click();setTimeout(()=>{document.body.removeChild(a);URL.revokeObjectURL(a.href)},2e3)}function _pthread_attr_destroy(attr){return 0}function _pthread_attr_init(attr){return 0}function _pthread_attr_setstacksize(){}function _pthread_create(){return 6}function _pthread_detach(){}function _pthread_join(){}function _sem_destroy(){}function _sem_init(){}function _sem_post(){}function _sem_wait(){}function _setTempRet0($i){setTempRet0($i|0)}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value==="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var janFirst=new Date(date.tm_year+1900,0,1);var firstSunday=janFirst.getDay()===0?janFirst:__addDays(janFirst,7-janFirst.getDay());var endDate=new Date(date.tm_year+1900,date.tm_mon,date.tm_mday);if(compareByDay(firstSunday,endDate)<0){var februaryFirstUntilEndMonth=__arraySum(__isLeapYear(endDate.getFullYear())?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,endDate.getMonth()-1)-31;var firstSundayUntilEndJanuary=31-firstSunday.getDate();var days=firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();return leadingNulls(Math.ceil(days/7),2)}return compareByDay(firstSunday,janFirst)===0?"01":"00"},"%V":function(date){var janFourthThisYear=new Date(date.tm_year+1900,0,4);var janFourthNextYear=new Date(date.tm_year+1901,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);var endDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);if(compareByDay(endDate,firstWeekStartThisYear)<0){return"53"}if(compareByDay(firstWeekStartNextYear,endDate)<=0){return"01"}var daysDifference;if(firstWeekStartThisYear.getFullYear()=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};for(var rule in EXPANSION_RULES_2){if(pattern.indexOf(rule)>=0){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,format,tm)}function _supplyAnimationList(arrPtr,length){var animationIdArr=Module.HEAP32.subarray(arrPtr/4,arrPtr/4+length);console.log(animationIdArr);Module["animationArrayCallback"](animationIdArr)}function _supplyMeshIds(arrPtr,length){var meshIdArr=Module.HEAP32.subarray(arrPtr/4,arrPtr/4+length);console.log(meshIdArr);if(Module["meshIdArrayCallback"]){Module["meshIdArrayCallback"](meshIdArr)}}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:case 79:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}function _time(ptr){var ret=Date.now()/1e3|0;if(ptr){HEAP32[ptr>>2]=ret}return ret}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();Module["FS_createFolder"]=FS.createFolder;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createLink"]=FS.createLink;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");InternalError=Module["InternalError"]=extendError(Error,"InternalError");init_emval();Fetch.staticInit();var GLctx;var miniTempWebGLFloatBuffersStorage=new Float32Array(288);for(var i=0;i<288;++i){miniTempWebGLFloatBuffers[i]=miniTempWebGLFloatBuffersStorage.subarray(0,i+1)}var __miniTempWebGLIntBuffersStorage=new Int32Array(288);for(var i=0;i<288;++i){__miniTempWebGLIntBuffers[i]=__miniTempWebGLIntBuffersStorage.subarray(0,i+1)}Module["requestFullscreen"]=function Module_requestFullscreen(lockPointer,resizeCanvas){Browser.requestFullscreen(lockPointer,resizeCanvas)};Module["requestAnimationFrame"]=function Module_requestAnimationFrame(func){Browser.requestAnimationFrame(func)};Module["setCanvasSize"]=function Module_setCanvasSize(width,height,noUpdates){Browser.setCanvasSize(width,height,noUpdates)};Module["pauseMainLoop"]=function Module_pauseMainLoop(){Browser.mainLoop.pause()};Module["resumeMainLoop"]=function Module_resumeMainLoop(){Browser.mainLoop.resume()};Module["getUserMedia"]=function Module_getUserMedia(){Browser.getUserMedia()};Module["createContext"]=function Module_createContext(canvas,useWebGL,setInModule,webGLContextAttributes){return Browser.createContext(canvas,useWebGL,setInModule,webGLContextAttributes)};var ASSERTIONS=false;function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"__cxa_allocate_exception":___cxa_allocate_exception,"__cxa_atexit":___cxa_atexit,"__cxa_begin_catch":___cxa_begin_catch,"__cxa_call_unexpected":___cxa_call_unexpected,"__cxa_current_primary_exception":___cxa_current_primary_exception,"__cxa_decrement_exception_refcount":___cxa_decrement_exception_refcount,"__cxa_end_catch":___cxa_end_catch,"__cxa_find_matching_catch_2":___cxa_find_matching_catch_2,"__cxa_find_matching_catch_3":___cxa_find_matching_catch_3,"__cxa_find_matching_catch_5":___cxa_find_matching_catch_5,"__cxa_free_exception":___cxa_free_exception,"__cxa_increment_exception_refcount":___cxa_increment_exception_refcount,"__cxa_rethrow":___cxa_rethrow,"__cxa_rethrow_primary_exception":___cxa_rethrow_primary_exception,"__cxa_throw":___cxa_throw,"__cxa_uncaught_exceptions":___cxa_uncaught_exceptions,"__map_file":___map_file,"__resumeException":___resumeException,"__sys_fcntl64":___sys_fcntl64,"__sys_getdents64":___sys_getdents64,"__sys_ioctl":___sys_ioctl,"__sys_lstat64":___sys_lstat64,"__sys_mkdir":___sys_mkdir,"__sys_munmap":___sys_munmap,"__sys_open":___sys_open,"__sys_rmdir":___sys_rmdir,"__sys_stat64":___sys_stat64,"__sys_unlink":___sys_unlink,"_embind_register_bool":__embind_register_bool,"_embind_register_emval":__embind_register_emval,"_embind_register_float":__embind_register_float,"_embind_register_integer":__embind_register_integer,"_embind_register_memory_view":__embind_register_memory_view,"_embind_register_std_string":__embind_register_std_string,"_embind_register_std_wstring":__embind_register_std_wstring,"_embind_register_void":__embind_register_void,"_emscripten_fetch_free":__emscripten_fetch_free,"abort":_abort,"clock_gettime":_clock_gettime,"emscripten_get_sbrk_ptr":_emscripten_get_sbrk_ptr,"emscripten_is_main_browser_thread":_emscripten_is_main_browser_thread,"emscripten_memcpy_big":_emscripten_memcpy_big,"emscripten_resize_heap":_emscripten_resize_heap,"emscripten_run_script":_emscripten_run_script,"emscripten_start_fetch":_emscripten_start_fetch,"emscripten_webgl_enable_extension":_emscripten_webgl_enable_extension,"emscripten_webgl_get_context_attributes":_emscripten_webgl_get_context_attributes,"emscripten_webgl_get_current_context":_emscripten_webgl_get_current_context,"environ_get":_environ_get,"environ_sizes_get":_environ_sizes_get,"exit":_exit,"fd_close":_fd_close,"fd_read":_fd_read,"fd_seek":_fd_seek,"fd_write":_fd_write,"getTempRet0":_getTempRet0,"glActiveTexture":_glActiveTexture,"glAttachShader":_glAttachShader,"glBindAttribLocation":_glBindAttribLocation,"glBindBuffer":_glBindBuffer,"glBindBufferRange":_glBindBufferRange,"glBindFramebuffer":_glBindFramebuffer,"glBindRenderbuffer":_glBindRenderbuffer,"glBindTexture":_glBindTexture,"glBindVertexArray":_glBindVertexArray,"glBlendFuncSeparate":_glBlendFuncSeparate,"glBlitFramebuffer":_glBlitFramebuffer,"glBufferData":_glBufferData,"glBufferSubData":_glBufferSubData,"glClear":_glClear,"glClearColor":_glClearColor,"glClearDepthf":_glClearDepthf,"glColorMask":_glColorMask,"glCompileShader":_glCompileShader,"glCompressedTexImage2D":_glCompressedTexImage2D,"glCreateProgram":_glCreateProgram,"glCreateShader":_glCreateShader,"glDeleteBuffers":_glDeleteBuffers,"glDeleteFramebuffers":_glDeleteFramebuffers,"glDeleteRenderbuffers":_glDeleteRenderbuffers,"glDeleteTextures":_glDeleteTextures,"glDeleteVertexArrays":_glDeleteVertexArrays,"glDepthFunc":_glDepthFunc,"glDepthMask":_glDepthMask,"glDepthRangef":_glDepthRangef,"glDisable":_glDisable,"glDrawElements":_glDrawElements,"glEnable":_glEnable,"glEnableVertexAttribArray":_glEnableVertexAttribArray,"glFramebufferRenderbuffer":_glFramebufferRenderbuffer,"glFramebufferTexture2D":_glFramebufferTexture2D,"glFrontFace":_glFrontFace,"glGenBuffers":_glGenBuffers,"glGenFramebuffers":_glGenFramebuffers,"glGenRenderbuffers":_glGenRenderbuffers,"glGenTextures":_glGenTextures,"glGenVertexArrays":_glGenVertexArrays,"glGenerateMipmap":_glGenerateMipmap,"glGetActiveUniform":_glGetActiveUniform,"glGetFloatv":_glGetFloatv,"glGetIntegerv":_glGetIntegerv,"glGetInternalformativ":_glGetInternalformativ,"glGetProgramInfoLog":_glGetProgramInfoLog,"glGetProgramiv":_glGetProgramiv,"glGetShaderInfoLog":_glGetShaderInfoLog,"glGetShaderiv":_glGetShaderiv,"glGetUniformBlockIndex":_glGetUniformBlockIndex,"glGetUniformLocation":_glGetUniformLocation,"glLinkProgram":_glLinkProgram,"glReadBuffer":_glReadBuffer,"glReadPixels":_glReadPixels,"glRenderbufferStorageMultisample":_glRenderbufferStorageMultisample,"glScissor":_glScissor,"glShaderSource":_glShaderSource,"glTexImage2D":_glTexImage2D,"glTexParameterf":_glTexParameterf,"glTexParameteri":_glTexParameteri,"glUniform1i":_glUniform1i,"glUniform4fv":_glUniform4fv,"glUniform4iv":_glUniform4iv,"glUniformBlockBinding":_glUniformBlockBinding,"glUniformMatrix2fv":_glUniformMatrix2fv,"glUniformMatrix3fv":_glUniformMatrix3fv,"glUniformMatrix4fv":_glUniformMatrix4fv,"glUseProgram":_glUseProgram,"glVertexAttribPointer":_glVertexAttribPointer,"glViewport":_glViewport,"glfwCreateWindow":_glfwCreateWindow,"glfwGetCursorPos":_glfwGetCursorPos,"glfwInit":_glfwInit,"glfwMakeContextCurrent":_glfwMakeContextCurrent,"glfwPollEvents":_glfwPollEvents,"glfwSetCursorPosCallback":_glfwSetCursorPosCallback,"glfwSetInputMode":_glfwSetInputMode,"glfwSetKeyCallback":_glfwSetKeyCallback,"glfwSetMouseButtonCallback":_glfwSetMouseButtonCallback,"glfwSetScrollCallback":_glfwSetScrollCallback,"glfwSetWindowSizeCallback":_glfwSetWindowSizeCallback,"glfwSetWindowUserPointer":_glfwSetWindowUserPointer,"glfwSwapBuffers":_glfwSwapBuffers,"glfwTerminate":_glfwTerminate,"glfwWindowHint":_glfwWindowHint,"invoke_diii":invoke_diii,"invoke_fiii":invoke_fiii,"invoke_i":invoke_i,"invoke_ii":invoke_ii,"invoke_iii":invoke_iii,"invoke_iiii":invoke_iiii,"invoke_iiiii":invoke_iiiii,"invoke_iiiiid":invoke_iiiiid,"invoke_iiiiii":invoke_iiiiii,"invoke_iiiiiii":invoke_iiiiiii,"invoke_iiiiiiii":invoke_iiiiiiii,"invoke_iiiiiiiiiii":invoke_iiiiiiiiiii,"invoke_iiiiiiiiiiii":invoke_iiiiiiiiiiii,"invoke_iiiiiiiiiiiii":invoke_iiiiiiiiiiiii,"invoke_jii":invoke_jii,"invoke_jiiii":invoke_jiiii,"invoke_v":invoke_v,"invoke_vi":invoke_vi,"invoke_vii":invoke_vii,"invoke_viii":invoke_viii,"invoke_viiii":invoke_viiii,"invoke_viiiiiii":invoke_viiiiiii,"invoke_viiiiiiiiii":invoke_viiiiiiiiii,"invoke_viiiiiiiiiiiiiii":invoke_viiiiiiiiiiiiiii,"invoke_viijii":invoke_viijii,"llvm_eh_typeid_for":_llvm_eh_typeid_for,"memory":wasmMemory,"nanosleep":_nanosleep,"offerFileAsDownload":_offerFileAsDownload,"pthread_attr_destroy":_pthread_attr_destroy,"pthread_attr_init":_pthread_attr_init,"pthread_attr_setstacksize":_pthread_attr_setstacksize,"pthread_create":_pthread_create,"pthread_detach":_pthread_detach,"pthread_join":_pthread_join,"sem_destroy":_sem_destroy,"sem_init":_sem_init,"sem_post":_sem_post,"sem_wait":_sem_wait,"setTempRet0":_setTempRet0,"strftime_l":_strftime_l,"supplyAnimationList":_supplyAnimationList,"supplyMeshIds":_supplyMeshIds,"sysconf":_sysconf,"table":wasmTable,"time":_time};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["__wasm_call_ctors"]).apply(null,arguments)};var _createWebJsScene=Module["_createWebJsScene"]=function(){return(_createWebJsScene=Module["_createWebJsScene"]=Module["asm"]["createWebJsScene"]).apply(null,arguments)};var _createScreenshot=Module["_createScreenshot"]=function(){return(_createScreenshot=Module["_createScreenshot"]=Module["asm"]["createScreenshot"]).apply(null,arguments)};var _startExport=Module["_startExport"]=function(){return(_startExport=Module["_startExport"]=Module["asm"]["startExport"]).apply(null,arguments)};var _setNewUrls=Module["_setNewUrls"]=function(){return(_setNewUrls=Module["_setNewUrls"]=Module["asm"]["setNewUrls"]).apply(null,arguments)};var _setScene=Module["_setScene"]=function(){return(_setScene=Module["_setScene"]=Module["asm"]["setScene"]).apply(null,arguments)};var _setMap=Module["_setMap"]=function(){return(_setMap=Module["_setMap"]=Module["asm"]["setMap"]).apply(null,arguments)};var _setSceneFileDataId=Module["_setSceneFileDataId"]=function(){return(_setSceneFileDataId=Module["_setSceneFileDataId"]=Module["asm"]["setSceneFileDataId"]).apply(null,arguments)};var _setSceneSize=Module["_setSceneSize"]=function(){return(_setSceneSize=Module["_setSceneSize"]=Module["asm"]["setSceneSize"]).apply(null,arguments)};var _addHorizontalViewDir=Module["_addHorizontalViewDir"]=function(){return(_addHorizontalViewDir=Module["_addHorizontalViewDir"]=Module["asm"]["addHorizontalViewDir"]).apply(null,arguments)};var _addVerticalViewDir=Module["_addVerticalViewDir"]=function(){return(_addVerticalViewDir=Module["_addVerticalViewDir"]=Module["asm"]["addVerticalViewDir"]).apply(null,arguments)};var _zoomInFromMouseScroll=Module["_zoomInFromMouseScroll"]=function(){return(_zoomInFromMouseScroll=Module["_zoomInFromMouseScroll"]=Module["asm"]["zoomInFromMouseScroll"]).apply(null,arguments)};var _addCameraViewOffset=Module["_addCameraViewOffset"]=function(){return(_addCameraViewOffset=Module["_addCameraViewOffset"]=Module["asm"]["addCameraViewOffset"]).apply(null,arguments)};var _startMovingForward=Module["_startMovingForward"]=function(){return(_startMovingForward=Module["_startMovingForward"]=Module["asm"]["startMovingForward"]).apply(null,arguments)};var _startMovingBackwards=Module["_startMovingBackwards"]=function(){return(_startMovingBackwards=Module["_startMovingBackwards"]=Module["asm"]["startMovingBackwards"]).apply(null,arguments)};var _stopMovingForward=Module["_stopMovingForward"]=function(){return(_stopMovingForward=Module["_stopMovingForward"]=Module["asm"]["stopMovingForward"]).apply(null,arguments)};var _stopMovingBackwards=Module["_stopMovingBackwards"]=function(){return(_stopMovingBackwards=Module["_stopMovingBackwards"]=Module["asm"]["stopMovingBackwards"]).apply(null,arguments)};var _setClearColor=Module["_setClearColor"]=function(){return(_setClearColor=Module["_setClearColor"]=Module["asm"]["setClearColor"]).apply(null,arguments)};var _enablePortalCulling=Module["_enablePortalCulling"]=function(){return(_enablePortalCulling=Module["_enablePortalCulling"]=Module["asm"]["enablePortalCulling"]).apply(null,arguments)};var _setFarPlane=Module["_setFarPlane"]=function(){return(_setFarPlane=Module["_setFarPlane"]=Module["asm"]["setFarPlane"]).apply(null,arguments)};var _setFarPlaneForCulling=Module["_setFarPlaneForCulling"]=function(){return(_setFarPlaneForCulling=Module["_setFarPlaneForCulling"]=Module["asm"]["setFarPlaneForCulling"]).apply(null,arguments)};var _setAnimationId=Module["_setAnimationId"]=function(){return(_setAnimationId=Module["_setAnimationId"]=Module["asm"]["setAnimationId"]).apply(null,arguments)};var _setMeshIdArray=Module["_setMeshIdArray"]=function(){return(_setMeshIdArray=Module["_setMeshIdArray"]=Module["asm"]["setMeshIdArray"]).apply(null,arguments)};var _setReplaceParticleColors=Module["_setReplaceParticleColors"]=function(){return(_setReplaceParticleColors=Module["_setReplaceParticleColors"]=Module["asm"]["setReplaceParticleColors"]).apply(null,arguments)};var _resetReplaceParticleColor=Module["_resetReplaceParticleColor"]=function(){return(_resetReplaceParticleColor=Module["_resetReplaceParticleColor"]=Module["asm"]["resetReplaceParticleColor"]).apply(null,arguments)};var _setTextures=Module["_setTextures"]=function(){return(_setTextures=Module["_setTextures"]=Module["asm"]["setTextures"]).apply(null,arguments)};var _setScenePos=Module["_setScenePos"]=function(){return(_setScenePos=Module["_setScenePos"]=Module["asm"]["setScenePos"]).apply(null,arguments)};var _gameloop=Module["_gameloop"]=function(){return(_gameloop=Module["_gameloop"]=Module["asm"]["gameloop"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["__errno_location"]).apply(null,arguments)};var _main=Module["_main"]=function(){return(_main=Module["_main"]=Module["asm"]["main"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["free"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["malloc"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["__getTypeName"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["__embind_register_native_and_builtin_types"]).apply(null,arguments)};var __get_tzname=Module["__get_tzname"]=function(){return(__get_tzname=Module["__get_tzname"]=Module["asm"]["_get_tzname"]).apply(null,arguments)};var __get_daylight=Module["__get_daylight"]=function(){return(__get_daylight=Module["__get_daylight"]=Module["asm"]["_get_daylight"]).apply(null,arguments)};var __get_timezone=Module["__get_timezone"]=function(){return(__get_timezone=Module["__get_timezone"]=Module["asm"]["_get_timezone"]).apply(null,arguments)};var _setThrew=Module["_setThrew"]=function(){return(_setThrew=Module["_setThrew"]=Module["asm"]["setThrew"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["stackSave"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["stackRestore"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["stackAlloc"]).apply(null,arguments)};var __ZSt18uncaught_exceptionv=Module["__ZSt18uncaught_exceptionv"]=function(){return(__ZSt18uncaught_exceptionv=Module["__ZSt18uncaught_exceptionv"]=Module["asm"]["_ZSt18uncaught_exceptionv"]).apply(null,arguments)};var ___cxa_can_catch=Module["___cxa_can_catch"]=function(){return(___cxa_can_catch=Module["___cxa_can_catch"]=Module["asm"]["__cxa_can_catch"]).apply(null,arguments)};var ___cxa_is_pointer_type=Module["___cxa_is_pointer_type"]=function(){return(___cxa_is_pointer_type=Module["___cxa_is_pointer_type"]=Module["asm"]["__cxa_is_pointer_type"]).apply(null,arguments)};var _emscripten_main_thread_process_queued_calls=Module["_emscripten_main_thread_process_queued_calls"]=function(){return(_emscripten_main_thread_process_queued_calls=Module["_emscripten_main_thread_process_queued_calls"]=Module["asm"]["emscripten_main_thread_process_queued_calls"]).apply(null,arguments)};var dynCall_v=Module["dynCall_v"]=function(){return(dynCall_v=Module["dynCall_v"]=Module["asm"]["dynCall_v"]).apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return(dynCall_vi=Module["dynCall_vi"]=Module["asm"]["dynCall_vi"]).apply(null,arguments)};var dynCall_vii=Module["dynCall_vii"]=function(){return(dynCall_vii=Module["dynCall_vii"]=Module["asm"]["dynCall_vii"]).apply(null,arguments)};var dynCall_viii=Module["dynCall_viii"]=function(){return(dynCall_viii=Module["dynCall_viii"]=Module["asm"]["dynCall_viii"]).apply(null,arguments)};var dynCall_viiii=Module["dynCall_viiii"]=function(){return(dynCall_viiii=Module["dynCall_viiii"]=Module["asm"]["dynCall_viiii"]).apply(null,arguments)};var dynCall_viiiiiii=Module["dynCall_viiiiiii"]=function(){return(dynCall_viiiiiii=Module["dynCall_viiiiiii"]=Module["asm"]["dynCall_viiiiiii"]).apply(null,arguments)};var dynCall_viiiiiiiiii=Module["dynCall_viiiiiiiiii"]=function(){return(dynCall_viiiiiiiiii=Module["dynCall_viiiiiiiiii"]=Module["asm"]["dynCall_viiiiiiiiii"]).apply(null,arguments)};var dynCall_viiiiiiiiiiiiiii=Module["dynCall_viiiiiiiiiiiiiii"]=function(){return(dynCall_viiiiiiiiiiiiiii=Module["dynCall_viiiiiiiiiiiiiii"]=Module["asm"]["dynCall_viiiiiiiiiiiiiii"]).apply(null,arguments)};var dynCall_viijii=Module["dynCall_viijii"]=function(){return(dynCall_viijii=Module["dynCall_viijii"]=Module["asm"]["dynCall_viijii"]).apply(null,arguments)};var dynCall_i=Module["dynCall_i"]=function(){return(dynCall_i=Module["dynCall_i"]=Module["asm"]["dynCall_i"]).apply(null,arguments)};var dynCall_ii=Module["dynCall_ii"]=function(){return(dynCall_ii=Module["dynCall_ii"]=Module["asm"]["dynCall_ii"]).apply(null,arguments)};var dynCall_iii=Module["dynCall_iii"]=function(){return(dynCall_iii=Module["dynCall_iii"]=Module["asm"]["dynCall_iii"]).apply(null,arguments)};var dynCall_iiii=Module["dynCall_iiii"]=function(){return(dynCall_iiii=Module["dynCall_iiii"]=Module["asm"]["dynCall_iiii"]).apply(null,arguments)};var dynCall_iiiii=Module["dynCall_iiiii"]=function(){return(dynCall_iiiii=Module["dynCall_iiiii"]=Module["asm"]["dynCall_iiiii"]).apply(null,arguments)};var dynCall_iiiiii=Module["dynCall_iiiiii"]=function(){return(dynCall_iiiiii=Module["dynCall_iiiiii"]=Module["asm"]["dynCall_iiiiii"]).apply(null,arguments)};var dynCall_iiiiiii=Module["dynCall_iiiiiii"]=function(){return(dynCall_iiiiiii=Module["dynCall_iiiiiii"]=Module["asm"]["dynCall_iiiiiii"]).apply(null,arguments)};var dynCall_iiiiiiii=Module["dynCall_iiiiiiii"]=function(){return(dynCall_iiiiiiii=Module["dynCall_iiiiiiii"]=Module["asm"]["dynCall_iiiiiiii"]).apply(null,arguments)};var dynCall_iiiiiiiiiii=Module["dynCall_iiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiii=Module["dynCall_iiiiiiiiiii"]=Module["asm"]["dynCall_iiiiiiiiiii"]).apply(null,arguments)};var dynCall_iiiiiiiiiiii=Module["dynCall_iiiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiiii=Module["dynCall_iiiiiiiiiiii"]=Module["asm"]["dynCall_iiiiiiiiiiii"]).apply(null,arguments)};var dynCall_iiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiii"]=function(){return(dynCall_iiiiiiiiiiiii=Module["dynCall_iiiiiiiiiiiii"]=Module["asm"]["dynCall_iiiiiiiiiiiii"]).apply(null,arguments)};var dynCall_iiiiid=Module["dynCall_iiiiid"]=function(){return(dynCall_iiiiid=Module["dynCall_iiiiid"]=Module["asm"]["dynCall_iiiiid"]).apply(null,arguments)};var dynCall_jii=Module["dynCall_jii"]=function(){return(dynCall_jii=Module["dynCall_jii"]=Module["asm"]["dynCall_jii"]).apply(null,arguments)};var dynCall_jiiii=Module["dynCall_jiiii"]=function(){return(dynCall_jiiii=Module["dynCall_jiiii"]=Module["asm"]["dynCall_jiiii"]).apply(null,arguments)};var dynCall_fiii=Module["dynCall_fiii"]=function(){return(dynCall_fiii=Module["dynCall_fiii"]=Module["asm"]["dynCall_fiii"]).apply(null,arguments)};var dynCall_diii=Module["dynCall_diii"]=function(){return(dynCall_diii=Module["dynCall_diii"]=Module["asm"]["dynCall_diii"]).apply(null,arguments)};var dynCall_viiiii=Module["dynCall_viiiii"]=function(){return(dynCall_viiiii=Module["dynCall_viiiii"]=Module["asm"]["dynCall_viiiii"]).apply(null,arguments)};var dynCall_vidd=Module["dynCall_vidd"]=function(){return(dynCall_vidd=Module["dynCall_vidd"]=Module["asm"]["dynCall_vidd"]).apply(null,arguments)};var dynCall_viiiiii=Module["dynCall_viiiiii"]=function(){return(dynCall_viiiiii=Module["dynCall_viiiiii"]=Module["asm"]["dynCall_viiiiii"]).apply(null,arguments)};var dynCall_iiiiiiiiii=Module["dynCall_iiiiiiiiii"]=function(){return(dynCall_iiiiiiiiii=Module["dynCall_iiiiiiiiii"]=Module["asm"]["dynCall_iiiiiiiiii"]).apply(null,arguments)};var dynCall_vif=Module["dynCall_vif"]=function(){return(dynCall_vif=Module["dynCall_vif"]=Module["asm"]["dynCall_vif"]).apply(null,arguments)};var dynCall_viff=Module["dynCall_viff"]=function(){return(dynCall_viff=Module["dynCall_viff"]=Module["asm"]["dynCall_viff"]).apply(null,arguments)};var dynCall_vifff=Module["dynCall_vifff"]=function(){return(dynCall_vifff=Module["dynCall_vifff"]=Module["asm"]["dynCall_vifff"]).apply(null,arguments)};var dynCall_viiffff=Module["dynCall_viiffff"]=function(){return(dynCall_viiffff=Module["dynCall_viiffff"]=Module["asm"]["dynCall_viiffff"]).apply(null,arguments)};var dynCall_vid=Module["dynCall_vid"]=function(){return(dynCall_vid=Module["dynCall_vid"]=Module["asm"]["dynCall_vid"]).apply(null,arguments)};var dynCall_di=Module["dynCall_di"]=function(){return(dynCall_di=Module["dynCall_di"]=Module["asm"]["dynCall_di"]).apply(null,arguments)};var dynCall_viiiiiiiii=Module["dynCall_viiiiiiiii"]=function(){return(dynCall_viiiiiiiii=Module["dynCall_viiiiiiiii"]=Module["asm"]["dynCall_viiiiiiiii"]).apply(null,arguments)};var dynCall_viiiiiiii=Module["dynCall_viiiiiiii"]=function(){return(dynCall_viiiiiiii=Module["dynCall_viiiiiiii"]=Module["asm"]["dynCall_viiiiiiii"]).apply(null,arguments)};var dynCall_fi=Module["dynCall_fi"]=function(){return(dynCall_fi=Module["dynCall_fi"]=Module["asm"]["dynCall_fi"]).apply(null,arguments)};var dynCall_vidi=Module["dynCall_vidi"]=function(){return(dynCall_vidi=Module["dynCall_vidi"]=Module["asm"]["dynCall_vidi"]).apply(null,arguments)};var dynCall_viid=Module["dynCall_viid"]=function(){return(dynCall_viid=Module["dynCall_viid"]=Module["asm"]["dynCall_viid"]).apply(null,arguments)};var dynCall_viffff=Module["dynCall_viffff"]=function(){return(dynCall_viffff=Module["dynCall_viffff"]=Module["asm"]["dynCall_viffff"]).apply(null,arguments)};var dynCall_iiiji=Module["dynCall_iiiji"]=function(){return(dynCall_iiiji=Module["dynCall_iiiji"]=Module["asm"]["dynCall_iiiji"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["dynCall_jiji"]).apply(null,arguments)};var dynCall_iidiiii=Module["dynCall_iidiiii"]=function(){return(dynCall_iidiiii=Module["dynCall_iidiiii"]=Module["asm"]["dynCall_iidiiii"]).apply(null,arguments)};var dynCall_iiiiiiiii=Module["dynCall_iiiiiiiii"]=function(){return(dynCall_iiiiiiiii=Module["dynCall_iiiiiiiii"]=Module["asm"]["dynCall_iiiiiiiii"]).apply(null,arguments)};var dynCall_iiiiij=Module["dynCall_iiiiij"]=function(){return(dynCall_iiiiij=Module["dynCall_iiiiij"]=Module["asm"]["dynCall_iiiiij"]).apply(null,arguments)};var dynCall_iiiiijj=Module["dynCall_iiiiijj"]=function(){return(dynCall_iiiiijj=Module["dynCall_iiiiijj"]=Module["asm"]["dynCall_iiiiijj"]).apply(null,arguments)};var dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=function(){return(dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=Module["asm"]["dynCall_iiiiiijj"]).apply(null,arguments)};var __growWasmMemory=Module["__growWasmMemory"]=function(){return(__growWasmMemory=Module["__growWasmMemory"]=Module["asm"]["__growWasmMemory"]).apply(null,arguments)};function invoke_v(index){var sp=stackSave();try{dynCall_v(index)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{dynCall_vii(index,a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{dynCall_vi(index,a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return dynCall_iii(index,a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{dynCall_viii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return dynCall_ii(index,a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return dynCall_iiii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_i(index){var sp=stackSave();try{return dynCall_i(index)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return dynCall_iiiii(index,a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{dynCall_viiii(index,a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return dynCall_iiiiiii(index,a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return dynCall_iiiiii(index,a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiid(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return dynCall_iiiiid(index,a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return dynCall_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{return dynCall_iiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12){var sp=stackSave();try{return dynCall_iiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_fiii(index,a1,a2,a3){var sp=stackSave();try{return dynCall_fiii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_diii(index,a1,a2,a3){var sp=stackSave();try{return dynCall_diii(index,a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{dynCall_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{return dynCall_iiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{dynCall_viiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15){var sp=stackSave();try{dynCall_viiiiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_jii(index,a1,a2){var sp=stackSave();try{return dynCall_jii(index,a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viijii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{dynCall_viijii(index,a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_jiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return dynCall_jiiii(index,a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}Module["getMemory"]=getMemory;Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["FS_createFolder"]=FS.createFolder;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createLink"]=FS.createLink;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;Module["FS"]=FS;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}var calledMain=false;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(args){var entryFunction=Module["_main"];var argc=0;var argv=0;try{var ret=entryFunction(argc,argv);exit(ret,true)}catch(e){if(e instanceof ExitStatus){return}else if(e=="unwind"){noExitRuntime=true;return}else{var toLog=e;if(e&&typeof e==="object"&&e.stack){toLog=[e,e.stack]}err("exception thrown: "+toLog);quit_(1,e)}}finally{calledMain=true}}function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(shouldRunNow)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&noExitRuntime&&status===0){return}if(noExitRuntime){}else{ABORT=true;EXITSTATUS=status;exitRuntime();if(Module["onExit"])Module["onExit"](status)}quit_(status,new ExitStatus(status))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"])shouldRunNow=false;noExitRuntime=true;run(); diff --git a/emscripten_port/project.wasm b/emscripten_port/project.wasm index 23f9d0b3f..3ad127391 100644 Binary files a/emscripten_port/project.wasm and b/emscripten_port/project.wasm differ diff --git a/emscripten_port/src/HttpRequestProcessor.cpp b/emscripten_port/src/HttpRequestProcessor.cpp index 8edd64c4d..48cf12ccb 100644 --- a/emscripten_port/src/HttpRequestProcessor.cpp +++ b/emscripten_port/src/HttpRequestProcessor.cpp @@ -14,6 +14,7 @@ struct UserDataForRequest { std::string fileName; CacheHolderType holderType; HttpRequestProcessor *processor; + std::shared_ptr s_file; }; //void downloadProgress(emscripten_fetch_t *fetch) { @@ -44,12 +45,8 @@ void downloadSucceeded(emscripten_fetch_t *fetch) { return; } - - userDataForRequest->processor->provideResult(userDataForRequest->fileName, fileContent, - userDataForRequest->holderType); - userDataForRequest->processor->currentlyProcessing--; - - + userDataForRequest->s_file->process(fileContent, userDataForRequest->fileName); + userDataForRequest->processor->toBeProcessed--; // The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1]; delete userDataForRequest; @@ -63,7 +60,7 @@ void downloadSucceeded(emscripten_fetch_t *fetch) { void downloadFailed(emscripten_fetch_t *fetch) { printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status); UserDataForRequest * userDataForRequest = (UserDataForRequest *)fetch->userData; - userDataForRequest->processor->currentlyProcessing--; + userDataForRequest->processor->toBeProcessed--; emscripten_fetch_close(fetch); // Also free data on failure. @@ -87,12 +84,6 @@ std::string char_to_escape( char i ) return stream.str(); } -void HttpRequestProcessor::requestFile(const char *fileName, CacheHolderType holderType) { - std::string fileName_s(fileName); - this->addRequest(fileName_s, holderType); -} - - std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) { size_t start_pos = 0; while((start_pos = str.find(from, start_pos)) != std::string::npos) { @@ -102,7 +93,12 @@ std::string ReplaceAll(std::string str, const std::string& from, const std::stri return str; } -void HttpRequestProcessor::processFileRequest(std::string &fileName, CacheHolderType holderType) { +void HttpRequestProcessor::processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) { + auto perstFile = s_file.lock(); + if (perstFile == nullptr){ + toBeProcessed--; + return; + } // std::cout << "processFileRequest : filename = " << fileName << std::endl; const std::string charsToEscape = " !*'();:@&=+$,/?#[]"; @@ -122,7 +118,7 @@ void HttpRequestProcessor::processFileRequest(std::string &fileName, CacheHolder ss >> fileDataId; if (fileDataId == 0) { - currentlyProcessing--; + toBeProcessed--; return; } @@ -137,6 +133,7 @@ void HttpRequestProcessor::processFileRequest(std::string &fileName, CacheHolder userDataForRequest->fileName = fileName; userDataForRequest->holderType = holderType; userDataForRequest->processor = this; + userDataForRequest->s_file = perstFile; emscripten_fetch_attr_t attr; emscripten_fetch_attr_init(&attr); diff --git a/emscripten_port/src/HttpRequestProcessor.h b/emscripten_port/src/HttpRequestProcessor.h index 561e9084c..f47318be6 100644 --- a/emscripten_port/src/HttpRequestProcessor.h +++ b/emscripten_port/src/HttpRequestProcessor.h @@ -5,6 +5,7 @@ #ifndef WEBWOWVIEWERCPP_HTTPREQUESTPROCESSOR_H #define WEBWOWVIEWERCPP_HTTPREQUESTPROCESSOR_H +#include #include "RequestProcessor.h" class HttpRequestProcessor : public RequestProcessor { @@ -16,10 +17,11 @@ class HttpRequestProcessor : public RequestProcessor { std::string m_urlBase; std::string m_urlBaseFileId; protected: - void processFileRequest(std::string &fileName, CacheHolderType holderType) override; + void processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) override; public: - void requestFile(const char* fileName, CacheHolderType holderType) override; + friend void downloadFailed(emscripten_fetch_t *fetch); + friend void downloadSucceeded(emscripten_fetch_t *fetch); void setUrls(const char *urlBase, const char *urlBaseFileId) { m_urlBase = std::string(urlBase); diff --git a/emscripten_port/src/RequestProcessor.cpp b/emscripten_port/src/RequestProcessor.cpp index d36ebfb26..b402eee03 100644 --- a/emscripten_port/src/RequestProcessor.cpp +++ b/emscripten_port/src/RequestProcessor.cpp @@ -1,7 +1,7 @@ - #include #include #include +#include #include "RequestProcessor.h" std::mutex requestMtx; // mutex for critical section @@ -13,13 +13,23 @@ std::mutex setProcessingMtx; // mutex for critical section //2. Get filename from FIFO //3. Add result to ResultFIFO //4. Get ready results from FIFO -void RequestProcessor::addRequest (std::string &fileName, CacheHolderType holderType) { -// std::cout << "RequestProcessor::addRequest : fileName = " << fileName << std::endl; - +void +RequestProcessor::requestFile(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) { std::unique_lock setLck (setProcessingMtx,std::defer_lock); setLck.lock(); if (currentlyProcessingFnames.count(fileName) > 0) { -// std::cout << "RequestProcessor::addRequest : duplicate detected for fileName = " << fileName << std::endl; + uint32_t fileDataId = 0; + if (fileName.find("File") == 0) { + std::stringstream ss; + std::string fileDataIdHex = fileName.substr(4, fileName.find(".") - 4); + + ss << std::hex << fileDataIdHex; + ss >> fileDataId; + } + std::cout << "RequestProcessor::addRequest : duplicate detected for fileName = " << fileName + << " " << ((fileDataId > 0) ? ("(fileDataId = "+std::to_string(fileDataId)+")"): "") + << " holderTypeReq = " << (int)holderType + < lck (requestMtx,std::defer_lock); + if (calledFromThread){ - std::unique_lock lck (requestMtx,std::defer_lock); + while (!this->isTerminating) { + if (m_requestQueue.empty()) { + std::this_thread::sleep_for(1ms); + continue; + } + + lck.lock(); + auto it = m_requestQueue.front(); + m_requestQueue.pop_front(); + lck.unlock(); + this->processFileRequest(it.fileName, it.holderType, it.s_file); + std::unique_lock setLck (setProcessingMtx,std::defer_lock); + setLck.lock(); + currentlyProcessingFnames.erase(it.fileName); + setLck.unlock(); + } } else if (!m_threaded) { while (!m_requestQueue.empty()) { + lck.lock(); auto it = m_requestQueue.front(); m_requestQueue.pop_front(); + lck.unlock(); + + this->processFileRequest(it.fileName, it.holderType, it.s_file); - this->processFileRequest(it.fileName, it.holderType); + std::unique_lock setLck (setProcessingMtx,std::defer_lock); + setLck.lock(); + currentlyProcessingFnames.erase(it.fileName); + setLck.unlock(); } } } -void RequestProcessor::provideResult(std::string &fileName, HFileContent content, CacheHolderType holderType) { - std::unique_lock lck (resultMtx,std::defer_lock); - - ResultStruct resultStructObj; - resultStructObj.buffer = content; - resultStructObj.fileName = fileName; - resultStructObj.holderType = holderType; - - if (m_threaded) lck.lock(); - m_resultQueue.push_back(resultStructObj); - if (m_threaded) lck.unlock(); -} - -void RequestProcessor::processResults(int limit) { - std::unique_lock lck (resultMtx,std::defer_lock); - - for (int i = 0; i < limit; i++) { - if (m_resultQueue.empty()) break; - - if (m_threaded) lck.lock(); - auto it = &m_resultQueue.front(); - if (m_threaded) lck.unlock(); - -// std::cout << "it->buffer.use_count() = " << it->buffer.use_count() << std::endl << std::flush; - - HFileContent bufferCpy = it->buffer; - m_fileRequester->provideFile( - it->holderType, - it->fileName.c_str(), - bufferCpy - ); - - std::unique_lock setLck (setProcessingMtx,std::defer_lock); - setLck.lock(); - currentlyProcessingFnames.erase(it->fileName); - setLck.unlock(); - - - if (m_threaded) lck.lock(); - m_resultQueue.pop_front(); - if (m_threaded) lck.unlock(); +void +RequestProcessor::processResult(std::shared_ptr s_file, HFileContent content, const std::string &fileName) { + if (s_file->getStatus() == FileStatus::FSLoaded) { + std::cout << "sharedPtr->getStatus == FileStatus::FSLoaded " << fileName << std::endl; + } if (s_file->getStatus() == FileStatus::FSRejected) { + std::cout << "sharedPtr->getStatus == FileStatus::FSRejected" << fileName << std::endl; + } else { + s_file->process(content, fileName); } - -} \ No newline at end of file +} diff --git a/emscripten_port/src/RequestProcessor.h b/emscripten_port/src/RequestProcessor.h index 8c2888304..35b29479a 100644 --- a/emscripten_port/src/RequestProcessor.h +++ b/emscripten_port/src/RequestProcessor.h @@ -18,21 +18,30 @@ class RequestProcessor : public IFileRequest { RequestProcessor() { } + ~RequestProcessor() { + if (loaderThread != nullptr) { + isTerminating = true; + loaderThread->join(); + loaderThread = nullptr; + } + }; protected: IFileRequester *m_fileRequester = nullptr; - virtual void processFileRequest(std::string &fileName, CacheHolderType holderType) = 0; + virtual void processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) = 0; public: void setFileRequester(IFileRequester *fileRequester) { m_fileRequester = fileRequester; } + void requestFile(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) override; private: class RequestStruct { public: std::string fileName; CacheHolderType holderType; + std::weak_ptr s_file; }; class ResultStruct { @@ -48,32 +57,38 @@ class RequestProcessor : public IFileRequest { HFileContent buffer = nullptr; }; - std::thread *loaderThread; + bool isTerminating = false; + std::shared_ptr loaderThread = nullptr; std::list m_requestQueue; std::list m_resultQueue; std::unordered_set currentlyProcessingFnames; bool m_threaded = false; - public: - void processResults(int limit); void processRequests(bool calledFromThread); + bool completedAllJobs() { + return (m_requestQueue.empty()) && (m_resultQueue.empty()) && (toBeProcessed == 0); + }; + + bool getThreaded() { + return m_threaded; + } void setThreaded(bool value) { m_threaded = value; if (value) { - loaderThread = new std::thread(([&](){ + loaderThread = std::make_shared(([&](){ this->processRequests(true); })); } } - int currentlyProcessing = 0; - void provideResult(std::string &fileName, HFileContent content, CacheHolderType holderType); -protected: - void addRequest (std::string &fileName, CacheHolderType holderType); - +protected: + int toBeProcessed = 0; + void processResult( std::shared_ptr s_file, HFileContent content, const std::string &fileName); }; +typedef std::shared_ptr HRequestProcessor; + #endif //WEBWOWVIEWERCPP_REQUESTPROCESSOR_H diff --git a/emscripten_port/src/main.cpp b/emscripten_port/src/main.cpp index 648d70f18..2f18b7581 100644 --- a/emscripten_port/src/main.cpp +++ b/emscripten_port/src/main.cpp @@ -1,6 +1,7 @@ #include "../../wowViewerLib/src/gapi/IDeviceFactory.h" #include "RequestProcessor.h" #include "HttpRequestProcessor.h" +#include "../../src/screenshots/screenshotMaker.h" #include "../../wowViewerLib/src/engine/ApiContainer.h" #include "../../wowViewerLib/src/engine/camera/firstPersonCamera.h" #include "../../wowViewerLib/src/engine/SceneComposer.h" @@ -9,14 +10,18 @@ #include "../../wowViewerLib/src/engine/objects/scenes/m2Scene.h" #include "../../wowViewerLib/src/engine/objects/scenes/wmoScene.h" #include "../../wowViewerLib/src/engine/objects/scenes/map.h" +#include "../../src/exporters/gltfExporter/GLTFExporter.h" +#include "../../3rdparty/filesystem_impl/include/ghc/filesystem.hpp" #include #include #include #include #include +#include #include +#include -ApiContainer apiContainer; +std::shared_ptr apiContainer = std::make_shared(); HttpRequestProcessor *processor; SceneComposer *sceneComposer; std::shared_ptr currentScene = std::make_shared(); @@ -24,7 +29,16 @@ std::shared_ptr currentScene = std::make_shared(); std::shared_ptr planarCamera = std::make_shared(); std::shared_ptr firstPersonCamera = std::make_shared(); +//Variable for making a screenshot +std::string screenshotFilename = ""; +HDrawStage screenshotDS = nullptr; +bool needToMakeScreenshot = false; +int screenShotWidth = 100; +int screenShotHeight = 100; +int screenshotFrame = -1; +std::shared_ptr exporter = nullptr; +int exporterFramesReady = 0; int canvWidth = 640; int canvHeight = 480; @@ -75,7 +89,7 @@ void convertColors(int index, int start, int mid, int end, std::arraycamera; // if (!pointerIsLocked) { if (mleft_pressed == 1) { @@ -133,7 +147,7 @@ void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) static void onKey(GLFWwindow* window, int key, int scancode, int action, int mods) { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; if ( action == GLFW_PRESS) { switch (key) { case 'W' : @@ -191,7 +205,7 @@ static void onKey(GLFWwindow* window, int key, int scancode, int action, int mod break; case 'O': - apiContainer.camera->setCameraPos(0,0,0); + apiContainer->camera->setCameraPos(0,0,0); break; case GLFW_KEY_LEFT_SHIFT: controllable->setMovementSpeed(1.0f); @@ -204,7 +218,7 @@ static void onKey(GLFWwindow* window, int key, int scancode, int action, int mod } void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->zoomInFromMouseScroll(yoffset/240.0f); } @@ -222,15 +236,15 @@ void setScene(int sceneType, std::string name, int cameraNum) { currentScene = std::make_shared(); } else if (sceneType == 0) { // m_usePlanarCamera = cameraNum == -1; - apiContainer.camera = planarCamera; + apiContainer->camera = planarCamera; - currentScene = std::make_shared(&apiContainer, name , cameraNum); + currentScene = std::make_shared(apiContainer, name , cameraNum); } else if (sceneType == 1) { - apiContainer.camera = firstPersonCamera; + apiContainer->camera = firstPersonCamera; - currentScene = std::make_shared(&apiContainer, name); + currentScene = std::make_shared(apiContainer, name); } else if (sceneType == 2) { - apiContainer.camera = firstPersonCamera; + apiContainer->camera = firstPersonCamera; std::string &adtFileName = name; @@ -248,20 +262,87 @@ void setScene(int sceneType, std::string name, int cameraNum) { float adt_y_min = AdtIndexToWorldCoordinate(i); float adt_y_max = AdtIndexToWorldCoordinate(i+1); - apiContainer.camera->setCameraPos( + apiContainer->camera->setCameraPos( (adt_x_min+adt_x_max) / 2.0, (adt_y_min+adt_y_max) / 2.0, 200 ); - currentScene = std::make_shared(&apiContainer, adtFileName, i, j, mapName); + currentScene = std::make_shared(apiContainer, adtFileName, i, j, mapName); } } +HDrawStage createSceneDrawStage(HFrameScenario sceneScenario, int width, int height, double deltaTime, + bool isScreenshot, ApiContainer &apiContainer, + const std::shared_ptr ¤tScene, + HCullStage &cullStage, + std::vector &drawStageDependencies +) { + float farPlaneRendering = apiContainer.getConfig()->farPlane; + float farPlaneCulling = apiContainer.getConfig()->farPlaneForCulling; + + float nearPlane = 1.0; + float fov = toRadian(45.0); + + float canvasAspect = (float)width / (float)height; + + HCameraMatrices cameraMatricesCulling = apiContainer.camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneCulling); + HCameraMatrices cameraMatricesRendering = apiContainer.camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneRendering); + //Frustum matrix with reversed Z + + bool isInfZSupported = apiContainer.camera->isCompatibleWithInfiniteZ(); + if (isInfZSupported) + { + float f = 1.0f / tan(fov / 2.0f); + cameraMatricesRendering->perspectiveMat = mathfu::mat4( + f / canvasAspect, 0.0f, 0.0f, 0.0f, + 0.0f, f, 0.0f, 0.0f, + 0.0f, 0.0f, 1, -1.0f, + 0.0f, 0.0f, 1, 0.0f); + } + + if (apiContainer.hDevice->getIsVulkanAxisSystem() ) { + auto &perspectiveMatrix = cameraMatricesRendering->perspectiveMat; + + static const mathfu::mat4 vulkanMatrixFix2 = mathfu::mat4(1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1.0/2.0, 1/2.0, + 0, 0, 0, 1).Transpose(); + + perspectiveMatrix = vulkanMatrixFix2 * perspectiveMatrix; + } + + auto clearColor = apiContainer.getConfig()->clearColor; + + if (currentScene != nullptr) { + ViewPortDimensions dimensions = {{0, 0}, {width, height}}; + + HFrameBuffer fb = nullptr; + if (isScreenshot) { + fb = apiContainer.hDevice->createFrameBuffer(width, height, + {ITextureFormat::itRGBA}, + ITextureFormat::itDepth32, + apiContainer.hDevice->getMaxSamplesCnt(), 4); + } + + cullStage = sceneScenario->addCullStage(cameraMatricesCulling, currentScene); + auto updateStage = sceneScenario->addUpdateStage(cullStage, deltaTime*(1000.0f), cameraMatricesRendering); + HDrawStage sceneDrawStage = sceneScenario->addDrawStage(updateStage, currentScene, cameraMatricesRendering, drawStageDependencies, true, + dimensions, + true, isInfZSupported, clearColor, fb); + + return sceneDrawStage; + } + + return nullptr; +} + extern "C" { EMSCRIPTEN_KEEPALIVE void createWebJsScene(int p_canvWidth, int p_canvHeight, char *url, char *urlFileId) { + auto temp_gc = new tbb::global_control(tbb::global_control::max_allowed_parallelism, 1); + if (!glfwInit()) { fputs("Failed to initialize GLFW", stderr); return; @@ -270,7 +351,16 @@ extern "C" { canvHeight = p_canvHeight; - apiContainer.camera = firstPersonCamera; + apiContainer->camera = firstPersonCamera; + apiContainer->getConfig()->globalLighting = EParameterSource::eConfig; + + auto ambient = mathfu::vec4(1.0,1.0,1.0,1.0); + + apiContainer->getConfig()->exteriorAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + apiContainer->getConfig()->exteriorHorizontAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + apiContainer->getConfig()->exteriorGroundAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + apiContainer->getConfig()->exteriorDirectColor = mathfu::vec4(0.3,0.3,0.3,0.3); + apiContainer->getConfig()->exteriorDirectColorDir = mathfu::vec3(0.0,0.0,0.0); emscripten_run_script("Module['hammerJsAssignControl']()"); @@ -295,8 +385,8 @@ extern "C" { processor = new HttpRequestProcessor(url, urlFileId); processor->setThreaded(false); - apiContainer.cacheStorage = std::make_shared(processor); - processor->setFileRequester(apiContainer.cacheStorage.get()); + apiContainer->cacheStorage = std::make_shared(processor); + processor->setFileRequester(apiContainer->cacheStorage.get()); // testConf = new Config(); // testConf->setSunColor(0.0,0.0,0.0,0.0); @@ -316,16 +406,15 @@ extern "C" { if (uboSize > 0) { glVersion = "ogl3"; } - } auto hdevice = IDeviceFactory::createDevice(glVersion, nullptr); - apiContainer.databaseHandler = nullptr; - apiContainer.hDevice = hdevice; - apiContainer.camera = std::make_shared(); + apiContainer->databaseHandler = nullptr; + apiContainer->hDevice = hdevice; + apiContainer->camera = std::make_shared(); - sceneComposer = new SceneComposer(&apiContainer); + sceneComposer = new SceneComposer(apiContainer); glfwSetWindowUserPointer(window, nullptr); glfwSetKeyCallback(window, onKey); @@ -335,10 +424,23 @@ extern "C" { glfwSetMouseButtonCallback(window, mouse_button_callback); } + EMSCRIPTEN_KEEPALIVE + void createScreenshot() { + screenshotFilename = "screenshot.png"; + needToMakeScreenshot = true; + } + + EMSCRIPTEN_KEEPALIVE + void startExport() { + exporter = std::make_shared("./gltf/"); + currentScene->exportScene(exporter.get()); + + } + EMSCRIPTEN_KEEPALIVE void setNewUrls(char *url, char *urlFileId) { processor->setUrls(url, urlFileId); - apiContainer.cacheStorage->actuallDropCache(); + apiContainer->cacheStorage->actuallDropCache(); } EMSCRIPTEN_KEEPALIVE void setScene(int sceneType, char *name, int cameraNum) { @@ -346,15 +448,15 @@ extern "C" { } EMSCRIPTEN_KEEPALIVE void setMap(int mapId, int wdtFileId, float x, float y, float z) { - apiContainer.camera = firstPersonCamera; + apiContainer->camera = firstPersonCamera; - apiContainer.camera->setCameraPos( + apiContainer->camera->setCameraPos( x, y, z ); - currentScene = std::make_shared(&apiContainer, mapId, wdtFileId); + currentScene = std::make_shared(apiContainer, mapId, wdtFileId); } EMSCRIPTEN_KEEPALIVE void setSceneFileDataId(int sceneType, int fileDataId, int cameraNum) { @@ -362,13 +464,13 @@ extern "C" { currentScene = std::make_shared(); } else if (sceneType == 0) { // m_usePlanarCamera = cameraNum == -1; - apiContainer.camera = planarCamera; + apiContainer->camera = planarCamera; - currentScene = std::make_shared(&apiContainer, fileDataId , cameraNum); + currentScene = std::make_shared(apiContainer, fileDataId , cameraNum); } else if (sceneType == 1) { - apiContainer.camera = firstPersonCamera; + apiContainer->camera = firstPersonCamera; - currentScene = std::make_shared(&apiContainer, fileDataId); + currentScene = std::make_shared(apiContainer, fileDataId); } } EMSCRIPTEN_KEEPALIVE @@ -379,56 +481,60 @@ extern "C" { } EMSCRIPTEN_KEEPALIVE void addHorizontalViewDir(float val) { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->addHorizontalViewDir(val); } EMSCRIPTEN_KEEPALIVE void addVerticalViewDir(float val ) { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->addVerticalViewDir(val); } EMSCRIPTEN_KEEPALIVE void zoomInFromMouseScroll(float val ) { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->zoomInFromMouseScroll(val); } EMSCRIPTEN_KEEPALIVE void addCameraViewOffset(float val1, float val2 ) { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->addCameraViewOffset(val1, val2); } EMSCRIPTEN_KEEPALIVE void startMovingForward() { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->startMovingForward(); } EMSCRIPTEN_KEEPALIVE void startMovingBackwards() { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->startMovingBackwards(); } EMSCRIPTEN_KEEPALIVE void stopMovingForward() { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->stopMovingForward(); } EMSCRIPTEN_KEEPALIVE void stopMovingBackwards() { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->stopMovingBackwards(); } EMSCRIPTEN_KEEPALIVE void setClearColor(float r, float g, float b) { - apiContainer.getConfig()->setClearColor(r,g,b,0.0); + apiContainer->getConfig()->clearColor = mathfu::vec4(r,g,b,0.0); + } + EMSCRIPTEN_KEEPALIVE + void enablePortalCulling(bool value) { + apiContainer->getConfig()->usePortalCulling = value; } EMSCRIPTEN_KEEPALIVE void setFarPlane(float value) { - apiContainer.getConfig()->setFarPlane(value); + apiContainer->getConfig()->farPlane = value; } EMSCRIPTEN_KEEPALIVE void setFarPlaneForCulling(float value) { - apiContainer.getConfig()->setFarPlaneForCulling(value); + apiContainer->getConfig()->farPlaneForCulling = value; } EMSCRIPTEN_KEEPALIVE @@ -437,6 +543,15 @@ extern "C" { currentScene->setAnimationId(animationId); } } + + EMSCRIPTEN_KEEPALIVE + void setMeshIdArray(uint8_t *meshIds, int length) { + if (currentScene != nullptr) { + auto meshIdsVec = std::vector(meshIds, meshIds+length); + currentScene->setMeshIds(meshIdsVec); + } + } + EMSCRIPTEN_KEEPALIVE void setReplaceParticleColors( int start0, int start1, int start2, @@ -478,49 +593,93 @@ extern "C" { EMSCRIPTEN_KEEPALIVE void setScenePos(float x, float y, float z) { - auto controllable = apiContainer.camera; + auto controllable = apiContainer->camera; controllable->setCameraPos(x,y,z); } + extern void offerFileAsDownload(const char *filename, int filename_len); EMSCRIPTEN_KEEPALIVE void gameloop(double deltaTime) { + + if (sceneComposer == nullptr) return; + if (apiContainer->hDevice == nullptr) return; + + if (exporter != nullptr && processor != nullptr) { + if (processor->completedAllJobs() && !apiContainer->hDevice->wasTexturesUploaded()) { + exporterFramesReady++; + } + if (exporterFramesReady > 5) { + std::string exportDir = "./gltf/"; + if (!ghc::filesystem::is_directory(exportDir) || !ghc::filesystem::exists(exportDir)) { + ghc::filesystem::create_directory(exportDir); // create src folder + } + exporter->saveToFile("model.gltf"); + exporter = nullptr; + + std::error_code errorCode; + + //Add files to archive and download it + std::string archiveName = "export.zip"; + ghc::filesystem::remove_all(archiveName, errorCode); //Delete file if exists + + zipper::Zipper zipper(archiveName); + + for (const auto& entry : ghc::filesystem::directory_iterator(exportDir)) { + const auto filenameStr = entry.path().filename().string(); + if (entry.status().type() == ghc::filesystem::file_type::regular) { + std::ifstream input1(exportDir+filenameStr); + + zipper.add(input1, filenameStr); + } + } + zipper.close(); + ghc::filesystem::remove_all(exportDir, errorCode); // Deletes one or more files recursively. + + offerFileAsDownload(archiveName.c_str(), archiveName.size()); + } + } + + if (screenshotDS != nullptr) { + if (screenshotFrame + 5 <= apiContainer->hDevice->getFrameNumber()) { + std::vector buffer = std::vector(screenShotWidth*screenShotHeight*4+1); + + saveDataFromDrawStage(screenshotDS->target, screenshotFilename, screenShotWidth, screenShotHeight, buffer); + offerFileAsDownload(screenshotFilename.c_str(), screenshotFilename.size()); + screenshotDS = nullptr; + } + } glfwPollEvents(); processor->processRequests(false); - processor->processResults(10); - - apiContainer.camera->tick(deltaTime*(1000.0f)); - float farPlaneRendering = apiContainer.getConfig()->getFarPlane(); - float farPlaneCulling = apiContainer.getConfig()->getFarPlaneForCulling(); + apiContainer->camera->tick(deltaTime*(1000.0f)); - float nearPlane = 1.0; - float fov = toRadian(45.0); + HFrameScenario sceneScenario = std::make_shared(); + std::vector additionalDependencies = {}; + if (needToMakeScreenshot) + { + HCullStage temp = nullptr; + screenShotWidth = canvWidth; + screenShotHeight = canvHeight; + std::vector deps = {}; + auto drawStage = createSceneDrawStage(sceneScenario, screenShotWidth, screenShotHeight, 0, true, *apiContainer, + currentScene,temp, deps); + if (drawStage != nullptr) { + additionalDependencies.push_back(drawStage); + screenshotDS = drawStage; + screenshotFrame = apiContainer->hDevice->getFrameNumber(); + } + needToMakeScreenshot = false; + } - float canvasAspect = (float)canvWidth / (float)canvHeight; + HCullStage cullStage = nullptr; + auto sceneDrawStage = createSceneDrawStage(sceneScenario, canvWidth, canvHeight, deltaTime, + false, *apiContainer, currentScene, cullStage, additionalDependencies); - auto cameraMatricesCulling = apiContainer.camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneCulling); - auto cameraMatricesRendering = apiContainer.camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneRendering); - HFrameScenario sceneScenario = std::make_shared(); - bool clearOnUi = true; - auto clearColor = apiContainer.getConfig()->getClearColor(); -// if (currentScene != nullptr) { - auto cullStage = sceneScenario->addCullStage(cameraMatricesCulling, currentScene); - auto updateStage = sceneScenario->addUpdateStage(cullStage, deltaTime*(1000.0f), cameraMatricesRendering); - ViewPortDimensions dimensions = {{0, 0}, {canvWidth, canvHeight}}; - auto sceneDrawStage = sceneScenario->addDrawStage(updateStage, currentScene, cameraMatricesRendering, {}, true, - dimensions, true, clearColor, nullptr); - clearOnUi = false; -// } -// else { -// auto sceneDrawStage = sceneScenario->addDrawStage(nullptr, nullptr, cameraMatricesRendering, {}, true, -// {{0, 0}, {canvWidth, canvHeight}}, -// true, clearColor); -// } // try { sceneComposer->draw(sceneScenario); diff --git a/src/database/CEmptySqliteDB.cpp b/src/database/CEmptySqliteDB.cpp new file mode 100644 index 000000000..6f931886a --- /dev/null +++ b/src/database/CEmptySqliteDB.cpp @@ -0,0 +1,5 @@ +// +// Created by Deamon on 8/1/2021. +// + +#include "CEmptySqliteDB.h" diff --git a/src/database/CEmptySqliteDB.h b/src/database/CEmptySqliteDB.h new file mode 100644 index 000000000..3826e9964 --- /dev/null +++ b/src/database/CEmptySqliteDB.h @@ -0,0 +1,25 @@ +// +// Created by Deamon on 8/1/2021. +// + +#ifndef AWEBWOWVIEWERCPP_CEMPTYSQLITEDB_H +#define AWEBWOWVIEWERCPP_CEMPTYSQLITEDB_H + + +#include "../../wowViewerLib/src/include/databaseHandler.h" + +class CEmptySqliteDB : public IClientDatabase { +public: + void getMapArray(std::vector &mapRecords) override {}; + bool getMapById(int mapId, MapRecord &mapRecord) override {return false;}; + AreaRecord getArea(int areaId) override { return {}; }; + AreaRecord getWmoArea(int wmoId, int nameId, int groupId) override { return {}; }; + void getLightById(int lightId, int time, LightResult &lightResult) override {}; + void getEnvInfo(int mapId, float x, float y, float z, int time, std::vector &lightResults) override {}; + void getLiquidObjectData(int liquidObjectId, std::vector &loData) override {}; + void getLiquidTypeData(int liquidTypeId, std::vector &liquidTypeData) override {}; + void getZoneLightsForMap(int mapId, std::vector &zoneLights) override {}; +}; + + +#endif //AWEBWOWVIEWERCPP_CEMPTYSQLITEDB_H diff --git a/src/database/CSqliteDB.cpp b/src/database/CSqliteDB.cpp index a608530c4..d150733d8 100644 --- a/src/database/CSqliteDB.cpp +++ b/src/database/CSqliteDB.cpp @@ -10,58 +10,78 @@ CSqliteDB::CSqliteDB(std::string dbFileName) : m_sqliteDatabase(dbFileName, SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE), - getWmoAreaAreaName(m_sqliteDatabase, - "select wat.AreaName_lang as wmoAreaName, at.AreaName_lang, at.ID, at.ParentAreaID, at.Ambient_multiplier from WMOAreaTable wat " - "left join AreaTable at on at.id = wat.AreaTableID " - "where wat.WMOID = ? and wat.NameSetID = ? and (wat.WMOGroupID = -1 or wat.WMOGroupID = ?) ORDER BY wat.WMOGroupID DESC"), + getWmoAreaAreaName(m_sqliteDatabase, R"===( + select wat.AreaName_lang as wmoAreaName, at.AreaName_lang, at.ID, at.ParentAreaID, at.Ambient_multiplier from WMOAreaTable wat + left join AreaTable at on at.id = wat.AreaTableID + where wat.WMOID = ? and wat.NameSetID = ? and (wat.WMOGroupID = -1 or wat.WMOGroupID = ?) ORDER BY wat.WMOGroupID DESC + )==="), getAreaNameStatement(m_sqliteDatabase, "select at.AreaName_lang, at.ID, at.ParentAreaID, at.Ambient_multiplier from AreaTable at where at.ID = ?"), getLightStatement(m_sqliteDatabase, - "select l.GameCoords_0, l.GameCoords_1, l.GameCoords_2, l.GameFalloffStart, l.GameFalloffEnd, l.LightParamsID_0, IFNULL(ls.SkyboxFileDataID, 0), IFNULL(lp.LightSkyboxID, 0), lp.Glow, IFNULL(ls.Flags, 0) from Light l " - " left join LightParams lp on lp.ID = l.LightParamsID_0 " - " left join LightSkybox ls on ls.ID = lp.LightSkyboxID " - " where " - " ((l.ContinentID = ?) and (( " - " abs(l.GameCoords_0 - ?) < l.GameFalloffEnd and " - " abs(l.GameCoords_1 - ?) < l.GameFalloffEnd and " - " abs(l.GameCoords_2 - ?) < l.GameFalloffEnd " - " ) or (l.GameCoords_0 = 0 and l.GameCoords_1 = 0 and l.GameCoords_2 = 0))) " - " or (l.GameCoords_0 = 0 and l.GameCoords_1 = 0 and l.GameCoords_2 = 0 and l.ContinentID = 0) " - "ORDER BY l.ID desc"), + R"===( + select l.GameCoords_0, l.GameCoords_1, l.GameCoords_2, l.GameFalloffStart, l.GameFalloffEnd, l.LightParamsID_0, + IFNULL(ls.SkyboxFileDataID, 0), IFNULL(lp.LightSkyboxID, 0), lp.Glow, IFNULL(ls.Flags, 0) + from Light l + left join LightParams lp on lp.ID = l.LightParamsID_0 + left join LightSkybox ls on ls.ID = lp.LightSkyboxID + where + ((l.ContinentID = ?) and (( + abs(l.GameCoords_0 - ?) < l.GameFalloffEnd and + abs(l.GameCoords_1 - ?) < l.GameFalloffEnd and + abs(l.GameCoords_2 - ?) < l.GameFalloffEnd + ) or (l.GameCoords_0 = 0 and l.GameCoords_1 = 0 and l.GameCoords_2 = 0))) + or (l.GameCoords_0 = 0 and l.GameCoords_1 = 0 and l.GameCoords_2 = 0 and l.ContinentID = 0) + ORDER BY l.ID desc + )==="), getLightByIdStatement(m_sqliteDatabase, - "select l.GameCoords_0, l.GameCoords_1, l.GameCoords_2, l.GameFalloffStart, l.GameFalloffEnd, l.LightParamsID_0, IFNULL(ls.SkyboxFileDataID, 0), IFNULL(lp.LightSkyboxID, 0), lp.Glow, IFNULL(ls.Flags, 0) from Light l " - " left join LightParams lp on lp.ID = l.LightParamsID_0 " - " left join LightSkybox ls on ls.ID = lp.LightSkyboxID " - " where l.ID = ?"), + R"===( + select l.GameCoords_0, l.GameCoords_1, l.GameCoords_2, l.GameFalloffStart, l.GameFalloffEnd, l.LightParamsID_0, + IFNULL(ls.SkyboxFileDataID, 0), IFNULL(lp.LightSkyboxID, 0), lp.Glow, IFNULL(ls.Flags, 0) from Light l + left join LightParams lp on lp.ID = l.LightParamsID_0 + left join LightSkybox ls on ls.ID = lp.LightSkyboxID + where l.ID = ? + )==="), getLightData(m_sqliteDatabase, - "select ld.AmbientColor, ld.HorizonAmbientColor, ld.GroundAmbientColor, ld.DirectColor, ld.RiverCloseColor, " - "ld.SkyTopColor, ld.SkyMiddleColor, ld.SkyBand1Color, ld.SkyBand2Color, ld.SkySmogColor, ld.SkyFogColor, " - "ld.FogEnd, ld.FogScaler, ld.FogDensity, ld.FogHeight, ld.FogHeightScaler, ld.FogHeightDensity, ld.SunFogAngle, " - "ld.EndFogColor, ld.EndFogColorDistance, ld.SunFogColor, ld.SunFogStrength, ld.FogHeightColor, " - "ld.FogHeightCoefficients_0, ld.FogHeightCoefficients_1, ld.FogHeightCoefficients_2, ld.FogHeightCoefficients_3, " - "ld.Time from LightData ld " - "where ld.LightParamID = ? ORDER BY Time ASC" + R"===( + select ld.AmbientColor, ld.HorizonAmbientColor, ld.GroundAmbientColor, ld.DirectColor, + ld.RiverCloseColor, ld.RiverFarColor, ld.OceanCloseColor, ld.OceanFarColor, + ld.SkyTopColor, ld.SkyMiddleColor, ld.SkyBand1Color, ld.SkyBand2Color, ld.SkySmogColor, ld.SkyFogColor, + ld.FogEnd, ld.FogScaler, ld.FogDensity, ld.FogHeight, ld.FogHeightScaler, ld.FogHeightDensity, ld.SunFogAngle, + ld.EndFogColor, ld.EndFogColorDistance, ld.SunFogColor, ld.SunFogStrength, ld.FogHeightColor, + ld.FogHeightCoefficients_0, ld.FogHeightCoefficients_1, ld.FogHeightCoefficients_2, ld.FogHeightCoefficients_3, + ld.Time from LightData ld + where ld.LightParamID = ? ORDER BY Time ASC + )===" ), getLiquidObjectInfo(m_sqliteDatabase, - "select ltxt.FileDataID, lm.LVF, ltxt.OrderIndex, lt.MinimapStaticCol from LiquidObject lo " - "left join LiquidTypeXTexture ltxt on ltxt.LiquidTypeID = lo.LiquidTypeID " - "left join LiquidType lt on lt.ID = lo.LiquidTypeID " - "left join LiquidMaterial lm on lt.MaterialID = lm.ID " - "where lo.ID = ? " + R"===( + select ltxt.FileDataID, lm.LVF, ltxt.OrderIndex, lt.Color_0, lt.Color_1, lt.Flags, lt.MinimapStaticCol from LiquidObject lo + left join LiquidTypeXTexture ltxt on ltxt.LiquidTypeID = lo.LiquidTypeID + left join LiquidType lt on lt.ID = lo.LiquidTypeID + left join LiquidMaterial lm on lt.MaterialID = lm.ID + where lo.ID = ? + )===" ), getLiquidTypeInfo(m_sqliteDatabase, - "select ltxt.FileDataID from LiquidTypeXTexture ltxt " - "where ltxt.LiquidTypeID = ? order by ltxt.OrderIndex" + R"===( + select ltxt.FileDataID, lt.Color_0, lt.Color_1, lt.Flags, lt.MinimapStaticCol from LiquidType lt + left join LiquidTypeXTexture ltxt on ltxt.LiquidTypeID = lt.ID + where lt.ID = ? order by ltxt.OrderIndex + )===" ), getZoneLightInfo(m_sqliteDatabase, "select ID, Name, LightID, Zmin, Zmax from ZoneLight where MapID = ?" ), getZoneLightPointsInfo(m_sqliteDatabase, "select Pos_0, Pos_1 from ZoneLightPoint where ZoneLightID = ? order by PointOrder;" + ), + getMapList(m_sqliteDatabase, + "select m.ID, m.Directory, m.MapName_lang, m.WdtFileDataID, m.MapType from Map m where m.WdtFileDataID > 0" + ), + getMapByIdStatement(m_sqliteDatabase, + "select m.ID, m.Directory, m.MapName_lang, m.WdtFileDataID, m.MapType from Map m where m.ID = ?" ) - - { char *sErrMsg = ""; sqlite3_exec(m_sqliteDatabase.getHandle(), "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg); @@ -71,7 +91,7 @@ CSqliteDB::CSqliteDB(std::string dbFileName) : } void CSqliteDB::getMapArray(std::vector &mapRecords) { - SQLite::Statement getMapList(m_sqliteDatabase, "select m.ID, m.Directory, m.MapName_lang, m.WdtFileDataID, m.MapType from Map m where m.WdtFileDataID > 0"); + getMapList.reset(); while (getMapList.executeStep()) { @@ -154,6 +174,11 @@ void initWithZeros(float *colorF) { colorF[1] = 0; colorF[2] = 0; } +void initWithZeros(std::array &colorF) { + colorF[0] = 0; + colorF[1] = 0; + colorF[2] = 0; +} void blendTwoAndAdd(float *colorF, int currLdRes, int lastLdRes, float timeAlphaBlend, float innerAlpha) { colorF[0] += (getFloatFromInt<0>(currLdRes) * timeAlphaBlend + getFloatFromInt<0>(lastLdRes) * @@ -165,6 +190,17 @@ void blendTwoAndAdd(float *colorF, int currLdRes, int lastLdRes, float timeAlpha getFloatFromInt<2>(lastLdRes) * (1.0 - timeAlphaBlend)) * innerAlpha; } +void blendTwoAndAdd(std::array &colorF, int currLdRes, int lastLdRes, float timeAlphaBlend, float innerAlpha) { + colorF[0] += (getFloatFromInt<0>(currLdRes) * timeAlphaBlend + + getFloatFromInt<0>(lastLdRes) * + (1.0 - timeAlphaBlend)) * innerAlpha; + colorF[1] += (getFloatFromInt<1>(currLdRes) * timeAlphaBlend + + getFloatFromInt<1>(lastLdRes) * + (1.0 - timeAlphaBlend)) * innerAlpha; + colorF[2] += (getFloatFromInt<2>(currLdRes) * timeAlphaBlend + + getFloatFromInt<2>(lastLdRes) * + (1.0 - timeAlphaBlend)) * innerAlpha; +} void blendTwoAndAdd(float &colorF, float currLdRes, float lastLdRes, float timeAlphaBlend, float innerAlpha) { colorF += (currLdRes * timeAlphaBlend + lastLdRes * (1.0 - timeAlphaBlend)) * innerAlpha; } @@ -175,6 +211,12 @@ void addOnlyOne(float *colorF, int currLdRes, float innerAlpha) { colorF[2] += getFloatFromInt<2>(currLdRes) * innerAlpha; } +void addOnlyOne(std::array &colorF, int currLdRes, float innerAlpha) { + colorF[0] += getFloatFromInt<0>(currLdRes) * innerAlpha; + colorF[1] += getFloatFromInt<1>(currLdRes) * innerAlpha; + colorF[2] += getFloatFromInt<2>(currLdRes) * innerAlpha; +} + void addOnlyOne(float &colorF, float currLdRes, float innerAlpha) { colorF += currLdRes * innerAlpha; } @@ -273,7 +315,11 @@ void CSqliteDB::addOnlyOne(LightResult &lightResult, ::addOnlyOne(lightResult.groundAmbientColor, currLdRes.groundAmbientColor, innerAlpha); ::addOnlyOne(lightResult.directColor, currLdRes.directLight, innerAlpha); + ::addOnlyOne(lightResult.closeRiverColor, currLdRes.closeRiverColor, innerAlpha); + ::addOnlyOne(lightResult.farRiverColor, currLdRes.farRiverColor, innerAlpha); + ::addOnlyOne(lightResult.closeOceanColor, currLdRes.closeOceanColor, innerAlpha); + ::addOnlyOne(lightResult.farOceanColor, currLdRes.farOceanColor, innerAlpha); ::addOnlyOne(lightResult.SkyTopColor, currLdRes.SkyTopColor, innerAlpha); ::addOnlyOne(lightResult.SkyMiddleColor, currLdRes.SkyMiddleColor, innerAlpha); @@ -319,6 +365,15 @@ void CSqliteDB::blendTwoAndAdd(LightResult &lightResult, const CSqliteDB::InnerL ::blendTwoAndAdd(lightResult.closeRiverColor, currLdRes.closeRiverColor, lastLdRes.closeRiverColor, timeAlphaBlend, innerAlpha); + ::blendTwoAndAdd(lightResult.farRiverColor, + currLdRes.farRiverColor, lastLdRes.farRiverColor, + timeAlphaBlend, innerAlpha); + ::blendTwoAndAdd(lightResult.closeOceanColor, + currLdRes.closeOceanColor, lastLdRes.closeOceanColor, + timeAlphaBlend, innerAlpha); + ::blendTwoAndAdd(lightResult.farOceanColor, + currLdRes.farOceanColor, lastLdRes.farOceanColor, + timeAlphaBlend, innerAlpha); ::blendTwoAndAdd(lightResult.SkyTopColor, currLdRes.SkyTopColor, lastLdRes.SkyTopColor, @@ -375,6 +430,9 @@ void CSqliteDB::convertInnerResultsToPublic(int ptime, std::vector initWithZeros(lightResult.groundAmbientColor); initWithZeros(lightResult.directColor); initWithZeros(lightResult.closeRiverColor); + initWithZeros(lightResult.farRiverColor); + initWithZeros(lightResult.closeOceanColor); + initWithZeros(lightResult.farOceanColor); initWithZeros(lightResult.SkyTopColor); initWithZeros(lightResult.SkyMiddleColor); @@ -387,14 +445,27 @@ void CSqliteDB::convertInnerResultsToPublic(int ptime, std::vector initWithZeros(lightResult.SunFogColor); initWithZeros(lightResult.FogHeightColor); initWithZeros(lightResult.FogHeightCoefficients); - lightResult.FogHeightCoefficients[4] = 0; + lightResult.FogHeightCoefficients[3] = 0; lightResult.skyBoxFdid = 0; lightResult.lightSkyboxId = 0; + lightResult.lightParamId = 0; + + lightResult.FogEnd = 0.0; + lightResult.FogScaler = 0.0; + lightResult.FogDensity = 0.0; + lightResult.FogHeight = 0.0; + lightResult.FogHeightScaler = 0.0; + lightResult.FogHeightDensity = 0.0; + lightResult.SunFogAngle = 0.0; + lightResult.EndFogColorDistance = 0.0; + lightResult.SunFogStrength = 0.0; + auto &innerResult = innerResults[i]; lightResult.isDefault = innerResult.isDefault; + lightResult.lightParamId = innerResult.paramId; getLightData.reset(); @@ -428,33 +499,37 @@ void CSqliteDB::convertInnerResultsToPublic(int ptime, std::vector } currLdRes.directLight = getLightData.getColumn(3); - currLdRes.closeRiverColor = getLightData.getColumn(4); - currLdRes.SkyTopColor = getLightData.getColumn(5); - currLdRes.SkyMiddleColor = getLightData.getColumn(6); - currLdRes.SkyBand1Color = getLightData.getColumn(7); - currLdRes.SkyBand2Color = getLightData.getColumn(8); - currLdRes.SkySmogColor = getLightData.getColumn(9); - currLdRes.SkyFogColor = getLightData.getColumn(10); - - currLdRes.FogEnd = getLightData.getColumn(11).getDouble(); - currLdRes.FogScaler = getLightData.getColumn(12).getDouble(); - currLdRes.FogDensity = getLightData.getColumn(13).getDouble(); - currLdRes.FogHeight = getLightData.getColumn(14).getDouble(); - currLdRes.FogHeightScaler = getLightData.getColumn(15).getDouble(); - currLdRes.FogHeightDensity = getLightData.getColumn(16).getDouble(); - currLdRes.SunFogAngle = getLightData.getColumn(17).getDouble(); - currLdRes.EndFogColor = getLightData.getColumn(18); - currLdRes.EndFogColorDistance = getLightData.getColumn(19).getDouble(); - currLdRes.SunFogColor = getLightData.getColumn(20); - currLdRes.SunFogStrength = getLightData.getColumn(21).getDouble(); - currLdRes.FogHeightColor = getLightData.getColumn(22); - currLdRes.FogHeightCoefficients[0] = getLightData.getColumn(23).getDouble(); - currLdRes.FogHeightCoefficients[1] = getLightData.getColumn(24).getDouble(); - currLdRes.FogHeightCoefficients[2] = getLightData.getColumn(25).getDouble(); - currLdRes.FogHeightCoefficients[3] = getLightData.getColumn(26).getDouble(); - - currLdRes.time = getLightData.getColumn(27); + currLdRes.closeRiverColor = getLightData.getColumn(4); + currLdRes.farRiverColor = getLightData.getColumn(5); + currLdRes.closeOceanColor = getLightData.getColumn(6); + currLdRes.farOceanColor = getLightData.getColumn(7); + + currLdRes.SkyTopColor = getLightData.getColumn(8); + currLdRes.SkyMiddleColor = getLightData.getColumn(9); + currLdRes.SkyBand1Color = getLightData.getColumn(10); + currLdRes.SkyBand2Color = getLightData.getColumn(11); + currLdRes.SkySmogColor = getLightData.getColumn(12); + currLdRes.SkyFogColor = getLightData.getColumn(13); + + currLdRes.FogEnd = getLightData.getColumn(14).getDouble(); + currLdRes.FogScaler = getLightData.getColumn(15).getDouble(); + currLdRes.FogDensity = getLightData.getColumn(16).getDouble(); + currLdRes.FogHeight = getLightData.getColumn(17).getDouble(); + currLdRes.FogHeightScaler = getLightData.getColumn(18).getDouble(); + currLdRes.FogHeightDensity = getLightData.getColumn(19).getDouble(); + currLdRes.SunFogAngle = getLightData.getColumn(20).getDouble(); + currLdRes.EndFogColor = getLightData.getColumn(21); + currLdRes.EndFogColorDistance = getLightData.getColumn(22).getDouble(); + currLdRes.SunFogColor = getLightData.getColumn(23); + currLdRes.SunFogStrength = getLightData.getColumn(24).getDouble(); + currLdRes.FogHeightColor = getLightData.getColumn(25); + currLdRes.FogHeightCoefficients[0] = getLightData.getColumn(26).getDouble(); + currLdRes.FogHeightCoefficients[1] = getLightData.getColumn(27).getDouble(); + currLdRes.FogHeightCoefficients[2] = getLightData.getColumn(28).getDouble(); + currLdRes.FogHeightCoefficients[3] = getLightData.getColumn(29).getDouble(); + + currLdRes.time = getLightData.getColumn(30); if (!currLdRes.EndFogColor) { currLdRes.EndFogColor = currLdRes.SkyFogColor; @@ -488,6 +563,7 @@ void CSqliteDB::convertInnerResultsToPublic(int ptime, std::vector addOnlyOne(lightResult, lastLdRes, innerAlpha); } + lightResults.push_back(lightResult); totalSummator += innerResult.blendAlpha; @@ -511,17 +587,43 @@ void CSqliteDB::getLiquidObjectData(int liquidObjectId, std::vector & lm.color1[0] = getFloatFromInt<0>(color1); lm.color1[1] = getFloatFromInt<1>(color1); lm.color1[2] = getFloatFromInt<2>(color1); + int color2 = getLiquidObjectInfo.getColumn(4).getInt(); + lm.color2[0] = getFloatFromInt<0>(color2); + lm.color2[1] = getFloatFromInt<1>(color2); + lm.color2[2] = getFloatFromInt<2>(color2); + lm.flags = getLiquidObjectInfo.getColumn(5).getInt(); + int minimapStaticCol = getLiquidObjectInfo.getColumn(6).getInt(); + lm.minimapStaticCol[0] = getFloatFromInt<0>(minimapStaticCol); + lm.minimapStaticCol[1] = getFloatFromInt<1>(minimapStaticCol); + lm.minimapStaticCol[2] = getFloatFromInt<2>(minimapStaticCol); + loData.push_back(lm); } } -void CSqliteDB::getLiquidTypeData(int liquidTypeId, std::vector &fileDataIds) { +void CSqliteDB::getLiquidTypeData(int liquidTypeId, std::vector &liquidTypeData) { getLiquidTypeInfo.reset(); getLiquidTypeInfo.bind(1, liquidTypeId); while (getLiquidTypeInfo.executeStep()) { - fileDataIds.push_back(getLiquidTypeInfo.getColumn(0).getInt()); + LiquidTypeData ltd = {}; + ltd.FileDataId = getLiquidTypeInfo.getColumn(0).getInt(); + int color1 = getLiquidTypeInfo.getColumn(1).getInt(); + ltd.color1[0] = getFloatFromInt<0>(color1); + ltd.color1[1] = getFloatFromInt<1>(color1); + ltd.color1[2] = getFloatFromInt<2>(color1); + int color2 = getLiquidTypeInfo.getColumn(2).getInt(); + ltd.color2[0] = getFloatFromInt<0>(color2); + ltd.color2[1] = getFloatFromInt<1>(color2); + ltd.color2[2] = getFloatFromInt<2>(color2); + ltd.flags = getLiquidTypeInfo.getColumn(3).getInt(); + int minimapStaticCol = getLiquidTypeInfo.getColumn(4).getInt(); + ltd.minimapStaticCol[0] = getFloatFromInt<0>(minimapStaticCol); + ltd.minimapStaticCol[1] = getFloatFromInt<1>(minimapStaticCol); + ltd.minimapStaticCol[2] = getFloatFromInt<2>(minimapStaticCol); + + liquidTypeData.push_back(ltd); } } @@ -555,4 +657,23 @@ void CSqliteDB::getZoneLightsForMap(int mapId, std::vector &zoneLight zoneLight.points.push_back(pt); } } -} \ No newline at end of file +} + +bool CSqliteDB::getMapById(int mapId, MapRecord &mapRecord) { + getMapByIdStatement.reset(); + + getMapByIdStatement.bind(1, mapId); + + while (getMapByIdStatement.executeStep()) + { + // Demonstrate how to get some typed column value + mapRecord.ID = getMapByIdStatement.getColumn(0); + mapRecord.MapDirectory = std::string((const char*) getMapByIdStatement.getColumn(1)); + mapRecord.MapName = std::string((const char*) getMapByIdStatement.getColumn(2)); + mapRecord.WdtFileID = getMapByIdStatement.getColumn(3); + mapRecord.MapType = getMapByIdStatement.getColumn(4); + + return true; + } + return false; +} diff --git a/src/database/CSqliteDB.h b/src/database/CSqliteDB.h index 4205a52c0..c217c12c1 100644 --- a/src/database/CSqliteDB.h +++ b/src/database/CSqliteDB.h @@ -12,15 +12,16 @@ class CSqliteDB : public IClientDatabase { public: - CSqliteDB(std::string dbFileName); + explicit CSqliteDB(std::string dbFileName); void getMapArray(std::vector &mapRecords) override; + bool getMapById(int mapId, MapRecord &mapRecord) override; AreaRecord getArea(int areaId) override; AreaRecord getWmoArea(int wmoId, int nameId, int groupId) override; void getEnvInfo(int mapId, float x, float y, float z, int time, std::vector &lightResults) override; void getLightById(int lightId, int time, LightResult &lightResult) override; void getLiquidObjectData(int liquidObjectId, std::vector &loData) override; - void getLiquidTypeData(int liquidTypeId, std::vector &fileDataIds) override; + void getLiquidTypeData(int liquidTypeId, std::vector &liquidTypeData) override; void getZoneLightsForMap(int mapId, std::vector &zoneLights) override; private: SQLite::Database m_sqliteDatabase; @@ -35,6 +36,8 @@ class CSqliteDB : public IClientDatabase { SQLite::Statement getZoneLightInfo; SQLite::Statement getZoneLightPointsInfo; + SQLite::Statement getMapList; + SQLite::Statement getMapByIdStatement; struct InnerLightResult { @@ -57,6 +60,9 @@ class CSqliteDB : public IClientDatabase { int directLight; int closeRiverColor; + int farRiverColor; + int closeOceanColor; + int farOceanColor; int SkyTopColor; int SkyMiddleColor; diff --git a/src/database/csvtest/csv.h b/src/database/csvtest/csv.h new file mode 100644 index 000000000..1f697e17b --- /dev/null +++ b/src/database/csvtest/csv.h @@ -0,0 +1,1272 @@ +// Copyright: (2012-2015) Ben Strasser +// License: BSD-3 +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef CSV_H +#define CSV_H + +#include +#include +#include +#include +#include +#include +#include +#ifndef CSV_IO_NO_THREAD +#include +#include +#include +#endif +#include +#include +#include +#include + +namespace io{ + //////////////////////////////////////////////////////////////////////////// + // LineReader // + //////////////////////////////////////////////////////////////////////////// + + namespace error{ + struct base : std::exception{ + virtual void format_error_message()const = 0; + + const char*what()const noexcept override{ + format_error_message(); + return error_message_buffer; + } + + mutable char error_message_buffer[512]; + }; + + const int max_file_name_length = 255; + + struct with_file_name{ + with_file_name(){ + std::memset(file_name, 0, sizeof(file_name)); + } + + void set_file_name(const char*file_name){ + if(file_name != nullptr){ + // This call to strncpy has parenthesis around it + // to silence the GCC -Wstringop-truncation warning + (strncpy(this->file_name, file_name, sizeof(this->file_name))); + this->file_name[sizeof(this->file_name)-1] = '\0'; + }else{ + this->file_name[0] = '\0'; + } + } + + char file_name[max_file_name_length+1]; + }; + + struct with_file_line{ + with_file_line(){ + file_line = -1; + } + + void set_file_line(int file_line){ + this->file_line = file_line; + } + + int file_line; + }; + + struct with_errno{ + with_errno(){ + errno_value = 0; + } + + void set_errno(int errno_value){ + this->errno_value = errno_value; + } + + int errno_value; + }; + + struct can_not_open_file : + base, + with_file_name, + with_errno{ + void format_error_message()const override{ + if(errno_value != 0) + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Can not open file \"%s\" because \"%s\"." + , file_name, std::strerror(errno_value)); + else + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Can not open file \"%s\"." + , file_name); + } + }; + + struct line_length_limit_exceeded : + base, + with_file_name, + with_file_line{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Line number %d in file \"%s\" exceeds the maximum length of 2^24-1." + , file_line, file_name); + } + }; + } + + class ByteSourceBase{ + public: + virtual int read(char*buffer, int size)=0; + virtual ~ByteSourceBase(){} + }; + + namespace detail{ + + class OwningStdIOByteSourceBase : public ByteSourceBase{ + public: + explicit OwningStdIOByteSourceBase(FILE*file):file(file){ + // Tell the std library that we want to do the buffering ourself. + std::setvbuf(file, 0, _IONBF, 0); + } + + int read(char*buffer, int size){ + return std::fread(buffer, 1, size, file); + } + + ~OwningStdIOByteSourceBase(){ + std::fclose(file); + } + + private: + FILE*file; + }; + + class NonOwningIStreamByteSource : public ByteSourceBase{ + public: + explicit NonOwningIStreamByteSource(std::istream&in):in(in){} + + int read(char*buffer, int size){ + in.read(buffer, size); + return in.gcount(); + } + + ~NonOwningIStreamByteSource(){} + + private: + std::istream∈ + }; + + class NonOwningStringByteSource : public ByteSourceBase{ + public: + NonOwningStringByteSource(const char*str, long long size):str(str), remaining_byte_count(size){} + + int read(char*buffer, int desired_byte_count){ + int to_copy_byte_count = desired_byte_count; + if(remaining_byte_count < to_copy_byte_count) + to_copy_byte_count = remaining_byte_count; + std::memcpy(buffer, str, to_copy_byte_count); + remaining_byte_count -= to_copy_byte_count; + str += to_copy_byte_count; + return to_copy_byte_count; + } + + ~NonOwningStringByteSource(){} + + private: + const char*str; + long long remaining_byte_count; + }; + + #ifndef CSV_IO_NO_THREAD + class AsynchronousReader{ + public: + void init(std::unique_ptrarg_byte_source){ + std::unique_lockguard(lock); + byte_source = std::move(arg_byte_source); + desired_byte_count = -1; + termination_requested = false; + worker = std::thread( + [&]{ + std::unique_lockguard(lock); + try{ + for(;;){ + read_requested_condition.wait( + guard, + [&]{ + return desired_byte_count != -1 || termination_requested; + } + ); + if(termination_requested) + return; + + read_byte_count = byte_source->read(buffer, desired_byte_count); + desired_byte_count = -1; + if(read_byte_count == 0) + break; + read_finished_condition.notify_one(); + } + }catch(...){ + read_error = std::current_exception(); + } + read_finished_condition.notify_one(); + } + ); + } + + bool is_valid()const{ + return byte_source != nullptr; + } + + void start_read(char*arg_buffer, int arg_desired_byte_count){ + std::unique_lockguard(lock); + buffer = arg_buffer; + desired_byte_count = arg_desired_byte_count; + read_byte_count = -1; + read_requested_condition.notify_one(); + } + + int finish_read(){ + std::unique_lockguard(lock); + read_finished_condition.wait( + guard, + [&]{ + return read_byte_count != -1 || read_error; + } + ); + if(read_error) + std::rethrow_exception(read_error); + else + return read_byte_count; + } + + ~AsynchronousReader(){ + if(byte_source != nullptr){ + { + std::unique_lockguard(lock); + termination_requested = true; + } + read_requested_condition.notify_one(); + worker.join(); + } + } + + private: + std::unique_ptrbyte_source; + + std::thread worker; + + bool termination_requested; + std::exception_ptr read_error; + char*buffer; + int desired_byte_count; + int read_byte_count; + + std::mutex lock; + std::condition_variable read_finished_condition; + std::condition_variable read_requested_condition; + }; + #endif + + class SynchronousReader{ + public: + void init(std::unique_ptrarg_byte_source){ + byte_source = std::move(arg_byte_source); + } + + bool is_valid()const{ + return byte_source != nullptr; + } + + void start_read(char*arg_buffer, int arg_desired_byte_count){ + buffer = arg_buffer; + desired_byte_count = arg_desired_byte_count; + } + + int finish_read(){ + return byte_source->read(buffer, desired_byte_count); + } + private: + std::unique_ptrbyte_source; + char*buffer; + int desired_byte_count; + }; + } + + class LineReader{ + private: + static const int block_len = 1<<20; + std::unique_ptrbuffer; // must be constructed before (and thus destructed after) the reader! + #ifdef CSV_IO_NO_THREAD + detail::SynchronousReader reader; + #else + detail::AsynchronousReader reader; + #endif + int data_begin; + int data_end; + + char file_name[error::max_file_name_length+1]; + unsigned file_line; + + static std::unique_ptr open_file(const char*file_name){ + // We open the file in binary mode as it makes no difference under *nix + // and under Windows we handle \r\n newlines ourself. + FILE*file = std::fopen(file_name, "rb"); + if(file == 0){ + int x = errno; // store errno as soon as possible, doing it after constructor call can fail. + error::can_not_open_file err; + err.set_errno(x); + err.set_file_name(file_name); + throw err; + } + return std::unique_ptr(new detail::OwningStdIOByteSourceBase(file)); + } + + void init(std::unique_ptrbyte_source){ + file_line = 0; + + buffer = std::unique_ptr(new char[3*block_len]); + data_begin = 0; + data_end = byte_source->read(buffer.get(), 2*block_len); + + // Ignore UTF-8 BOM + if(data_end >= 3 && buffer[0] == '\xEF' && buffer[1] == '\xBB' && buffer[2] == '\xBF') + data_begin = 3; + + if(data_end == 2*block_len){ + reader.init(std::move(byte_source)); + reader.start_read(buffer.get() + 2*block_len, block_len); + } + } + + public: + LineReader() = delete; + LineReader(const LineReader&) = delete; + LineReader&operator=(const LineReader&) = delete; + + explicit LineReader(const char*file_name){ + set_file_name(file_name); + init(open_file(file_name)); + } + + explicit LineReader(const std::string&file_name){ + set_file_name(file_name.c_str()); + init(open_file(file_name.c_str())); + } + + LineReader(const char*file_name, std::unique_ptrbyte_source){ + set_file_name(file_name); + init(std::move(byte_source)); + } + + LineReader(const std::string&file_name, std::unique_ptrbyte_source){ + set_file_name(file_name.c_str()); + init(std::move(byte_source)); + } + + LineReader(const char*file_name, const char*data_begin, const char*data_end){ + set_file_name(file_name); + init(std::unique_ptr(new detail::NonOwningStringByteSource(data_begin, data_end-data_begin))); + } + + LineReader(const std::string&file_name, const char*data_begin, const char*data_end){ + set_file_name(file_name.c_str()); + init(std::unique_ptr(new detail::NonOwningStringByteSource(data_begin, data_end-data_begin))); + } + + LineReader(const char*file_name, FILE*file){ + set_file_name(file_name); + init(std::unique_ptr(new detail::OwningStdIOByteSourceBase(file))); + } + + LineReader(const std::string&file_name, FILE*file){ + set_file_name(file_name.c_str()); + init(std::unique_ptr(new detail::OwningStdIOByteSourceBase(file))); + } + + LineReader(const char*file_name, std::istream&in){ + set_file_name(file_name); + init(std::unique_ptr(new detail::NonOwningIStreamByteSource(in))); + } + + LineReader(const std::string&file_name, std::istream&in){ + set_file_name(file_name.c_str()); + init(std::unique_ptr(new detail::NonOwningIStreamByteSource(in))); + } + + void set_file_name(const std::string&file_name){ + set_file_name(file_name.c_str()); + } + + void set_file_name(const char*file_name){ + if(file_name != nullptr){ + strncpy(this->file_name, file_name, sizeof(this->file_name)); + this->file_name[sizeof(this->file_name)-1] = '\0'; + }else{ + this->file_name[0] = '\0'; + } + } + + const char*get_truncated_file_name()const{ + return file_name; + } + + void set_file_line(unsigned file_line){ + this->file_line = file_line; + } + + unsigned get_file_line()const{ + return file_line; + } + + char*next_line(){ + if(data_begin == data_end) + return nullptr; + + ++file_line; + + assert(data_begin < data_end); + assert(data_end <= block_len*2); + + if(data_begin >= block_len){ + std::memcpy(buffer.get(), buffer.get()+block_len, block_len); + data_begin -= block_len; + data_end -= block_len; + if(reader.is_valid()) + { + data_end += reader.finish_read(); + std::memcpy(buffer.get()+block_len, buffer.get()+2*block_len, block_len); + reader.start_read(buffer.get() + 2*block_len, block_len); + } + } + + int line_end = data_begin; + while(buffer[line_end] != '\n' && line_end != data_end){ + ++line_end; + } + + if(line_end - data_begin + 1 > block_len){ + error::line_length_limit_exceeded err; + err.set_file_name(file_name); + err.set_file_line(file_line); + throw err; + } + + if(buffer[line_end] == '\n' && line_end != data_end){ + buffer[line_end] = '\0'; + }else{ + // some files are missing the newline at the end of the + // last line + ++data_end; + buffer[line_end] = '\0'; + } + + // handle windows \r\n-line breaks + if(line_end != data_begin && buffer[line_end-1] == '\r') + buffer[line_end-1] = '\0'; + + char*ret = buffer.get() + data_begin; + data_begin = line_end+1; + return ret; + } + }; + + + //////////////////////////////////////////////////////////////////////////// + // CSV // + //////////////////////////////////////////////////////////////////////////// + + namespace error{ + const int max_column_name_length = 63; + struct with_column_name{ + with_column_name(){ + std::memset(column_name, 0, max_column_name_length+1); + } + + void set_column_name(const char*column_name){ + if(column_name != nullptr){ + std::strncpy(this->column_name, column_name, max_column_name_length); + this->column_name[max_column_name_length] = '\0'; + }else{ + this->column_name[0] = '\0'; + } + } + + char column_name[max_column_name_length+1]; + }; + + + const int max_column_content_length = 63; + + struct with_column_content{ + with_column_content(){ + std::memset(column_content, 0, max_column_content_length+1); + } + + void set_column_content(const char*column_content){ + if(column_content != nullptr){ + std::strncpy(this->column_content, column_content, max_column_content_length); + this->column_content[max_column_content_length] = '\0'; + }else{ + this->column_content[0] = '\0'; + } + } + + char column_content[max_column_content_length+1]; + }; + + + struct extra_column_in_header : + base, + with_file_name, + with_column_name{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(Extra column "%s" in header of file "%s".)" + , column_name, file_name); + } + }; + + struct missing_column_in_header : + base, + with_file_name, + with_column_name{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(Missing column "%s" in header of file "%s".)" + , column_name, file_name); + } + }; + + struct duplicated_column_in_header : + base, + with_file_name, + with_column_name{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(Duplicated column "%s" in header of file "%s".)" + , column_name, file_name); + } + }; + + struct header_missing : + base, + with_file_name{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Header missing in file \"%s\"." + , file_name); + } + }; + + struct too_few_columns : + base, + with_file_name, + with_file_line{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Too few columns in line %d in file \"%s\"." + , file_line, file_name); + } + }; + + struct too_many_columns : + base, + with_file_name, + with_file_line{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Too many columns in line %d in file \"%s\"." + , file_line, file_name); + } + }; + + struct escaped_string_not_closed : + base, + with_file_name, + with_file_line{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Escaped string was not closed in line %d in file \"%s\"." + , file_line, file_name); + } + }; + + struct integer_must_be_positive : + base, + with_file_name, + with_file_line, + with_column_name, + with_column_content{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(The integer "%s" must be positive or 0 in column "%s" in file "%s" in line "%d".)" + , column_content, column_name, file_name, file_line); + } + }; + + struct no_digit : + base, + with_file_name, + with_file_line, + with_column_name, + with_column_content{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(The integer "%s" contains an invalid digit in column "%s" in file "%s" in line "%d".)" + , column_content, column_name, file_name, file_line); + } + }; + + struct integer_overflow : + base, + with_file_name, + with_file_line, + with_column_name, + with_column_content{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(The integer "%s" overflows in column "%s" in file "%s" in line "%d".)" + , column_content, column_name, file_name, file_line); + } + }; + + struct integer_underflow : + base, + with_file_name, + with_file_line, + with_column_name, + with_column_content{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(The integer "%s" underflows in column "%s" in file "%s" in line "%d".)" + , column_content, column_name, file_name, file_line); + } + }; + + struct invalid_single_character : + base, + with_file_name, + with_file_line, + with_column_name, + with_column_content{ + void format_error_message()const override{ + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(The content "%s" of column "%s" in file "%s" in line "%d" is not a single character.)" + , column_content, column_name, file_name, file_line); + } + }; + } + + using ignore_column = unsigned int; + static const ignore_column ignore_no_column = 0; + static const ignore_column ignore_extra_column = 1; + static const ignore_column ignore_missing_column = 2; + + template + struct trim_chars{ + private: + constexpr static bool is_trim_char(char){ + return false; + } + + template + constexpr static bool is_trim_char(char c, char trim_char, OtherTrimChars...other_trim_chars){ + return c == trim_char || is_trim_char(c, other_trim_chars...); + } + + public: + static void trim(char*&str_begin, char*&str_end){ + while(str_begin != str_end && is_trim_char(*str_begin, trim_char_list...)) + ++str_begin; + while(str_begin != str_end && is_trim_char(*(str_end-1), trim_char_list...)) + --str_end; + *str_end = '\0'; + } + }; + + + struct no_comment{ + static bool is_comment(const char*){ + return false; + } + }; + + template + struct single_line_comment{ + private: + constexpr static bool is_comment_start_char(char){ + return false; + } + + template + constexpr static bool is_comment_start_char(char c, char comment_start_char, OtherCommentStartChars...other_comment_start_chars){ + return c == comment_start_char || is_comment_start_char(c, other_comment_start_chars...); + } + + public: + + static bool is_comment(const char*line){ + return is_comment_start_char(*line, comment_start_char_list...); + } + }; + + struct empty_line_comment{ + static bool is_comment(const char*line){ + if(*line == '\0') + return true; + while(*line == ' ' || *line == '\t'){ + ++line; + if(*line == 0) + return true; + } + return false; + } + }; + + template + struct single_and_empty_line_comment{ + static bool is_comment(const char*line){ + return single_line_comment::is_comment(line) || empty_line_comment::is_comment(line); + } + }; + + template + struct no_quote_escape{ + static const char*find_next_column_end(const char*col_begin){ + while(*col_begin != sep && *col_begin != '\0') + ++col_begin; + return col_begin; + } + + static void unescape(char*&, char*&){ + + } + }; + + template + struct double_quote_escape{ + static const char*find_next_column_end(const char*col_begin){ + while(*col_begin != sep && *col_begin != '\0') + if(*col_begin != quote) + ++col_begin; + else{ + do{ + ++col_begin; + while(*col_begin != quote){ + if(*col_begin == '\0') + throw error::escaped_string_not_closed(); + ++col_begin; + } + ++col_begin; + }while(*col_begin == quote); + } + return col_begin; + } + + static void unescape(char*&col_begin, char*&col_end){ + if(col_end - col_begin >= 2){ + if(*col_begin == quote && *(col_end-1) == quote){ + ++col_begin; + --col_end; + char*out = col_begin; + for(char*in = col_begin; in!=col_end; ++in){ + if(*in == quote && (in+1) != col_end && *(in+1) == quote){ + ++in; + } + *out = *in; + ++out; + } + col_end = out; + *col_end = '\0'; + } + } + + } + }; + + struct throw_on_overflow{ + template + static void on_overflow(T&){ + throw error::integer_overflow(); + } + + template + static void on_underflow(T&){ + throw error::integer_underflow(); + } + }; + + struct ignore_overflow{ + template + static void on_overflow(T&){} + + template + static void on_underflow(T&){} + }; + + struct set_to_max_on_overflow{ + template + static void on_overflow(T&x){ + // using (std::numeric_limits::max) instead of std::numeric_limits::max + // to make code including windows.h with its max macro happy + x = (std::numeric_limits::max)(); + } + + template + static void on_underflow(T&x){ + x = (std::numeric_limits::min)(); + } + }; + + + namespace detail{ + template + void chop_next_column( + char*&line, char*&col_begin, char*&col_end + ){ + assert(line != nullptr); + + col_begin = line; + // the col_begin + (... - col_begin) removes the constness + col_end = col_begin + (quote_policy::find_next_column_end(col_begin) - col_begin); + + if(*col_end == '\0'){ + line = nullptr; + }else{ + *col_end = '\0'; + line = col_end + 1; + } + } + + template + void parse_line( + char*line, + char**sorted_col, + const std::vector&col_order + ){ + for (int i : col_order) { + if(line == nullptr) + throw ::io::error::too_few_columns(); + char*col_begin, *col_end; + chop_next_column(line, col_begin, col_end); + + if (i != -1) { + trim_policy::trim(col_begin, col_end); + quote_policy::unescape(col_begin, col_end); + + sorted_col[i] = col_begin; + } + } + if(line != nullptr) + throw ::io::error::too_many_columns(); + } + + template + void parse_header_line( + char*line, + std::vector&col_order, + const std::string*col_name, + ignore_column ignore_policy + ){ + col_order.clear(); + + bool found[column_count]; + std::fill(found, found + column_count, false); + while(line){ + char*col_begin,*col_end; + chop_next_column(line, col_begin, col_end); + + trim_policy::trim(col_begin, col_end); + quote_policy::unescape(col_begin, col_end); + + for(unsigned i=0; i + void parse(char*col, char &x){ + if(!*col) + throw error::invalid_single_character(); + x = *col; + ++col; + if(*col) + throw error::invalid_single_character(); + } + + template + void parse(char*col, std::string&x){ + x = col; + } + + template + void parse(char*col, const char*&x){ + x = col; + } + + template + void parse(char*col, char*&x){ + x = col; + } + + template + void parse_unsigned_integer(const char*col, T&x){ + x = 0; + while(*col != '\0'){ + if('0' <= *col && *col <= '9'){ + T y = *col - '0'; + if(x > ((std::numeric_limits::max)()-y)/10){ + overflow_policy::on_overflow(x); + return; + } + x = 10*x+y; + }else + throw error::no_digit(); + ++col; + } + } + + templatevoid parse(char*col, unsigned char &x) + {parse_unsigned_integer(col, x);} + templatevoid parse(char*col, unsigned short &x) + {parse_unsigned_integer(col, x);} + templatevoid parse(char*col, unsigned int &x) + {parse_unsigned_integer(col, x);} + templatevoid parse(char*col, unsigned long &x) + {parse_unsigned_integer(col, x);} + templatevoid parse(char*col, unsigned long long &x) + {parse_unsigned_integer(col, x);} + + template + void parse_signed_integer(const char*col, T&x){ + if(*col == '-'){ + ++col; + + x = 0; + while(*col != '\0'){ + if('0' <= *col && *col <= '9'){ + T y = *col - '0'; + if(x < ((std::numeric_limits::min)()+y)/10){ + overflow_policy::on_underflow(x); + return; + } + x = 10*x-y; + }else + throw error::no_digit(); + ++col; + } + return; + }else if(*col == '+') + ++col; + parse_unsigned_integer(col, x); + } + + templatevoid parse(char*col, signed char &x) + {parse_signed_integer(col, x);} + templatevoid parse(char*col, signed short &x) + {parse_signed_integer(col, x);} + templatevoid parse(char*col, signed int &x) + {parse_signed_integer(col, x);} + templatevoid parse(char*col, signed long &x) + {parse_signed_integer(col, x);} + templatevoid parse(char*col, signed long long &x) + {parse_signed_integer(col, x);} + + template + void parse_float(const char*col, T&x){ + bool is_neg = false; + if(*col == '-'){ + is_neg = true; + ++col; + }else if(*col == '+') + ++col; + + x = 0; + while('0' <= *col && *col <= '9'){ + int y = *col - '0'; + x *= 10; + x += y; + ++col; + } + + if(*col == '.'|| *col == ','){ + ++col; + T pos = 1; + while('0' <= *col && *col <= '9'){ + pos /= 10; + int y = *col - '0'; + ++col; + x += y*pos; + } + } + + if(*col == 'e' || *col == 'E'){ + ++col; + int e; + + parse_signed_integer(col, e); + + if(e != 0){ + T base; + if(e < 0){ + base = T(0.1); + e = -e; + }else{ + base = T(10); + } + + while(e != 1){ + if((e & 1) == 0){ + base = base*base; + e >>= 1; + }else{ + x *= base; + --e; + } + } + x *= base; + } + }else{ + if(*col != '\0') + throw error::no_digit(); + } + + if(is_neg) + x = -x; + } + + template void parse(char*col, float&x) { parse_float(col, x); } + template void parse(char*col, double&x) { parse_float(col, x); } + template void parse(char*col, long double&x) { parse_float(col, x); } + + template + void parse(char*col, T&x){ + // Mute unused variable compiler warning + (void)col; + (void)x; + // GCC evalutes "false" when reading the template and + // "sizeof(T)!=sizeof(T)" only when instantiating it. This is why + // this strange construct is used. + static_assert(sizeof(T)!=sizeof(T), + "Can not parse this type. Only buildin integrals, floats, char, char*, const char* and std::string are supported"); + } + + } + + template, + class quote_policy = no_quote_escape<','>, + class overflow_policy = throw_on_overflow, + class comment_policy = no_comment + > + class CSVReader{ + private: + LineReader in; + + char*row[column_count]; + std::string column_names[column_count]; + + std::vectorcol_order; + + template + void set_column_names(std::string s, ColNames...cols){ + column_names[column_count-sizeof...(ColNames)-1] = std::move(s); + set_column_names(std::forward(cols)...); + } + + void set_column_names(){} + + + public: + CSVReader() = delete; + CSVReader(const CSVReader&) = delete; + CSVReader&operator=(const CSVReader&); + + template + explicit CSVReader(Args&&...args):in(std::forward(args)...){ + std::fill(row, row+column_count, nullptr); + col_order.resize(column_count); + for(unsigned i=0; i + void read_header(ignore_column ignore_policy, ColNames...cols){ + static_assert(sizeof...(ColNames)>=column_count, "not enough column names specified"); + static_assert(sizeof...(ColNames)<=column_count, "too many column names specified"); + try{ + set_column_names(std::forward(cols)...); + + char*line; + do{ + line = in.next_line(); + if(!line) + throw error::header_missing(); + }while(comment_policy::is_comment(line)); + + detail::parse_header_line + + (line, col_order, column_names, ignore_policy); + }catch(error::with_file_name&err){ + err.set_file_name(in.get_truncated_file_name()); + throw; + } + } + + template + void set_header(ColNames...cols){ + static_assert(sizeof...(ColNames)>=column_count, + "not enough column names specified"); + static_assert(sizeof...(ColNames)<=column_count, + "too many column names specified"); + set_column_names(std::forward(cols)...); + std::fill(row, row+column_count, nullptr); + col_order.resize(column_count); + for(unsigned i=0; i + void parse_helper(std::size_t r, T&t, ColType&...cols){ + if(row[r]){ + try{ + try{ + ::io::detail::parse(row[r], t); + }catch(error::with_column_content&err){ + err.set_column_content(row[r]); + throw; + } + }catch(error::with_column_name&err){ + err.set_column_name(column_names[r].c_str()); + throw; + } + } + parse_helper(r+1, cols...); + } + + + public: + template + bool read_row(ColType& ...cols){ + static_assert(sizeof...(ColType)>=column_count, + "not enough columns specified"); + static_assert(sizeof...(ColType)<=column_count, + "too many columns specified"); + try{ + try{ + + char*line; + do{ + line = in.next_line(); + if(!line) + return false; + }while(comment_policy::is_comment(line)); + + detail::parse_line + (line, row, col_order); + + parse_helper(0, cols...); + }catch(error::with_file_name&err){ + err.set_file_name(in.get_truncated_file_name()); + throw; + } + }catch(error::with_file_line&err){ + err.set_file_line(in.get_file_line()); + throw; + } + + return true; + } + }; +} +#endif + diff --git a/src/db2FileList.txt b/src/db2FileList.txt new file mode 100644 index 000000000..e941ca233 --- /dev/null +++ b/src/db2FileList.txt @@ -0,0 +1,14 @@ +1353545;dbfilesclient/areatable.db2 +Light +LightParams +LightSkybox +LiquidType +LiquidTypeXTexture +LiquidMaterial +LiquidObject +LiquidType +LiquidTypeXTexture +Map +WMOAreaTable +ZoneLight +ZoneLightPoint \ No newline at end of file diff --git a/src/engine/HeadersGL.h b/src/engine/HeadersGL.h deleted file mode 100644 index 1bbff7094..000000000 --- a/src/engine/HeadersGL.h +++ /dev/null @@ -1,68 +0,0 @@ -// -// Created by Deamon on 3/12/2018. -// - -#ifndef WEBWOWVIEWERCPP_HEADERSGL_H -#define WEBWOWVIEWERCPP_HEADERSGL_H - -#ifdef GLES -// Core GLES - #define glClearDepth glClearDepthf - - // OES_vertex_array_object - #define glGenVertexArrays glGenVertexArraysOES - #define glBindVertexArray glBindVertexArrayOES - #define glDeleteVertexArrays glDeleteVertexArraysOES - - // OES_mapbuffer - #define glMapBuffer glMapBufferOES - #define glUnmapBuffer glUnmapBufferOES - #define GL_WRITE_ONLY GL_WRITE_ONLY_OES - - // EXT_map_buffer_range - #define glMapBufferRange glMapBufferRangeEXT - #define GL_MAP_WRITE_BIT GL_MAP_WRITE_BIT_EXT - #define GL_MAP_INVALIDATE_BUFFER_BIT GL_MAP_INVALIDATE_BUFFER_BIT_EXT - - // EXT_texture_storage - #define glTexStorage2D glTexStorage2DEXT - #define GL_LUMINANCE8 GL_LUMINANCE8_EXT - #define GL_LUMINANCE8_ALPHA8 GL_LUMINANCE8_ALPHA8_EXT - - // GL_OES_packed_depth_stencil - #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES - #define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES - #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES - - // OES_rgb8_rgba8 - #define GL_RGBA8 GL_RGBA8_OES - - // OES_texture_half_float - #define GL_HALF_FLOAT GL_HALF_FLOAT_OES - - // EXT_color_buffer_half_float - #define GL_RGBA16F GL_RGBA16F_EXT - - // EXT_texture_compression_s3tc - #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0 - #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0 - #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0 - - // 3D textures: not supported - #define GL_TEXTURE_3D 0 - #define glTexImage3D (void)sizeof - #define glTexSubImage3D (void)sizeof - #define glTexStorage3D (void)sizeof - - // RG format: not supported - #define GL_RG 0 - #define GL_RG16 0 -#else -// IMG_texture_compression_pvrtc -#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0 -#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0 -#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0 -#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0 -#endif - -#endif //WEBWOWVIEWERCPP_HEADERSGL_H diff --git a/src/exporters/dataExporter/DataExporterClass.cpp b/src/exporters/dataExporter/DataExporterClass.cpp new file mode 100644 index 000000000..fd362369f --- /dev/null +++ b/src/exporters/dataExporter/DataExporterClass.cpp @@ -0,0 +1,96 @@ +// +// Created by Deamon on 2/16/2021. +// + +#include "DataExporterClass.h" +#include "../../../wowViewerLib/src/include/string_utils.h" + +DataExporterClass::DataExporterClass(HApiContainer apiContainer) { + m_apiContainer = apiContainer; + processedFiles = 0; + outputLog.open ("m2Log.txt"); + csv = new io::CSVReader<2, io::trim_chars<' '>, io::no_quote_escape<';'>>("listfile.csv"); +} + +void DataExporterClass::process() { + if (finished) + return; + + if (m_m2Geom == nullptr) { + + while (csv->read_row(currentFileDataId, currentFileName)) { + if (endsWith(currentFileName, ".m2")) + break; + } + if (!endsWith(currentFileName, ".m2")) { + finished = true; + return; + } + m_m2Geom = m_apiContainer->cacheStorage->getM2GeomCache()->getFileId(currentFileDataId); + } + + if (m_m2Geom != nullptr && m_m2Geom->getStatus() == FileStatus::FSRejected) + m_m2Geom = nullptr; + + if (m_m2Geom != nullptr && m_m2Geom->getStatus() == FileStatus::FSLoaded) { + if (m_m2Geom->m_m2Data->particle_emitters.size > 0 || m_m2Geom->m_m2Data->ribbon_emitters.size > 0) { + + outputLog << currentFileName << " "; + for (int i = 0; i < m_m2Geom->m_m2Data->particle_emitters.size; i++) { + auto pe = m_m2Geom->m_m2Data->particle_emitters[i]; + bool isRecursive = (i < m_m2Geom->recursiveFileDataIDs.size()) && (m_m2Geom->recursiveFileDataIDs[i] > 0); + bool isModelEmitter = (i < m_m2Geom->particleModelFileDataIDs.size()) && (m_m2Geom->particleModelFileDataIDs[i] > 0); + + std::string emitterGenerator = ""; + if (pe->old.emitterType == 1) { + emitterGenerator = "PLANE"; + } else if (pe->old.emitterType == 2) { + emitterGenerator = "SPHERE"; + } else if (pe->old.emitterType == 3) { + emitterGenerator = "SPLINE"; + } else if (pe->old.emitterType == 4) { + emitterGenerator = "BONE"; + } else { + emitterGenerator = "UNK_GENERATOR_"+std::to_string(pe->old.emitterType); + } + + std::string txacVal = ""; + if (i < m_m2Geom->txacMParticle.size() && (m_m2Geom->txacMParticle[i].value != 0)) { + txacVal = "TXAC = (" + + std::to_string((int)m_m2Geom->txacMParticle[i].perByte[0]) + + ", " + + std::to_string((int)m_m2Geom->txacMParticle[i].perByte[0]) + + ") "; + } + + outputLog << "( " + << (isRecursive ? ("RECURSIVE " + std::to_string(m_m2Geom->recursiveFileDataIDs[i]) + " ") : "") + << (isModelEmitter ? ("MODEL " + std::to_string(m_m2Geom->particleModelFileDataIDs[i]) + " ") : "") + << txacVal + << emitterGenerator + << " ) "; + } + + for (int i = 0; i < m_m2Geom->m_m2Data->ribbon_emitters.size; i++) { + auto ribbon = m_m2Geom->m_m2Data->ribbon_emitters[i]; + bool isRibbonSubstitution = ribbon->ribbonColorIndex > 0; + bool isTextureTransform = (m_m2Geom->m_m2Data->global_flags.flag_unk_0x20000 > 0); + + if (isRibbonSubstitution || isTextureTransform) { + outputLog << "( ribbon " << i << " " + << (isRibbonSubstitution ? ("ribbonColorIndex = " + std::to_string(ribbon->ribbonColorIndex) + " " ) : "") + << (isTextureTransform ? ("textureTransformLookupIndex = " + std::to_string(ribbon->textureTransformLookupIndex) + " ") : "") + << " ) "; + } + } + processedFiles++; + outputLog << std::endl; + if (processedFiles % 1000 == 0) { + outputLog << std::flush; + } + + } + + m_m2Geom = nullptr; + } +} diff --git a/src/exporters/dataExporter/DataExporterClass.h b/src/exporters/dataExporter/DataExporterClass.h new file mode 100644 index 000000000..c68043069 --- /dev/null +++ b/src/exporters/dataExporter/DataExporterClass.h @@ -0,0 +1,40 @@ +// +// Created by Deamon on 2/16/2021. +// + +#ifndef AWEBWOWVIEWERCPP_DATAEXPORTERCLASS_H +#define AWEBWOWVIEWERCPP_DATAEXPORTERCLASS_H + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#include +#include "../../../wowViewerLib/src/engine/objects/m2/m2Object.h" +#include "../../database/csvtest/csv.h" +#include + +class DataExporterClass { +public: + DataExporterClass(HApiContainer apiContainer); + void process(); + bool isDone() { + return finished; + } +private: + HApiContainer m_apiContainer; + + int currentFileDataId; + std::string currentFileName; + + HM2Geom m_m2Geom = nullptr; + io::CSVReader<2, io::trim_chars<' '>, io::no_quote_escape<';'>> *csv ; + + bool finished = false; + int processedFiles = 0; + + std::ofstream outputLog; +}; + + +#endif //AWEBWOWVIEWERCPP_DATAEXPORTERCLASS_H diff --git a/src/exporters/gltfExporter/GLTFExporter.cpp b/src/exporters/gltfExporter/GLTFExporter.cpp new file mode 100644 index 000000000..8b000a71a --- /dev/null +++ b/src/exporters/gltfExporter/GLTFExporter.cpp @@ -0,0 +1,706 @@ +// +// Created by Deamon on 12/22/2020. +// +#define _USE_MATH_DEFINES // for C++ +#include +#include "GLTFExporter.h" +#define TINYGLTF_IMPLEMENTATION +#define STB_IMAGE_IMPLEMENTATION + +//#define TINYGLTF_NO_STB_IMAGE_WRITE + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +#define STB_IMAGE_WRITE_IMPLEMENTATION +#endif + +#include "../../../wowViewerLib/src/engine/algorithms/mathHelper.h" +#include "../../../wowViewerLib/3rdparty/tinygltf/tiny_gltf.h" +#include "../../../wowViewerLib/src/engine/ApiContainer.h" +#include "../../../wowViewerLib/src/engine/objects/m2/m2Object.h" +#include "../../screenshots/screenshotMaker.h" +#include "../../../wowViewerLib/src/engine/texture/DxtDecompress.h" +#include "../../../wowViewerLib/src/include/string_utils.h" + +void GLTFExporter::addM2Object(std::shared_ptr &m2Object) { + m2sToExport.push_back(m2Object); +} +void GLTFExporter::exportM2Object(std::shared_ptr &m2Object) { + M2ModelData m2ModelData; + + //1. Create Buffers + createVboAndIbo(m2ModelData, m2Object); + + //2. Create buffer views + createVBOAndIBOViews(m2ModelData, m2Object); + + //2. Create accesors + //2.1 Accessors for VBO: + createAttributesForVBO(m2ModelData, m2Object); + + //Export textures + { + createTextures(m2ModelData, m2Object); + } + + //Primitives + tinygltf::Mesh mesh; + { + createIBOAccessors(m2ModelData, m2Object); + createGlTFBatches(m2ModelData, m2Object, mesh); + } + + model.meshes.push_back(mesh); + + //Add skin (armature of bones) to model + { + tinygltf::Skin skin; + createSkeleton(m2ModelData, m2Object, skin); + model.skins.push_back(skin); + } + + //Export current animation + { + int animationBufferIndex = model.buffers.size(); + //Create animation buffer; + tinygltf::Buffer animationBuffer; + animationBuffer.name = "animationDataBuffer"; + + animationBuffer.data = std::vector(); + animationBuffer.data.reserve(1000); + + model.buffers.push_back(animationBuffer); + + + tinygltf::Animation animation; + + auto animIndex = m2Object->getCurrentAnimationIndex(); + + auto &bones = * getBoneMasterData(m2Object)->getSkelData()->m_m2CompBones; + auto &animations = * getBoneMasterData(m2Object)->getSkelData()->m_sequences; + auto &globalSequences = * getBoneMasterData(m2Object)->getSkelData()->m_globalSequences; + auto animLen = animations.getElement(animIndex)->duration / 1000.f; + for (int i = 0; i < bones.size; i++) { + addTrack(animation, bones[i]->translation, animIndex, animLen,"translation", m2ModelData.boneEndIndexes[i], animationBufferIndex,globalSequences); + addTrack(animation, bones[i]->rotation, animIndex, animLen,"rotation",m2ModelData.boneEndIndexes[i], animationBufferIndex,globalSequences); + addTrack(animation, bones[i]->scaling, animIndex, animLen, "scale", m2ModelData.boneEndIndexes[i],animationBufferIndex,globalSequences); + } + model.animations.push_back(animation); + } + + + + //Add scene + int rootSceneNodeIndex = model.nodes.size(); + { + tinygltf::Node node; + node.mesh = 0; + node.skin = 0; + node.name = getM2Geom(m2Object)->getName(); + + model.nodes.push_back(node); + } + + { + tinygltf::Scene scene; + scene.name = "scene"; + scene.nodes = {rootSceneNodeIndex, m2ModelData.skeletonIndex}; + model.scenes.push_back(scene); + } + model.defaultScene = 0; +} + + +void GLTFExporter::createTextures(GLTFExporter::M2ModelData &m2ModelData, std::shared_ptr &m2Object) { + m2ModelData.textureIndexStart = model.textures.size(); + + auto m2Geom = getM2Geom(m2Object); + auto &m2Texts = m2Geom->getM2Data()->textures; + auto m2TextsSize = std::max(m2Texts.size, m2Geom->textureFileDataIDs.size()); + for (int i = 0; i < m2TextsSize; i++) { + bool wrapX = false; + bool wrapY = false; + if (i < m2Texts.size) { + auto *m2Texture = m2Texts[i]; + + wrapX = (m2Texture->flags & 1) > 0; + wrapY = (m2Texture->flags & 2) > 0; + } + + //1. Save texture to file + auto textureData = getM2BlpTextureData(m2Object, i); + std::string texturePath; + if (textureData != nullptr) { + texturePath = textureData->getTextureName(); + + //Fix unk extension for textures + std::string unksuffix = ".unk"; + if (endsWith(texturePath, unksuffix)) { + texturePath.resize(texturePath.size() - unksuffix.size()); + texturePath = texturePath + ".png"; + } + std::cout << "m_folderPath+texturePath = " << m_folderPath+texturePath << std::endl; + decodeTextureAndSaveToFile(textureData, m_folderPath + texturePath); + } else { + texturePath = "blackTexture.png"; + std::vector rgbaBuff = {0}; + std::cout << "m_folderPath+texturePath = " << m_folderPath+texturePath << std::endl; + saveScreenshotLodePng( m_folderPath+texturePath, 1, 1, rgbaBuff); + } + + //2. Create sampler + int samplerIndex = model.samplers.size(); + { + tinygltf::Sampler sampler; + sampler.name = "text_sampler_"+std::to_string(i); + sampler.minFilter = TINYGLTF_TEXTURE_FILTER_LINEAR; + sampler.magFilter = TINYGLTF_TEXTURE_FILTER_LINEAR; + sampler.wrapS = wrapX ? TINYGLTF_TEXTURE_WRAP_REPEAT : TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE; + sampler.wrapT = wrapX ? TINYGLTF_TEXTURE_WRAP_REPEAT : TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE; + + model.samplers.push_back(sampler); + } + //3. Create image + int imageIndex = model.images.size(); + { + + model.images.resize(model.images.size() + 1); + tinygltf::Image &image = model.images[imageIndex]; + image.name = "text_image_"+std::to_string(i); + if (textureData != nullptr) { + auto &topMipMap = (*textureData->getMipmapsVector())[0]; + image.height = topMipMap.height; + image.width = topMipMap.width; + } + image.bits = 8; + image.pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + image.mimeType = "image/png"; + image.uri = texturePath; + } + //4. Create texture + int textureIndex = model.textures.size(); + { + tinygltf::Texture gltfText; + gltfText.name = "texture_"+std::to_string(i); + gltfText.sampler = samplerIndex; + gltfText.source = imageIndex; + model.textures.push_back(gltfText); + } + } +} + +void GLTFExporter::decodeTextureAndSaveToFile(HBlpTexture textureData, const std::string &texturePath) { + HMipmapsVector mipmaps = textureData->getMipmapsVector(); + + //1. Save texture to file + auto &topMipMap = (*mipmaps)[0]; + //1.1 Decompress + auto decodedResult = std::vector(topMipMap.width * topMipMap.height * 4); + auto textureFormat = textureData->getTextureFormat(); + if (textureFormat == TextureFormat::S3TC_RGB_DXT1 || textureFormat == TextureFormat::S3TC_RGBA_DXT1) + { + bool hasAlpha = (textureFormat == TextureFormat::S3TC_RGBA_DXT1); + DecompressBC1( topMipMap.width, topMipMap.height, topMipMap.texture.data(), decodedResult.data(), hasAlpha); + } else if (textureFormat == TextureFormat::S3TC_RGBA_DXT3) { + DecompressBC2(topMipMap.width, topMipMap.height, topMipMap.texture.data(), decodedResult.data()); + } + else if (textureFormat == TextureFormat::S3TC_RGBA_DXT5){ + DecompressBC3(topMipMap.width, topMipMap.height, topMipMap.texture.data(), decodedResult.data()); + } else { + decodedResult = topMipMap.texture; + } + + saveScreenshotLodePng( texturePath, topMipMap.width, topMipMap.height, decodedResult); +} + +template +void GLTFExporter::addTrack(tinygltf::Animation &animation, M2Track &track, int animIndex, float animationLength, + std::string trackName, int nodeIndex, int bufferIndex, M2Array &globalSequences) { + std::vector &buffer = model.buffers[bufferIndex].data; + + if (track.timestamps.size <= animIndex) { + animIndex = 0; + } + + if (track.timestamps.size == 0 || animIndex >= track.timestamps.size) + return; + if (track.values.size == 0 || animIndex >= track.timestamps.size) + return; + + if (track.global_sequence > -1) { + if (track.global_sequence >= globalSequences.size) return; + + animationLength = globalSequences[track.global_sequence]->timestamp / 1000.0f; + } + + //1. Add time + int timesAccesor = -1; + { + auto ×Arr = *track.timestamps[animIndex]; + if (timesArr.size == 0) + return; + + auto timesInFloat = std::vector(timesArr.size); + for (int i = 0; i < timesInFloat.size(); i++) { + timesInFloat[i] = (*timesArr[i]) / 1000.f; + } + //Copy values to buffer + + int timesOffset = buffer.size(); + buffer.resize(timesOffset + (timesInFloat.size() * sizeof(float))); + std::copy((uint8_t *)×InFloat[0], (uint8_t *)(×InFloat[0]+timesInFloat.size()), &buffer[timesOffset]); + + //1.2 Create bufferView for times + int timesBufferView = model.bufferViews.size(); + { + tinygltf::BufferView timesBufferView; + timesBufferView.buffer = bufferIndex; + timesBufferView.byteOffset = timesOffset; + timesBufferView.byteLength = timesInFloat.size() * sizeof(float); + timesBufferView.byteStride = 0; + timesBufferView.target = 0; + model.bufferViews.push_back(timesBufferView); + } + + //1.3 Create accesor for times + timesAccesor = model.accessors.size(); + { + tinygltf::Accessor a_times; + a_times.bufferView = timesBufferView; + a_times.name = "times_"+trackName + "_" + std::to_string(nodeIndex) + "_accessor"; + a_times.byteOffset = 0; + a_times.normalized = false; + a_times.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + a_times.count = timesArr.size; + a_times.type = TINYGLTF_TYPE_SCALAR; + a_times.minValues = {0}; + a_times.maxValues = {animationLength}; + model.accessors.push_back(a_times); + } + } + + //2. Add values + int valuesAccesor = -1; + { + int valueOffset = -1; + + auto &valueArr = *track.values[animIndex]; + int accessor_Type = 0; + int valuesOffset = buffer.size(); + int valuesSizeInBytes = 0; + if constexpr (std::is_same::value) { + accessor_Type = TINYGLTF_TYPE_VEC4; + auto valueVector = std::vector(valueArr.size); + + for (int i = 0; i < valueArr.size; i++) { + auto quat = convertHelper(*valueArr[i]); + + valueVector[i] = {quat[1], quat[2], quat[3], quat[0]}; + } + + //Copy values to buffer + valuesSizeInBytes = valueVector.size() * sizeof(mathfu::vec4_packed); + buffer.resize(valuesOffset + valuesSizeInBytes); + std::copy((uint8_t *)&valueVector[0], (uint8_t *)(&valueVector[0] + valueVector.size()), &buffer[valuesOffset]); + + } else if constexpr (std::is_same::value) { + accessor_Type = TINYGLTF_TYPE_VEC3; + valuesSizeInBytes = valueArr.size * sizeof(C3Vector); + buffer.resize(valuesOffset + valuesSizeInBytes); + std::copy((uint8_t *)valueArr[0], (uint8_t *)(valueArr[0]+valueArr.size), &buffer[valuesOffset]); + + } else { + assert("Oops!"); + } + + //2.2 Create bufferView for values + int valuesBufferView = model.bufferViews.size(); + { + tinygltf::BufferView valueBufferView; + valueBufferView.buffer = bufferIndex; + valueBufferView.byteOffset = valuesOffset; + valueBufferView.byteLength = valuesSizeInBytes; + valueBufferView.byteStride = 0; + valueBufferView.target = 0; + model.bufferViews.push_back(valueBufferView); + } + + //2.3 Create accesor for values + valuesAccesor = model.accessors.size(); + { + tinygltf::Accessor a_values; + a_values.bufferView = valuesBufferView; + a_values.name = "value_"+trackName + "_" + std::to_string(nodeIndex) + "_accessor"; + a_values.byteOffset = 0; + a_values.normalized = false; + a_values.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + a_values.count = valueArr.size; + a_values.type = accessor_Type; + + model.accessors.push_back(a_values); + } + } + + //3. Create animation sampler + int animationSamplerInd = animation.samplers.size(); + { + tinygltf::AnimationSampler sampler; + sampler.input = timesAccesor; + sampler.output = valuesAccesor; + sampler.interpolation = "LINEAR"; + animation.samplers.push_back(sampler); + } + tinygltf::AnimationChannel animationChannel; + animationChannel.sampler = animationSamplerInd; + animationChannel.target_node = nodeIndex; + animationChannel.target_path = trackName; + animation.channels.push_back(animationChannel); +} + +void GLTFExporter::createSkeleton(M2ModelData &m2ModelData, std::shared_ptr &m2Object, tinygltf::Skin &skin) { + auto &bones = * getBoneMasterData(m2Object)->getSkelData()->m_m2CompBones; + + //Create skeleton + m2ModelData.skeletonIndex = model.nodes.size(); + { + { + tinygltf::Node node; + node.mesh = -1; + node.skin = -1; + node.name = "Armature"; + auto quat = mathfu::quat::FromMatrix(MathHelper::RotationX(-M_PI_2)); + node.rotation = {quat[1], quat[2], quat[3], quat[0]}; + + model.nodes.push_back(node); + } + skin.skeleton = m2ModelData.skeletonIndex; + } + for (int i = 0; i < bones.size; i++) { + + auto bone = bones[i]; + mathfu::vec3 relativePivot = mathfu::vec3(bone->pivot); + if (bone->parent_bone>-1) { + relativePivot = mathfu::vec3(bone->pivot) - mathfu::vec3(bones[bone->parent_bone]->pivot); + } + + int bonePrefixNodeIndex = model.nodes.size(); + { + tinygltf::Node node; + node.mesh = -1; + node.skin = -1; + node.name = "bone " + std::to_string(i) + " prefix"; + node.translation = { + relativePivot.x, + relativePivot.y, + relativePivot.z, + }; + model.nodes.push_back(node); + m2ModelData.boneStartIndexes.push_back(bonePrefixNodeIndex); + } + int boneNodeIndex = model.nodes.size(); + { + model.nodes[bonePrefixNodeIndex].children.push_back(boneNodeIndex); + + tinygltf::Node node; + node.mesh = -1; + node.skin = -1; + node.name = "bone " + std::to_string(i); + model.nodes.push_back(node); + + m2ModelData.boneEndIndexes.push_back(boneNodeIndex); + skin.joints.push_back(boneNodeIndex); + } + + if (bone->parent_bone>-1) { + model.nodes[m2ModelData.boneEndIndexes[bone->parent_bone]].children.push_back(bonePrefixNodeIndex); + } else { + model.nodes[m2ModelData.skeletonIndex].children.push_back(bonePrefixNodeIndex); + } + + m2ModelData.inverBindMatrices.push_back(mathfu::mat4::FromTranslationVector(-mathfu::vec3(bone->pivot))); + } + { + //Create inverseMat buffer + int inverseMatBufferIndex = model.buffers.size(); + { + tinygltf::Buffer inverseMatBuffer; + inverseMatBuffer.name = "inverseMatBuffer"; + + inverseMatBuffer.data = std::vector((uint8_t *) m2ModelData.inverBindMatrices.data(), + (uint8_t *) (m2ModelData.inverBindMatrices.data() + + m2ModelData.inverBindMatrices.size())); + + model.buffers.push_back(inverseMatBuffer); + } + //Create inverseMat bufferView + int inverseMatBufferViewIndex = model.bufferViews.size(); + { + tinygltf::BufferView inverseMatBufferView; + inverseMatBufferView.buffer = inverseMatBufferIndex; + inverseMatBufferView.byteOffset = 0; + inverseMatBufferView.byteLength = model.buffers[inverseMatBufferIndex].data.size(); + inverseMatBufferView.byteStride = 0; + inverseMatBufferView.target = 0; + model.bufferViews.push_back(inverseMatBufferView); + } + //Create inverseMat accessor + { + int accesorInverseMatIndex = model.accessors.size(); + + tinygltf::Accessor a_inverseMat; + a_inverseMat.bufferView = inverseMatBufferViewIndex; + a_inverseMat.name = "inverseMatBuffer"; + a_inverseMat.byteOffset = 0; + a_inverseMat.normalized = false; + a_inverseMat.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + a_inverseMat.count = m2ModelData.inverBindMatrices.size(); + a_inverseMat.type = TINYGLTF_TYPE_MAT4; + + model.accessors.push_back(a_inverseMat); + skin.inverseBindMatrices = accesorInverseMatIndex; + } + } +} + +void GLTFExporter::createGlTFBatches(M2ModelData &m2ModelData, std::shared_ptr &m2Object, tinygltf::Mesh &mesh) { + auto skinProfile = getSkinGeom(m2Object)->getSkinData(); + auto m_m2Data = getM2Geom(m2Object)->getM2Data(); + M2Array &batches = skinProfile->batches; + + + for (int i = 0; i < batches.size; i++) { + auto m2Batch = batches[i]; + + int renderFlagIndex = m2Batch->materialIndex; + auto renderFlag = m_m2Data->materials[renderFlagIndex]; + + int materialIndex = model.materials.size(); + tinygltf::Material material; + material.name = "batch_" + std::to_string(i); + if (renderFlag->blending_mode == 0) { + material.alphaMode = "OPAQUE"; + material.alphaCutoff = 0; + } else if (renderFlag->blending_mode == 1) { + material.alphaMode = "MASK"; + material.alphaCutoff = 128.0f/255.0f; + } else { + material.alphaMode = "BLEND"; + material.alphaCutoff = 1.0f/255.0f; + } + material.doubleSided = (renderFlag->flags & 0x4); + material.pbrMetallicRoughness.baseColorTexture.index = + m2ModelData.textureIndexStart + + *m_m2Data->texture_lookup_table[m2Batch->textureComboIndex + 0]; + material.pbrMetallicRoughness.baseColorTexture.texCoord = 0; + model.materials.push_back(material); + + + tinygltf::Primitive p; + p.material = materialIndex; + p.attributes["POSITION"] = m2ModelData.accesorPosIndex; + p.attributes["NORMAL"] = m2ModelData.accesorNormalIndex; + p.attributes["TEXCOORD_0"] = m2ModelData.accesorTexCoordIndex; + p.attributes["TEXCOORD_1"] = m2ModelData.accesorTexCoordIndex2; + p.attributes["JOINTS_0"] = m2ModelData.accesorJointsIndex; + p.attributes["WEIGHTS_0"] = m2ModelData.accesorWeightsIndex; + p.indices = m2Batch->skinSectionIndex + m2ModelData.IBOAccessorsStart; + p.mode = TINYGLTF_MODE_TRIANGLES; + p.material = materialIndex; + + mesh.primitives.push_back(p); + } +} + +void GLTFExporter::createIBOAccessors(M2ModelData &m2ModelData, std::shared_ptr &m2Object) { + auto skinProfile = getSkinGeom(m2Object)->getSkinData(); + auto sections = skinProfile->skinSections; + + m2ModelData.IBOAccessorsStart = model.accessors.size(); + + for (int i = 0; i < sections.size; i++) { + auto skinSection = skinProfile->skinSections[i]; + + //Create accessor for IBO + tinygltf::Accessor a_primitive; + a_primitive.bufferView = m2ModelData.IBOBufferViewIndex; + a_primitive.name = ""; + a_primitive.byteOffset = (skinSection->indexStart + (skinSection->Level << 16)) * 2 ; + a_primitive.normalized = false; + a_primitive.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + a_primitive.count = skinSection->indexCount; + a_primitive.type = TINYGLTF_TYPE_SCALAR; + + assert(a_primitive.byteOffset <= m2ModelData.iboSize); + assert((a_primitive.byteOffset+a_primitive.count) <= m2ModelData.iboSize); + + model.accessors.push_back(a_primitive); + } +} + +void GLTFExporter::createAttributesForVBO(M2ModelData &m2ModelData, std::shared_ptr &m2Object) { + auto m2Data = getM2Geom(m2Object)->getM2Data(); + //Position + { + m2ModelData.accesorPosIndex = model.accessors.size(); + + tinygltf::Accessor a_position; + a_position.bufferView = m2ModelData.VBOBufferViewIndex; + a_position.name = "POSITION"; + a_position.byteOffset = 0; + a_position.normalized = false; + a_position.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + a_position.count = m2Data->vertices.size; + a_position.type = TINYGLTF_TYPE_VEC3; + + model.accessors.push_back(a_position); + } + //Weights + { + m2ModelData.accesorWeightsIndex = model.accessors.size(); + + tinygltf::Accessor a_weights; + a_weights.bufferView = m2ModelData.VBOBufferViewIndex; + a_weights.name = "WEIGHTS_0"; + a_weights.byteOffset = 12; + a_weights.normalized = true; + a_weights.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + a_weights.count = m2Data->vertices.size; + a_weights.type = TINYGLTF_TYPE_VEC4; + + model.accessors.push_back(a_weights); + } + //Joints (bone indices) + { + m2ModelData.accesorJointsIndex = model.accessors.size(); + + tinygltf::Accessor a_weights; + a_weights.bufferView = m2ModelData.VBOBufferViewIndex; + a_weights.name = "JOINTS_0"; + a_weights.byteOffset = 16; + a_weights.normalized = false; + a_weights.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + a_weights.count = m2Data->vertices.size; + a_weights.type = TINYGLTF_TYPE_VEC4; + + model.accessors.push_back(a_weights); + } + //Normal + { + m2ModelData.accesorNormalIndex = model.accessors.size(); + + tinygltf::Accessor a_normal; + a_normal.bufferView = m2ModelData.VBOBufferViewIndex; + a_normal.name = "NORMAL"; + a_normal.byteOffset = 20; + a_normal.normalized = false; + a_normal.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + a_normal.count = m2Data->vertices.size; + a_normal.type = TINYGLTF_TYPE_VEC3; + + model.accessors.push_back(a_normal); + } + //aTexCoord + { + m2ModelData.accesorTexCoordIndex = model.accessors.size(); + + tinygltf::Accessor a_texturecoord; + a_texturecoord.bufferView = m2ModelData.VBOBufferViewIndex; + a_texturecoord.name = "TEXCOORD_0"; + a_texturecoord.byteOffset = 32; + a_texturecoord.normalized = false; + a_texturecoord.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + a_texturecoord.count = m2Data->vertices.size; + a_texturecoord.type = TINYGLTF_TYPE_VEC2; + + model.accessors.push_back(a_texturecoord); + } + //aTexCoord + { + m2ModelData.accesorTexCoordIndex2 = model.accessors.size(); + + tinygltf::Accessor a_texturecoord; + a_texturecoord.bufferView = m2ModelData.VBOBufferViewIndex; + a_texturecoord.name = "TEXCOORD_1"; + a_texturecoord.byteOffset = 40; + a_texturecoord.normalized = false; + a_texturecoord.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + a_texturecoord.count = m2Data->vertices.size; + a_texturecoord.type = TINYGLTF_TYPE_VEC2; + + model.accessors.push_back(a_texturecoord); + } +} + +GLTFExporter::GLTFExporter(std::string folderPath) : m_folderPath(folderPath) { + model.asset.version = "2.0"; +} + +void GLTFExporter::createVboAndIbo(M2ModelData &m2ModelData, std::shared_ptr &m2Object) { + auto m2Data = getM2Geom(m2Object)->getM2Data(); + { + //1. Create VBO + m2ModelData.VBOBufferIndex = model.buffers.size(); + { + tinygltf::Buffer modelVBO; + modelVBO.name = "vbo"; + + auto firstVertex = m2Data->vertices.getElement(0); + modelVBO.data = std::vector((uint8_t *) firstVertex, + (uint8_t *) (firstVertex + m2Data->vertices.size)); + + model.buffers.push_back(modelVBO); + } + + //1.1 Create IBO + m2ModelData.IBOBufferIndex = model.buffers.size(); + { + tinygltf::Buffer modelIBO; + modelIBO.name = "ibo"; + + auto indicies = getSkinGeom(m2Object)->generateIndexBuffer(); + + modelIBO.data = std::vector((uint8_t *) indicies.data(), + (uint8_t *) (indicies.data() + indicies.size())); + + m2ModelData.iboSize = modelIBO.data.size(); + + model.buffers.push_back(modelIBO); + } + } +} + +void GLTFExporter::createVBOAndIBOViews(M2ModelData &m2ModelData, std::shared_ptr &m2Object) { + { + //2.1 Create VBO buffer view + m2ModelData.VBOBufferViewIndex = model.bufferViews.size(); + { + tinygltf::BufferView modelVBOView; + modelVBOView.buffer = m2ModelData.VBOBufferIndex; + modelVBOView.byteOffset = 0; + modelVBOView.byteLength = getM2Geom(m2Object)->getM2Data()->vertices.size * sizeof(M2Vertex); + modelVBOView.byteStride = 48; + modelVBOView.target = TINYGLTF_TARGET_ARRAY_BUFFER; + model.bufferViews.push_back(modelVBOView); + } + //2.1 Create IBO buffer view + m2ModelData.IBOBufferViewIndex = model.bufferViews.size(); + { + tinygltf::BufferView modelIBOView; + modelIBOView.buffer = m2ModelData.IBOBufferIndex; + modelIBOView.byteOffset = 0; + modelIBOView.byteLength = model.buffers[m2ModelData.IBOBufferIndex].data.size(); + modelIBOView.byteStride = 1; + modelIBOView.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; + model.bufferViews.push_back(modelIBOView); + } + } +} + +void GLTFExporter::saveToFile(std::string filePath) { + tinygltf::TinyGLTF loader; + + for (auto &m2Object : m2sToExport) { + exportM2Object(m2Object); + } + + loader.WriteGltfSceneToFile(&model, m_folderPath+filePath); +} diff --git a/src/exporters/gltfExporter/GLTFExporter.h b/src/exporters/gltfExporter/GLTFExporter.h new file mode 100644 index 000000000..02a9b73a0 --- /dev/null +++ b/src/exporters/gltfExporter/GLTFExporter.h @@ -0,0 +1,77 @@ +// +// Created by Deamon on 12/22/2020. +// + +#ifndef AWEBWOWVIEWERCPP_GLTFEXPORTER_H +#define AWEBWOWVIEWERCPP_GLTFEXPORTER_H + + +#include +#include "../../../wowViewerLib/src/exporters/IExporter.h" +#include "../../../3rdparty/tinygltf/tiny_gltf.h" + +class GLTFExporter : public IExporter { +private: + tinygltf::Model model; + + std::vector> m2sToExport; + + template + void addTrack(tinygltf::Animation &animation, M2Track &track, int animIndex, float animationLength, + std::string trackName, int nodeIndex, int animationBufferIndex, M2Array &globalSequences); + + struct M2ModelData { + int VBOBufferIndex = -1; + int IBOBufferIndex = -1; + + int iboSize; + + int VBOBufferViewIndex = -1; + int IBOBufferViewIndex = -1; + + int accesorPosIndex = -1; + int accesorNormalIndex = -1; + int accesorTexCoordIndex = -1; + int accesorTexCoordIndex2 = -1; + int accesorJointsIndex = -1; + int accesorWeightsIndex = -1; + + int IBOAccessorsStart = -1; + + int textureIndexStart = -1; + + //Bones + int skeletonIndex; + + std::vector boneStartIndexes; + std::vector boneEndIndexes; + std::vector inverBindMatrices; + }; + std::string m_folderPath; + +public: + GLTFExporter(std::string folderPath); + + void addM2Object(std::shared_ptr &m2Object) override; + void exportM2Object(std::shared_ptr &m2Object); + void saveToFile(std::string filePath) override; + + void createVBOAndIBOViews(M2ModelData &m2ModelData, std::shared_ptr &m2Object) ; + + void createVboAndIbo(M2ModelData &m2ModelData, std::shared_ptr &m2Object) ; + + void createAttributesForVBO(M2ModelData &m2ModelData, std::shared_ptr &m2Object); + + void createIBOAccessors(M2ModelData &m2ModelData, std::shared_ptr &m2Object); + + void createGlTFBatches(M2ModelData &m2ModelData, std::shared_ptr &m2Object, tinygltf::Mesh &mesh) ; + + void createSkeleton(M2ModelData &m2ModelData, std::shared_ptr &m2Object, tinygltf::Skin &skin); + + void decodeTextureAndSaveToFile(HBlpTexture textureData, const std::string &texturePath); + + void createTextures(M2ModelData &m2ModelData, std::shared_ptr &m2Object); +}; + + +#endif //AWEBWOWVIEWERCPP_GLTFEXPORTER_H diff --git a/src/jniLib.cpp b/src/jniLib.cpp index 35abbb4c2..322daa004 100644 --- a/src/jniLib.cpp +++ b/src/jniLib.cpp @@ -53,7 +53,6 @@ JNIEXPORT void JNICALL Java_livingwallpaper_wow_deamon87_wowlivingwallpaper_NativeWoWLib_draw(JNIEnv *env, jobject thiz, jdouble deltaTime) { processor->processRequests(false); - processor->processResults(10); if (windowSizeChanged) { scene->setScreenSize(canvWidth, canvHeight); diff --git a/src/main.cpp b/src/main.cpp index dd036fcdf..9dfbb8bb0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,6 @@ #define GLFW_INCLUDE_GLCOREARB //#define __EMSCRIPTEN__ -#include "engine/HeadersGL.h" #ifdef LINK_VULKAN #define GLFW_INCLUDE_VULKAN @@ -23,18 +22,15 @@ #undef GLFW_INCLUDE_VULKAN #endif -#include + +#include +#include #include #include -#include #include #include +#include -//#include "persistance/ZipRequestProcessor.h" -#include "persistance/CascRequestProcessor.h" -#include "persistance/HttpZipRequestProcessor.h" -//#include "persistance/MpqRequestProcessor.h" -#include "persistance/HttpRequestProcessor.h" #include "../wowViewerLib/src/gapi/interface/IDevice.h" @@ -48,8 +44,13 @@ #include "../wowViewerLib/src/engine/ApiContainer.h" #include "../wowViewerLib/src/engine/objects/scenes/wmoScene.h" #include "../wowViewerLib/src/engine/objects/scenes/m2Scene.h" -#include +#include "screenshots/screenshotMaker.h" +#include "database/CEmptySqliteDB.h" +#include +//TODO: Consider using dedicated buffers for uniform data for OGL in update thread, fill them up there +//Anb upload these buffers from main thread just before drawing +//Reason: void SceneComposer::DoUpdate takes 25% of time with OGL backend int mleft_pressed = 0; int mright_pressed = 0; @@ -63,14 +64,16 @@ std::string screenshotFileName = ""; int screenshotWidth = 100; int screenshotHeight = 100; bool needToMakeScreenshot = false; -int screenshotFrame = -1; -HDrawStage screenshotDS = nullptr; - static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos){ if (stopMouse) return; - ApiContainer * apiContainer = (ApiContainer *)glfwGetWindowUserPointer(window); + HApiContainer apiContainer = *(HApiContainer *)glfwGetWindowUserPointer(window); auto controllable = apiContainer->camera; + if (apiContainer->getConfig()->doubleCameraDebug && + apiContainer->getConfig()->controlSecondCamera && + apiContainer->debugCamera != nullptr) { + controllable = apiContainer->debugCamera; + } // if (!pointerIsLocked) { if (mleft_pressed == 1) { @@ -118,8 +121,13 @@ void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) static void onKey(GLFWwindow* window, int key, int scancode, int action, int mods) { if (stopKeyboard) return; - ApiContainer * apiContainer = (ApiContainer *)glfwGetWindowUserPointer(window); + HApiContainer apiContainer = *(HApiContainer *)glfwGetWindowUserPointer(window); auto controllable = apiContainer->camera; + if (apiContainer->getConfig()->doubleCameraDebug && + apiContainer->getConfig()->controlSecondCamera && + apiContainer->debugCamera != nullptr) { + controllable = apiContainer->debugCamera; + } if ( action == GLFW_PRESS) { switch (key) { @@ -187,8 +195,13 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { if (stopMouse) return; - ApiContainer * apiContainer = (ApiContainer *)glfwGetWindowUserPointer(window); + HApiContainer apiContainer = *(HApiContainer *)glfwGetWindowUserPointer(window); auto controllable = apiContainer->camera; + if (apiContainer->getConfig()->doubleCameraDebug && + apiContainer->getConfig()->controlSecondCamera && + apiContainer->debugCamera != nullptr) { + controllable = apiContainer->debugCamera; + } controllable->zoomInFromMouseScroll(-yoffset/2.0f); } @@ -268,64 +281,7 @@ void window_size_callback(GLFWwindow* window, int width, int height) void beforeCrash(void); -HDrawStage createSceneDrawStage(HFrameScenario sceneScenario, int width, int height, double deltaTime, bool isScreenshot, - ApiContainer &apiContainer, const std::shared_ptr ¤tScene) { - float farPlaneRendering = apiContainer.getConfig()->getFarPlane(); - float farPlaneCulling = apiContainer.getConfig()->getFarPlaneForCulling(); - - float nearPlane = 1.0; - float fov = toRadian(45.0); - - float canvasAspect = (float)width / (float)height; - HCameraMatrices cameraMatricesCulling = apiContainer.camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneCulling); - HCameraMatrices cameraMatricesRendering = apiContainer.camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneRendering); - //Frustum matrix with reversed Z - - bool isInfZSupported = apiContainer.camera->isCompatibleWithInfiniteZ(); - apiContainer.hDevice->setInvertZ(isInfZSupported); - if (isInfZSupported) - { - float f = 1.0f / tan(fov / 2.0f); - cameraMatricesRendering->perspectiveMat = mathfu::mat4( - f / canvasAspect, 0.0f, 0.0f, 0.0f, - 0.0f, f, 0.0f, 0.0f, - 0.0f, 0.0f, 1, -1.0f, - 0.0f, 0.0f, 1, 0.0f); - } - - if (apiContainer.hDevice->getIsVulkanAxisSystem() ) { - auto &perspectiveMatrix = cameraMatricesRendering->perspectiveMat; - - static const mathfu::mat4 vulkanMatrixFix2 = mathfu::mat4(1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, 1.0/2.0, 1/2.0, - 0, 0, 0, 1).Transpose(); - - perspectiveMatrix = vulkanMatrixFix2 * perspectiveMatrix; - } - - auto clearColor = apiContainer.getConfig()->getClearColor(); - - if (currentScene != nullptr) { - ViewPortDimensions dimensions = {{0, 0}, {width, height}}; - - HFrameBuffer fb = nullptr; - if (isScreenshot) { - fb = apiContainer.hDevice->createFrameBuffer(width, height, {ITextureFormat::itRGBA},ITextureFormat::itDepth32, 4); - } - - auto cullStage = sceneScenario->addCullStage(cameraMatricesCulling, currentScene); - auto updateStage = sceneScenario->addUpdateStage(cullStage, deltaTime*(1000.0f), cameraMatricesRendering); - HDrawStage sceneDrawStage = sceneScenario->addDrawStage(updateStage, currentScene, cameraMatricesRendering, {}, true, - dimensions, - true, clearColor, fb); - - return sceneDrawStage; - } - - return nullptr; -} extern "C" void my_function_to_handle_aborts(int signal_number) @@ -366,63 +322,6 @@ static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS * ExceptionInfo) } #endif -void saveScreenshot(const std::string& name, int width, int height, std::vector &rgbaBuff) { - FILE *fp = fopen(name.c_str(), "wb"); - if (!fp) { - return; - } - - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (!png_ptr) { - fclose(fp); - return; - } - - png_infop png_info; - if (!(png_info = png_create_info_struct(png_ptr))) { - png_destroy_write_struct(&png_ptr, nullptr); - return; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_write_struct(&png_ptr, nullptr); - return; - } - - png_init_io(png_ptr, fp); - - png_set_IHDR(png_ptr, png_info, width, height, 8, PNG_COLOR_TYPE_RGB, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, - PNG_FILTER_TYPE_DEFAULT); - - std::shared_ptr> data = std::make_shared>(width*height*3); - std::shared_ptr> rows = std::make_shared>(height); - - for (int i = 0; i < height; ++i) { - (*rows)[height - i - 1] = data->data() + (i*width*3); - for (int j = 0; j < width; ++j) { - int i1 = (i*width+j)*3; - int i2 = (i*width+j)*4; - - char r = rgbaBuff[++i2]; - char g = rgbaBuff[++i2]; - char b = rgbaBuff[++i2]; - char a = rgbaBuff[++i2]; - - (*data)[i1++] = a; - (*data)[i1++] = r; - (*data)[i1++] = g; - } - } - png_set_rows(png_ptr, png_info, rows->data()); - png_write_png(png_ptr, png_info, PNG_TRANSFORM_IDENTITY, nullptr); - png_write_end(png_ptr, png_info); - - - png_destroy_write_struct(&png_ptr, nullptr); - fclose(fp); -} - double currentFrame; double lastFrame; @@ -436,28 +335,45 @@ int main(){ #ifdef _WIN32 SetUnhandledExceptionFilter(windows_exception_handler); const bool SET_TERMINATE = std::set_terminate(beforeCrash); - //const bool SET_TERMINATE_UNEXP = std::set_unexpected(beforeCrash); +#if !defined(_MSC_VER) && !defined(_LIBCPP_VERSION) + const bool SET_TERMINATE_UNEXP = std::set_unexpected(beforeCrash); +#endif #endif + signal(SIGABRT, &my_function_to_handle_aborts); + signal(SIGSEGV, &my_function_to_handle_aborts); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); glfwInit(); + bool egl = false; +#ifdef WITH_GLESv2 + egl = true; +#endif + // std::string rendererName = "ogl2"; - std::string rendererName = "ogl3"; + std::string rendererName = "ogl3"; // std::string rendererName = "vulkan"; //FOR OGL if (rendererName == "ogl3") { glfwWindowHint(GLFW_SAMPLES, 4); // 4x antialiasing - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // We want OpenGL 3.3 - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + if (egl) { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // We want OpenGL ES 3.1 + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + } else { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // We want OpenGL 3.3 + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //We don't want the old OpenGL + + } // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //We don't want the old OpenGL glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); //We don't want the old OpenGL } else if ( rendererName == "ogl2") { glfwWindowHint(GLFW_SAMPLES, 4); // 4x antialiasing @@ -465,6 +381,9 @@ int main(){ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); //We don't want the old OpenGL + + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); } else if (rendererName == "vulkan"){ //For Vulkan glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); @@ -500,145 +419,19 @@ int main(){ } //Open Sql storage - - CSqliteDB *sqliteDB = new CSqliteDB("./export.db3"); - - - ApiContainer apiContainer; - RequestProcessor *processor = nullptr; -// { - const char * url = "https://wow.tools/casc/file/fname?buildconfig=d40df72310590c634855b413870d97d2&cdnconfig=546b178da14301cf4749c1c772bb11c1&filename="; - const char * urlFileId = "https://wow.tools/casc/file/fdid?buildconfig=d40df72310590c634855b413870d97d2&cdnconfig=546b178da14301cf4749c1c772bb11c1&filename=data&filedataid="; -// -//Classics -// const char * url = "https://wow.tools/casc/file/fname?buildconfig=bf24b9d67a4a9c7cc0ce59d63df459a8&cdnconfig=2b5b60cdbcd07c5f88c23385069ead40&filename="; -// const char * urlFileId = "https://wow.tools/casc/file/fdid?buildconfig=bf24b9d67a4a9c7cc0ce59d63df459a8&cdnconfig=2b5b60cdbcd07c5f88c23385069ead40&filename=data&filedataid="; -// processor = new HttpZipRequestProcessor(url); -//// processor = new ZipRequestProcessor(filePath); -//// processor = new MpqRequestProcessor(filePath); -// processor = new HttpRequestProcessor(url, urlFileId); - processor = new CascRequestProcessor("e:/games/wow beta/World of Warcraft Beta/"); -//// processor->setThreaded(false); -//// - processor->setThreaded(true); - apiContainer.cacheStorage = std::make_shared(processor); - processor->setFileRequester(apiContainer.cacheStorage.get()); - -// } + HApiContainer apiContainer = std::make_shared(); + //Create device auto hdevice = IDeviceFactory::createDevice(rendererName, &callback); + apiContainer->databaseHandler = std::make_shared() ; + apiContainer->hDevice = hdevice; + apiContainer->camera = std::make_shared(); - std::shared_ptr currentScene = nullptr; - - apiContainer.databaseHandler = sqliteDB; - apiContainer.hDevice = hdevice; - apiContainer.camera = std::make_shared(); - - SceneComposer sceneComposer = SceneComposer(&apiContainer); + SceneComposer sceneComposer = SceneComposer(apiContainer); // WoWScene *scene = createWoWScene(testConf, storage, sqliteDB, device, canvWidth, canvHeight); - std::shared_ptr frontendUI = std::make_shared(&apiContainer); - frontendUI->overrideCascOpened(true); - frontendUI->setOpenCascStorageCallback([&processor, &apiContainer, &sceneComposer](std::string cascPath) -> bool { - CascRequestProcessor *newProcessor = nullptr; - std::shared_ptr newStorage = nullptr; - try { - newProcessor = new CascRequestProcessor(cascPath.c_str()); - newStorage = std::make_shared(newProcessor); - newProcessor->setThreaded(true); - newProcessor->setFileRequester(newStorage.get()); - } catch (...){ - delete newProcessor; - return false; - }; - - apiContainer.cacheStorage = newStorage; - processor = newProcessor; - - return true; - }); - frontendUI->setOpenSceneByfdidCallback([¤tScene, &apiContainer](int mapId, int wdtFileId, float x, float y, float z) { -// scene->setSceneWithFileDataId(1, 113992, -1); //Ironforge - if (apiContainer.cacheStorage) { -// storage->actuallDropCache(); - } - - currentScene = std::make_shared(&apiContainer, mapId, wdtFileId); - apiContainer.camera = std::make_shared(); - apiContainer.camera->setCameraPos(x, y, z); -// scene->setMap(mapId, wdtFileId, x, y, z); //Ironforge - }); - frontendUI->setOpenWMOSceneByfdidCallback([¤tScene, &apiContainer](int wmoFDid) { - currentScene = std::make_shared(&apiContainer, wmoFDid); - apiContainer.camera->setCameraPos(0, 0, 0); - }); - frontendUI->setOpenM2SceneByfdidCallback([¤tScene, &apiContainer](int m2FDid, std::vector &replacementTextureIds) { - currentScene = std::make_shared(&apiContainer, m2FDid, -1); - currentScene->setReplaceTextureArray(replacementTextureIds); - - - apiContainer.camera = std::make_shared(); - apiContainer.getConfig()->setBCLightHack(false); -// - apiContainer.camera->setCameraPos(0, 0, 0); - }); - frontendUI->setOpenM2SceneByFilenameCallback([¤tScene, &apiContainer](std::string m2FileName, std::vector &replacementTextureIds) { - currentScene = std::make_shared(&apiContainer, m2FileName, -1); - currentScene->setReplaceTextureArray(replacementTextureIds); - - apiContainer.camera = std::make_shared(); - apiContainer.camera->setCameraPos(0, 0, 0); - }); - - - frontendUI->setUnloadScene([&apiContainer, ¤tScene]()->void { - if (apiContainer.cacheStorage) { - apiContainer.cacheStorage->actuallDropCache(); - } - currentScene = nullptr; - //scene->setSceneWithFileDataId(-1, 0, -1); - }); - - frontendUI->setGetCameraNum([&apiContainer, ¤tScene]()-> int { - if (currentScene != nullptr) { - return currentScene->getCameraNum(); - } - - return 0; - }); - frontendUI->setSelectNewCamera([&apiContainer, ¤tScene](int cameraNum)-> bool { - if (currentScene == nullptr) return false; - - auto newCamera = currentScene->createCamera(cameraNum); - if (newCamera == nullptr) { - apiContainer.camera = std::make_shared(); - return false; - } - - apiContainer.camera = newCamera; - return true; - }); - frontendUI->setResetAnimation([¤tScene]() -> void { - currentScene->resetAnimation(); - }); - - frontendUI->setGetCameraPos([&apiContainer](float &cameraX,float &cameraY,float &cameraZ) -> void { - float currentCameraPos[4] = {0,0,0,0}; - apiContainer.camera->getCameraPosition(¤tCameraPos[0]); - cameraX = currentCameraPos[0]; - cameraY = currentCameraPos[1]; - cameraZ = currentCameraPos[2]; - }); - - frontendUI->setMakeScreenshotCallback([&apiContainer](std::string fileName, int width, int height) -> void { - screenshotWidth = width; - screenshotHeight = height; - screenshotFileName = fileName; - - needToMakeScreenshot = true; - }); - + std::shared_ptr frontendUI = std::make_shared(apiContainer, nullptr); glfwSetWindowUserPointer(window, &apiContainer); glfwSetKeyCallback(window, onKey); @@ -648,11 +441,20 @@ int main(){ glfwSetWindowSizeLimits( window, canvWidth, canvHeight, GLFW_DONT_CARE, GLFW_DONT_CARE); glfwSetMouseButtonCallback( window, mouse_button_callback); + GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor(); + float xscale, yscale; + glfwGetMonitorContentScale(primaryMonitor, &xscale, &yscale); + + frontendUI->setUIScale(std::max(xscale, yscale)); + + + //This has to be called after setting all callbacks specific to this app. //ImGUI takes care of previous callbacks and calls them before applying it's own logic over data //Otherwise keys like backspace, delete etc wont work frontendUI->initImgui(window); + frontendUI->createDefaultprocessor(); glfwSwapInterval(0); //try { @@ -667,82 +469,27 @@ int main(){ // Render scene currentFrame = glfwGetTime(); // seconds double deltaTime = currentFrame - lastFrame; + { + auto processor = frontendUI->getProcessor(); + if (frontendUI->getProcessor()) { - if (processor) { - if (!processor->getThreaded()) { - processor->processRequests(false); + if (!processor->getThreaded()) { + processor->processRequests(false); + } } - processor->processResults(10); } -// if (windowSizeChanged) { -// scene->setScreenSize(canvWidth, canvHeight); -// windowSizeChanged = false; -// } - -// scene->draw((deltaTime*(1000.0f))); //miliseconds - - apiContainer.camera->tick(deltaTime*(1000.0f)); - - if (screenshotDS != nullptr) { - if (screenshotFrame + 5 <= apiContainer.hDevice->getFrameNumber()) { - std::vector buffer = std::vector(screenshotWidth*screenshotHeight*4+1); - - screenshotDS->target->readRGBAPixels( 0, 0, screenshotWidth, screenshotHeight, buffer.data()); - saveScreenshot(screenshotFileName, screenshotWidth, screenshotHeight, buffer); - - screenshotDS = nullptr; - } + apiContainer->camera->tick(deltaTime*(1000.0f)); + if (apiContainer->debugCamera != nullptr) { + apiContainer->debugCamera->tick(deltaTime * (1000.0f)); } - HFrameScenario sceneScenario = std::make_shared(); - std::vector uiDependecies = {}; - //DrawStage for screenshot // needToMakeScreenshot = true; - if (needToMakeScreenshot) - { - auto drawStage = createSceneDrawStage(sceneScenario, screenshotWidth, screenshotHeight, deltaTime, true, apiContainer, - currentScene); - if (drawStage != nullptr) { - uiDependecies.push_back(drawStage); - screenshotDS = drawStage; - screenshotFrame = apiContainer.hDevice->getFrameNumber(); - } - needToMakeScreenshot = false; - } - - //DrawStage for current frame - bool clearOnUi = true; - { - auto drawStage = createSceneDrawStage(sceneScenario, canvWidth, canvHeight, deltaTime, false, apiContainer, - currentScene); - if (drawStage != nullptr) { - uiDependecies.push_back(drawStage); - clearOnUi = false; - } - } - //DrawStage for UI - { - ViewPortDimensions dimension = { - {0, 0}, - {canvWidth, canvHeight} - }; - auto clearColor = apiContainer.getConfig()->getClearColor(); - - auto uiCullStage = sceneScenario->addCullStage(nullptr, frontendUI); - auto uiUpdateStage = sceneScenario->addUpdateStage(uiCullStage, deltaTime * (1000.0f), nullptr); - HDrawStage frontUIDrawStage = sceneScenario->addDrawStage(uiUpdateStage, frontendUI, nullptr, uiDependecies, true, - dimension, clearOnUi, clearColor, nullptr); + if (apiContainer->getConfig()->pauseAnimation) { + deltaTime = 0.0; } - // auto updateResult = scene->cull(camera)->update(camera); -// SceneComposer::All({ -// updateResult->render(camera)->toFB(frameBuffer, viewPortDims), -// updateResult->render(cameraDebug)->toFB(frameBuffer, viewPortDims); -// }).then([]{ -// frontendUI->bind("", frameBuffer.getTexture()) -// SceneComposer.renderToScreen() -// }); + auto sceneScenario = frontendUI->createFrameScenario(canvWidth, canvHeight, deltaTime); sceneComposer.draw(sceneScenario); @@ -750,7 +497,7 @@ int main(){ lastFrame = currentFrame; if (currentDeltaAfterDraw < 5.0) { using namespace std::chrono_literals; - std::this_thread::sleep_for(std::chrono::milliseconds((int)(5.0 - currentDeltaAfterDraw))); + std::this_thread::sleep_for(std::chrono::milliseconds((int)(1.0))); } if (rendererName == "ogl3" || rendererName == "ogl2") { diff --git a/src/minimapGenerator/entities.h b/src/minimapGenerator/entities.h new file mode 100644 index 000000000..b3696d063 --- /dev/null +++ b/src/minimapGenerator/entities.h @@ -0,0 +1,49 @@ +// +// Created by Deamon on 1/22/2021. +// + +#ifndef AWEBWOWVIEWERCPP_ENTITIES_H +#define AWEBWOWVIEWERCPP_ENTITIES_H + +#include +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#include + +enum class ScenarioOrientation { + soTopDownOrtho, + so45DegreeTick0, + so45DegreeTick1, + so45DegreeTick2, + so45DegreeTick3, +}; +enum class EMGMode { + eNone, + eBoundingBoxCalculation, + eScreenshotGeneration, + ePreview, +}; + +struct ScenarioDef { + int id; + int mapId; + std::string name; + + mathfu::vec4 closeOceanColor; + + mathfu::vec2 minWowWorldCoord; + mathfu::vec2 maxWowWorldCoord; + + int imageHeight; + int imageWidth; + + float zoom = 1.0f; + + ScenarioOrientation orientation = ScenarioOrientation::so45DegreeTick0; + + std::string folderToSave; +}; + + +#endif //AWEBWOWVIEWERCPP_ENTITIES_H diff --git a/src/minimapGenerator/minimapGenerator.cpp b/src/minimapGenerator/minimapGenerator.cpp new file mode 100644 index 000000000..af92a91a3 --- /dev/null +++ b/src/minimapGenerator/minimapGenerator.cpp @@ -0,0 +1,851 @@ +// +// Created by Deamon on 9/8/2020. +// +//#include "../../wowViewerLib/src/engine/SceneScenario.h" +//#include +#include "../../3rdparty/filesystem_impl/include/ghc/filesystem.hpp" +#include "minimapGenerator.h" +#include "../../wowViewerLib/src/engine/algorithms/mathHelper.h" +#include "../../wowViewerLib/src/engine/objects/scenes/map.h" +#include "../persistance/RequestProcessor.h" +#include "../screenshots/screenshotMaker.h" +#include "../../wowViewerLib/src/engine/camera/firstPersonOrthoStaticCamera.h" +#include "../../wowViewerLib/src/engine/camera/firstPersonOrthoStaticTopDownCamera.h" + + +MinimapGenerator::MinimapGenerator(HWoWFilesCacheStorage cacheStorage, const HGDevice &hDevice, + HRequestProcessor processor, std::shared_ptr dbhandler, + HADTBoundingBoxHolder boundingBoxHolder) { + m_apiContainer = std::make_shared(); + + m_apiContainer->hDevice = hDevice; + m_apiContainer->cacheStorage = cacheStorage; + m_apiContainer->camera = std::make_shared(); + m_apiContainer->databaseHandler = dbhandler; + + m_processor = processor; + m_boundingBoxHolder = boundingBoxHolder; + + auto config = m_apiContainer->getConfig(); + config->glowSource = EParameterSource::eConfig; + config->globalFog = EParameterSource::eConfig; + config->skyParams = EParameterSource::eConfig; + config->globalLighting = EParameterSource::eConfig; + config->waterColorParams = EParameterSource::eConfig; + config->useMinimapWaterColor = false; + config->useCloseRiverColorForDB = true; +// config->adtTTLWithoutUpdate = 500; //0.5 sec + config->adtFreeStrategy = EFreeStrategy::eFrameBase; // + + config->exteriorAmbientColor = mathfu::vec4(0.5f,0.5f,0.5f,1.0);; + config->exteriorHorizontAmbientColor = mathfu::vec4(0.5f,0.5f,0.5f,1.0); + config->exteriorGroundAmbientColor = mathfu::vec4(0.5f,0.5f,0.5f,1.0); + config->exteriorDirectColor = mathfu::vec4(0.5f,0.5f,0.5f,1.0); + + config->adtSpecMult = 0.0; + + config->disableFog = true; + config->renderSkyDom = false; + config->currentGlow = 0; + + config->farPlane = 10000; + config->farPlaneForCulling = 10000; +} +void MinimapGenerator::startScenarios(std::vector &scenarioList) { + m_mgMode = EMGMode::eScreenshotGeneration; + + setZoom(1.0f); + + scenarioListToProcess = {}; + for (auto &scenario : scenarioList) { +// auto boundingBoxHolder = scenario.boundingBoxHolder; +// if (boundingBoxHolder == nullptr && scenario.doBoundingBoxPreCalc) { +// scenario.boundingBoxHolder = boundingBoxHolder; +// } + + scenarioListToProcess.push_back(scenario); + +// if (scenario.doBoundingBoxPreCalc) { +// auto scenarionCopy = scenario; +// scenarionCopy.mode = EMGMode::eBoundingBoxCalculation; +// scenarionCopy.orientation = ScenarioOrientation::soTopDownOrtho; +// +// scenarionCopy.minWowWorldCoord = mathfu::vec2(-MathHelper::TILESIZE * 32, -MathHelper::TILESIZE * 32); +// scenarionCopy.maxWowWorldCoord = mathfu::vec2(MathHelper::TILESIZE * 32, MathHelper::TILESIZE * 32); +// +// scenarionCopy.doBoundingBoxPreCalc = false; +// scenarionCopy.zoom = 1.0f; +// scenarionCopy.imageWidth = 512; +// scenarionCopy.imageHeight = 512; +// scenarionCopy.boundingBoxHolder = boundingBoxHolder; +// +// scenarioListToProcess.push_back(scenarionCopy); +// } + } + + startNextScenario(); +} + +void MinimapGenerator::startPreview(ScenarioDef &scenarioDef) { + m_mgMode = EMGMode::ePreview; + currentScenario = scenarioDef; + + setupScenarioData(); +} +void MinimapGenerator::stopPreview() { + m_mgMode = EMGMode::eNone; +} +void MinimapGenerator::startBoundingBoxCalc(ScenarioDef &scenarioDef) { + m_mgMode = EMGMode::eBoundingBoxCalculation; + currentScenario = scenarioDef; + + currentScenario.orientation = ScenarioOrientation::soTopDownOrtho; +// currentScenario.minWowWorldCoord = mathfu::vec2(-MathHelper::TILESIZE * 32, -MathHelper::TILESIZE * 32); + currentScenario.minWowWorldCoord = mathfu::vec2(-MathHelper::TILESIZE * 32, -MathHelper::TILESIZE * 32); + currentScenario.maxWowWorldCoord = mathfu::vec2(MathHelper::TILESIZE * 32, MathHelper::TILESIZE * 32); + currentScenario.zoom = 1.0f; + + setupScenarioData(); +} +void MinimapGenerator::stopBoundingBoxCalc() { + m_mgMode = EMGMode::eNone; +} + + +void MinimapGenerator::startNextScenario() { + if (scenarioListToProcess.size() == 0) { + m_mgMode = EMGMode::eNone; + return; + } + + currentScenario = scenarioListToProcess.back(); + + scenarioListToProcess.pop_back(); + + setupScenarioData(); +} + + +void MinimapGenerator::setupScenarioData() { + m_zoom = currentScenario.zoom; + + mandatoryADTMap.resize(0); + + stackOfCullStages = {nullptr, nullptr, nullptr, nullptr}; + + if (currentScenario.orientation == ScenarioOrientation::soTopDownOrtho) { + m_apiContainer->camera = std::make_shared(); + } else { + m_apiContainer->camera = std::make_shared(); + } + + MapRecord mapRecord; + if (!m_apiContainer->databaseHandler->getMapById(currentScenario.mapId, mapRecord)) { + std::cout << "Couldnt get data for mapId " << currentScenario.mapId << std::endl; + startNextScenario(); + return; + } + + m_currentScene = std::make_shared(m_apiContainer, mapRecord.ID, mapRecord.WdtFileID); + if (m_mgMode == EMGMode::eScreenshotGeneration) { + m_currentScene->setAdtBoundingBoxHolder(m_boundingBoxHolder); + } + + auto config = m_apiContainer->getConfig(); + config->closeOceanColor = currentScenario.closeOceanColor;//{0.0671968088, 0.294095874, 0.348881632, 0}; + config->closeRiverColor = {0.345206976, 0.329288304, 0.270450264, 0}; + + setMinMaxXYWidhtHeight(currentScenario.minWowWorldCoord, currentScenario.maxWowWorldCoord); + + m_x = 0; + //To make AABB work for sure! + m_y = m_chunkHeight - 1; + + m_width = currentScenario.imageWidth; + m_height = currentScenario.imageHeight; + + //Assign mandatory adt for every cell on the screen + if (m_mgMode == EMGMode::eScreenshotGeneration && m_boundingBoxHolder != nullptr) { + //currentScenario.boundingBoxHolder + mandatoryADTMap.resize(m_chunkWidth); + for (int i = 0; i < m_chunkWidth; i++) { + mandatoryADTMap[i].resize(m_chunkHeight); + } + + for (int adt_x = 0; adt_x < 64; adt_x++) { + for (int adt_y = 0; adt_y < 64; adt_y++) { + auto aaBB = (*m_boundingBoxHolder)[adt_x][adt_y]; + //Project aaBB into screenSpace coordinates + std::array minMaxX = {aaBB.min.x, aaBB.min.x}; + std::array minMaxY = {aaBB.min.y, aaBB.min.y}; + std::array minMaxZ = {aaBB.min.z, aaBB.min.z}; + std::vector corners; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + corners.push_back( + mathfu::vec4( + minMaxX[i], + minMaxY[j], + minMaxZ[k], + 1.0f + ) + ); + } + } + } + + mathfu::mat4 viewProj = genTempProjectMatrix(); + for (int i = 0; i < corners.size(); i++) { + corners[i] = viewProj * corners[i]; + corners[i] = corners[i] * (1.0f / corners[i].w); + } + + std::array minMaxScreenX = {20000, -20000}; + std::array minMaxScreenY = {20000, -20000}; + std::array minMaxScreenZ = {20000, -20000}; + + for (int i = 0; i < corners.size(); i++) { + minMaxScreenX[0] = std::min(corners[i].x, minMaxScreenX[0]); + minMaxScreenX[1] = std::max(corners[i].x, minMaxScreenX[1]); + + minMaxScreenY[0] = std::min(corners[i].y, minMaxScreenY[0]); + minMaxScreenY[1] = std::max(corners[i].y, minMaxScreenY[1]); + + minMaxScreenZ[0] = std::min(corners[i].z, minMaxScreenZ[0]); + minMaxScreenZ[1] = std::max(corners[i].z, minMaxScreenZ[1]); + } + + //Add adt to all occupied cells + for (int i = std::floor(minMaxScreenX[0]); i < std::ceil((minMaxScreenX[1] - minMaxScreenX[0]) / 2.0f); i++) { + for (int j = std::floor(minMaxScreenY[0]); j < std::ceil((minMaxScreenY[1] - minMaxScreenY[0]) / 2.0f); j++) { + if (i < m_chunkStartX) + return; + if (j < m_chunkStartY) + return; + if (i > m_chunkStartX + m_chunkWidth) + return; + if (i > m_chunkStartY + m_chunkHeight) + return; + + std::array adtCoord = {static_cast(adt_x), static_cast(adt_y)}; + mandatoryADTMap[i - m_chunkStartX][j - m_chunkStartY].push_back(adtCoord); + } + } + } + } + } + + prepearCandidate = false; + setupCameraData(); +} + + +void +MinimapGenerator::setMinMaxXYWidhtHeight(const mathfu::vec2 &minWowWorldCoord, const mathfu::vec2 &maxWowWorldCoord) { + calcXtoYCoef(); + + bool useZCoord = false; + float minZ = 20000; float maxZ = -20000; + if (m_boundingBoxHolder && m_mgMode != EMGMode::eBoundingBoxCalculation) { + std::cout << "Using bounding box data to calc minMax Z" << std::endl; + useZCoord = true; + + for (int adt_y = worldCoordinateToAdtIndex(minWowWorldCoord.x); + adt_y < worldCoordinateToAdtIndex(maxWowWorldCoord.x); adt_y++) { + + for (int adt_x = worldCoordinateToAdtIndex(minWowWorldCoord.y); + adt_x < worldCoordinateToAdtIndex(maxWowWorldCoord.y); adt_x++) { + + auto adtMinZ = (*m_boundingBoxHolder)[adt_x][adt_y].min.z; + if (adtMinZ < 32*MathHelper::TILESIZE) + minZ = std::min(adtMinZ, minZ); + + auto adtMaxZ = (*m_boundingBoxHolder)[adt_x][adt_y].max.z; + if (adtMaxZ > -32*MathHelper::TILESIZE) { + maxZ = std::max((*m_boundingBoxHolder)[adt_x][adt_y].max.z, maxZ); + } + } + } + } + std::array minMaxX = {minWowWorldCoord.x, maxWowWorldCoord.x}; + std::array minMaxY = {minWowWorldCoord.y, maxWowWorldCoord.y}; + std::array minMaxZ = {minZ, maxZ}; + + mathfu::mat4 viewProj = genTempProjectMatrix(); + + std::vector corners; + if (useZCoord) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + corners.push_back( + mathfu::vec4( + minMaxX[i], + minMaxY[j], + minMaxZ[k], + 1.0f + ) + ); + } + } + } + } else { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + corners.push_back( + mathfu::vec4( + minMaxX[i], + minMaxY[j], + 0, + 1.0f + ) + ); + } + } + } + + for (int i = 0; i < corners.size(); i++) { + corners[i] = viewProj * corners[i]; + corners[i] = corners[i] * (1.0f / corners[i].w); + } + + std::array minMaxScreenX = {20000, -20000}; + std::array minMaxScreenY = {20000, -20000}; + std::array minMaxScreenZ = {20000, -20000}; + + for (int i = 0; i < corners.size(); i++) { + minMaxScreenX[0] = std::min(corners[i].x, minMaxScreenX[0]); + minMaxScreenX[1] = std::max(corners[i].x, minMaxScreenX[1]); + + minMaxScreenY[0] = std::min(corners[i].y, minMaxScreenY[0]); + minMaxScreenY[1] = std::max(corners[i].y, minMaxScreenY[1]); + + minMaxScreenZ[0] = std::min(corners[i].z, minMaxScreenZ[0]); + minMaxScreenZ[1] = std::max(corners[i].z, minMaxScreenZ[1]); + } + + + mathfu::vec2 minOrthoMapCoord = mathfu::vec2( + minMaxScreenX[0], + minMaxScreenY[0] + ); + mathfu::vec2 maxOrthoMapCoord = mathfu::vec2( + minMaxScreenX[1], + minMaxScreenY[1] + ); + + std::cout <<"Orient = " << (int)currentScenario.orientation << std::endl; + + m_chunkStartX = std::floor((minOrthoMapCoord.x) / 2.0f) ; + m_chunkStartY = std::floor((minOrthoMapCoord.y) / 2.0f); + m_chunkWidth = std::ceil(((maxOrthoMapCoord.x - minOrthoMapCoord.x) / 2.0f)) ; + m_chunkHeight = std::ceil(((maxOrthoMapCoord.y - minOrthoMapCoord.y)/ 2.0f)); + + std::cout << "m_chunkStartX = " << m_chunkStartX << " m_chunkWidth = " << m_chunkWidth + << " m_chunkStartY = " << m_chunkStartY << " m_chunkHeight = " << m_chunkHeight << std::endl; +} + +mathfu::mat4 MinimapGenerator::genTempProjectMatrix() { + auto orthoProjection = getOrthoMatrix(); + + mathfu::vec3 lookAtPoint = mathfu::vec3(0, 0, 0); + lookAtPoint = lookAtPoint; + //std::cout << "lookAtPoint = (" << lookAtPoint.x << ", " << lookAtPoint.y << ", " << lookAtPoint.z << ") " << std::endl; + + mathfu::vec3 lookAtVec3 = getLookAtVec3(); + lookAtPoint -= (4000.0f*lookAtVec3); + + mathfu::vec3 cameraPos = lookAtPoint-(2000.0f*lookAtVec3); + + std::shared_ptr tempCamera; + if (currentScenario.orientation == ScenarioOrientation::soTopDownOrtho) { + tempCamera = std::make_shared(); + } else { + tempCamera = std::make_shared(); + } + + tempCamera->setCameraPos(cameraPos.x, cameraPos.y, cameraPos.z); + tempCamera->setCameraLookAt(lookAtPoint.x, lookAtPoint.y, lookAtPoint.z); + tempCamera->tick(0); + + float nearPlane = 1.0; + float fov = toRadian(45.0); + + float canvasAspect = (float) m_width / (float) m_height; + float farPlaneRendering = m_apiContainer->getConfig()->farPlane; + + HCameraMatrices cameraMatricesRendering = tempCamera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneRendering); + auto viewProj = orthoProjection * cameraMatricesRendering->lookAtMat; + return viewProj; +} + +void MinimapGenerator::calcXtoYCoef() { + + //Code that tries to calc the step on y and x + { + auto orthoProjection = getOrthoMatrix(); + + // + const mathfu::vec4 vec4Top = {1, 1, 1, 1}; + const mathfu::vec4 vec4TopBottom = {1, -1, 1, 1}; + const mathfu::vec4 vec4Bottom = {-1, -1, 1, 1}; + const mathfu::vec4 vec4BottomTop = {-1, 1, 1, 1}; + + + //Round1 + { + auto viewProj = genTempProjectMatrix(); + + mathfu::vec4 vec4TopTrans = viewProj.Inverse() * vec4Top; + vec4TopTrans *= (1.0f / vec4TopTrans.w); + + mathfu::vec4 vec4TopBottomTrans = viewProj.Inverse() * vec4TopBottom; + vec4TopBottomTrans *= (1.0f / vec4TopBottomTrans.w); + + mathfu::vec4 vec4BotTrans = viewProj.Inverse() * vec4Bottom; + vec4BotTrans *= (1.0f / vec4BotTrans.w); + + mathfu::vec4 vec4BottomTopTrans = viewProj.Inverse() * vec4BottomTop; + vec4BottomTopTrans *= (1.0f / vec4BottomTopTrans.w); + + + float minX = std::min(std::min(vec4TopTrans.x, vec4TopBottomTrans.x), + std::min(vec4BotTrans.x, vec4BottomTopTrans.x)); + float minY = std::min(std::min(vec4TopTrans.y, vec4TopBottomTrans.y), + std::min(vec4BotTrans.y, vec4BottomTopTrans.y)); + float maxX = std::max(std::max(vec4TopTrans.x, vec4TopBottomTrans.x), + std::max(vec4BotTrans.x, vec4BottomTopTrans.x)); + float maxY = std::max(std::max(vec4TopTrans.y, vec4TopBottomTrans.y), + std::max(vec4BotTrans.y, vec4BottomTopTrans.y)); + + std::cout << "vec4TopTrans1 = (" << vec4TopTrans[0] << " " << vec4TopTrans[1] << " " << vec4TopTrans[2] + << std::endl; + std::cout << "vec4TopBottomTrans1 = (" << vec4TopBottomTrans[0] << " " << vec4TopBottomTrans[1] << " " << vec4TopBottomTrans[2] + << std::endl; + std::cout << "vec4BotTrans1 = (" << vec4BotTrans[0] << " " << vec4BotTrans[1] << " " << vec4BotTrans[2] + << std::endl; + std::cout << "vec4BottomTopTrans1 = (" << vec4BottomTopTrans[0] << " " << vec4BottomTopTrans[1] << " " << vec4BottomTopTrans[2] + << std::endl; + + } + + } +} +void MinimapGenerator::setupCameraData() { + //Do reverse transform here + auto min1 = mathfu::vec4( + (m_chunkStartX + m_x) * 2.0f, + (m_chunkStartY + m_chunkHeight - (m_y - 1)) * 2.0f, + 0, + 1 + ); + + auto min2 = mathfu::vec4( + (m_chunkStartX + m_x) * 2.0f, + (m_chunkStartY + m_chunkHeight - (m_y - 1)) * 2.0f, + 1, + 1 + ); + + auto max1 = mathfu::vec4( + (m_chunkStartX + m_x + 1) * 2.0f, + (m_chunkStartY + m_chunkHeight - m_y) * 2.0f, + 0, + 1 + ); + + auto max2 = mathfu::vec4( + (m_chunkStartX + m_x + 1) * 2.0f, + (m_chunkStartY + m_chunkHeight - m_y) * 2.0f, + 1, + 1 + ); + + auto tempViewProjMatInv = genTempProjectMatrix().Inverse(); + + min1 = tempViewProjMatInv * min1; + min1 = min1 * ( 1.0f / min1.w); + + min2 = tempViewProjMatInv * min2; + min2 = min2 * ( 1.0f / min2.w); + + max1 = tempViewProjMatInv * max1; + max1 = max1 * ( 1.0f / max1.w); + + max2 = tempViewProjMatInv * max2; + max2 = max2 * ( 1.0f / max2.w); + + //Reproject to get proper zero in z + float alphaMin = -min2.z / (min1.z - min2.z); + auto min = (min1 - min2) * alphaMin + min2; + + float alphaMax = -max2.z / (max1.z - max2.z); + auto max = (max1 - max2) * alphaMax + max2; + + + if (mandatoryADTMap.size() > 0) { + auto adtVec = mandatoryADTMap[m_x][m_y]; + m_currentScene->setMandatoryADTs(adtVec); + } + + std::cout << "(Debug) m_x = " << m_x << " m_y = " << m_y << + ", x = " << (max.x + min.x) * 0.5f << + ", y = " << (max.y + min.y) * 0.5f << std::endl; + + setLookAtPoint( + (max.x + min.x) * 0.5, + (max.y + min.y) * 0.5 + ); +} + +void MinimapGenerator::setZoom(float zoom) { + m_zoom = zoom; +} + +void MinimapGenerator::setLookAtPoint(float x, float y) { + mathfu::vec3 lookAtPoint = mathfu::vec3(x, y, 0); + + lookAtPoint = lookAtPoint; + //std::cout << "lookAtPoint = (" << lookAtPoint.x << ", " << lookAtPoint.y << ", " << lookAtPoint.z << ") " << std::endl; + + mathfu::vec3 lookAtVec3 = getLookAtVec3(); + lookAtPoint -= (7000.0f*lookAtVec3); + + mathfu::vec3 cameraPos = lookAtPoint-(3000.0f*lookAtVec3); +// std::cout << "cameraPos = (" << cameraPos.x << ", " << cameraPos.y << ", " << cameraPos.z << ") " << std::endl; + + m_apiContainer->camera->setCameraPos( + cameraPos.x, cameraPos.y, cameraPos.z + ); + m_apiContainer->camera->setCameraLookAt( + lookAtPoint.x, lookAtPoint.y, lookAtPoint.z + ); + m_apiContainer->camera->tick(0); +} + + +const int waitQueueLen = 5; +void MinimapGenerator::process() { + if (m_processor->completedAllJobs() && !m_apiContainer->hDevice->wasTexturesUploaded()) { + framesReady++; + } else { + framesReady = 0; + prepearCandidate = false; + m_candidateDS = nullptr; + m_candidateCS = nullptr; + return; + } + + if (framesReady < waitQueueLen) { + return; + } + + if (!prepearCandidate) { + prepearCandidate = true; + m_candidateDS = nullptr; + m_candidateCS = nullptr; + framesReady = 0; + return; + } + + + auto lastFrameIt = m_candidateDS; + auto lastFrameCull = m_candidateCS; + m_candidateDS = nullptr; + m_candidateCS = nullptr; + prepearCandidate = false; + framesReady = 0; + + if (m_mgMode == EMGMode::eBoundingBoxCalculation) { + int adt_x = m_x; + int adt_y = m_y; + + vec2 minAdt ={ + AdtIndexToWorldCoordinate(adt_y+1), + AdtIndexToWorldCoordinate(adt_x+1) + }; + vec2 maxAdt = { + AdtIndexToWorldCoordinate(adt_y), + AdtIndexToWorldCoordinate(adt_x), + }; + CAaBox adtBox2d = { + mathfu::vec3_packed(mathfu::vec3(minAdt.x, minAdt.y, 0)), + mathfu::vec3_packed(mathfu::vec3(maxAdt.x, maxAdt.y, 0)) + }; + + mathfu::vec3 minCoord = mathfu::vec3(20000, 20000, 20000); + mathfu::vec3 maxCoord = mathfu::vec3(-20000, -20000, -20000); + + for (auto &m2Object: lastFrameCull->m2Array) { + auto objBB = m2Object->getAABB(); + + if (!MathHelper::isAabbIntersect2d(objBB, adtBox2d)) continue; + + minCoord = mathfu::vec3( + std::min(minCoord.x, objBB.min.x), + std::min(minCoord.y, objBB.min.y), + std::min(minCoord.z, objBB.min.z) + ); + maxCoord = mathfu::vec3( + std::max(maxCoord.x, objBB.max.x), + std::max(maxCoord.y, objBB.max.y), + std::max(maxCoord.z, objBB.max.z) + ); + } + + for (auto &wmoObject: lastFrameCull->wmoArray) { + auto objBB = wmoObject->getAABB(); + if (!MathHelper::isAabbIntersect2d(objBB, adtBox2d)) continue; + + minCoord = mathfu::vec3( + std::min(minCoord.x, objBB.min.x), + std::min(minCoord.y, objBB.min.y), + std::min(minCoord.z, objBB.min.z) + ); + maxCoord = mathfu::vec3( + std::max(maxCoord.x, objBB.max.x), + std::max(maxCoord.y, objBB.max.y), + std::max(maxCoord.z, objBB.max.z) + ); + } + + + for (auto &adtObjectRes: lastFrameCull->adtArray) { + auto adtObj = adtObjectRes->adtObject; + if (adtObj->getAdtX() != adt_x || adtObj->getAdtY() != adt_y) { + std::cout << "skipping adtObj( " << + adtObj->getAdtX() << "," << adtObj->getAdtY() << " " + ") for adt(" << adt_x << ", " << adt_y << ")" << std::endl; + + continue; + } + + auto objBB = adtObj->calcAABB(); + minCoord = mathfu::vec3( + std::min(minCoord.x, objBB.min.x), + std::min(minCoord.y, objBB.min.y), + std::min(minCoord.z, objBB.min.z) + ); + maxCoord = mathfu::vec3( + std::max(maxCoord.x, objBB.max.x), + std::max(maxCoord.y, objBB.max.y), + std::max(maxCoord.z, objBB.max.z) + ); + } + + std::cout << "minCoord = (" << minCoord.x << ", " << minCoord.y << ", " << minCoord.z << ")" << std::endl; + std::cout << "maxCoord = (" << maxCoord.x << ", " << maxCoord.y << ", " << maxCoord.z << ")" << std::endl; + + //Set x-y limits according to current adt index, since bounding box counting process is done per ADT + minCoord = mathfu::vec3( + minAdt.x, + minAdt.y, + minCoord.z); + + maxCoord = mathfu::vec3( + maxAdt.x, + maxAdt.y, + maxCoord.z); + + if (m_boundingBoxHolder != nullptr) { + (*m_boundingBoxHolder)[adt_x][adt_y] = CAaBox( + C3Vector(minCoord), + C3Vector(maxCoord) + ); + } + + saveDrawStageToFile(currentScenario.folderToSave+"/minimap", lastFrameIt); + } else if (m_mgMode == EMGMode::eScreenshotGeneration) { + //Make screenshot out of this drawStage + saveDrawStageToFile(currentScenario.folderToSave, lastFrameIt); + } + + m_y--; + if (m_y < 0) { + m_x++; + m_y = m_chunkHeight - 1; + } +// std::cout << "m_x = " << m_x << " out of (" << m_chunkStartX+m_chunkWidth << ") m_y = " << m_y << " out of (" << m_chunkStartY+m_chunkHeight << ")" << std::endl; + + if (m_x >= m_chunkWidth) { + startNextScenario(); + } else { + setupCameraData(); + } +} + +void MinimapGenerator::saveDrawStageToFile(std::string folderToSave, const std::shared_ptr &lastFrameIt) { + if (!ghc::filesystem::is_directory(folderToSave) || + !ghc::filesystem::exists(folderToSave)) { // Check if src folder exists + ghc::filesystem::create_directories(folderToSave); // create src folder + } + + std::string fileName = folderToSave + "/map_" + std::to_string( + (m_x) + ) + "_" + std::to_string( + (m_y) + ) + ".png"; + + std::vector buffer = std::vector(m_width * m_height * 4 + 1); + saveDataFromDrawStage(lastFrameIt->target, fileName, m_width, m_height, buffer); + if (lastFrameIt->opaqueMeshes != nullptr) { +// std::cout << "Saved " << fileName << ": opaqueMeshes (" << lastFrameIt->opaqueMeshes->meshes.size() << ") " +// << std::endl; + } +} + +HDrawStage MinimapGenerator::createSceneDrawStage(HFrameScenario sceneScenario) { + float farPlaneRendering = m_apiContainer->getConfig()->farPlane; + float farPlaneCulling = m_apiContainer->getConfig()->farPlaneForCulling; + + float nearPlane = 1.0; + float fov = toRadian(45.0); + + float canvasAspect = (float)m_width / (float)m_height; + + HCameraMatrices cameraMatricesCulling = m_apiContainer->camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneCulling); + HCameraMatrices cameraMatricesRendering = m_apiContainer->camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneRendering); + //Frustum matrix with reversed Z + + { + float f = 1.0f / tan(fov / 2.0f); + cameraMatricesCulling->perspectiveMat = getOrthoMatrix(); + cameraMatricesRendering->perspectiveMat = getOrthoMatrix(); + } + + if (m_apiContainer->hDevice->getIsVulkanAxisSystem() ) { + auto &perspectiveMatrix = cameraMatricesRendering->perspectiveMat; + + static const mathfu::mat4 vulkanMatrixFix2 = mathfu::mat4(1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1.0/2.0, 1/2.0, + 0, 0, 0, 1).Transpose(); + + perspectiveMatrix = vulkanMatrixFix2 * perspectiveMatrix; + } + + mathfu::vec4 clearColor = m_apiContainer->getConfig()->clearColor; + m_lastDraw = nullptr; + if (m_currentScene != nullptr) { + ViewPortDimensions dimensions = {{0, 0}, {m_width, m_height}}; + + HFrameBuffer fb = nullptr; + int frameWait = 4; + if (prepearCandidate && m_candidateDS == nullptr ) { + frameWait = waitQueueLen+4; + } + + fb = m_apiContainer->hDevice->createFrameBuffer(m_width, m_height, + {ITextureFormat::itRGBA}, ITextureFormat::itDepth32, + m_apiContainer->hDevice->getMaxSamplesCnt(), frameWait); + + std::vector drawStageDependencies = {}; + + auto cullStage = sceneScenario->addCullStage(cameraMatricesCulling, m_currentScene); + std::shared_ptr updateStage = sceneScenario->addUpdateStage(cullStage, 0, cameraMatricesRendering); + HDrawStage sceneDrawStage = sceneScenario->addDrawStage( + updateStage, m_currentScene, cameraMatricesRendering, + drawStageDependencies, true, dimensions, true, false, clearColor, fb); + + m_lastDraw = sceneDrawStage; + //We dont need stack in preview mode + if (m_mgMode != EMGMode::ePreview && prepearCandidate && m_candidateDS == nullptr) { + m_candidateDS = sceneDrawStage; + m_candidateCS = cullStage; + } + stackOfCullStages[m_apiContainer->hDevice->getDrawFrameNumber()] = cullStage; + + return sceneDrawStage; + } + + return nullptr; +} + +float MinimapGenerator::GetOrthoDimension() { +// return MathHelper::TILESIZE*0.25*0.5f; +// return MathHelper::TILESIZE*0.25; + return MathHelper::TILESIZE / m_zoom; +} + +mathfu::mat4 MinimapGenerator::getOrthoMatrix() { + return mathfu::mat4::Ortho( + -GetOrthoDimension() / 2.0f,GetOrthoDimension() / 2.0f, + -GetOrthoDimension() / 2.0f,GetOrthoDimension() / 2.0f, + 1,10000 + ); +} + +mathfu::vec3 MinimapGenerator::getLookAtVec3() { + switch (currentScenario.orientation) { + case ScenarioOrientation::soTopDownOrtho: + return mathfu::vec3(0,0,-1); + case ScenarioOrientation::so45DegreeTick0: + return mathfu::vec3(1,1,-1); + case ScenarioOrientation::so45DegreeTick1: + return mathfu::vec3(1,-1,-1); + case ScenarioOrientation::so45DegreeTick2: + return mathfu::vec3(-1,-1,-1); + case ScenarioOrientation::so45DegreeTick3: + return mathfu::vec3(-1,1,-1); + } + + return mathfu::vec3(0,0,0); +} + +HDrawStage MinimapGenerator::getLastDrawStage() { + return m_lastDraw; +} + +Config *MinimapGenerator::getConfig() { + return m_apiContainer->getConfig(); +} + +void MinimapGenerator::reload() { + m_currentScene = nullptr; + + MapRecord mapRecord; + if (!m_apiContainer->databaseHandler->getMapById(currentScenario.mapId, mapRecord)) { + std::cout << "Couldnt get data for mapId " << currentScenario.mapId << std::endl; + startNextScenario(); + return; + } + + m_currentScene = std::make_shared(m_apiContainer, mapRecord.ID, mapRecord.WdtFileID); + if (m_mgMode == EMGMode::eScreenshotGeneration) { + m_currentScene->setAdtBoundingBoxHolder(m_boundingBoxHolder); + } +} + +void MinimapGenerator::getCurrentFDData(int &areaId, int &parentAreaId, mathfu::vec4 &riverColor) { + areaId = 0; + parentAreaId = 0; + riverColor = mathfu::vec4(0,0,0,0); + + auto cullStage = stackOfCullStages[m_apiContainer->hDevice->getUpdateFrameNumber()]; + if (cullStage == nullptr) return; + + areaId = cullStage->adtAreadId; + parentAreaId = cullStage->parentAreaId; + riverColor = cullStage->frameDepedantData->closeRiverColor; + + mathfu::vec4 cameraPos = {0,0,0,1}; + this->m_apiContainer->camera->getCameraPosition(cameraPos.data_); + + for (auto &adtCullObj : cullStage->adtArray) { + auto adt_x = worldCoordinateToAdtIndex(cameraPos.y); + auto adt_y = worldCoordinateToAdtIndex(cameraPos.x); + + if (adt_x != adtCullObj->adtObject->getAdtX() || adt_y != adtCullObj->adtObject->getAdtY()) + continue; + + mathfu::vec3 riverColorV3; + adtCullObj->adtObject->getWaterColorFromDB(cameraPos, riverColorV3); + riverColor = mathfu::vec4(riverColorV3, 0); + break; + + + } +} + diff --git a/src/minimapGenerator/minimapGenerator.h b/src/minimapGenerator/minimapGenerator.h new file mode 100644 index 000000000..813154e42 --- /dev/null +++ b/src/minimapGenerator/minimapGenerator.h @@ -0,0 +1,107 @@ +// +// Created by Deamon on 9/8/2020. +// + +#ifndef AWEBWOWVIEWERCPP_MINIMAPGENERATOR_H +#define AWEBWOWVIEWERCPP_MINIMAPGENERATOR_H + +#include "../../wowViewerLib/src/engine/ApiContainer.h" +#include "../../wowViewerLib/src/engine/SceneScenario.h" +#include "../persistance/RequestProcessor.h" +#include "entities.h" + + +class MinimapGenerator { +private: + HApiContainer m_apiContainer; + HRequestProcessor m_processor; + + std::vector scenarioListToProcess; + ScenarioDef currentScenario; + + HScene m_currentScene = nullptr; + int m_width = 1024; + int m_height = 1024; + + //Position that's being rendered + int m_x = 0; + int m_y = 0; + + float m_zoom; + + int m_chunkStartX = -32; + int m_chunkStartY= -32; + int m_chunkWidth = 64; + int m_chunkHeight = 64; + + EMGMode m_mgMode = EMGMode::eNone; + + HDrawStage m_lastDraw = nullptr; + + HADTBoundingBoxHolder m_boundingBoxHolder = nullptr; + + //Per X dimension, per Y dimension, vector of mandatory adt {x, y} coordinates + std::vector>>> mandatoryADTMap; + + std::array stackOfCullStages; + + HDrawStage m_candidateDS = nullptr; + HCullStage m_candidateCS = nullptr; + int framesReady = 0; + bool prepearCandidate = false; + + mathfu::mat4 getOrthoMatrix(); + mathfu::vec3 getLookAtVec3(); + void startNextScenario(); + + mathfu::mat4 genTempProjectMatrix(); +public: + + MinimapGenerator(HWoWFilesCacheStorage cacheStorage, + const HGDevice &hDevice, + HRequestProcessor processor, + std::shared_ptr dbhandler, HADTBoundingBoxHolder boundingBoxHolder); + + void startScenarios(std::vector &scenarioListToProcess); + void process(); + EMGMode getCurrentMode() { return m_mgMode;} + void setupCameraData(); + HDrawStage createSceneDrawStage(HFrameScenario sceneScenario); + HDrawStage getLastDrawStage(); + Config *getConfig(); + + void getCurrentTileCoordinates(int &x, int &y, int &maxX, int &maxY) { + x = m_x; + y = m_y; + maxX = m_chunkWidth; + maxY = m_chunkHeight; + } + + void getCurrentFDData(int &areaId, int &parentAreaId, mathfu::vec4 &riverColor); + + float GetOrthoDimension(); + + void calcXtoYCoef(); + + void setMinMaxXYWidhtHeight(const mathfu::vec2 &minWowWorldCoord, const mathfu::vec2 &maxWowWorldCoord); + + void setupScenarioData(); + + void setLookAtPoint(float x, float y); + void setZoom(float zoom); + + void reload(); + + void startPreview(ScenarioDef &scenarioDef); + void stopPreview(); + + void startBoundingBoxCalc(ScenarioDef &scenarioDef); + void stopBoundingBoxCalc(); + + void saveDrawStageToFile(std::string folderToSave, const std::shared_ptr &lastFrameIt); + +}; + +typedef std::shared_ptr HMinimapGenerator; + +#endif //AWEBWOWVIEWERCPP_MINIMAPGENERATOR_H diff --git a/src/minimapGenerator/storage/CMinimapDataDB.cpp b/src/minimapGenerator/storage/CMinimapDataDB.cpp new file mode 100644 index 000000000..d15a7c139 --- /dev/null +++ b/src/minimapGenerator/storage/CMinimapDataDB.cpp @@ -0,0 +1,375 @@ +// +// Created by Deamon on 1/22/2021. +// + +#include "CMinimapDataDB.h" +#include +#include +#include + +CMinimapDataDB::CMinimapDataDB(std::string fileName) : m_sqliteDatabase(fileName, SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE) { + char *sErrMsg = ""; +// sqlite3_exec(m_sqliteDatabase.getHandle(), "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg); +// sqlite3_exec(m_sqliteDatabase.getHandle(), "PRAGMA schema.journal_mode = MEMORY", NULL, NULL, &sErrMsg); + + //------------------------------- + // Create Scenario table + // ------------------------------ + { + SQLite::Transaction transaction(m_sqliteDatabase); + + m_sqliteDatabase.exec("CREATE TABLE IF NOT EXISTS scenarios\n" + "(\n" + " id INTEGER PRIMARY KEY,\n" + " map_id INTEGER,\n" + " orientation INTEGER,\n" + " name VARCHAR(256),\n" + " ocean_color_0 FLOAT,\n" + " ocean_color_1 FLOAT,\n" + " ocean_color_2 FLOAT,\n" + " ocean_color_3 FLOAT\n" + " world_coord_min_x FLOAT,\n" + " world_coord_min_y FLOAT,\n" + " world_coord_max_x FLOAT,\n" + " world_coord_max_y FLOAT,\n" + " image_height INTEGER,\n" + " image_width INTEGER,\n" + " zoom FLOAT,\n" + " folderToSave VARCHAR(256)\n" + ");"); + + transaction.commit(); + } + + //------------------------------- + // Create Bounding boxes table + // ------------------------------ + { + SQLite::Transaction transaction(m_sqliteDatabase); + + m_sqliteDatabase.exec("CREATE TABLE IF NOT EXISTS adt_bounding_boxes (\n" + " id INTEGER PRIMARY KEY,\n" + " map_id INTEGER,\n" + " adt_x INTEGER,\n" + " adt_y INTEGER,\n" + " min_x FLOAT,\n" + " min_y FLOAT,\n" + " min_z FLOAT,\n" + " max_x FLOAT,\n" + " max_y FLOAT,\n" + " max_z FLOAT\n" + ")"); + + transaction.commit(); + } + + //------------------------------- + // Create river color overrides + // ------------------------------ + + { + SQLite::Transaction transaction(m_sqliteDatabase); + + m_sqliteDatabase.exec("CREATE TABLE IF NOT EXISTS river_color_overrides\n" + "(\n" + " id INTEGER PRIMARY KEY,\n" + " map_id INTEGER,\n" + " area_id INTEGER,\n" + " color_0 FLOAT,\n" + " color_1 FLOAT,\n" + " color_2 FLOAT,\n" + " color_3 FLOAT\n" + ")"); + + transaction.commit(); + } + + //------------------------------- + // Create WMO aabb overrides + // ------------------------------ + { + SQLite::Transaction transaction(m_sqliteDatabase); + + m_sqliteDatabase.exec("CREATE TABLE IF NOT EXISTS wmo_aabb_overrides\n" + "(\n" + " id INTEGER PRIMARY KEY,\n" + " map_id INTEGER,\n" + " unique_id INTEGER,\n" + " min_x FLOAT,\n" + " min_y FLOAT,\n" + " min_z FLOAT,\n" + " max_x FLOAT,\n" + " max_y FLOAT,\n" + " max_z FLOAT\n" + ")"); + + transaction.commit(); + } +} + +void CMinimapDataDB::getScenarios(std::vector &scenarioList) { + SQLite::Statement getScenarioList(m_sqliteDatabase, + "select s.id, s.map_id, s.orientation, s.name, \n" + " s.ocean_color_0, s.ocean_color_1, s.ocean_color_2, s.ocean_color_3,\n" + " s.world_coord_min_x, s.world_coord_min_y, s.world_coord_max_x, s.world_coord_max_y,\n" + " s.image_width, s.image_height, s.zoom, s.folderToSave from scenarios s;" + ); + getScenarioList.reset(); + + while (getScenarioList.executeStep()) + { + ScenarioDef scenarioDef; + + scenarioDef.id = getScenarioList.getColumn(0).getInt(); + scenarioDef.mapId = getScenarioList.getColumn(1).getInt(); + scenarioDef.orientation = static_cast(getScenarioList.getColumn(2).getInt()); + scenarioDef.name = getScenarioList.getColumn(3).getString(); + scenarioDef.closeOceanColor = mathfu::vec4( + getScenarioList.getColumn(4).getDouble(), + getScenarioList.getColumn(5).getDouble(), + getScenarioList.getColumn(6).getDouble(), + getScenarioList.getColumn(7).getDouble() + ); + scenarioDef.minWowWorldCoord = mathfu::vec2(getScenarioList.getColumn(8).getDouble(), + getScenarioList.getColumn(9).getDouble()); + scenarioDef.maxWowWorldCoord = mathfu::vec2(getScenarioList.getColumn(10).getDouble(), + getScenarioList.getColumn(11).getDouble()); + + scenarioDef.imageWidth = getScenarioList.getColumn(12).getInt(); + scenarioDef.imageHeight = getScenarioList.getColumn(13).getInt(); + + scenarioDef.zoom = getScenarioList.getColumn(14).getDouble(); + scenarioDef.folderToSave = getScenarioList.getColumn(15).getDouble(); + + scenarioList.push_back(scenarioDef); + } +} + +void CMinimapDataDB::getScenario(int id, ScenarioDef &scenario) { + SQLite::Statement getScenarioById(m_sqliteDatabase, + "select s.id, s.map_id, s.orientation,\n" + " s.ocean_color_0, s.ocean_color_1, s.ocean_color_2, s.ocean_color_3,\n" + " s.world_coord_min_x, s.world_coord_min_y, s.world_coord_max_x, s.world_coord_max_y,\n" + " s.image_width, s.image_height, s.zoom, s.folderToSave from scenarios s" + " where s.id = ?;" + ); + getScenarioById.reset(); + getScenarioById.bind(1, id); + + while (getScenarioById.executeStep()) + { + ScenarioDef scenarioDef; + + scenarioDef.id = getScenarioById.getColumn(0).getInt(); + scenarioDef.mapId = getScenarioById.getColumn(1).getInt(); + scenarioDef.orientation = static_cast(getScenarioById.getColumn(3).getInt()); + scenarioDef.closeOceanColor = mathfu::vec4( + getScenarioById.getColumn(4).getDouble(), + getScenarioById.getColumn(5).getDouble(), + getScenarioById.getColumn(6).getDouble(), + getScenarioById.getColumn(7).getDouble() + ); + scenarioDef.minWowWorldCoord = mathfu::vec2(getScenarioById.getColumn(8).getDouble(), + getScenarioById.getColumn(9).getDouble()); + scenarioDef.maxWowWorldCoord = mathfu::vec2(getScenarioById.getColumn(10).getDouble(), + getScenarioById.getColumn(11).getDouble()); + + scenarioDef.imageWidth = getScenarioById.getColumn(12).getInt(); + scenarioDef.imageHeight = getScenarioById.getColumn(13).getInt(); + + scenarioDef.zoom = getScenarioById.getColumn(14).getDouble(); + scenarioDef.folderToSave = getScenarioById.getColumn(15).getString(); + } +} + +void CMinimapDataDB::saveScenario(ScenarioDef &scenario) { + SQLite::Transaction transaction(m_sqliteDatabase); + + if (scenario.id == -1) { + SQLite::Statement insertScenario(m_sqliteDatabase, + "insert into scenarios (map_id, orientation, \n" + " ocean_color_0, ocean_color_1, ocean_color_2, ocean_color_3, \n" + " world_coord_min_x, world_coord_min_y, world_coord_max_x, world_coord_max_y, \n" + " image_width, image_height, zoom, folderToSave) \n" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + ); + + insertScenario.bind(1, scenario.mapId); + insertScenario.bind(2, (int)scenario.orientation); + insertScenario.bind(3, scenario.closeOceanColor.x); + insertScenario.bind(4, scenario.closeOceanColor.y); + insertScenario.bind(5, scenario.closeOceanColor.z); + insertScenario.bind(6, scenario.closeOceanColor.w); + insertScenario.bind(7, scenario.minWowWorldCoord.x); + insertScenario.bind(8, scenario.minWowWorldCoord.y); + insertScenario.bind(9, scenario.maxWowWorldCoord.x); + insertScenario.bind(10, scenario.maxWowWorldCoord.y); + insertScenario.bind(11, scenario.imageWidth); + insertScenario.bind(12, scenario.imageHeight); + insertScenario.bind(13, scenario.zoom); + insertScenario.bind(14, scenario.folderToSave.c_str()); + insertScenario.exec(); + } else { + SQLite::Transaction transaction(m_sqliteDatabase); + + SQLite::Statement updateScenario(m_sqliteDatabase, + "update scenarios set map_id = ?, orientation = ?,\n" + " ocean_color_0 = ?, ocean_color_1 = ?, ocean_color_2 = ?, ocean_color_3 = ?,\n" + " world_coord_min_x = ?, world_coord_min_y = ?, world_coord_max_x = ?, world_coord_max_y = ?,\n" + " image_width = ?, image_height = ?, zoom = ?, folderToSave = ?\n" + " where id = ?;" + ); + + updateScenario.bind(1, scenario.mapId); + updateScenario.bind(2, (int)scenario.orientation); + updateScenario.bind(3, scenario.closeOceanColor.x); + updateScenario.bind(4, scenario.closeOceanColor.y); + updateScenario.bind(5, scenario.closeOceanColor.z); + updateScenario.bind(6, scenario.closeOceanColor.w); + updateScenario.bind(7, scenario.minWowWorldCoord.x); + updateScenario.bind(8, scenario.minWowWorldCoord.y); + updateScenario.bind(9, scenario.maxWowWorldCoord.x); + updateScenario.bind(10, scenario.maxWowWorldCoord.y); + updateScenario.bind(11, scenario.imageWidth); + updateScenario.bind(12, scenario.imageHeight); + updateScenario.bind(13, scenario.zoom); + updateScenario.bind(14, scenario.folderToSave.c_str()); + updateScenario.bind(15, scenario.id); + updateScenario.exec(); + } + + // Commit transaction + transaction.commit(); +} + +void CMinimapDataDB::getAdtBoundingBoxes(int mapId, ADTBoundingBoxHolder &boundingBoxHolder) { + SQLite::Statement getAdtBB(m_sqliteDatabase, + "select abb.adt_x, abb.adt_y, \n" + " abb.min_x, abb.min_y, abb.min_z, \n" + " abb.max_x, abb.max_y, abb.max_z from adt_bounding_boxes abb \n" + " where map_id = ?" + ); + getAdtBB.reset(); + getAdtBB.bind(1, mapId); + + while (getAdtBB.executeStep()) + { + int adt_x = getAdtBB.getColumn(0).getInt(); + int adt_y = getAdtBB.getColumn(1).getInt(); + CAaBox aaBox = CAaBox( + C3Vector(mathfu::vec3( + getAdtBB.getColumn(2).getDouble(), + getAdtBB.getColumn(3).getDouble(), + getAdtBB.getColumn(4).getDouble() + )), + C3Vector(mathfu::vec3( + getAdtBB.getColumn(5).getDouble(), + getAdtBB.getColumn(6).getDouble(), + getAdtBB.getColumn(7).getDouble() + )) + ); + + boundingBoxHolder[adt_x][adt_y] = aaBox; + } +} + +void CMinimapDataDB::saveAdtBoundingBoxes(int mapId, ADTBoundingBoxHolder &boundingBoxHolder) { + SQLite::Transaction transaction(m_sqliteDatabase); + + //1. Clear all records for that mapId from DB + { + SQLite::Statement cleanABB(m_sqliteDatabase, + "delete from adt_bounding_boxes where map_id = ?" + ); + cleanABB.exec(); + } + + //2. Insert into database + SQLite::Statement insertBB(m_sqliteDatabase, "insert into adt_bounding_boxes(map_id, adt_x, adt_y, min_x, min_y, min_z, max_x, max_y, max_z) " + "values (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + for (int i = 0; i < 64; i++) { + for (int j = 0; j < 64; j++) { + auto aabb = boundingBoxHolder[i][j]; + insertBB.reset(); + + insertBB.bind(1, mapId); + insertBB.bind(2, i); + insertBB.bind(3, j); + insertBB.bind(4, aabb.min.x); + insertBB.bind(5, aabb.min.y); + insertBB.bind(6, aabb.min.z); + insertBB.bind(7, aabb.max.x); + insertBB.bind(8, aabb.max.y); + insertBB.bind(9, aabb.max.z); + + insertBB.exec(); + } + } + + //3. Commit transaction + transaction.commit(); +} + +void CMinimapDataDB::getRiverColorOverrides(int mapId, std::vector &riverOverrides) { + SQLite::Statement getRiverColor(m_sqliteDatabase, + "select rco.area_id, rco.color_0, rco.color_1, rco.color_2, rco.color_3 " + " from river_color_overrides rco where rco.map_id = ?;" + ); + getRiverColor.reset(); + getRiverColor.bind(1, mapId); + + while (getRiverColor.executeStep()) + { + RiverColorOverride colorOverride; + colorOverride.areaId = getRiverColor.getColumn(0).getInt(); + colorOverride.color = mathfu::vec4( + getRiverColor.getColumn(1).getDouble(), + getRiverColor.getColumn(2).getDouble(), + getRiverColor.getColumn(3).getDouble(), + getRiverColor.getColumn(4).getDouble() + ); + riverOverrides.push_back(colorOverride); + } +} + +void CMinimapDataDB::saveRiverColorOverrides(int mapId, std::vector &riverOverrides) { + SQLite::Transaction transaction(m_sqliteDatabase); + + //1. Clear all records for that mapId from DB + { + SQLite::Statement cleanRiverColor(m_sqliteDatabase, + "delete from river_color_overrides where map_id = ?" + ); + cleanRiverColor.bind(1, mapId); + cleanRiverColor.exec(); + } + + SQLite::Statement saveRiverColor(m_sqliteDatabase, "insert into river_color_overrides(map_id, area_id, " + "color_0, color_1, color_2, color_3) \n" + "values (?, ?, ?, ?, ?, ?);"); + + for (int j = 0; j < riverOverrides.size(); j++) { + auto &roRec = riverOverrides[j]; + saveRiverColor.reset(); + + saveRiverColor.bind(1, mapId); + saveRiverColor.bind(2, roRec.areaId); + saveRiverColor.bind(3, roRec.color.x); + saveRiverColor.bind(4, roRec.color.y); + saveRiverColor.bind(5, roRec.color.z); + saveRiverColor.bind(6, roRec.color.w); + + saveRiverColor.exec(); + } + transaction.commit(); +} + +bool CMinimapDataDB::getWmoAABBOverride(int mapId, int uniqueId, CAaBox &aaBox) { + return false; +} + +void CMinimapDataDB::saveWmoAABBOverride(int mapId, int uniqueId, const CAaBox &aaBox) { + +} + + diff --git a/src/minimapGenerator/storage/CMinimapDataDB.h b/src/minimapGenerator/storage/CMinimapDataDB.h new file mode 100644 index 000000000..6763e1636 --- /dev/null +++ b/src/minimapGenerator/storage/CMinimapDataDB.h @@ -0,0 +1,39 @@ +// +// Created by Deamon on 1/22/2021. +// + +#ifndef AWEBWOWVIEWERCPP_CMINIMAPDATADB_H +#define AWEBWOWVIEWERCPP_CMINIMAPDATADB_H + +#include +#include "../entities.h" +#include "../minimapGenerator.h" + + +class CMinimapDataDB { +private: + SQLite::Database m_sqliteDatabase; + +public: + explicit CMinimapDataDB(std::string fileName); + + //Scenarios + void getScenarios(std::vector &scenarioList); + void getScenario(int id, ScenarioDef &scenario); + void saveScenario(ScenarioDef &scenario); + + //ADT Bounding boxes + void getAdtBoundingBoxes(int mapId, ADTBoundingBoxHolder &boundingBoxHolder); + void saveAdtBoundingBoxes(int mapId, ADTBoundingBoxHolder &boundingBoxHolder); + + //River color overrides + void getRiverColorOverrides(int mapId, std::vector &riverOverrides); + void saveRiverColorOverrides(int mapId, std::vector &riverOverrides); + + //Get WMO ADT aabb override + bool getWmoAABBOverride(int mapId, int uniqueId, CAaBox &aaBox); + void saveWmoAABBOverride(int mapId, int uniqueId, const CAaBox &aaBox); +}; + + +#endif //AWEBWOWVIEWERCPP_CMINIMAPDATADB_H diff --git a/src/persistance/CascRequestProcessor.cpp b/src/persistance/CascRequestProcessor.cpp index ecf2cb127..cc3c4bdc2 100644 --- a/src/persistance/CascRequestProcessor.cpp +++ b/src/persistance/CascRequestProcessor.cpp @@ -6,12 +6,22 @@ #include #include "CascRequestProcessor.h" -void CascRequestProcessor::requestFile(const char *fileName, CacheHolderType holderType) { - std::string fileName_s(fileName); - this->addRequest(fileName_s,holderType); -} +void CascRequestProcessor::processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) { + auto perstFile = s_file.lock(); + if (perstFile == nullptr) { + uint32_t fileDataId = 0; + if (fileName.find("File") == 0) { + std::stringstream ss; + std::string fileDataIdHex = fileName.substr(4, fileName.find(".") - 4); + + ss << std::hex << fileDataIdHex; + ss >> fileDataId; + } + std::cout << "perstFile for " << fileName << "(fileDataId = "<m_fileRequester->rejectFile(holderType, fileName.c_str()); return; } @@ -58,8 +70,12 @@ void CascRequestProcessor::processFileRequest(std::string &fileName, CacheHolder } if (fileOpened) { - this->provideResult(fileName, fileContent, holderType); + toBeProcessed--; + processResult(perstFile, fileContent, fileName); + +// this->provideResult(fileName, fileContent, holderType); } else { + toBeProcessed--; std::cout << "Could not open file "<< fileName << std::endl << std::flush; this->m_fileRequester->rejectFile(holderType, fileName.c_str()); } diff --git a/src/persistance/CascRequestProcessor.h b/src/persistance/CascRequestProcessor.h index 2b0ad5610..845caf5d3 100644 --- a/src/persistance/CascRequestProcessor.h +++ b/src/persistance/CascRequestProcessor.h @@ -26,10 +26,7 @@ class CascRequestProcessor : public RequestProcessor { void* m_storage; protected: - void processFileRequest(std::string &fileName, CacheHolderType holderType) override; - -public: - void requestFile(const char* fileName, CacheHolderType holderType) override; + void processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) override; }; diff --git a/src/persistance/HttpRequestProcessor.cpp b/src/persistance/HttpRequestProcessor.cpp index 9a762c1c9..eb7f8df92 100644 --- a/src/persistance/HttpRequestProcessor.cpp +++ b/src/persistance/HttpRequestProcessor.cpp @@ -22,10 +22,6 @@ std::string int_to_hex( T i ) return stream.str(); } -void HttpRequestProcessor::requestFile(const char *fileName, CacheHolderType holderType) { - std::string fileName_s(fileName); - this->addRequest(fileName_s, holderType); -} std::string char_to_escape( char i ) { std::stringstream stream; @@ -44,7 +40,12 @@ std::string ReplaceAll(std::string str, const std::string& from, const std::stri return str; } -void HttpRequestProcessor::processFileRequest(std::string &fileName, CacheHolderType holderType) { +void HttpRequestProcessor::processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) { + auto perstFile = s_file.lock(); + if (perstFile == nullptr){ + toBeProcessed--; + return; + } const std::string charsToEscape = " !*'();:@&=+$,/?#[]"; @@ -96,14 +97,15 @@ void HttpRequestProcessor::processFileRequest(std::string &fileName, CacheHolder std::istream_iterator(), std::back_inserter(*vec.get())); - provideResult(fileName, vec, holderType); + processResult(perstFile, vec, fileName); + toBeProcessed--; return; } // - HttpFile * httpFile = new HttpFile(fullUrl); + auto httpFile = std::make_shared(fullUrl); httpFile->setCallback( - [fileName, this, holderType, httpFile, fullUrl](HFileContent fileContent) -> void { + [fileName, this, holderType, httpFile, fullUrl, perstFile](HFileContent fileContent) -> void { std::string newFileName = fileName; if (holderType != CacheHolderType::CACHE_ANIM && fileContent->size() > 4 && (*(uint32_t *)fileContent->data() == 0)) { @@ -121,13 +123,14 @@ void HttpRequestProcessor::processFileRequest(std::string &fileName, CacheHolder FILE.close(); //Provide file! - provideResult(newFileName, fileContent, holderType); + processResult(perstFile, fileContent, fileName); + toBeProcessed--; } ); httpFile->setFailCallback([fileName, this, holderType, httpFile](HFileContent fileContent) -> void { this->m_fileRequester->rejectFile(holderType, fileName.c_str()); + toBeProcessed--; }); httpFile->startDownloading(); - delete httpFile; } diff --git a/src/persistance/HttpRequestProcessor.h b/src/persistance/HttpRequestProcessor.h index 769d66591..b7a0eeef1 100644 --- a/src/persistance/HttpRequestProcessor.h +++ b/src/persistance/HttpRequestProcessor.h @@ -17,10 +17,7 @@ class HttpRequestProcessor : public RequestProcessor { std::string m_urlBase; std::string m_urlBaseFileId; protected: - void processFileRequest(std::string &fileName, CacheHolderType holderType) override; - -public: - void requestFile(const char* fileName, CacheHolderType holderType) override; + void processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) override; }; diff --git a/src/persistance/RequestProcessor.cpp b/src/persistance/RequestProcessor.cpp index 7943b4329..b402eee03 100644 --- a/src/persistance/RequestProcessor.cpp +++ b/src/persistance/RequestProcessor.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "RequestProcessor.h" std::mutex requestMtx; // mutex for critical section @@ -12,11 +13,23 @@ std::mutex setProcessingMtx; // mutex for critical section //2. Get filename from FIFO //3. Add result to ResultFIFO //4. Get ready results from FIFO -void RequestProcessor::addRequest (std::string &fileName, CacheHolderType holderType) { +void +RequestProcessor::requestFile(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) { std::unique_lock setLck (setProcessingMtx,std::defer_lock); setLck.lock(); if (currentlyProcessingFnames.count(fileName) > 0) { - std::cout << "RequestProcessor::addRequest : duplicate detected for fileName = " << fileName << std::endl; + uint32_t fileDataId = 0; + if (fileName.find("File") == 0) { + std::stringstream ss; + std::string fileDataIdHex = fileName.substr(4, fileName.find(".") - 4); + + ss << std::hex << fileDataIdHex; + ss >> fileDataId; + } + std::cout << "RequestProcessor::addRequest : duplicate detected for fileName = " << fileName + << " " << ((fileDataId > 0) ? ("(fileDataId = "+std::to_string(fileDataId)+")"): "") + << " holderTypeReq = " << (int)holderType + < lck (requestMtx,std::defer_lock); + std::unique_lock lck (requestMtx,std::defer_lock); - while (true) { + if (calledFromThread){ + while (!this->isTerminating) { if (m_requestQueue.empty()) { std::this_thread::sleep_for(1ms); continue; } - lck.lock(); - auto it = m_requestQueue.begin(); + lck.lock(); + auto it = m_requestQueue.front(); + m_requestQueue.pop_front(); lck.unlock(); - this->processFileRequest(it->fileName, it->holderType); + this->processFileRequest(it.fileName, it.holderType, it.s_file); - lck.lock(); - m_requestQueue.erase(it); - lck.unlock(); + std::unique_lock setLck (setProcessingMtx,std::defer_lock); + setLck.lock(); + currentlyProcessingFnames.erase(it.fileName); + setLck.unlock(); } } else if (!m_threaded) { while (!m_requestQueue.empty()) { + lck.lock(); auto it = m_requestQueue.front(); m_requestQueue.pop_front(); + lck.unlock(); + + this->processFileRequest(it.fileName, it.holderType, it.s_file); - this->processFileRequest(it.fileName, it.holderType); + std::unique_lock setLck (setProcessingMtx,std::defer_lock); + setLck.lock(); + currentlyProcessingFnames.erase(it.fileName); + setLck.unlock(); } } } -void RequestProcessor::provideResult(std::string &fileName, HFileContent content, CacheHolderType holderType) { - std::unique_lock lck (resultMtx,std::defer_lock); - - ResultStruct resultStructObj; - resultStructObj.buffer = content; - resultStructObj.fileName = fileName; - resultStructObj.holderType = holderType; +void +RequestProcessor::processResult(std::shared_ptr s_file, HFileContent content, const std::string &fileName) { + if (s_file->getStatus() == FileStatus::FSLoaded) { + std::cout << "sharedPtr->getStatus == FileStatus::FSLoaded " << fileName << std::endl; + } if (s_file->getStatus() == FileStatus::FSRejected) { + std::cout << "sharedPtr->getStatus == FileStatus::FSRejected" << fileName << std::endl; + } else { + s_file->process(content, fileName); + } - if (m_threaded) lck.lock(); - m_resultQueue.push_back(resultStructObj); - if (m_threaded) lck.unlock(); } - -void RequestProcessor::processResults(int limit) { - std::unique_lock lck (resultMtx,std::defer_lock); - - for (int i = 0; i < limit; i++) { - if (m_resultQueue.empty()) break; - - if (m_threaded) lck.lock(); - auto it = &m_resultQueue.front(); - if (m_threaded) lck.unlock(); - -// std::cout << "it->buffer.use_count() = " << it->buffer.use_count() << std::endl << std::flush; - - HFileContent bufferCpy = it->buffer; - m_fileRequester->provideFile( - it->holderType, - it->fileName.c_str(), - bufferCpy - ); - - std::unique_lock setLck (setProcessingMtx,std::defer_lock); - setLck.lock(); - currentlyProcessingFnames.erase(it->fileName); - setLck.unlock(); - - if (m_threaded) lck.lock(); - m_resultQueue.pop_front(); - if (m_threaded) lck.unlock(); - } -} \ No newline at end of file diff --git a/src/persistance/RequestProcessor.h b/src/persistance/RequestProcessor.h index 23486f43c..6c0518744 100644 --- a/src/persistance/RequestProcessor.h +++ b/src/persistance/RequestProcessor.h @@ -10,29 +10,39 @@ #include #include #include +#include #include #include class RequestProcessor : public IFileRequest { protected: - RequestProcessor() { + RequestProcessor() : toBeProcessed(0){ } + ~RequestProcessor() { + if (loaderThread != nullptr) { + isTerminating = true; + loaderThread->join(); + loaderThread = nullptr; + } + }; protected: IFileRequester *m_fileRequester = nullptr; - virtual void processFileRequest(std::string &fileName, CacheHolderType holderType) = 0; + virtual void processFileRequest(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) = 0; public: void setFileRequester(IFileRequester *fileRequester) { m_fileRequester = fileRequester; } + void requestFile(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) override; private: class RequestStruct { public: std::string fileName; CacheHolderType holderType; + std::weak_ptr s_file; }; class ResultStruct { @@ -48,7 +58,8 @@ class RequestProcessor : public IFileRequest { HFileContent buffer = nullptr; }; - std::thread *loaderThread; + bool isTerminating = false; + std::shared_ptr loaderThread = nullptr; std::list m_requestQueue; std::list m_resultQueue; @@ -58,26 +69,29 @@ class RequestProcessor : public IFileRequest { int currentlyProcessing = 0; public: - void processResults(int limit); void processRequests(bool calledFromThread); + bool completedAllJobs() { + return (m_requestQueue.empty()) && (m_resultQueue.empty()) && (toBeProcessed == 0); + }; + bool getThreaded() { return m_threaded; } void setThreaded(bool value) { m_threaded = value; if (value) { - loaderThread = new std::thread(([&](){ + loaderThread = std::make_shared(([&](){ this->processRequests(true); })); } } protected: - void addRequest (std::string &fileName, CacheHolderType holderType); - - void provideResult(std::string &fileName, HFileContent content, CacheHolderType holderType); - + std::atomic toBeProcessed; + void processResult( std::shared_ptr s_file, HFileContent content, const std::string &fileName); }; +typedef std::shared_ptr HRequestProcessor; + #endif //WEBWOWVIEWERCPP_REQUESTPROCESSOR_H diff --git a/src/persistance/ZipRequestProcessor.cpp b/src/persistance/ZipRequestProcessor.cpp index 5235e1e8d..bee292d50 100644 --- a/src/persistance/ZipRequestProcessor.cpp +++ b/src/persistance/ZipRequestProcessor.cpp @@ -31,7 +31,8 @@ void ZipRequestProcessor::loadingFinished(std::vector * file) { // zip_source_keep(src); } -void ZipRequestProcessor::requestFile(const char* fileName, CacheHolderType holderType) { +void ZipRequestProcessor::requestFile(std::string &fileName, CacheHolderType holderType, + std::weak_ptr s_file) { // std::string s_fileName(fileName); // zip_error_t error; // struct zip_stat sb; @@ -65,4 +66,4 @@ void ZipRequestProcessor::requestFile(const char* fileName, CacheHolderType hold // std::cout << "Could not load file " << std::string(fileName) << std::endl << std::flush; // m_fileRequester->rejectFile(fileName); // } -}; \ No newline at end of file +} diff --git a/src/persistance/ZipRequestProcessor.h b/src/persistance/ZipRequestProcessor.h index 6c2441bfa..c6321cd15 100644 --- a/src/persistance/ZipRequestProcessor.h +++ b/src/persistance/ZipRequestProcessor.h @@ -40,7 +40,7 @@ class ZipRequestProcessor : public IFileRequest { m_fileRequester = fileRequester; } void loadingFinished(std::vector * file); - void requestFile(const char* fileName, CacheHolderType holderType) override; + void requestFile(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) override; }; diff --git a/src/persistance/httpFile/httpFile.cpp b/src/persistance/httpFile/httpFile.cpp index 2e2d54a45..dde1834a3 100644 --- a/src/persistance/httpFile/httpFile.cpp +++ b/src/persistance/httpFile/httpFile.cpp @@ -1,6 +1,11 @@ #include "httpFile.h" +#ifndef __ANDROID__ +#include "cpr/cpr.h" +#endif #include #include +#include + std::string url_encode(const std::string &value) { std::ostringstream escaped; @@ -41,18 +46,30 @@ void HttpFile::setCallback(HTTPReadyCallback callback) { } void HttpFile::startDownloading() { - httplib::Client cli(m_httpUrl, 80); + std::string escaped_url = m_httpUrl; +#ifndef __ANDROID__ + auto verSSL = cpr::VerifySsl{false}; + + auto r = cpr::Get(cpr::Url{escaped_url}, verSSL ); + if (r.status_code == 200) { + this->m_fileBuffer = std::make_shared(FileContent(r.text.begin(), r.text.end())); - httplib::ContentReceiver contentReceiver = [](const char *data, size_t data_length) -> bool { - return true; - }; - auto res = cli.Get("/hi"); -// if (res) { - std::cout << res->status << std::endl; - std::cout << res->body << std::endl; -// } + if (this->m_fileBuffer->size() == 0) { + std::cout << "File " << this->m_httpUrl.c_str() << " is empty" << std::endl << + escaped_url << std::endl << std::flush; + m_failCallback({}); + } else if (this->m_callback != nullptr) { + m_callback(this->m_fileBuffer); + } + } else { + std::cout << "Could not download file " << this->m_httpUrl.c_str() << std::endl << + escaped_url << std::endl << std::flush; + m_failCallback({}); + } +#else +#endif } void HttpFile::setFailCallback(HTTPReadyCallback callback) { diff --git a/src/screenshots/lodepng/lodepng.cpp b/src/screenshots/lodepng/lodepng.cpp new file mode 100644 index 000000000..51492b887 --- /dev/null +++ b/src/screenshots/lodepng/lodepng.cpp @@ -0,0 +1,6464 @@ +/* +LodePNG version 20201017 + +Copyright (c) 2005-2020 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +/* +The manual and changelog are in the header file "lodepng.h" +Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. +*/ + +#include "lodepng.h" + +#ifdef LODEPNG_COMPILE_DISK +#include /* LONG_MAX */ +#include /* file handling */ +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ALLOCATORS +#include /* allocations */ +#endif /* LODEPNG_COMPILE_ALLOCATORS */ + +#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ +#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ +#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ +#endif /*_MSC_VER */ + +const char* LODEPNG_VERSION_STRING = "20201017"; + +/* +This source file is built up in the following large parts. The code sections +with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. +-Tools for C and common code for PNG and Zlib +-C Code for Zlib (huffman, deflate, ...) +-C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) +-The C++ wrapper around all of the above +*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // Tools for C, and common code for PNG and Zlib. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*The malloc, realloc and free functions defined here with "lodepng_" in front +of the name, so that you can easily change them to others related to your +platform if needed. Everything else in the code calls these. Pass +-DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out +#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and +define them in your own project's source files without needing to change +lodepng source code. Don't forget to remove "static" if you copypaste them +from here.*/ + +#ifdef LODEPNG_COMPILE_ALLOCATORS +static void* lodepng_malloc(size_t size) { +#ifdef LODEPNG_MAX_ALLOC + if(size > LODEPNG_MAX_ALLOC) return 0; +#endif + return malloc(size); +} + +/* NOTE: when realloc returns NULL, it leaves the original memory untouched */ +static void* lodepng_realloc(void* ptr, size_t new_size) { +#ifdef LODEPNG_MAX_ALLOC + if(new_size > LODEPNG_MAX_ALLOC) return 0; +#endif + return realloc(ptr, new_size); +} + +static void lodepng_free(void* ptr) { + free(ptr); +} +#else /*LODEPNG_COMPILE_ALLOCATORS*/ +/* TODO: support giving additional void* payload to the custom allocators */ +void* lodepng_malloc(size_t size); +void* lodepng_realloc(void* ptr, size_t new_size); +void lodepng_free(void* ptr); +#endif /*LODEPNG_COMPILE_ALLOCATORS*/ + +/* convince the compiler to inline a function, for use when this measurably improves performance */ +/* inline is not available in C90, but use it when supported by the compiler */ +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L)) +#define LODEPNG_INLINE inline +#else +#define LODEPNG_INLINE /* not available */ +#endif + +/* restrict is not available in C90, but use it when supported by the compiler */ +#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\ + (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \ + (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus)) +#define LODEPNG_RESTRICT __restrict +#else +#define LODEPNG_RESTRICT /* not available */ +#endif + +/* Replacements for C library functions such as memcpy and strlen, to support platforms +where a full C library is not available. The compiler can recognize them and compile +to something as fast. */ + +static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, + const void* LODEPNG_RESTRICT src, size_t size) { + size_t i; + for(i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i]; +} + +static void lodepng_memset(void* LODEPNG_RESTRICT dst, + int value, size_t num) { + size_t i; + for(i = 0; i < num; i++) ((char*)dst)[i] = (char)value; +} + +/* does not check memory out of bounds, do not use on untrusted data */ +static size_t lodepng_strlen(const char* a) { + const char* orig = a; + /* avoid warning about unused function in case of disabled COMPILE... macros */ + (void)(&lodepng_strlen); + while(*a) a++; + return (size_t)(a - orig); +} + +#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x)) + +#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER) +/* Safely check if adding two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_addofl(size_t a, size_t b, size_t* result) { + *result = a + b; /* Unsigned addition is well defined and safe in C90 */ + return *result < a; +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)*/ + +#ifdef LODEPNG_COMPILE_DECODER +/* Safely check if multiplying two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_mulofl(size_t a, size_t b, size_t* result) { + *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */ + return (a != 0 && *result / a != b); +} + +#ifdef LODEPNG_COMPILE_ZLIB +/* Safely check if a + b > c, even if overflow could happen. */ +static int lodepng_gtofl(size_t a, size_t b, size_t c) { + size_t d; + if(lodepng_addofl(a, b, &d)) return 1; + return d > c; +} +#endif /*LODEPNG_COMPILE_ZLIB*/ +#endif /*LODEPNG_COMPILE_DECODER*/ + + +/* +Often in case of an error a value is assigned to a variable and then it breaks +out of a loop (to go to the cleanup phase of a function). This macro does that. +It makes the error handling code shorter and more readable. + +Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83); +*/ +#define CERROR_BREAK(errorvar, code){\ + errorvar = code;\ + break;\ +} + +/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ +#define ERROR_BREAK(code) CERROR_BREAK(error, code) + +/*Set error var to the error code, and return it.*/ +#define CERROR_RETURN_ERROR(errorvar, code){\ + errorvar = code;\ + return code;\ +} + +/*Try the code, if it returns error, also return the error.*/ +#define CERROR_TRY_RETURN(call){\ + unsigned error = call;\ + if(error) return error;\ +} + +/*Set error var to the error code, and return from the void function.*/ +#define CERROR_RETURN(errorvar, code){\ + errorvar = code;\ + return;\ +} + +/* +About uivector, ucvector and string: +-All of them wrap dynamic arrays or text strings in a similar way. +-LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. +-The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. +-They're not used in the interface, only internally in this file as static functions. +-As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. +*/ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_ENCODER +/*dynamic vector of unsigned ints*/ +typedef struct uivector { + unsigned* data; + size_t size; /*size in number of unsigned longs*/ + size_t allocsize; /*allocated size in bytes*/ +} uivector; + +static void uivector_cleanup(void* p) { + ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; + lodepng_free(((uivector*)p)->data); + ((uivector*)p)->data = NULL; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_resize(uivector* p, size_t size) { + size_t allocsize = size * sizeof(unsigned); + if(allocsize > p->allocsize) { + size_t newsize = allocsize + (p->allocsize >> 1u); + void* data = lodepng_realloc(p->data, newsize); + if(data) { + p->allocsize = newsize; + p->data = (unsigned*)data; + } + else return 0; /*error: not enough memory*/ + } + p->size = size; + return 1; /*success*/ +} + +static void uivector_init(uivector* p) { + p->data = NULL; + p->size = p->allocsize = 0; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_push_back(uivector* p, unsigned c) { + if(!uivector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* /////////////////////////////////////////////////////////////////////////// */ + +/*dynamic vector of unsigned chars*/ +typedef struct ucvector { + unsigned char* data; + size_t size; /*used size*/ + size_t allocsize; /*allocated size*/ +} ucvector; + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_resize(ucvector* p, size_t size) { + if(size > p->allocsize) { + size_t newsize = size + (p->allocsize >> 1u); + void* data = lodepng_realloc(p->data, newsize); + if(data) { + p->allocsize = newsize; + p->data = (unsigned char*)data; + } + else return 0; /*error: not enough memory*/ + } + p->size = size; + return 1; /*success*/ +} + +static ucvector ucvector_init(unsigned char* buffer, size_t size) { + ucvector v; + v.data = buffer; + v.allocsize = v.size = size; + return v; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +/*free string pointer and set it to NULL*/ +static void string_cleanup(char** out) { + lodepng_free(*out); + *out = NULL; +} + +/*also appends null termination character*/ +static char* alloc_string_sized(const char* in, size_t insize) { + char* out = (char*)lodepng_malloc(insize + 1); + if(out) { + lodepng_memcpy(out, in, insize); + out[insize] = 0; + } + return out; +} + +/* dynamically allocates a new string with a copy of the null terminated input text */ +static char* alloc_string(const char* in) { + return alloc_string_sized(in, lodepng_strlen(in)); +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG) +static unsigned lodepng_read32bitInt(const unsigned char* buffer) { + return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) | + ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]); +} +#endif /*defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG)*/ + +#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) +/*buffer must have at least 4 allocated bytes available*/ +static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) { + buffer[0] = (unsigned char)((value >> 24) & 0xff); + buffer[1] = (unsigned char)((value >> 16) & 0xff); + buffer[2] = (unsigned char)((value >> 8) & 0xff); + buffer[3] = (unsigned char)((value ) & 0xff); +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / File IO / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DISK + +/* returns negative value on error. This should be pure C compatible, so no fstat. */ +static long lodepng_filesize(const char* filename) { + FILE* file; + long size; + file = fopen(filename, "rb"); + if(!file) return -1; + + if(fseek(file, 0, SEEK_END) != 0) { + fclose(file); + return -1; + } + + size = ftell(file); + /* It may give LONG_MAX as directory size, this is invalid for us. */ + if(size == LONG_MAX) size = -1; + + fclose(file); + return size; +} + +/* load file into buffer that already has the correct allocated size. Returns error code.*/ +static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) { + FILE* file; + size_t readsize; + file = fopen(filename, "rb"); + if(!file) return 78; + + readsize = fread(out, 1, size, file); + fclose(file); + + if(readsize != size) return 78; + return 0; +} + +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) { + long size = lodepng_filesize(filename); + if(size < 0) return 78; + *outsize = (size_t)size; + + *out = (unsigned char*)lodepng_malloc((size_t)size); + if(!(*out) && size > 0) return 83; /*the above malloc failed*/ + + return lodepng_buffer_file(*out, (size_t)size, filename); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) { + FILE* file; + file = fopen(filename, "wb" ); + if(!file) return 79; + fwrite(buffer, 1, buffersize, file); + fclose(file); + return 0; +} + +#endif /*LODEPNG_COMPILE_DISK*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of common code and tools. Begin of Zlib related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_ENCODER + +typedef struct { + ucvector* data; + unsigned char bp; /*ok to overflow, indicates bit pos inside byte*/ +} LodePNGBitWriter; + +static void LodePNGBitWriter_init(LodePNGBitWriter* writer, ucvector* data) { + writer->data = data; + writer->bp = 0; +} + +/*TODO: this ignores potential out of memory errors*/ +#define WRITEBIT(writer, bit){\ + /* append new byte */\ + if(((writer->bp) & 7u) == 0) {\ + if(!ucvector_resize(writer->data, writer->data->size + 1)) return;\ + writer->data->data[writer->data->size - 1] = 0;\ + }\ + (writer->data->data[writer->data->size - 1]) |= (bit << ((writer->bp) & 7u));\ + ++writer->bp;\ +} + +/* LSB of value is written first, and LSB of bytes is used first */ +static void writeBits(LodePNGBitWriter* writer, unsigned value, size_t nbits) { + if(nbits == 1) { /* compiler should statically compile this case if nbits == 1 */ + WRITEBIT(writer, value); + } else { + /* TODO: increase output size only once here rather than in each WRITEBIT */ + size_t i; + for(i = 0; i != nbits; ++i) { + WRITEBIT(writer, (unsigned char)((value >> i) & 1)); + } + } +} + +/* This one is to use for adding huffman symbol, the value bits are written MSB first */ +static void writeBitsReversed(LodePNGBitWriter* writer, unsigned value, size_t nbits) { + size_t i; + for(i = 0; i != nbits; ++i) { + /* TODO: increase output size only once here rather than in each WRITEBIT */ + WRITEBIT(writer, (unsigned char)((value >> (nbits - 1u - i)) & 1u)); + } +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +typedef struct { + const unsigned char* data; + size_t size; /*size of data in bytes*/ + size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/ + size_t bp; + unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/ +} LodePNGBitReader; + +/* data size argument is in bytes. Returns error if size too large causing overflow */ +static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size) { + size_t temp; + reader->data = data; + reader->size = size; + /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */ + if(lodepng_mulofl(size, 8u, &reader->bitsize)) return 105; + /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and + trying to ensure 32 more bits*/ + if(lodepng_addofl(reader->bitsize, 64u, &temp)) return 105; + reader->bp = 0; + reader->buffer = 0; + return 0; /*ok*/ +} + +/* +ensureBits functions: +Ensures the reader can at least read nbits bits in one or more readBits calls, +safely even if not enough bits are available. +Returns 1 if there are enough bits available, 0 if not. +*/ + +/*See ensureBits documentation above. This one ensures exactly 1 bit */ +/*static unsigned ensureBits1(LodePNGBitReader* reader) { + if(reader->bp >= reader->bitsize) return 0; + reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u); + return 1; +}*/ + +/*See ensureBits documentation above. This one ensures up to 9 bits */ +static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits) { + size_t start = reader->bp >> 3u; + size_t size = reader->size; + if(start + 1u < size) { + reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u); + reader->buffer >>= (reader->bp & 7u); + return 1; + } else { + reader->buffer = 0; + if(start + 0u < size) reader->buffer |= reader->data[start + 0]; + reader->buffer >>= (reader->bp & 7u); + return reader->bp + nbits <= reader->bitsize; + } +} + +/*See ensureBits documentation above. This one ensures up to 17 bits */ +static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits) { + size_t start = reader->bp >> 3u; + size_t size = reader->size; + if(start + 2u < size) { + reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | + ((unsigned)reader->data[start + 2] << 16u); + reader->buffer >>= (reader->bp & 7u); + return 1; + } else { + reader->buffer = 0; + if(start + 0u < size) reader->buffer |= reader->data[start + 0]; + if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); + reader->buffer >>= (reader->bp & 7u); + return reader->bp + nbits <= reader->bitsize; + } +} + +/*See ensureBits documentation above. This one ensures up to 25 bits */ +static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits) { + size_t start = reader->bp >> 3u; + size_t size = reader->size; + if(start + 3u < size) { + reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | + ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); + reader->buffer >>= (reader->bp & 7u); + return 1; + } else { + reader->buffer = 0; + if(start + 0u < size) reader->buffer |= reader->data[start + 0]; + if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); + if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); + reader->buffer >>= (reader->bp & 7u); + return reader->bp + nbits <= reader->bitsize; + } +} + +/*See ensureBits documentation above. This one ensures up to 32 bits */ +static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits) { + size_t start = reader->bp >> 3u; + size_t size = reader->size; + if(start + 4u < size) { + reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | + ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); + reader->buffer >>= (reader->bp & 7u); + reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u))); + return 1; + } else { + reader->buffer = 0; + if(start + 0u < size) reader->buffer |= reader->data[start + 0]; + if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); + if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); + if(start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u); + reader->buffer >>= (reader->bp & 7u); + return reader->bp + nbits <= reader->bitsize; + } +} + +/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */ +static unsigned peekBits(LodePNGBitReader* reader, size_t nbits) { + /* The shift allows nbits to be only up to 31. */ + return reader->buffer & ((1u << nbits) - 1u); +} + +/* Must have enough bits available with ensureBits */ +static void advanceBits(LodePNGBitReader* reader, size_t nbits) { + reader->buffer >>= nbits; + reader->bp += nbits; +} + +/* Must have enough bits available with ensureBits */ +static unsigned readBits(LodePNGBitReader* reader, size_t nbits) { + unsigned result = peekBits(reader, nbits); + advanceBits(reader, nbits); + return result; +} + +/* Public for testing only. steps and result must have numsteps values. */ +unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, + size_t numsteps, const size_t* steps, unsigned* result) { + size_t i; + LodePNGBitReader reader; + unsigned error = LodePNGBitReader_init(&reader, data, size); + if(error) return 0; + for(i = 0; i < numsteps; i++) { + size_t step = steps[i]; + unsigned ok; + if(step > 25) ok = ensureBits32(&reader, step); + else if(step > 17) ok = ensureBits25(&reader, step); + else if(step > 9) ok = ensureBits17(&reader, step); + else ok = ensureBits9(&reader, step); + if(!ok) return 0; + result[i] = readBits(&reader, step); + } + return 1; +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +static unsigned reverseBits(unsigned bits, unsigned num) { + /*TODO: implement faster lookup table based version when needed*/ + unsigned i, result = 0; + for(i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i; + return result; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflate - Huffman / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 +/*256 literals, the end code, some length codes, and 2 unused codes*/ +#define NUM_DEFLATE_CODE_SYMBOLS 288 +/*the distance codes have their own symbols, 30 used, 2 unused*/ +#define NUM_DISTANCE_SYMBOLS 32 +/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ +#define NUM_CODE_LENGTH_CODES 19 + +/*the base lengths represented by codes 257-285*/ +static const unsigned LENGTHBASE[29] + = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258}; + +/*the extra bits used by codes 257-285 (added to base length)*/ +static const unsigned LENGTHEXTRA[29] + = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 0}; + +/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ +static const unsigned DISTANCEBASE[30] + = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; + +/*the extra bits of backwards distances (added to base)*/ +static const unsigned DISTANCEEXTRA[30] + = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + +/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman +tree of the dynamic huffman tree lengths is generated*/ +static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] + = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Huffman tree struct, containing multiple representations of the tree +*/ +typedef struct HuffmanTree { + unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/ + unsigned* lengths; /*the lengths of the huffman codes*/ + unsigned maxbitlen; /*maximum number of bits a single code can get*/ + unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ + /* for reading only */ + unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/ + unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/ +} HuffmanTree; + +static void HuffmanTree_init(HuffmanTree* tree) { + tree->codes = 0; + tree->lengths = 0; + tree->table_len = 0; + tree->table_value = 0; +} + +static void HuffmanTree_cleanup(HuffmanTree* tree) { + lodepng_free(tree->codes); + lodepng_free(tree->lengths); + lodepng_free(tree->table_len); + lodepng_free(tree->table_value); +} + +/* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/ +/* values 8u and 9u work the fastest */ +#define FIRSTBITS 9u + +/* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination, +which is possible in case of only 0 or 1 present symbols. */ +#define INVALIDSYMBOL 65535u + +/* make table for huffman decoding */ +static unsigned HuffmanTree_makeTable(HuffmanTree* tree) { + static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/ + static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u; + size_t i, numpresent, pointer, size; /*total table size*/ + unsigned* maxlens = (unsigned*)lodepng_malloc(headsize * sizeof(unsigned)); + if(!maxlens) return 83; /*alloc fail*/ + + /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/ + lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens)); + for(i = 0; i < tree->numcodes; i++) { + unsigned symbol = tree->codes[i]; + unsigned l = tree->lengths[i]; + unsigned index; + if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/ + /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/ + index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS); + maxlens[index] = LODEPNG_MAX(maxlens[index], l); + } + /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */ + size = headsize; + for(i = 0; i < headsize; ++i) { + unsigned l = maxlens[i]; + if(l > FIRSTBITS) size += (1u << (l - FIRSTBITS)); + } + tree->table_len = (unsigned char*)lodepng_malloc(size * sizeof(*tree->table_len)); + tree->table_value = (unsigned short*)lodepng_malloc(size * sizeof(*tree->table_value)); + if(!tree->table_len || !tree->table_value) { + lodepng_free(maxlens); + /* freeing tree->table values is done at a higher scope */ + return 83; /*alloc fail*/ + } + /*initialize with an invalid length to indicate unused entries*/ + for(i = 0; i < size; ++i) tree->table_len[i] = 16; + + /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/ + pointer = headsize; + for(i = 0; i < headsize; ++i) { + unsigned l = maxlens[i]; + if(l <= FIRSTBITS) continue; + tree->table_len[i] = l; + tree->table_value[i] = pointer; + pointer += (1u << (l - FIRSTBITS)); + } + lodepng_free(maxlens); + + /*fill in the first table for short symbols, or secondary table for long symbols*/ + numpresent = 0; + for(i = 0; i < tree->numcodes; ++i) { + unsigned l = tree->lengths[i]; + unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/ + /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/ + unsigned reverse = reverseBits(symbol, l); + if(l == 0) continue; + numpresent++; + + if(l <= FIRSTBITS) { + /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/ + unsigned num = 1u << (FIRSTBITS - l); + unsigned j; + for(j = 0; j < num; ++j) { + /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/ + unsigned index = reverse | (j << l); + if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ + tree->table_len[index] = l; + tree->table_value[index] = i; + } + } else { + /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/ + /*the FIRSTBITS MSBs of the symbol are the first table index*/ + unsigned index = reverse & mask; + unsigned maxlen = tree->table_len[index]; + /*log2 of secondary table length, should be >= l - FIRSTBITS*/ + unsigned tablelen = maxlen - FIRSTBITS; + unsigned start = tree->table_value[index]; /*starting index in secondary table*/ + unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/ + unsigned j; + if(maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ + for(j = 0; j < num; ++j) { + unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */ + unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS))); + tree->table_len[index2] = l; + tree->table_value[index2] = i; + } + } + } + + if(numpresent < 2) { + /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits, + but deflate uses 1 bit instead. In case of 0 symbols, no symbols can + appear at all, but such huffman tree could still exist (e.g. if distance + codes are never used). In both cases, not all symbols of the table will be + filled in. Fill them in with an invalid symbol value so returning them from + huffmanDecodeSymbol will cause error. */ + for(i = 0; i < size; ++i) { + if(tree->table_len[i] == 16) { + /* As length, use a value smaller than FIRSTBITS for the head table, + and a value larger than FIRSTBITS for the secondary table, to ensure + valid behavior for advanceBits when reading this symbol. */ + tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1); + tree->table_value[i] = INVALIDSYMBOL; + } + } + } else { + /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. + If that is not the case (due to too long length codes), the table will not + have been fully used, and this is an error (not all bit combinations can be + decoded): an oversubscribed huffman tree, indicated by error 55. */ + for(i = 0; i < size; ++i) { + if(tree->table_len[i] == 16) return 55; + } + } + + return 0; +} + +/* +Second step for the ...makeFromLengths and ...makeFromFrequencies functions. +numcodes, lengths and maxbitlen must already be filled in correctly. return +value is error. +*/ +static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) { + unsigned* blcount; + unsigned* nextcode; + unsigned error = 0; + unsigned bits, n; + + tree->codes = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); + blcount = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned)); + nextcode = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned)); + if(!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/ + + if(!error) { + for(n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0; + /*step 1: count number of instances of each code length*/ + for(bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]]; + /*step 2: generate the nextcode values*/ + for(bits = 1; bits <= tree->maxbitlen; ++bits) { + nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u; + } + /*step 3: generate all the codes*/ + for(n = 0; n != tree->numcodes; ++n) { + if(tree->lengths[n] != 0) { + tree->codes[n] = nextcode[tree->lengths[n]]++; + /*remove superfluous bits from the code*/ + tree->codes[n] &= ((1u << tree->lengths[n]) - 1u); + } + } + } + + lodepng_free(blcount); + lodepng_free(nextcode); + + if(!error) error = HuffmanTree_makeTable(tree); + return error; +} + +/* +given the code lengths (as stored in the PNG file), generate the tree as defined +by Deflate. maxbitlen is the maximum bits that a code in the tree can have. +return value is error. +*/ +static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, + size_t numcodes, unsigned maxbitlen) { + unsigned i; + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->maxbitlen = maxbitlen; + return HuffmanTree_makeFromLengths2(tree); +} + +#ifdef LODEPNG_COMPILE_ENCODER + +/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", +Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ + +/*chain node for boundary package merge*/ +typedef struct BPMNode { + int weight; /*the sum of all weights in this chain*/ + unsigned index; /*index of this leaf node (called "count" in the paper)*/ + struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ + int in_use; +} BPMNode; + +/*lists of chains*/ +typedef struct BPMLists { + /*memory pool*/ + unsigned memsize; + BPMNode* memory; + unsigned numfree; + unsigned nextfree; + BPMNode** freelist; + /*two heads of lookahead chains per list*/ + unsigned listsize; + BPMNode** chains0; + BPMNode** chains1; +} BPMLists; + +/*creates a new chain node with the given parameters, from the memory in the lists */ +static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) { + unsigned i; + BPMNode* result; + + /*memory full, so garbage collect*/ + if(lists->nextfree >= lists->numfree) { + /*mark only those that are in use*/ + for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; + for(i = 0; i != lists->listsize; ++i) { + BPMNode* node; + for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; + for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; + } + /*collect those that are free*/ + lists->numfree = 0; + for(i = 0; i != lists->memsize; ++i) { + if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; + } + lists->nextfree = 0; + } + + result = lists->freelist[lists->nextfree++]; + result->weight = weight; + result->index = index; + result->tail = tail; + return result; +} + +/*sort the leaves with stable mergesort*/ +static void bpmnode_sort(BPMNode* leaves, size_t num) { + BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); + size_t width, counter = 0; + for(width = 1; width < num; width *= 2) { + BPMNode* a = (counter & 1) ? mem : leaves; + BPMNode* b = (counter & 1) ? leaves : mem; + size_t p; + for(p = 0; p < num; p += 2 * width) { + size_t q = (p + width > num) ? num : (p + width); + size_t r = (p + 2 * width > num) ? num : (p + 2 * width); + size_t i = p, j = q, k; + for(k = p; k < r; k++) { + if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; + else b[k] = a[j++]; + } + } + counter++; + } + if(counter & 1) lodepng_memcpy(leaves, mem, sizeof(*leaves) * num); + lodepng_free(mem); +} + +/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ +static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) { + unsigned lastindex = lists->chains1[c]->index; + + if(c == 0) { + if(lastindex >= numpresent) return; + lists->chains0[c] = lists->chains1[c]; + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); + } else { + /*sum of the weights of the head nodes of the previous lookahead chains.*/ + int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; + lists->chains0[c] = lists->chains1[c]; + if(lastindex < numpresent && sum > leaves[lastindex].weight) { + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); + return; + } + lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); + /*in the end we are only interested in the chain of the last list, so no + need to recurse if we're at the last one (this gives measurable speedup)*/ + if(num + 1 < (int)(2 * numpresent - 2)) { + boundaryPM(lists, leaves, numpresent, c - 1, num); + boundaryPM(lists, leaves, numpresent, c - 1, num); + } + } +} + +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen) { + unsigned error = 0; + unsigned i; + size_t numpresent = 0; /*number of symbols with non-zero frequency*/ + BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ + + if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ + if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/ + + leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); + if(!leaves) return 83; /*alloc fail*/ + + for(i = 0; i != numcodes; ++i) { + if(frequencies[i] > 0) { + leaves[numpresent].weight = (int)frequencies[i]; + leaves[numpresent].index = i; + ++numpresent; + } + } + + lodepng_memset(lengths, 0, numcodes * sizeof(*lengths)); + + /*ensure at least two present symbols. There should be at least one symbol + according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To + make these work as well ensure there are at least two symbols. The + Package-Merge code below also doesn't work correctly if there's only one + symbol, it'd give it the theoretical 0 bits but in practice zlib wants 1 bit*/ + if(numpresent == 0) { + lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ + } else if(numpresent == 1) { + lengths[leaves[0].index] = 1; + lengths[leaves[0].index == 0 ? 1 : 0] = 1; + } else { + BPMLists lists; + BPMNode* node; + + bpmnode_sort(leaves, numpresent); + + lists.listsize = maxbitlen; + lists.memsize = 2 * maxbitlen * (maxbitlen + 1); + lists.nextfree = 0; + lists.numfree = lists.memsize; + lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); + lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); + lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ + + if(!error) { + for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; + + bpmnode_create(&lists, leaves[0].weight, 1, 0); + bpmnode_create(&lists, leaves[1].weight, 2, 0); + + for(i = 0; i != lists.listsize; ++i) { + lists.chains0[i] = &lists.memory[0]; + lists.chains1[i] = &lists.memory[1]; + } + + /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ + for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); + + for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) { + for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; + } + } + + lodepng_free(lists.memory); + lodepng_free(lists.freelist); + lodepng_free(lists.chains0); + lodepng_free(lists.chains1); + } + + lodepng_free(leaves); + return error; +} + +/*Create the Huffman tree given the symbol frequencies*/ +static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, + size_t mincodes, size_t numcodes, unsigned maxbitlen) { + unsigned error = 0; + while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + tree->maxbitlen = maxbitlen; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + + error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); + if(!error) error = HuffmanTree_makeFromLengths2(tree); + return error; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ +static unsigned generateFixedLitLenTree(HuffmanTree* tree) { + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ + for(i = 0; i <= 143; ++i) bitlen[i] = 8; + for(i = 144; i <= 255; ++i) bitlen[i] = 9; + for(i = 256; i <= 279; ++i) bitlen[i] = 7; + for(i = 280; i <= 287; ++i) bitlen[i] = 8; + + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static unsigned generateFixedDistanceTree(HuffmanTree* tree) { + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*there are 32 distance codes, but 30-31 are unused*/ + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* +returns the code. The bit reader must already have been ensured at least 15 bits +*/ +static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree) { + unsigned short code = peekBits(reader, FIRSTBITS); + unsigned short l = codetree->table_len[code]; + unsigned short value = codetree->table_value[code]; + if(l <= FIRSTBITS) { + advanceBits(reader, l); + return value; + } else { + unsigned index2; + advanceBits(reader, FIRSTBITS); + index2 = value + peekBits(reader, l - FIRSTBITS); + advanceBits(reader, codetree->table_len[index2] - FIRSTBITS); + return codetree->table_value[index2]; + } +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Inflator (Decompressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification +Returns error code.*/ +static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) { + unsigned error = generateFixedLitLenTree(tree_ll); + if(error) return error; + return generateFixedDistanceTree(tree_d); +} + +/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ +static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, + LodePNGBitReader* reader) { + /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ + unsigned error = 0; + unsigned n, HLIT, HDIST, HCLEN, i; + + /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ + unsigned* bitlen_ll = 0; /*lit,len code lengths*/ + unsigned* bitlen_d = 0; /*dist code lengths*/ + /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ + unsigned* bitlen_cl = 0; + HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ + + if(!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/ + + /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ + HLIT = readBits(reader, 5) + 257; + /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ + HDIST = readBits(reader, 5) + 1; + /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ + HCLEN = readBits(reader, 4) + 4; + + bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); + if(!bitlen_cl) return 83 /*alloc fail*/; + + HuffmanTree_init(&tree_cl); + + while(!error) { + /*read the code length codes out of 3 * (amount of code length codes) bits*/ + if(lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) { + ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/ + } + for(i = 0; i != HCLEN; ++i) { + ensureBits9(reader, 3); /*out of bounds already checked above */ + bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3); + } + for(i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) { + bitlen_cl[CLCL_ORDER[i]] = 0; + } + + error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); + if(error) break; + + /*now we can use this tree to read the lengths for the tree that this function will return*/ + bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); + lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll)); + lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d)); + + /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ + i = 0; + while(i < HLIT + HDIST) { + unsigned code; + ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/ + code = huffmanDecodeSymbol(reader, &tree_cl); + if(code <= 15) /*a length code*/ { + if(i < HLIT) bitlen_ll[i] = code; + else bitlen_d[i - HLIT] = code; + ++i; + } else if(code == 16) /*repeat previous*/ { + unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ + unsigned value; /*set value to the previous code*/ + + if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + + replength += readBits(reader, 2); + + if(i < HLIT + 1) value = bitlen_ll[i - 1]; + else value = bitlen_d[i - HLIT - 1]; + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) { + if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ + if(i < HLIT) bitlen_ll[i] = value; + else bitlen_d[i - HLIT] = value; + ++i; + } + } else if(code == 17) /*repeat "0" 3-10 times*/ { + unsigned replength = 3; /*read in the bits that indicate repeat length*/ + replength += readBits(reader, 3); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) { + if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } else if(code == 18) /*repeat "0" 11-138 times*/ { + unsigned replength = 11; /*read in the bits that indicate repeat length*/ + replength += readBits(reader, 7); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) { + if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } else /*if(code == INVALIDSYMBOL)*/ { + ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ + } + /*check if any of the ensureBits above went out of bounds*/ + if(reader->bp > reader->bitsize) { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ + ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + } + } + if(error) break; + + if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ + + /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ + error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); + if(error) break; + error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); + + break; /*end of error-while*/ + } + + lodepng_free(bitlen_cl); + lodepng_free(bitlen_ll); + lodepng_free(bitlen_d); + HuffmanTree_cleanup(&tree_cl); + + return error; +} + +/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/ +static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, + unsigned btype, size_t max_output_size) { + unsigned error = 0; + HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ + HuffmanTree tree_d; /*the huffman tree for distance codes*/ + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + if(btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d); + else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader); + + while(!error) /*decode all symbols until end reached, breaks at end code*/ { + /*code_ll is literal, length or end code*/ + unsigned code_ll; + ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */ + code_ll = huffmanDecodeSymbol(reader, &tree_ll); + if(code_ll <= 255) /*literal symbol*/ { + if(!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/); + out->data[out->size - 1] = (unsigned char)code_ll; + } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { + unsigned code_d, distance; + unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ + size_t start, backward, length; + + /*part 1: get length base*/ + length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; + + /*part 2: get extra bits and add the value of that to length*/ + numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; + if(numextrabits_l != 0) { + /* bits already ensured above */ + length += readBits(reader, numextrabits_l); + } + + /*part 3: get distance code*/ + ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */ + code_d = huffmanDecodeSymbol(reader, &tree_d); + if(code_d > 29) { + if(code_d <= 31) { + ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/ + } else /* if(code_d == INVALIDSYMBOL) */{ + ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ + } + } + distance = DISTANCEBASE[code_d]; + + /*part 4: get extra bits from distance*/ + numextrabits_d = DISTANCEEXTRA[code_d]; + if(numextrabits_d != 0) { + /* bits already ensured above */ + distance += readBits(reader, numextrabits_d); + } + + /*part 5: fill in all the out[n] values based on the length and dist*/ + start = out->size; + if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ + backward = start - distance; + + if(!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/); + if(distance < length) { + size_t forward; + lodepng_memcpy(out->data + start, out->data + backward, distance); + start += distance; + for(forward = distance; forward < length; ++forward) { + out->data[start++] = out->data[backward++]; + } + } else { + lodepng_memcpy(out->data + start, out->data + backward, length); + } + } else if(code_ll == 256) { + break; /*end code, break the loop*/ + } else /*if(code_ll == INVALIDSYMBOL)*/ { + ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ + } + /*check if any of the ensureBits above went out of bounds*/ + if(reader->bp > reader->bitsize) { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ + ERROR_BREAK(51); /*error, bit pointer jumps past memory*/ + } + if(max_output_size && out->size > max_output_size) { + ERROR_BREAK(109); /*error, larger than max size*/ + } + } + + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, + const LodePNGDecompressSettings* settings) { + size_t bytepos; + size_t size = reader->size; + unsigned LEN, NLEN, error = 0; + + /*go to first boundary of byte*/ + bytepos = (reader->bp + 7u) >> 3u; + + /*read LEN (2 bytes) and NLEN (2 bytes)*/ + if(bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/ + LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; + NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; + + /*check if 16-bit NLEN is really the one's complement of LEN*/ + if(!settings->ignore_nlen && LEN + NLEN != 65535) { + return 21; /*error: NLEN is not one's complement of LEN*/ + } + + if(!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/ + + /*read the literal data: LEN bytes are now stored in the out buffer*/ + if(bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/ + + lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN); + bytepos += LEN; + + reader->bp = bytepos << 3u; + + return error; +} + +static unsigned lodepng_inflatev(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { + unsigned BFINAL = 0; + LodePNGBitReader reader; + unsigned error = LodePNGBitReader_init(&reader, in, insize); + + if(error) return error; + + while(!BFINAL) { + unsigned BTYPE; + if(!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/ + BFINAL = readBits(&reader, 1); + BTYPE = readBits(&reader, 2); + + if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ + else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/ + else error = inflateHuffmanBlock(out, &reader, BTYPE, settings->max_output_size); /*compression, BTYPE 01 or 10*/ + if(!error && settings->max_output_size && out->size > settings->max_output_size) error = 109; + if(error) break; + } + + return error; +} + +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_inflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { + if(settings->custom_inflate) { + unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings); + out->allocsize = out->size; + if(error) { + /*the custom inflate is allowed to have its own error codes, however, we translate it to code 110*/ + error = 110; + /*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/ + if(settings->max_output_size && out->size > settings->max_output_size) error = 109; + } + return error; + } else { + return lodepng_inflatev(out, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflator (Compressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; + +/*search the index in the array, that has the largest value smaller than or equal to the given value, +given array must be sorted (if no value is smaller, it returns the size of the given array)*/ +static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) { + /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ + size_t left = 1; + size_t right = array_size - 1; + + while(left <= right) { + size_t mid = (left + right) >> 1; + if(array[mid] >= value) right = mid - 1; + else left = mid + 1; + } + if(left >= array_size || array[left] > value) left--; + return left; +} + +static void addLengthDistance(uivector* values, size_t length, size_t distance) { + /*values in encoded vector are those used by deflate: + 0-255: literal bytes + 256: end + 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) + 286-287: invalid*/ + + unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); + unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); + unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); + unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); + + size_t pos = values->size; + /*TODO: return error when this fails (out of memory)*/ + unsigned ok = uivector_resize(values, values->size + 4); + if(ok) { + values->data[pos + 0] = length_code + FIRST_LENGTH_CODE_INDEX; + values->data[pos + 1] = extra_length; + values->data[pos + 2] = dist_code; + values->data[pos + 3] = extra_distance; + } +} + +/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 +bytes as input because 3 is the minimum match length for deflate*/ +static const unsigned HASH_NUM_VALUES = 65536; +static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ + +typedef struct Hash { + int* head; /*hash value to head circular pos - can be outdated if went around window*/ + /*circular pos to prev circular pos*/ + unsigned short* chain; + int* val; /*circular pos to hash value*/ + + /*TODO: do this not only for zeros but for any repeated byte. However for PNG + it's always going to be the zeros that dominate, so not important for PNG*/ + int* headz; /*similar to head, but for chainz*/ + unsigned short* chainz; /*those with same amount of zeros*/ + unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ +} Hash; + +static unsigned hash_init(Hash* hash, unsigned windowsize) { + unsigned i; + hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); + hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); + hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); + hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) { + return 83; /*alloc fail*/ + } + + /*initialize hash table*/ + for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; + for(i = 0; i != windowsize; ++i) hash->val[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + + for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ + + return 0; +} + +static void hash_cleanup(Hash* hash) { + lodepng_free(hash->head); + lodepng_free(hash->val); + lodepng_free(hash->chain); + + lodepng_free(hash->zeros); + lodepng_free(hash->headz); + lodepng_free(hash->chainz); +} + + + +static unsigned getHash(const unsigned char* data, size_t size, size_t pos) { + unsigned result = 0; + if(pos + 2 < size) { + /*A simple shift and xor hash is used. Since the data of PNGs is dominated + by zeroes due to the filters, a better hash does not have a significant + effect on speed in traversing the chain, and causes more time spend on + calculating the hash.*/ + result ^= ((unsigned)data[pos + 0] << 0u); + result ^= ((unsigned)data[pos + 1] << 4u); + result ^= ((unsigned)data[pos + 2] << 8u); + } else { + size_t amount, i; + if(pos >= size) return 0; + amount = size - pos; + for(i = 0; i != amount; ++i) result ^= ((unsigned)data[pos + i] << (i * 8u)); + } + return result & HASH_BIT_MASK; +} + +static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) { + const unsigned char* start = data + pos; + const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; + if(end > data + size) end = data + size; + data = start; + while(data != end && *data == 0) ++data; + /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ + return (unsigned)(data - start); +} + +/*wpos = pos & (windowsize - 1)*/ +static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) { + hash->val[wpos] = (int)hashval; + if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; + hash->head[hashval] = (int)wpos; + + hash->zeros[wpos] = numzeros; + if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; + hash->headz[numzeros] = (int)wpos; +} + +/* +LZ77-encode the data. Return value is error code. The input are raw bytes, the output +is in the form of unsigned integers with codes representing for example literal bytes, or +length/distance pairs. +It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a +sliding window (of windowsize) is used, and all past bytes in that window can be used as +the "dictionary". A brute force search through all possible distances would be slow, and +this hash technique is one out of several ways to speed this up. +*/ +static unsigned encodeLZ77(uivector* out, Hash* hash, + const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, + unsigned minmatch, unsigned nicematch, unsigned lazymatching) { + size_t pos; + unsigned i, error = 0; + /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ + unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8u; + unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; + + unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ + unsigned numzeros = 0; + + unsigned offset; /*the offset represents the distance in LZ77 terminology*/ + unsigned length; + unsigned lazy = 0; + unsigned lazylength = 0, lazyoffset = 0; + unsigned hashval; + unsigned current_offset, current_length; + unsigned prev_offset; + const unsigned char *lastptr, *foreptr, *backptr; + unsigned hashpos; + + if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ + if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ + + if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; + + for(pos = inpos; pos < insize; ++pos) { + size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ + unsigned chainlength = 0; + + hashval = getHash(in, insize, pos); + + if(usezeros && hashval == 0) { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } else { + numzeros = 0; + } + + updateHashChain(hash, wpos, hashval, numzeros); + + /*the length and offset found for the current position*/ + length = 0; + offset = 0; + + hashpos = hash->chain[wpos]; + + lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; + + /*search for the longest string*/ + prev_offset = 0; + for(;;) { + if(chainlength++ >= maxchainlength) break; + current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize); + + if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ + prev_offset = current_offset; + if(current_offset > 0) { + /*test the next characters*/ + foreptr = &in[pos]; + backptr = &in[pos - current_offset]; + + /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ + if(numzeros >= 3) { + unsigned skip = hash->zeros[hashpos]; + if(skip > numzeros) skip = numzeros; + backptr += skip; + foreptr += skip; + } + + while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ { + ++backptr; + ++foreptr; + } + current_length = (unsigned)(foreptr - &in[pos]); + + if(current_length > length) { + length = current_length; /*the longest length*/ + offset = current_offset; /*the offset that is related to this longest length*/ + /*jump out once a length of max length is found (speed gain). This also jumps + out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ + if(current_length >= nicematch) break; + } + } + + if(hashpos == hash->chain[hashpos]) break; + + if(numzeros >= 3 && length > numzeros) { + hashpos = hash->chainz[hashpos]; + if(hash->zeros[hashpos] != numzeros) break; + } else { + hashpos = hash->chain[hashpos]; + /*outdated hash value, happens if particular value was not encountered in whole last window*/ + if(hash->val[hashpos] != (int)hashval) break; + } + } + + if(lazymatching) { + if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) { + lazy = 1; + lazylength = length; + lazyoffset = offset; + continue; /*try the next byte*/ + } + if(lazy) { + lazy = 0; + if(pos == 0) ERROR_BREAK(81); + if(length > lazylength + 1) { + /*push the previous character as literal*/ + if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); + } else { + length = lazylength; + offset = lazyoffset; + hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ + hash->headz[numzeros] = -1; /*idem*/ + --pos; + } + } + } + if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); + + /*encode it as length/distance pair or literal value*/ + if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ { + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } else if(length < minmatch || (length == 3 && offset > 4096)) { + /*compensate for the fact that longer offsets have more extra bits, a + length of only 3 may be not worth it then*/ + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } else { + addLengthDistance(out, length, offset); + for(i = 1; i < length; ++i) { + ++pos; + wpos = pos & (windowsize - 1); + hashval = getHash(in, insize, pos); + if(usezeros && hashval == 0) { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } else { + numzeros = 0; + } + updateHashChain(hash, wpos, hashval, numzeros); + } + } + } /*end of the loop through each character of input*/ + + return error; +} + +/* /////////////////////////////////////////////////////////////////////////// */ + +static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) { + /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, + 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ + + size_t i, numdeflateblocks = (datasize + 65534u) / 65535u; + unsigned datapos = 0; + for(i = 0; i != numdeflateblocks; ++i) { + unsigned BFINAL, BTYPE, LEN, NLEN; + unsigned char firstbyte; + size_t pos = out->size; + + BFINAL = (i == numdeflateblocks - 1); + BTYPE = 0; + + LEN = 65535; + if(datasize - datapos < 65535u) LEN = (unsigned)datasize - datapos; + NLEN = 65535 - LEN; + + if(!ucvector_resize(out, out->size + LEN + 5)) return 83; /*alloc fail*/ + + firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1u) << 1u) + ((BTYPE & 2u) << 1u)); + out->data[pos + 0] = firstbyte; + out->data[pos + 1] = (unsigned char)(LEN & 255); + out->data[pos + 2] = (unsigned char)(LEN >> 8u); + out->data[pos + 3] = (unsigned char)(NLEN & 255); + out->data[pos + 4] = (unsigned char)(NLEN >> 8u); + lodepng_memcpy(out->data + pos + 5, data + datapos, LEN); + datapos += LEN; + } + + return 0; +} + +/* +write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. +tree_ll: the tree for lit and len codes. +tree_d: the tree for distance codes. +*/ +static void writeLZ77data(LodePNGBitWriter* writer, const uivector* lz77_encoded, + const HuffmanTree* tree_ll, const HuffmanTree* tree_d) { + size_t i = 0; + for(i = 0; i != lz77_encoded->size; ++i) { + unsigned val = lz77_encoded->data[i]; + writeBitsReversed(writer, tree_ll->codes[val], tree_ll->lengths[val]); + if(val > 256) /*for a length code, 3 more things have to be added*/ { + unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; + unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; + unsigned length_extra_bits = lz77_encoded->data[++i]; + + unsigned distance_code = lz77_encoded->data[++i]; + + unsigned distance_index = distance_code; + unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; + unsigned distance_extra_bits = lz77_encoded->data[++i]; + + writeBits(writer, length_extra_bits, n_length_extra_bits); + writeBitsReversed(writer, tree_d->codes[distance_code], tree_d->lengths[distance_code]); + writeBits(writer, distance_extra_bits, n_distance_extra_bits); + } + } +} + +/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ +static unsigned deflateDynamic(LodePNGBitWriter* writer, Hash* hash, + const unsigned char* data, size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) { + unsigned error = 0; + + /* + A block is compressed as follows: The PNG data is lz77 encoded, resulting in + literal bytes and length/distance pairs. This is then huffman compressed with + two huffman trees. One huffman tree is used for the lit and len values ("ll"), + another huffman tree is used for the dist values ("d"). These two trees are + stored using their code lengths, and to compress even more these code lengths + are also run-length encoded and huffman compressed. This gives a huffman tree + of code lengths "cl". The code lengths used to describe this third tree are + the code length code lengths ("clcl"). + */ + + /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ + uivector lz77_encoded; + HuffmanTree tree_ll; /*tree for lit,len values*/ + HuffmanTree tree_d; /*tree for distance codes*/ + HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ + unsigned* frequencies_ll = 0; /*frequency of lit,len codes*/ + unsigned* frequencies_d = 0; /*frequency of dist codes*/ + unsigned* frequencies_cl = 0; /*frequency of code length codes*/ + unsigned* bitlen_lld = 0; /*lit,len,dist code lengths (int bits), literally (without repeat codes).*/ + unsigned* bitlen_lld_e = 0; /*bitlen_lld encoded with repeat codes (this is a rudimentary run length compression)*/ + size_t datasize = dataend - datapos; + + /* + If we could call "bitlen_cl" the the code length code lengths ("clcl"), that is the bit lengths of codes to represent + tree_cl in CLCL_ORDER, then due to the huffman compression of huffman tree representations ("two levels"), there are + some analogies: + bitlen_lld is to tree_cl what data is to tree_ll and tree_d. + bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. + bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. + */ + + unsigned BFINAL = final; + size_t i; + size_t numcodes_ll, numcodes_d, numcodes_lld, numcodes_lld_e, numcodes_cl; + unsigned HLIT, HDIST, HCLEN; + + uivector_init(&lz77_encoded); + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + HuffmanTree_init(&tree_cl); + /* could fit on stack, but >1KB is on the larger side so allocate instead */ + frequencies_ll = (unsigned*)lodepng_malloc(286 * sizeof(*frequencies_ll)); + frequencies_d = (unsigned*)lodepng_malloc(30 * sizeof(*frequencies_d)); + frequencies_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl)); + + if(!frequencies_ll || !frequencies_d || !frequencies_cl) error = 83; /*alloc fail*/ + + /*This while loop never loops due to a break at the end, it is here to + allow breaking out of it to the cleanup phase on error conditions.*/ + while(!error) { + lodepng_memset(frequencies_ll, 0, 286 * sizeof(*frequencies_ll)); + lodepng_memset(frequencies_d, 0, 30 * sizeof(*frequencies_d)); + lodepng_memset(frequencies_cl, 0, NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl)); + + if(settings->use_lz77) { + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(error) break; + } else { + if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); + for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ + } + + /*Count the frequencies of lit, len and dist codes*/ + for(i = 0; i != lz77_encoded.size; ++i) { + unsigned symbol = lz77_encoded.data[i]; + ++frequencies_ll[symbol]; + if(symbol > 256) { + unsigned dist = lz77_encoded.data[i + 2]; + ++frequencies_d[dist]; + i += 3; + } + } + frequencies_ll[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ + + /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ + error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll, 257, 286, 15); + if(error) break; + /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ + error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d, 2, 30, 15); + if(error) break; + + numcodes_ll = LODEPNG_MIN(tree_ll.numcodes, 286); + numcodes_d = LODEPNG_MIN(tree_d.numcodes, 30); + /*store the code lengths of both generated trees in bitlen_lld*/ + numcodes_lld = numcodes_ll + numcodes_d; + bitlen_lld = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld)); + /*numcodes_lld_e never needs more size than bitlen_lld*/ + bitlen_lld_e = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld_e)); + if(!bitlen_lld || !bitlen_lld_e) ERROR_BREAK(83); /*alloc fail*/ + numcodes_lld_e = 0; + + for(i = 0; i != numcodes_ll; ++i) bitlen_lld[i] = tree_ll.lengths[i]; + for(i = 0; i != numcodes_d; ++i) bitlen_lld[numcodes_ll + i] = tree_d.lengths[i]; + + /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), + 17 (3-10 zeroes), 18 (11-138 zeroes)*/ + for(i = 0; i != numcodes_lld; ++i) { + unsigned j = 0; /*amount of repetitions*/ + while(i + j + 1 < numcodes_lld && bitlen_lld[i + j + 1] == bitlen_lld[i]) ++j; + + if(bitlen_lld[i] == 0 && j >= 2) /*repeat code for zeroes*/ { + ++j; /*include the first zero*/ + if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ { + bitlen_lld_e[numcodes_lld_e++] = 17; + bitlen_lld_e[numcodes_lld_e++] = j - 3; + } else /*repeat code 18 supports max 138 zeroes*/ { + if(j > 138) j = 138; + bitlen_lld_e[numcodes_lld_e++] = 18; + bitlen_lld_e[numcodes_lld_e++] = j - 11; + } + i += (j - 1); + } else if(j >= 3) /*repeat code for value other than zero*/ { + size_t k; + unsigned num = j / 6u, rest = j % 6u; + bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i]; + for(k = 0; k < num; ++k) { + bitlen_lld_e[numcodes_lld_e++] = 16; + bitlen_lld_e[numcodes_lld_e++] = 6 - 3; + } + if(rest >= 3) { + bitlen_lld_e[numcodes_lld_e++] = 16; + bitlen_lld_e[numcodes_lld_e++] = rest - 3; + } + else j -= rest; + i += j; + } else /*too short to benefit from repeat code*/ { + bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i]; + } + } + + /*generate tree_cl, the huffmantree of huffmantrees*/ + for(i = 0; i != numcodes_lld_e; ++i) { + ++frequencies_cl[bitlen_lld_e[i]]; + /*after a repeat code come the bits that specify the number of repetitions, + those don't need to be in the frequencies_cl calculation*/ + if(bitlen_lld_e[i] >= 16) ++i; + } + + error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl, + NUM_CODE_LENGTH_CODES, NUM_CODE_LENGTH_CODES, 7); + if(error) break; + + /*compute amount of code-length-code-lengths to output*/ + numcodes_cl = NUM_CODE_LENGTH_CODES; + /*trim zeros at the end (using CLCL_ORDER), but minimum size must be 4 (see HCLEN below)*/ + while(numcodes_cl > 4u && tree_cl.lengths[CLCL_ORDER[numcodes_cl - 1u]] == 0) { + numcodes_cl--; + } + + /* + Write everything into the output + + After the BFINAL and BTYPE, the dynamic block consists out of the following: + - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN + - (HCLEN+4)*3 bits code lengths of code length alphabet + - HLIT + 257 code lengths of lit/length alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - HDIST + 1 code lengths of distance alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - compressed data + - 256 (end code) + */ + + /*Write block type*/ + writeBits(writer, BFINAL, 1); + writeBits(writer, 0, 1); /*first bit of BTYPE "dynamic"*/ + writeBits(writer, 1, 1); /*second bit of BTYPE "dynamic"*/ + + /*write the HLIT, HDIST and HCLEN values*/ + /*all three sizes take trimmed ending zeroes into account, done either by HuffmanTree_makeFromFrequencies + or in the loop for numcodes_cl above, which saves space. */ + HLIT = (unsigned)(numcodes_ll - 257); + HDIST = (unsigned)(numcodes_d - 1); + HCLEN = (unsigned)(numcodes_cl - 4); + writeBits(writer, HLIT, 5); + writeBits(writer, HDIST, 5); + writeBits(writer, HCLEN, 4); + + /*write the code lengths of the code length alphabet ("bitlen_cl")*/ + for(i = 0; i != numcodes_cl; ++i) writeBits(writer, tree_cl.lengths[CLCL_ORDER[i]], 3); + + /*write the lengths of the lit/len AND the dist alphabet*/ + for(i = 0; i != numcodes_lld_e; ++i) { + writeBitsReversed(writer, tree_cl.codes[bitlen_lld_e[i]], tree_cl.lengths[bitlen_lld_e[i]]); + /*extra bits of repeat codes*/ + if(bitlen_lld_e[i] == 16) writeBits(writer, bitlen_lld_e[++i], 2); + else if(bitlen_lld_e[i] == 17) writeBits(writer, bitlen_lld_e[++i], 3); + else if(bitlen_lld_e[i] == 18) writeBits(writer, bitlen_lld_e[++i], 7); + } + + /*write the compressed data symbols*/ + writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); + /*error: the length of the end code 256 must be larger than 0*/ + if(tree_ll.lengths[256] == 0) ERROR_BREAK(64); + + /*write the end code*/ + writeBitsReversed(writer, tree_ll.codes[256], tree_ll.lengths[256]); + + break; /*end of error-while*/ + } + + /*cleanup*/ + uivector_cleanup(&lz77_encoded); + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + HuffmanTree_cleanup(&tree_cl); + lodepng_free(frequencies_ll); + lodepng_free(frequencies_d); + lodepng_free(frequencies_cl); + lodepng_free(bitlen_lld); + lodepng_free(bitlen_lld_e); + + return error; +} + +static unsigned deflateFixed(LodePNGBitWriter* writer, Hash* hash, + const unsigned char* data, + size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) { + HuffmanTree tree_ll; /*tree for literal values and length codes*/ + HuffmanTree tree_d; /*tree for distance codes*/ + + unsigned BFINAL = final; + unsigned error = 0; + size_t i; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + error = generateFixedLitLenTree(&tree_ll); + if(!error) error = generateFixedDistanceTree(&tree_d); + + if(!error) { + writeBits(writer, BFINAL, 1); + writeBits(writer, 1, 1); /*first bit of BTYPE*/ + writeBits(writer, 0, 1); /*second bit of BTYPE*/ + + if(settings->use_lz77) /*LZ77 encoded*/ { + uivector lz77_encoded; + uivector_init(&lz77_encoded); + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(!error) writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); + uivector_cleanup(&lz77_encoded); + } else /*no LZ77, but still will be Huffman compressed*/ { + for(i = datapos; i < dataend; ++i) { + writeBitsReversed(writer, tree_ll.codes[data[i]], tree_ll.lengths[data[i]]); + } + } + /*add END code*/ + if(!error) writeBitsReversed(writer,tree_ll.codes[256], tree_ll.lengths[256]); + } + + /*cleanup*/ + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) { + unsigned error = 0; + size_t i, blocksize, numdeflateblocks; + Hash hash; + LodePNGBitWriter writer; + + LodePNGBitWriter_init(&writer, out); + + if(settings->btype > 2) return 61; + else if(settings->btype == 0) return deflateNoCompression(out, in, insize); + else if(settings->btype == 1) blocksize = insize; + else /*if(settings->btype == 2)*/ { + /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ + blocksize = insize / 8u + 8; + if(blocksize < 65536) blocksize = 65536; + if(blocksize > 262144) blocksize = 262144; + } + + numdeflateblocks = (insize + blocksize - 1) / blocksize; + if(numdeflateblocks == 0) numdeflateblocks = 1; + + error = hash_init(&hash, settings->windowsize); + + if(!error) { + for(i = 0; i != numdeflateblocks && !error; ++i) { + unsigned final = (i == numdeflateblocks - 1); + size_t start = i * blocksize; + size_t end = start + blocksize; + if(end > insize) end = insize; + + if(settings->btype == 1) error = deflateFixed(&writer, &hash, in, start, end, settings, final); + else if(settings->btype == 2) error = deflateDynamic(&writer, &hash, in, start, end, settings, final); + } + } + + hash_cleanup(&hash); + + return error; +} + +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_deflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) { + if(settings->custom_deflate) { + unsigned error = settings->custom_deflate(out, outsize, in, insize, settings); + /*the custom deflate is allowed to have its own error codes, however, we translate it to code 111*/ + return error ? 111 : 0; + } else { + return lodepng_deflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Adler32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) { + unsigned s1 = adler & 0xffffu; + unsigned s2 = (adler >> 16u) & 0xffffu; + + while(len != 0u) { + unsigned i; + /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/ + unsigned amount = len > 5552u ? 5552u : len; + len -= amount; + for(i = 0; i != amount; ++i) { + s1 += (*data++); + s2 += s1; + } + s1 %= 65521u; + s2 %= 65521u; + } + + return (s2 << 16u) | s1; +} + +/*Return the adler32 of the bytes data[0..len-1]*/ +static unsigned adler32(const unsigned char* data, unsigned len) { + return update_adler32(1u, data, len); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DECODER + +static unsigned lodepng_zlib_decompressv(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { + unsigned error = 0; + unsigned CM, CINFO, FDICT; + + if(insize < 2) return 53; /*error, size of zlib data too small*/ + /*read information from zlib header*/ + if((in[0] * 256 + in[1]) % 31 != 0) { + /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ + return 24; + } + + CM = in[0] & 15; + CINFO = (in[0] >> 4) & 15; + /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ + FDICT = (in[1] >> 5) & 1; + /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ + + if(CM != 8 || CINFO > 7) { + /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ + return 25; + } + if(FDICT != 0) { + /*error: the specification of PNG says about the zlib stream: + "The additional flags shall not specify a preset dictionary."*/ + return 26; + } + + error = inflatev(out, in + 2, insize - 2, settings); + if(error) return error; + + if(!settings->ignore_adler32) { + unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); + unsigned checksum = adler32(out->data, (unsigned)(out->size)); + if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ + } + + return 0; /*no error*/ +} + + +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_zlib_decompressv(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */ +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, + const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { + unsigned error; + if(settings->custom_zlib) { + error = settings->custom_zlib(out, outsize, in, insize, settings); + if(error) { + /*the custom zlib is allowed to have its own error codes, however, we translate it to code 110*/ + error = 110; + /*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/ + if(settings->max_output_size && *outsize > settings->max_output_size) error = 109; + } + } else { + ucvector v = ucvector_init(*out, *outsize); + if(expected_size) { + /*reserve the memory to avoid intermediate reallocations*/ + ucvector_resize(&v, *outsize + expected_size); + v.size = *outsize; + } + error = lodepng_zlib_decompressv(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + } + return error; +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) { + size_t i; + unsigned error; + unsigned char* deflatedata = 0; + size_t deflatesize = 0; + + error = deflate(&deflatedata, &deflatesize, in, insize, settings); + + *out = NULL; + *outsize = 0; + if(!error) { + *outsize = deflatesize + 6; + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!*out) error = 83; /*alloc fail*/ + } + + if(!error) { + unsigned ADLER32 = adler32(in, (unsigned)insize); + /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ + unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ + unsigned FLEVEL = 0; + unsigned FDICT = 0; + unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; + unsigned FCHECK = 31 - CMFFLG % 31; + CMFFLG += FCHECK; + + (*out)[0] = (unsigned char)(CMFFLG >> 8); + (*out)[1] = (unsigned char)(CMFFLG & 255); + for(i = 0; i != deflatesize; ++i) (*out)[i + 2] = deflatedata[i]; + lodepng_set32bitInt(&(*out)[*outsize - 4], ADLER32); + } + + lodepng_free(deflatedata); + return error; +} + +/* compress using the default or custom zlib function */ +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) { + if(settings->custom_zlib) { + unsigned error = settings->custom_zlib(out, outsize, in, insize, settings); + /*the custom zlib is allowed to have its own error codes, however, we translate it to code 111*/ + return error ? 111 : 0; + } else { + return lodepng_zlib_compress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#else /*no LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DECODER +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, + const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + (void)expected_size; + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) { + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/*this is a good tradeoff between speed and compression ratio*/ +#define DEFAULT_WINDOWSIZE 2048 + +void lodepng_compress_settings_init(LodePNGCompressSettings* settings) { + /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ + settings->btype = 2; + settings->use_lz77 = 1; + settings->windowsize = DEFAULT_WINDOWSIZE; + settings->minmatch = 3; + settings->nicematch = 128; + settings->lazymatching = 1; + + settings->custom_zlib = 0; + settings->custom_deflate = 0; + settings->custom_context = 0; +} + +const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; + + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { + settings->ignore_adler32 = 0; + settings->ignore_nlen = 0; + settings->max_output_size = 0; + + settings->custom_zlib = 0; + settings->custom_inflate = 0; + settings->custom_context = 0; +} + +const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0, 0}; + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of Zlib related code. Begin of PNG related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / CRC32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + + +#ifndef LODEPNG_NO_COMPILE_CRC +/* CRC polynomial: 0xedb88320 */ +static unsigned lodepng_crc32_table[256] = { + 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, + 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, + 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, + 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, + 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, + 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, + 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, + 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, + 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, + 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, + 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, + 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, + 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, + 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, + 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, + 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, + 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, + 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, + 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, + 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, + 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, + 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, + 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, + 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, + 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, + 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, + 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, + 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, + 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, + 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, + 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, + 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u +}; + +/*Return the CRC of the bytes buf[0..len-1].*/ +unsigned lodepng_crc32(const unsigned char* data, size_t length) { + unsigned r = 0xffffffffu; + size_t i; + for(i = 0; i < length; ++i) { + r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u); + } + return r ^ 0xffffffffu; +} +#else /* !LODEPNG_NO_COMPILE_CRC */ +unsigned lodepng_crc32(const unsigned char* data, size_t length); +#endif /* !LODEPNG_NO_COMPILE_CRC */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing PNG color channel bits / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first, +so LodePNGBitWriter and LodePNGBitReader can't be used for those. */ + +static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) { + unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); + ++(*bitpointer); + return result; +} + +/* TODO: make this faster */ +static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { + unsigned result = 0; + size_t i; + for(i = 0 ; i < nbits; ++i) { + result <<= 1u; + result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); + } + return result; +} + +static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) { + /*the current bit in bitstream may be 0 or 1 for this to work*/ + if(bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u)))); + else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u))); + ++(*bitpointer); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG chunks / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_chunk_length(const unsigned char* chunk) { + return lodepng_read32bitInt(&chunk[0]); +} + +void lodepng_chunk_type(char type[5], const unsigned char* chunk) { + unsigned i; + for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; + type[4] = 0; /*null termination char*/ +} + +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) { + if(lodepng_strlen(type) != 4) return 0; + return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); +} + +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) { + return((chunk[4] & 32) != 0); +} + +unsigned char lodepng_chunk_private(const unsigned char* chunk) { + return((chunk[6] & 32) != 0); +} + +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) { + return((chunk[7] & 32) != 0); +} + +unsigned char* lodepng_chunk_data(unsigned char* chunk) { + return &chunk[8]; +} + +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) { + return &chunk[8]; +} + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk) { + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); + /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ + unsigned checksum = lodepng_crc32(&chunk[4], length + 4); + if(CRC != checksum) return 1; + else return 0; +} + +void lodepng_chunk_generate_crc(unsigned char* chunk) { + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_crc32(&chunk[4], length + 4); + lodepng_set32bitInt(chunk + 8 + length, CRC); +} + +unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end) { + if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/ + if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 + && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { + /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ + return chunk + 8; + } else { + size_t total_chunk_length; + unsigned char* result; + if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end; + result = chunk + total_chunk_length; + if(result < chunk) return end; /*pointer overflow*/ + return result; + } +} + +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) { + if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/ + if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 + && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { + /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ + return chunk + 8; + } else { + size_t total_chunk_length; + const unsigned char* result; + if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end; + result = chunk + total_chunk_length; + if(result < chunk) return end; /*pointer overflow*/ + return result; + } +} + +unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]) { + for(;;) { + if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */ + if(lodepng_chunk_type_equals(chunk, type)) return chunk; + chunk = lodepng_chunk_next(chunk, end); + } +} + +const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) { + for(;;) { + if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */ + if(lodepng_chunk_type_equals(chunk, type)) return chunk; + chunk = lodepng_chunk_next_const(chunk, end); + } +} + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk) { + unsigned i; + size_t total_chunk_length, new_length; + unsigned char *chunk_start, *new_buffer; + + if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return 77; + if(lodepng_addofl(*outsize, total_chunk_length, &new_length)) return 77; + + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outsize) = new_length; + chunk_start = &(*out)[new_length - total_chunk_length]; + + for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; + + return 0; +} + +/*Sets length and name and allocates the space for data and crc but does not +set data or crc yet. Returns the start of the chunk in chunk. The start of +the data is at chunk + 8. To finalize chunk, add the data, then use +lodepng_chunk_generate_crc */ +static unsigned lodepng_chunk_init(unsigned char** chunk, + ucvector* out, + unsigned length, const char* type) { + size_t new_length = out->size; + if(lodepng_addofl(new_length, length, &new_length)) return 77; + if(lodepng_addofl(new_length, 12, &new_length)) return 77; + if(!ucvector_resize(out, new_length)) return 83; /*alloc fail*/ + *chunk = out->data + new_length - length - 12u; + + /*1: length*/ + lodepng_set32bitInt(*chunk, length); + + /*2: chunk name (4 letters)*/ + lodepng_memcpy(*chunk + 4, type, 4); + + return 0; +} + +/* like lodepng_chunk_create but with custom allocsize */ +static unsigned lodepng_chunk_createv(ucvector* out, + unsigned length, const char* type, const unsigned char* data) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, length, type)); + + /*3: the data*/ + lodepng_memcpy(chunk + 8, data, length); + + /*4: CRC (of the chunkname characters and the data)*/ + lodepng_chunk_generate_crc(chunk); + + return 0; +} + +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, + unsigned length, const char* type, const unsigned char* data) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_chunk_createv(&v, length, type, data); + *out = v.data; + *outsize = v.size; + return error; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Color types, channels, bits / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*checks if the colortype is valid and the bitdepth bd is allowed for this colortype. +Return value is a LodePNG error code.*/ +static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) { + switch(colortype) { + case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; + case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break; + case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; + case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break; + case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break; + case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */ + default: return 31; /* invalid color type */ + } + return 0; /*allowed color type / bits combination*/ +} + +static unsigned getNumColorChannels(LodePNGColorType colortype) { + switch(colortype) { + case LCT_GREY: return 1; + case LCT_RGB: return 3; + case LCT_PALETTE: return 1; + case LCT_GREY_ALPHA: return 2; + case LCT_RGBA: return 4; + case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */ + default: return 0; /*invalid color type*/ + } +} + +static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) { + /*bits per pixel is amount of channels * bits per channel*/ + return getNumColorChannels(colortype) * bitdepth; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +void lodepng_color_mode_init(LodePNGColorMode* info) { + info->key_defined = 0; + info->key_r = info->key_g = info->key_b = 0; + info->colortype = LCT_RGBA; + info->bitdepth = 8; + info->palette = 0; + info->palettesize = 0; +} + +/*allocates palette memory if needed, and initializes all colors to black*/ +static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info) { + size_t i; + /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/ + /*the palette must have room for up to 256 colors with 4 bytes each.*/ + if(!info->palette) info->palette = (unsigned char*)lodepng_malloc(1024); + if(!info->palette) return; /*alloc fail*/ + for(i = 0; i != 256; ++i) { + /*Initialize all unused colors with black, the value used for invalid palette indices. + This is an error according to the PNG spec, but common PNG decoders make it black instead. + That makes color conversion slightly faster due to no error handling needed.*/ + info->palette[i * 4 + 0] = 0; + info->palette[i * 4 + 1] = 0; + info->palette[i * 4 + 2] = 0; + info->palette[i * 4 + 3] = 255; + } +} + +void lodepng_color_mode_cleanup(LodePNGColorMode* info) { + lodepng_palette_clear(info); +} + +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) { + lodepng_color_mode_cleanup(dest); + lodepng_memcpy(dest, source, sizeof(LodePNGColorMode)); + if(source->palette) { + dest->palette = (unsigned char*)lodepng_malloc(1024); + if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ + lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4); + } + return 0; +} + +LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth) { + LodePNGColorMode result; + lodepng_color_mode_init(&result); + result.colortype = colortype; + result.bitdepth = bitdepth; + return result; +} + +static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) { + size_t i; + if(a->colortype != b->colortype) return 0; + if(a->bitdepth != b->bitdepth) return 0; + if(a->key_defined != b->key_defined) return 0; + if(a->key_defined) { + if(a->key_r != b->key_r) return 0; + if(a->key_g != b->key_g) return 0; + if(a->key_b != b->key_b) return 0; + } + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) { + if(a->palette[i] != b->palette[i]) return 0; + } + return 1; +} + +void lodepng_palette_clear(LodePNGColorMode* info) { + if(info->palette) lodepng_free(info->palette); + info->palette = 0; + info->palettesize = 0; +} + +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + if(!info->palette) /*allocate palette if empty*/ { + lodepng_color_mode_alloc_palette(info); + if(!info->palette) return 83; /*alloc fail*/ + } + if(info->palettesize >= 256) { + return 108; /*too many palette values*/ + } + info->palette[4 * info->palettesize + 0] = r; + info->palette[4 * info->palettesize + 1] = g; + info->palette[4 * info->palettesize + 2] = b; + info->palette[4 * info->palettesize + 3] = a; + ++info->palettesize; + return 0; +} + +/*calculate bits per pixel out of colortype and bitdepth*/ +unsigned lodepng_get_bpp(const LodePNGColorMode* info) { + return lodepng_get_bpp_lct(info->colortype, info->bitdepth); +} + +unsigned lodepng_get_channels(const LodePNGColorMode* info) { + return getNumColorChannels(info->colortype); +} + +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) { + return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; +} + +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) { + return (info->colortype & 4) != 0; /*4 or 6*/ +} + +unsigned lodepng_is_palette_type(const LodePNGColorMode* info) { + return info->colortype == LCT_PALETTE; +} + +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) { + size_t i; + for(i = 0; i != info->palettesize; ++i) { + if(info->palette[i * 4 + 3] < 255) return 1; + } + return 0; +} + +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) { + return info->key_defined + || lodepng_is_alpha_type(info) + || lodepng_has_palette_alpha(info); +} + +static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = (size_t)w * (size_t)h; + return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u; +} + +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { + return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth); +} + + +#ifdef LODEPNG_COMPILE_PNG + +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, +and in addition has one extra byte per line: the filter byte. So this gives a larger +result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */ +static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp) { + /* + 1 for the filter byte, and possibly plus padding bits per line. */ + /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */ + size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u; + return (size_t)h * line; +} + +#ifdef LODEPNG_COMPILE_DECODER +/*Safely checks whether size_t overflow can be caused due to amount of pixels. +This check is overcautious rather than precise. If this check indicates no overflow, +you can safely compute in a size_t (but not an unsigned): +-(size_t)w * (size_t)h * 8 +-amount of bytes in IDAT (including filter, padding and Adam7 bytes) +-amount of bytes in raw color model +Returns 1 if overflow possible, 0 if not. +*/ +static int lodepng_pixel_overflow(unsigned w, unsigned h, + const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) { + size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor)); + size_t numpixels, total; + size_t line; /* bytes per line in worst case */ + + if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1; + if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */ + + /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */ + if(lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1; + if(lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1; + + if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */ + if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */ + + return 0; /* no overflow */ +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static void LodePNGUnknownChunks_init(LodePNGInfo* info) { + unsigned i; + for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; +} + +static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) { + unsigned i; + for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); +} + +static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) { + unsigned i; + + LodePNGUnknownChunks_cleanup(dest); + + for(i = 0; i != 3; ++i) { + size_t j; + dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; + dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); + if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ + for(j = 0; j < src->unknown_chunks_size[i]; ++j) { + dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; + } + } + + return 0; +} + +/******************************************************************************/ + +static void LodePNGText_init(LodePNGInfo* info) { + info->text_num = 0; + info->text_keys = NULL; + info->text_strings = NULL; +} + +static void LodePNGText_cleanup(LodePNGInfo* info) { + size_t i; + for(i = 0; i != info->text_num; ++i) { + string_cleanup(&info->text_keys[i]); + string_cleanup(&info->text_strings[i]); + } + lodepng_free(info->text_keys); + lodepng_free(info->text_strings); +} + +static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { + size_t i = 0; + dest->text_keys = NULL; + dest->text_strings = NULL; + dest->text_num = 0; + for(i = 0; i != source->text_num; ++i) { + CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); + } + return 0; +} + +static unsigned lodepng_add_text_sized(LodePNGInfo* info, const char* key, const char* str, size_t size) { + char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); + + if(new_keys) info->text_keys = new_keys; + if(new_strings) info->text_strings = new_strings; + + if(!new_keys || !new_strings) return 83; /*alloc fail*/ + + ++info->text_num; + info->text_keys[info->text_num - 1] = alloc_string(key); + info->text_strings[info->text_num - 1] = alloc_string_sized(str, size); + if(!info->text_keys[info->text_num - 1] || !info->text_strings[info->text_num - 1]) return 83; /*alloc fail*/ + + return 0; +} + +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) { + return lodepng_add_text_sized(info, key, str, lodepng_strlen(str)); +} + +void lodepng_clear_text(LodePNGInfo* info) { + LodePNGText_cleanup(info); +} + +/******************************************************************************/ + +static void LodePNGIText_init(LodePNGInfo* info) { + info->itext_num = 0; + info->itext_keys = NULL; + info->itext_langtags = NULL; + info->itext_transkeys = NULL; + info->itext_strings = NULL; +} + +static void LodePNGIText_cleanup(LodePNGInfo* info) { + size_t i; + for(i = 0; i != info->itext_num; ++i) { + string_cleanup(&info->itext_keys[i]); + string_cleanup(&info->itext_langtags[i]); + string_cleanup(&info->itext_transkeys[i]); + string_cleanup(&info->itext_strings[i]); + } + lodepng_free(info->itext_keys); + lodepng_free(info->itext_langtags); + lodepng_free(info->itext_transkeys); + lodepng_free(info->itext_strings); +} + +static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { + size_t i = 0; + dest->itext_keys = NULL; + dest->itext_langtags = NULL; + dest->itext_transkeys = NULL; + dest->itext_strings = NULL; + dest->itext_num = 0; + for(i = 0; i != source->itext_num; ++i) { + CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], + source->itext_transkeys[i], source->itext_strings[i])); + } + return 0; +} + +void lodepng_clear_itext(LodePNGInfo* info) { + LodePNGIText_cleanup(info); +} + +static unsigned lodepng_add_itext_sized(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str, size_t size) { + char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); + char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); + char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); + + if(new_keys) info->itext_keys = new_keys; + if(new_langtags) info->itext_langtags = new_langtags; + if(new_transkeys) info->itext_transkeys = new_transkeys; + if(new_strings) info->itext_strings = new_strings; + + if(!new_keys || !new_langtags || !new_transkeys || !new_strings) return 83; /*alloc fail*/ + + ++info->itext_num; + + info->itext_keys[info->itext_num - 1] = alloc_string(key); + info->itext_langtags[info->itext_num - 1] = alloc_string(langtag); + info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey); + info->itext_strings[info->itext_num - 1] = alloc_string_sized(str, size); + + return 0; +} + +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str) { + return lodepng_add_itext_sized(info, key, langtag, transkey, str, lodepng_strlen(str)); +} + +/* same as set but does not delete */ +static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) { + if(profile_size == 0) return 100; /*invalid ICC profile size*/ + + info->iccp_name = alloc_string(name); + info->iccp_profile = (unsigned char*)lodepng_malloc(profile_size); + + if(!info->iccp_name || !info->iccp_profile) return 83; /*alloc fail*/ + + lodepng_memcpy(info->iccp_profile, profile, profile_size); + info->iccp_profile_size = profile_size; + + return 0; /*ok*/ +} + +unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) { + if(info->iccp_name) lodepng_clear_icc(info); + info->iccp_defined = 1; + + return lodepng_assign_icc(info, name, profile, profile_size); +} + +void lodepng_clear_icc(LodePNGInfo* info) { + string_cleanup(&info->iccp_name); + lodepng_free(info->iccp_profile); + info->iccp_profile = NULL; + info->iccp_profile_size = 0; + info->iccp_defined = 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +void lodepng_info_init(LodePNGInfo* info) { + lodepng_color_mode_init(&info->color); + info->interlace_method = 0; + info->compression_method = 0; + info->filter_method = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + info->background_defined = 0; + info->background_r = info->background_g = info->background_b = 0; + + LodePNGText_init(info); + LodePNGIText_init(info); + + info->time_defined = 0; + info->phys_defined = 0; + + info->gama_defined = 0; + info->chrm_defined = 0; + info->srgb_defined = 0; + info->iccp_defined = 0; + info->iccp_name = NULL; + info->iccp_profile = NULL; + + LodePNGUnknownChunks_init(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +void lodepng_info_cleanup(LodePNGInfo* info) { + lodepng_color_mode_cleanup(&info->color); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + LodePNGText_cleanup(info); + LodePNGIText_cleanup(info); + + lodepng_clear_icc(info); + + LodePNGUnknownChunks_cleanup(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) { + lodepng_info_cleanup(dest); + lodepng_memcpy(dest, source, sizeof(LodePNGInfo)); + lodepng_color_mode_init(&dest->color); + CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); + CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); + if(source->iccp_defined) { + CERROR_TRY_RETURN(lodepng_assign_icc(dest, source->iccp_name, source->iccp_profile, source->iccp_profile_size)); + } + + LodePNGUnknownChunks_init(dest); + CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + return 0; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ +static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) { + unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ + /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ + unsigned p = index & m; + in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ + in = in << (bits * (m - p)); + if(p == 0) out[index * bits / 8u] = in; + else out[index * bits / 8u] |= in; +} + +typedef struct ColorTree ColorTree; + +/* +One node of a color tree +This is the data structure used to count the number of unique colors and to get a palette +index for a color. It's like an octree, but because the alpha channel is used too, each +node has 16 instead of 8 children. +*/ +struct ColorTree { + ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ + int index; /*the payload. Only has a meaningful value if this is in the last level*/ +}; + +static void color_tree_init(ColorTree* tree) { + lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children)); + tree->index = -1; +} + +static void color_tree_cleanup(ColorTree* tree) { + int i; + for(i = 0; i != 16; ++i) { + if(tree->children[i]) { + color_tree_cleanup(tree->children[i]); + lodepng_free(tree->children[i]); + } + } +} + +/*returns -1 if color not present, its index otherwise*/ +static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + int bit = 0; + for(bit = 0; bit < 8; ++bit) { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) return -1; + else tree = tree->children[i]; + } + return tree ? tree->index : -1; +} + +#ifdef LODEPNG_COMPILE_ENCODER +static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + return color_tree_get(tree, r, g, b, a) >= 0; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*color is not allowed to already exist. +Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist") +Returns error code, or 0 if ok*/ +static unsigned color_tree_add(ColorTree* tree, + unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) { + int bit; + for(bit = 0; bit < 8; ++bit) { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) { + tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); + if(!tree->children[i]) return 83; /*alloc fail*/ + color_tree_init(tree->children[i]); + } + tree = tree->children[i]; + } + tree->index = (int)index; + return 0; +} + +/*put a pixel, given its RGBA color, into image of any color type*/ +static unsigned rgba8ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + if(mode->colortype == LCT_GREY) { + unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ + if(mode->bitdepth == 8) out[i] = gray; + else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray; + else { + /*take the most significant bits of gray*/ + gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u); + addColorBits(out, i, mode->bitdepth, gray); + } + } else if(mode->colortype == LCT_RGB) { + if(mode->bitdepth == 8) { + out[i * 3 + 0] = r; + out[i * 3 + 1] = g; + out[i * 3 + 2] = b; + } else { + out[i * 6 + 0] = out[i * 6 + 1] = r; + out[i * 6 + 2] = out[i * 6 + 3] = g; + out[i * 6 + 4] = out[i * 6 + 5] = b; + } + } else if(mode->colortype == LCT_PALETTE) { + int index = color_tree_get(tree, r, g, b, a); + if(index < 0) return 82; /*color not in palette*/ + if(mode->bitdepth == 8) out[i] = index; + else addColorBits(out, i, mode->bitdepth, (unsigned)index); + } else if(mode->colortype == LCT_GREY_ALPHA) { + unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ + if(mode->bitdepth == 8) { + out[i * 2 + 0] = gray; + out[i * 2 + 1] = a; + } else if(mode->bitdepth == 16) { + out[i * 4 + 0] = out[i * 4 + 1] = gray; + out[i * 4 + 2] = out[i * 4 + 3] = a; + } + } else if(mode->colortype == LCT_RGBA) { + if(mode->bitdepth == 8) { + out[i * 4 + 0] = r; + out[i * 4 + 1] = g; + out[i * 4 + 2] = b; + out[i * 4 + 3] = a; + } else { + out[i * 8 + 0] = out[i * 8 + 1] = r; + out[i * 8 + 2] = out[i * 8 + 3] = g; + out[i * 8 + 4] = out[i * 8 + 5] = b; + out[i * 8 + 6] = out[i * 8 + 7] = a; + } + } + + return 0; /*no error*/ +} + +/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ +static void rgba16ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, + unsigned short r, unsigned short g, unsigned short b, unsigned short a) { + if(mode->colortype == LCT_GREY) { + unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ + out[i * 2 + 0] = (gray >> 8) & 255; + out[i * 2 + 1] = gray & 255; + } else if(mode->colortype == LCT_RGB) { + out[i * 6 + 0] = (r >> 8) & 255; + out[i * 6 + 1] = r & 255; + out[i * 6 + 2] = (g >> 8) & 255; + out[i * 6 + 3] = g & 255; + out[i * 6 + 4] = (b >> 8) & 255; + out[i * 6 + 5] = b & 255; + } else if(mode->colortype == LCT_GREY_ALPHA) { + unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ + out[i * 4 + 0] = (gray >> 8) & 255; + out[i * 4 + 1] = gray & 255; + out[i * 4 + 2] = (a >> 8) & 255; + out[i * 4 + 3] = a & 255; + } else if(mode->colortype == LCT_RGBA) { + out[i * 8 + 0] = (r >> 8) & 255; + out[i * 8 + 1] = r & 255; + out[i * 8 + 2] = (g >> 8) & 255; + out[i * 8 + 3] = g & 255; + out[i * 8 + 4] = (b >> 8) & 255; + out[i * 8 + 5] = b & 255; + out[i * 8 + 6] = (a >> 8) & 255; + out[i * 8 + 7] = a & 255; + } +} + +/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ +static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, + unsigned char* b, unsigned char* a, + const unsigned char* in, size_t i, + const LodePNGColorMode* mode) { + if(mode->colortype == LCT_GREY) { + if(mode->bitdepth == 8) { + *r = *g = *b = in[i]; + if(mode->key_defined && *r == mode->key_r) *a = 0; + else *a = 255; + } else if(mode->bitdepth == 16) { + *r = *g = *b = in[i * 2 + 0]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 255; + } else { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = i * mode->bitdepth; + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + *r = *g = *b = (value * 255) / highest; + if(mode->key_defined && value == mode->key_r) *a = 0; + else *a = 255; + } + } else if(mode->colortype == LCT_RGB) { + if(mode->bitdepth == 8) { + *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; + if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; + else *a = 255; + } else { + *r = in[i * 6 + 0]; + *g = in[i * 6 + 2]; + *b = in[i * 6 + 4]; + if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 255; + } + } else if(mode->colortype == LCT_PALETTE) { + unsigned index; + if(mode->bitdepth == 8) index = in[i]; + else { + size_t j = i * mode->bitdepth; + index = readBitsFromReversedStream(&j, in, mode->bitdepth); + } + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + *r = mode->palette[index * 4 + 0]; + *g = mode->palette[index * 4 + 1]; + *b = mode->palette[index * 4 + 2]; + *a = mode->palette[index * 4 + 3]; + } else if(mode->colortype == LCT_GREY_ALPHA) { + if(mode->bitdepth == 8) { + *r = *g = *b = in[i * 2 + 0]; + *a = in[i * 2 + 1]; + } else { + *r = *g = *b = in[i * 4 + 0]; + *a = in[i * 4 + 2]; + } + } else if(mode->colortype == LCT_RGBA) { + if(mode->bitdepth == 8) { + *r = in[i * 4 + 0]; + *g = in[i * 4 + 1]; + *b = in[i * 4 + 2]; + *a = in[i * 4 + 3]; + } else { + *r = in[i * 8 + 0]; + *g = in[i * 8 + 2]; + *b = in[i * 8 + 4]; + *a = in[i * 8 + 6]; + } + } +} + +/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color +mode test cases, optimized to convert the colors much faster, when converting +to the common case of RGBA with 8 bit per channel. buffer must be RGBA with +enough memory.*/ +static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, + const unsigned char* LODEPNG_RESTRICT in, + const LodePNGColorMode* mode) { + unsigned num_channels = 4; + size_t i; + if(mode->colortype == LCT_GREY) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i]; + buffer[3] = 255; + } + if(mode->key_defined) { + buffer -= numpixels * num_channels; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + if(buffer[0] == mode->key_r) buffer[3] = 0; + } + } + } else if(mode->bitdepth == 16) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 2]; + buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; + } + } else { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; + buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; + } + } + } else if(mode->colortype == LCT_RGB) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + lodepng_memcpy(buffer, &in[i * 3], 3); + buffer[3] = 255; + } + if(mode->key_defined) { + buffer -= numpixels * num_channels; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + if(buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0; + } + } + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = in[i * 6 + 0]; + buffer[1] = in[i * 6 + 2]; + buffer[2] = in[i * 6 + 4]; + buffer[3] = mode->key_defined + && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; + } + } + } else if(mode->colortype == LCT_PALETTE) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned index = in[i]; + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + lodepng_memcpy(buffer, &mode->palette[index * 4], 4); + } + } else { + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + lodepng_memcpy(buffer, &mode->palette[index * 4], 4); + } + } + } else if(mode->colortype == LCT_GREY_ALPHA) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; + buffer[3] = in[i * 2 + 1]; + } + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; + buffer[3] = in[i * 4 + 2]; + } + } + } else if(mode->colortype == LCT_RGBA) { + if(mode->bitdepth == 8) { + lodepng_memcpy(buffer, in, numpixels * 4); + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = in[i * 8 + 0]; + buffer[1] = in[i * 8 + 2]; + buffer[2] = in[i * 8 + 4]; + buffer[3] = in[i * 8 + 6]; + } + } + } +} + +/*Similar to getPixelColorsRGBA8, but with 3-channel RGB output.*/ +static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, + const unsigned char* LODEPNG_RESTRICT in, + const LodePNGColorMode* mode) { + const unsigned num_channels = 3; + size_t i; + if(mode->colortype == LCT_GREY) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i]; + } + } else if(mode->bitdepth == 16) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 2]; + } + } else { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; + } + } + } else if(mode->colortype == LCT_RGB) { + if(mode->bitdepth == 8) { + lodepng_memcpy(buffer, in, numpixels * 3); + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = in[i * 6 + 0]; + buffer[1] = in[i * 6 + 2]; + buffer[2] = in[i * 6 + 4]; + } + } + } else if(mode->colortype == LCT_PALETTE) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned index = in[i]; + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + lodepng_memcpy(buffer, &mode->palette[index * 4], 3); + } + } else { + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + lodepng_memcpy(buffer, &mode->palette[index * 4], 3); + } + } + } else if(mode->colortype == LCT_GREY_ALPHA) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; + } + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; + } + } + } else if(mode->colortype == LCT_RGBA) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + lodepng_memcpy(buffer, &in[i * 4], 3); + } + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = in[i * 8 + 0]; + buffer[1] = in[i * 8 + 2]; + buffer[2] = in[i * 8 + 4]; + } + } + } +} + +/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with +given color type, but the given color type must be 16-bit itself.*/ +static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, + const unsigned char* in, size_t i, const LodePNGColorMode* mode) { + if(mode->colortype == LCT_GREY) { + *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 65535; + } else if(mode->colortype == LCT_RGB) { + *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; + *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; + *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; + if(mode->key_defined + && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 65535; + } else if(mode->colortype == LCT_GREY_ALPHA) { + *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; + *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; + } else if(mode->colortype == LCT_RGBA) { + *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; + *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; + *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; + *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; + } +} + +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h) { + size_t i; + ColorTree tree; + size_t numpixels = (size_t)w * (size_t)h; + unsigned error = 0; + + if(mode_in->colortype == LCT_PALETTE && !mode_in->palette) { + return 107; /* error: must provide palette if input mode is palette */ + } + + if(lodepng_color_mode_equal(mode_out, mode_in)) { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + lodepng_memcpy(out, in, numbytes); + return 0; + } + + if(mode_out->colortype == LCT_PALETTE) { + size_t palettesize = mode_out->palettesize; + const unsigned char* palette = mode_out->palette; + size_t palsize = (size_t)1u << mode_out->bitdepth; + /*if the user specified output palette but did not give the values, assume + they want the values of the input color type (assuming that one is palette). + Note that we never create a new palette ourselves.*/ + if(palettesize == 0) { + palettesize = mode_in->palettesize; + palette = mode_in->palette; + /*if the input was also palette with same bitdepth, then the color types are also + equal, so copy literally. This to preserve the exact indices that were in the PNG + even in case there are duplicate colors in the palette.*/ + if(mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + lodepng_memcpy(out, in, numbytes); + return 0; + } + } + if(palettesize < palsize) palsize = palettesize; + color_tree_init(&tree); + for(i = 0; i != palsize; ++i) { + const unsigned char* p = &palette[i * 4]; + error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); + if(error) break; + } + } + + if(!error) { + if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { + for(i = 0; i != numpixels; ++i) { + unsigned short r = 0, g = 0, b = 0, a = 0; + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + rgba16ToPixel(out, i, mode_out, r, g, b, a); + } + } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) { + getPixelColorsRGBA8(out, numpixels, in, mode_in); + } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) { + getPixelColorsRGB8(out, numpixels, in, mode_in); + } else { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); + if(error) break; + } + } + } + + if(mode_out->colortype == LCT_PALETTE) { + color_tree_cleanup(&tree); + } + + return error; +} + + +/* Converts a single rgb color without alpha from one type to another, color bits truncated to +their bitdepth. In case of single channel (gray or palette), only the r channel is used. Slow +function, do not use to process all pixels of an image. Alpha channel not supported on purpose: +this is for bKGD, supporting alpha may prevent it from finding a color in the palette, from the +specification it looks like bKGD should ignore the alpha values of the palette since it can use +any palette index but doesn't have an alpha channel. Idem with ignoring color key. */ +unsigned lodepng_convert_rgb( + unsigned* r_out, unsigned* g_out, unsigned* b_out, + unsigned r_in, unsigned g_in, unsigned b_in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in) { + unsigned r = 0, g = 0, b = 0; + unsigned mul = 65535 / ((1u << mode_in->bitdepth) - 1u); /*65535, 21845, 4369, 257, 1*/ + unsigned shift = 16 - mode_out->bitdepth; + + if(mode_in->colortype == LCT_GREY || mode_in->colortype == LCT_GREY_ALPHA) { + r = g = b = r_in * mul; + } else if(mode_in->colortype == LCT_RGB || mode_in->colortype == LCT_RGBA) { + r = r_in * mul; + g = g_in * mul; + b = b_in * mul; + } else if(mode_in->colortype == LCT_PALETTE) { + if(r_in >= mode_in->palettesize) return 82; + r = mode_in->palette[r_in * 4 + 0] * 257u; + g = mode_in->palette[r_in * 4 + 1] * 257u; + b = mode_in->palette[r_in * 4 + 2] * 257u; + } else { + return 31; + } + + /* now convert to output format */ + if(mode_out->colortype == LCT_GREY || mode_out->colortype == LCT_GREY_ALPHA) { + *r_out = r >> shift ; + } else if(mode_out->colortype == LCT_RGB || mode_out->colortype == LCT_RGBA) { + *r_out = r >> shift ; + *g_out = g >> shift ; + *b_out = b >> shift ; + } else if(mode_out->colortype == LCT_PALETTE) { + unsigned i; + /* a 16-bit color cannot be in the palette */ + if((r >> 8) != (r & 255) || (g >> 8) != (g & 255) || (b >> 8) != (b & 255)) return 82; + for(i = 0; i < mode_out->palettesize; i++) { + unsigned j = i * 4; + if((r >> 8) == mode_out->palette[j + 0] && (g >> 8) == mode_out->palette[j + 1] && + (b >> 8) == mode_out->palette[j + 2]) { + *r_out = i; + return 0; + } + } + return 82; + } else { + return 31; + } + + return 0; +} + +#ifdef LODEPNG_COMPILE_ENCODER + +void lodepng_color_stats_init(LodePNGColorStats* stats) { + /*stats*/ + stats->colored = 0; + stats->key = 0; + stats->key_r = stats->key_g = stats->key_b = 0; + stats->alpha = 0; + stats->numcolors = 0; + stats->bits = 1; + stats->numpixels = 0; + /*settings*/ + stats->allow_palette = 1; + stats->allow_greyscale = 1; +} + +/*function used for debug purposes with C++*/ +/*void printColorStats(LodePNGColorStats* p) { + std::cout << "colored: " << (int)p->colored << ", "; + std::cout << "key: " << (int)p->key << ", "; + std::cout << "key_r: " << (int)p->key_r << ", "; + std::cout << "key_g: " << (int)p->key_g << ", "; + std::cout << "key_b: " << (int)p->key_b << ", "; + std::cout << "alpha: " << (int)p->alpha << ", "; + std::cout << "numcolors: " << (int)p->numcolors << ", "; + std::cout << "bits: " << (int)p->bits << std::endl; +}*/ + +/*Returns how many bits needed to represent given value (max 8 bit)*/ +static unsigned getValueRequiredBits(unsigned char value) { + if(value == 0 || value == 255) return 1; + /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ + if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; + return 8; +} + +/*stats must already have been inited. */ +unsigned lodepng_compute_color_stats(LodePNGColorStats* stats, + const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* mode_in) { + size_t i; + ColorTree tree; + size_t numpixels = (size_t)w * (size_t)h; + unsigned error = 0; + + /* mark things as done already if it would be impossible to have a more expensive case */ + unsigned colored_done = lodepng_is_greyscale_type(mode_in) ? 1 : 0; + unsigned alpha_done = lodepng_can_have_alpha(mode_in) ? 0 : 1; + unsigned numcolors_done = 0; + unsigned bpp = lodepng_get_bpp(mode_in); + unsigned bits_done = (stats->bits == 1 && bpp == 1) ? 1 : 0; + unsigned sixteen = 0; /* whether the input image is 16 bit */ + unsigned maxnumcolors = 257; + if(bpp <= 8) maxnumcolors = LODEPNG_MIN(257, stats->numcolors + (1u << bpp)); + + stats->numpixels += numpixels; + + /*if palette not allowed, no need to compute numcolors*/ + if(!stats->allow_palette) numcolors_done = 1; + + color_tree_init(&tree); + + /*If the stats was already filled in from previous data, fill its palette in tree + and mark things as done already if we know they are the most expensive case already*/ + if(stats->alpha) alpha_done = 1; + if(stats->colored) colored_done = 1; + if(stats->bits == 16) numcolors_done = 1; + if(stats->bits >= bpp) bits_done = 1; + if(stats->numcolors >= maxnumcolors) numcolors_done = 1; + + if(!numcolors_done) { + for(i = 0; i < stats->numcolors; i++) { + const unsigned char* color = &stats->palette[i * 4]; + error = color_tree_add(&tree, color[0], color[1], color[2], color[3], i); + if(error) goto cleanup; + } + } + + /*Check if the 16-bit input is truly 16-bit*/ + if(mode_in->bitdepth == 16 && !sixteen) { + unsigned short r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || + (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ { + stats->bits = 16; + sixteen = 1; + bits_done = 1; + numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ + break; + } + } + } + + if(sixteen) { + unsigned short r = 0, g = 0, b = 0, a = 0; + + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + + if(!colored_done && (r != g || r != b)) { + stats->colored = 1; + colored_done = 1; + } + + if(!alpha_done) { + unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b); + if(a != 65535 && (a != 0 || (stats->key && !matchkey))) { + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + } else if(a == 0 && !stats->alpha && !stats->key) { + stats->key = 1; + stats->key_r = r; + stats->key_g = g; + stats->key_b = b; + } else if(a == 65535 && stats->key && matchkey) { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + } + } + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(stats->key && !stats->alpha) { + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + } + } + } + } else /* < 16-bit */ { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + + if(!bits_done && stats->bits < 8) { + /*only r is checked, < 8 bits is only relevant for grayscale*/ + unsigned bits = getValueRequiredBits(r); + if(bits > stats->bits) stats->bits = bits; + } + bits_done = (stats->bits >= bpp); + + if(!colored_done && (r != g || r != b)) { + stats->colored = 1; + colored_done = 1; + if(stats->bits < 8) stats->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ + } + + if(!alpha_done) { + unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b); + if(a != 255 && (a != 0 || (stats->key && !matchkey))) { + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } else if(a == 0 && !stats->alpha && !stats->key) { + stats->key = 1; + stats->key_r = r; + stats->key_g = g; + stats->key_b = b; + } else if(a == 255 && stats->key && matchkey) { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + + if(!numcolors_done) { + if(!color_tree_has(&tree, r, g, b, a)) { + error = color_tree_add(&tree, r, g, b, a, stats->numcolors); + if(error) goto cleanup; + if(stats->numcolors < 256) { + unsigned char* p = stats->palette; + unsigned n = stats->numcolors; + p[n * 4 + 0] = r; + p[n * 4 + 1] = g; + p[n * 4 + 2] = b; + p[n * 4 + 3] = a; + } + ++stats->numcolors; + numcolors_done = stats->numcolors >= maxnumcolors; + } + } + + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(stats->key && !stats->alpha) { + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + } + + /*make the stats's key always 16-bit for consistency - repeat each byte twice*/ + stats->key_r += (stats->key_r << 8); + stats->key_g += (stats->key_g << 8); + stats->key_b += (stats->key_b << 8); + } + +cleanup: + color_tree_cleanup(&tree); + return error; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*Adds a single color to the color stats. The stats must already have been inited. The color must be given as 16-bit +(with 2 bytes repeating for 8-bit and 65535 for opaque alpha channel). This function is expensive, do not call it for +all pixels of an image but only for a few additional values. */ +static unsigned lodepng_color_stats_add(LodePNGColorStats* stats, + unsigned r, unsigned g, unsigned b, unsigned a) { + unsigned error = 0; + unsigned char image[8]; + LodePNGColorMode mode; + lodepng_color_mode_init(&mode); + image[0] = r >> 8; image[1] = r; image[2] = g >> 8; image[3] = g; + image[4] = b >> 8; image[5] = b; image[6] = a >> 8; image[7] = a; + mode.bitdepth = 16; + mode.colortype = LCT_RGBA; + error = lodepng_compute_color_stats(stats, image, 1, 1, &mode); + lodepng_color_mode_cleanup(&mode); + return error; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Computes a minimal PNG color model that can contain all colors as indicated by the stats. +The stats should be computed with lodepng_compute_color_stats. +mode_in is raw color profile of the image the stats were computed on, to copy palette order from when relevant. +Minimal PNG color model means the color type and bit depth that gives smallest amount of bits in the output image, +e.g. gray if only grayscale pixels, palette if less than 256 colors, color key if only single transparent color, ... +This is used if auto_convert is enabled (it is by default). +*/ +static unsigned auto_choose_color(LodePNGColorMode* mode_out, + const LodePNGColorMode* mode_in, + const LodePNGColorStats* stats) { + unsigned error = 0; + unsigned palettebits; + size_t i, n; + size_t numpixels = stats->numpixels; + unsigned palette_ok, gray_ok; + + unsigned alpha = stats->alpha; + unsigned key = stats->key; + unsigned bits = stats->bits; + + mode_out->key_defined = 0; + + if(key && numpixels <= 16) { + alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + key = 0; + if(bits < 8) bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + + gray_ok = !stats->colored; + if(!stats->allow_greyscale) gray_ok = 0; + if(!gray_ok && bits < 8) bits = 8; + + n = stats->numcolors; + palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); + palette_ok = n <= 256 && bits <= 8 && n != 0; /*n==0 means likely numcolors wasn't computed*/ + if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(gray_ok && !alpha && bits <= palettebits) palette_ok = 0; /*gray is less overhead*/ + if(!stats->allow_palette) palette_ok = 0; + + if(palette_ok) { + const unsigned char* p = stats->palette; + lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ + for(i = 0; i != stats->numcolors; ++i) { + error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); + if(error) break; + } + + mode_out->colortype = LCT_PALETTE; + mode_out->bitdepth = palettebits; + + if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize + && mode_in->bitdepth == mode_out->bitdepth) { + /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ + lodepng_color_mode_cleanup(mode_out); + lodepng_color_mode_copy(mode_out, mode_in); + } + } else /*8-bit or 16-bit per channel*/ { + mode_out->bitdepth = bits; + mode_out->colortype = alpha ? (gray_ok ? LCT_GREY_ALPHA : LCT_RGBA) + : (gray_ok ? LCT_GREY : LCT_RGB); + if(key) { + unsigned mask = (1u << mode_out->bitdepth) - 1u; /*stats always uses 16-bit, mask converts it*/ + mode_out->key_r = stats->key_r & mask; + mode_out->key_g = stats->key_g & mask; + mode_out->key_b = stats->key_b & mask; + mode_out->key_defined = 1; + } + } + + return error; +} + +#endif /* #ifdef LODEPNG_COMPILE_ENCODER */ + +/* +Paeth predictor, used by PNG filter type 4 +The parameters are of type short, but should come from unsigned chars, the shorts +are only needed to make the paeth calculation correct. +*/ +static unsigned char paethPredictor(short a, short b, short c) { + short pa = LODEPNG_ABS(b - c); + short pb = LODEPNG_ABS(a - c); + short pc = LODEPNG_ABS(a + b - c - c); + /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */ + if(pb < pa) { a = b; pa = pb; } + return (pc < pa) ? c : a; +} + +/*shared values used by multiple Adam7 related functions*/ + +static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ +static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ +static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ +static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ + +/* +Outputs various dimensions and positions in the image related to the Adam7 reduced images. +passw: output containing the width of the 7 passes +passh: output containing the height of the 7 passes +filter_passstart: output containing the index of the start and end of each + reduced image with filter bytes +padded_passstart output containing the index of the start and end of each + reduced image when without filter bytes but with padded scanlines +passstart: output containing the index of the start and end of each reduced + image without padding between scanlines, but still padding between the images +w, h: width and height of non-interlaced image +bpp: bits per pixel +"padded" is only relevant if bpp is less than 8 and a scanline or image does not + end at a full byte +*/ +static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], + size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) { + /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ + unsigned i; + + /*calculate width and height in pixels of each pass*/ + for(i = 0; i != 7; ++i) { + passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; + passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; + if(passw[i] == 0) passh[i] = 0; + if(passh[i] == 0) passw[i] = 0; + } + + filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; + for(i = 0; i != 7; ++i) { + /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ + filter_passstart[i + 1] = filter_passstart[i] + + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0); + /*bits padded if needed to fill full byte at end of each scanline*/ + padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u); + /*only padded at end of reduced image*/ + passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u; + } +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Decoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*read the information from the header and store it in the LodePNGInfo. return value is error*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, + const unsigned char* in, size_t insize) { + unsigned width, height; + LodePNGInfo* info = &state->info_png; + if(insize == 0 || in == 0) { + CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ + } + if(insize < 33) { + CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ + } + + /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ + /* TODO: remove this. One should use a new LodePNGState for new sessions */ + lodepng_info_cleanup(info); + lodepng_info_init(info); + + if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 + || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { + CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ + } + if(lodepng_chunk_length(in + 8) != 13) { + CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ + } + if(!lodepng_chunk_type_equals(in + 8, "IHDR")) { + CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ + } + + /*read the values given in the header*/ + width = lodepng_read32bitInt(&in[16]); + height = lodepng_read32bitInt(&in[20]); + /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/ + if(w) *w = width; + if(h) *h = height; + info->color.bitdepth = in[24]; + info->color.colortype = (LodePNGColorType)in[25]; + info->compression_method = in[26]; + info->filter_method = in[27]; + info->interlace_method = in[28]; + + /*errors returned only after the parsing so other values are still output*/ + + /*error: invalid image size*/ + if(width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93); + /*error: invalid colortype or bitdepth combination*/ + state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); + if(state->error) return state->error; + /*error: only compression method 0 is allowed in the specification*/ + if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); + /*error: only filter method 0 is allowed in the specification*/ + if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); + /*error: only interlace methods 0 and 1 exist in the specification*/ + if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); + + if(!state->decoder.ignore_crc) { + unsigned CRC = lodepng_read32bitInt(&in[29]); + unsigned checksum = lodepng_crc32(&in[12], 17); + if(CRC != checksum) { + CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ + } + } + + return state->error; +} + +static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, + size_t bytewidth, unsigned char filterType, size_t length) { + /* + For PNG filter method 0 + unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, + the filter works byte per byte (bytewidth = 1) + precon is the previous unfiltered scanline, recon the result, scanline the current one + the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead + recon and scanline MAY be the same memory address! precon must be disjoint. + */ + + size_t i; + switch(filterType) { + case 0: + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + break; + case 1: + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; + break; + case 2: + if(precon) { + for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; + } else { + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + } + break; + case 3: + if(precon) { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u); + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u); + } else { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u); + } + break; + case 4: + if(precon) { + for(i = 0; i != bytewidth; ++i) { + recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ + } + + /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that + adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */ + if(bytewidth >= 4) { + for(; i + 3 < length; i += 4) { + size_t j = i - bytewidth; + unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3]; + unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3]; + unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3]; + unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3]; + recon[i + 0] = s0 + paethPredictor(r0, p0, q0); + recon[i + 1] = s1 + paethPredictor(r1, p1, q1); + recon[i + 2] = s2 + paethPredictor(r2, p2, q2); + recon[i + 3] = s3 + paethPredictor(r3, p3, q3); + } + } else if(bytewidth >= 3) { + for(; i + 2 < length; i += 3) { + size_t j = i - bytewidth; + unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2]; + unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2]; + unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2]; + unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2]; + recon[i + 0] = s0 + paethPredictor(r0, p0, q0); + recon[i + 1] = s1 + paethPredictor(r1, p1, q1); + recon[i + 2] = s2 + paethPredictor(r2, p2, q2); + } + } else if(bytewidth >= 2) { + for(; i + 1 < length; i += 2) { + size_t j = i - bytewidth; + unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1]; + unsigned char r0 = recon[j + 0], r1 = recon[j + 1]; + unsigned char p0 = precon[i + 0], p1 = precon[i + 1]; + unsigned char q0 = precon[j + 0], q1 = precon[j + 1]; + recon[i + 0] = s0 + paethPredictor(r0, p0, q0); + recon[i + 1] = s1 + paethPredictor(r1, p1, q1); + } + } + + for(; i != length; ++i) { + recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); + } + } else { + for(i = 0; i != bytewidth; ++i) { + recon[i] = scanline[i]; + } + for(i = bytewidth; i < length; ++i) { + /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ + recon[i] = (scanline[i] + recon[i - bytewidth]); + } + } + break; + default: return 36; /*error: invalid filter type given*/ + } + return 0; +} + +static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { + /* + For PNG filter method 0 + this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) + out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline + w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel + in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) + */ + + unsigned y; + unsigned char* prevline = 0; + + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7u) / 8u; + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; + + for(y = 0; y < h; ++y) { + size_t outindex = linebytes * y; + size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + unsigned char filterType = in[inindex]; + + CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); + + prevline = &out[outindex]; + } + + return 0; +} + +/* +in: Adam7 interlaced image, with no padding bits between scanlines, but between + reduced images so that each reduced image starts at a byte. +out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h +bpp: bits per pixel +out has the following size in bits: w * h * bpp. +in is possibly bigger due to padding bits between reduced images. +out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation +(because that's likely a little bit faster) +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) { + for(i = 0; i != 7; ++i) { + unsigned x, y, b; + size_t bytewidth = bpp / 8u; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { + size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; + size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth; + for(b = 0; b < bytewidth; ++b) { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { + for(i = 0; i != 7; ++i) { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { + ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp; + for(b = 0; b < bpp; ++b) { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + } + } + } +} + +static void removePaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) { + /* + After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need + to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers + for the Adam7 code, the color convert code and the output to the user. + in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must + have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits + also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 + only useful if (ilinebits - olinebits) is a value in the range 1..7 + */ + unsigned y; + size_t diff = ilinebits - olinebits; + size_t ibp = 0, obp = 0; /*input and output bit pointers*/ + for(y = 0; y < h; ++y) { + size_t x; + for(x = 0; x < olinebits; ++x) { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + ibp += diff; + } +} + +/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from +the IDAT chunks (with filter index bytes and possible padding bits) +return value is error*/ +static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, + unsigned w, unsigned h, const LodePNGInfo* info_png) { + /* + This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. + Steps: + *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8) + *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace + NOTE: the in buffer will be overwritten with intermediate data! + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + if(bpp == 0) return 31; /*error: invalid colortype*/ + + if(info_png->interlace_method == 0) { + if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) { + CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); + removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h); + } + /*we can immediately filter into the out buffer, no other steps needed*/ + else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); + } else /*interlace_method is 1 (Adam7)*/ { + unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + for(i = 0; i != 7; ++i) { + CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); + /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, + move bytes instead of bits or move not at all*/ + if(bpp < 8) { + /*remove padding bits in scanlines; after this there still may be padding + bits between the different reduced images: each reduced image still starts nicely at a byte*/ + removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, + ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]); + } + } + + Adam7_deinterlace(out, in, w, h, bpp); + } + + return 0; +} + +static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { + unsigned pos = 0, i; + color->palettesize = chunkLength / 3u; + if(color->palettesize == 0 || color->palettesize > 256) return 38; /*error: palette too small or big*/ + lodepng_color_mode_alloc_palette(color); + if(!color->palette && color->palettesize) { + color->palettesize = 0; + return 83; /*alloc fail*/ + } + + for(i = 0; i != color->palettesize; ++i) { + color->palette[4 * i + 0] = data[pos++]; /*R*/ + color->palette[4 * i + 1] = data[pos++]; /*G*/ + color->palette[4 * i + 2] = data[pos++]; /*B*/ + color->palette[4 * i + 3] = 255; /*alpha*/ + } + + return 0; /* OK */ +} + +static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { + unsigned i; + if(color->colortype == LCT_PALETTE) { + /*error: more alpha values given than there are palette entries*/ + if(chunkLength > color->palettesize) return 39; + + for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; + } else if(color->colortype == LCT_GREY) { + /*error: this chunk must be 2 bytes for grayscale image*/ + if(chunkLength != 2) return 30; + + color->key_defined = 1; + color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; + } else if(color->colortype == LCT_RGB) { + /*error: this chunk must be 6 bytes for RGB image*/ + if(chunkLength != 6) return 41; + + color->key_defined = 1; + color->key_r = 256u * data[0] + data[1]; + color->key_g = 256u * data[2] + data[3]; + color->key_b = 256u * data[4] + data[5]; + } + else return 42; /*error: tRNS chunk not allowed for other color models*/ + + return 0; /* OK */ +} + + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*background color chunk (bKGD)*/ +static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(info->color.colortype == LCT_PALETTE) { + /*error: this chunk must be 1 byte for indexed color image*/ + if(chunkLength != 1) return 43; + + /*error: invalid palette index, or maybe this chunk appeared before PLTE*/ + if(data[0] >= info->color.palettesize) return 103; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = data[0]; + } else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { + /*error: this chunk must be 2 bytes for grayscale image*/ + if(chunkLength != 2) return 44; + + /*the values are truncated to bitdepth in the PNG file*/ + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; + } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { + /*error: this chunk must be 6 bytes for grayscale image*/ + if(chunkLength != 6) return 45; + + /*the values are truncated to bitdepth in the PNG file*/ + info->background_defined = 1; + info->background_r = 256u * data[0] + data[1]; + info->background_g = 256u * data[2] + data[3]; + info->background_b = 256u * data[4] + data[5]; + } + + return 0; /* OK */ +} + +/*text chunk (tEXt)*/ +static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + unsigned error = 0; + char *key = 0, *str = 0; + + while(!error) /*not really a while loop, only used to break on error*/ { + unsigned length, string2_begin; + + length = 0; + while(length < chunkLength && data[length] != 0) ++length; + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(key, data, length); + key[length] = 0; + + string2_begin = length + 1; /*skip keyword null terminator*/ + + length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin); + str = (char*)lodepng_malloc(length + 1); + if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(str, data + string2_begin, length); + str[length] = 0; + + error = lodepng_add_text(info, key, str); + + break; + } + + lodepng_free(key); + lodepng_free(str); + + return error; +} + +/*compressed text chunk (zTXt)*/ +static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, + const unsigned char* data, size_t chunkLength) { + unsigned error = 0; + + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; + + unsigned length, string2_begin; + char *key = 0; + unsigned char* str = 0; + size_t size = 0; + + while(!error) /*not really a while loop, only used to break on error*/ { + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(key, data, length); + key[length] = 0; + + if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + + length = (unsigned)chunkLength - string2_begin; + zlibsettings.max_output_size = decoder->max_text_size; + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&str, &size, 0, &data[string2_begin], + length, &zlibsettings); + /*error: compressed text larger than decoder->max_text_size*/ + if(error && size > zlibsettings.max_output_size) error = 112; + if(error) break; + error = lodepng_add_text_sized(info, key, (char*)str, size); + break; + } + + lodepng_free(key); + lodepng_free(str); + + return error; +} + +/*international text chunk (iTXt)*/ +static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, + const unsigned char* data, size_t chunkLength) { + unsigned error = 0; + unsigned i; + + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; + + unsigned length, begin, compressed; + char *key = 0, *langtag = 0, *transkey = 0; + + while(!error) /*not really a while loop, only used to break on error*/ { + /*Quick check if the chunk length isn't too small. Even without check + it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ + if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ + + /*read the key*/ + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(key, data, length); + key[length] = 0; + + /*read the compression method*/ + compressed = data[length + 1]; + if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty for the next 3 texts*/ + + /*read the langtag*/ + begin = length + 3; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + langtag = (char*)lodepng_malloc(length + 1); + if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(langtag, data + begin, length); + langtag[length] = 0; + + /*read the transkey*/ + begin += length + 1; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + transkey = (char*)lodepng_malloc(length + 1); + if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(transkey, data + begin, length); + transkey[length] = 0; + + /*read the actual text*/ + begin += length + 1; + + length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin; + + if(compressed) { + unsigned char* str = 0; + size_t size = 0; + zlibsettings.max_output_size = decoder->max_text_size; + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&str, &size, 0, &data[begin], + length, &zlibsettings); + /*error: compressed text larger than decoder->max_text_size*/ + if(error && size > zlibsettings.max_output_size) error = 112; + if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size); + lodepng_free(str); + } else { + error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)(data + begin), length); + } + + break; + } + + lodepng_free(key); + lodepng_free(langtag); + lodepng_free(transkey); + + return error; +} + +static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ + + info->time_defined = 1; + info->time.year = 256u * data[0] + data[1]; + info->time.month = data[2]; + info->time.day = data[3]; + info->time.hour = data[4]; + info->time.minute = data[5]; + info->time.second = data[6]; + + return 0; /* OK */ +} + +static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ + + info->phys_defined = 1; + info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; + info->phys_unit = data[8]; + + return 0; /* OK */ +} + +static unsigned readChunk_gAMA(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 4) return 96; /*invalid gAMA chunk size*/ + + info->gama_defined = 1; + info->gama_gamma = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + + return 0; /* OK */ +} + +static unsigned readChunk_cHRM(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 32) return 97; /*invalid cHRM chunk size*/ + + info->chrm_defined = 1; + info->chrm_white_x = 16777216u * data[ 0] + 65536u * data[ 1] + 256u * data[ 2] + data[ 3]; + info->chrm_white_y = 16777216u * data[ 4] + 65536u * data[ 5] + 256u * data[ 6] + data[ 7]; + info->chrm_red_x = 16777216u * data[ 8] + 65536u * data[ 9] + 256u * data[10] + data[11]; + info->chrm_red_y = 16777216u * data[12] + 65536u * data[13] + 256u * data[14] + data[15]; + info->chrm_green_x = 16777216u * data[16] + 65536u * data[17] + 256u * data[18] + data[19]; + info->chrm_green_y = 16777216u * data[20] + 65536u * data[21] + 256u * data[22] + data[23]; + info->chrm_blue_x = 16777216u * data[24] + 65536u * data[25] + 256u * data[26] + data[27]; + info->chrm_blue_y = 16777216u * data[28] + 65536u * data[29] + 256u * data[30] + data[31]; + + return 0; /* OK */ +} + +static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 1) return 98; /*invalid sRGB chunk size (this one is never ignored)*/ + + info->srgb_defined = 1; + info->srgb_intent = data[0]; + + return 0; /* OK */ +} + +static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, + const unsigned char* data, size_t chunkLength) { + unsigned error = 0; + unsigned i; + size_t size = 0; + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; + + unsigned length, string2_begin; + + info->iccp_defined = 1; + if(info->iccp_name) lodepng_clear_icc(info); + + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) return 75; /*no null termination, corrupt?*/ + if(length < 1 || length > 79) return 89; /*keyword too short or long*/ + + info->iccp_name = (char*)lodepng_malloc(length + 1); + if(!info->iccp_name) return 83; /*alloc fail*/ + + info->iccp_name[length] = 0; + for(i = 0; i != length; ++i) info->iccp_name[i] = (char)data[i]; + + if(data[length + 1] != 0) return 72; /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/ + + length = (unsigned)chunkLength - string2_begin; + zlibsettings.max_output_size = decoder->max_icc_size; + error = zlib_decompress(&info->iccp_profile, &size, 0, + &data[string2_begin], + length, &zlibsettings); + /*error: ICC profile larger than decoder->max_icc_size*/ + if(error && size > zlibsettings.max_output_size) error = 113; + info->iccp_profile_size = size; + if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/ + return error; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos, + const unsigned char* in, size_t insize) { + const unsigned char* chunk = in + pos; + unsigned chunkLength; + const unsigned char* data; + unsigned unhandled = 0; + unsigned error = 0; + + if(pos + 4 > insize) return 30; + chunkLength = lodepng_chunk_length(chunk); + if(chunkLength > 2147483647) return 63; + data = lodepng_chunk_data_const(chunk); + if(data + chunkLength + 4 > in + insize) return 30; + + if(lodepng_chunk_type_equals(chunk, "PLTE")) { + error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "tRNS")) { + error = readChunk_tRNS(&state->info_png.color, data, chunkLength); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + } else if(lodepng_chunk_type_equals(chunk, "bKGD")) { + error = readChunk_bKGD(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "tEXt")) { + error = readChunk_tEXt(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "zTXt")) { + error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "iTXt")) { + error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "tIME")) { + error = readChunk_tIME(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { + error = readChunk_pHYs(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "gAMA")) { + error = readChunk_gAMA(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "cHRM")) { + error = readChunk_cHRM(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "sRGB")) { + error = readChunk_sRGB(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "iCCP")) { + error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } else { + /* unhandled chunk is ok (is not an error) */ + unhandled = 1; + } + + if(!error && !unhandled && !state->decoder.ignore_crc) { + if(lodepng_chunk_check_crc(chunk)) return 57; /*invalid CRC*/ + } + + return error; +} + +/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ +static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) { + unsigned char IEND = 0; + const unsigned char* chunk; + unsigned char* idat; /*the data from idat chunks, zlib compressed*/ + size_t idatsize = 0; + unsigned char* scanlines = 0; + size_t scanlines_size = 0, expected_size = 0; + size_t outsize = 0; + + /*for unknown chunk order*/ + unsigned unknown = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + + + /* safe output values in case error happens */ + *out = 0; + *w = *h = 0; + + state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ + if(state->error) return; + + if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) { + CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ + } + + /*the input filesize is a safe upper bound for the sum of idat chunks size*/ + idat = (unsigned char*)lodepng_malloc(insize); + if(!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/ + + chunk = &in[33]; /*first byte of the first chunk after the header*/ + + /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. + IDAT data is put at the start of the in buffer*/ + while(!IEND && !state->error) { + unsigned chunkLength; + const unsigned char* data; /*the data in the chunk*/ + + /*error: size of the in buffer too small to contain next chunk*/ + if((size_t)((chunk - in) + 12) > insize || chunk < in) { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 30); + } + + /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ + chunkLength = lodepng_chunk_length(chunk); + /*error: chunk length larger than the max PNG chunk size*/ + if(chunkLength > 2147483647) { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 63); + } + + if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) { + CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ + } + + data = lodepng_chunk_data_const(chunk); + + unknown = 0; + + /*IDAT chunk, containing compressed image data*/ + if(lodepng_chunk_type_equals(chunk, "IDAT")) { + size_t newsize; + if(lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); + if(newsize > insize) CERROR_BREAK(state->error, 95); + lodepng_memcpy(idat + idatsize, data, chunkLength); + idatsize += chunkLength; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 3; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } else if(lodepng_chunk_type_equals(chunk, "IEND")) { + /*IEND chunk*/ + IEND = 1; + } else if(lodepng_chunk_type_equals(chunk, "PLTE")) { + /*palette chunk (PLTE)*/ + state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 2; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } else if(lodepng_chunk_type_equals(chunk, "tRNS")) { + /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled + in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that + affects the alpha channel of pixels. */ + state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*background color chunk (bKGD)*/ + } else if(lodepng_chunk_type_equals(chunk, "bKGD")) { + state->error = readChunk_bKGD(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "tEXt")) { + /*text chunk (tEXt)*/ + if(state->decoder.read_text_chunks) { + state->error = readChunk_tEXt(&state->info_png, data, chunkLength); + if(state->error) break; + } + } else if(lodepng_chunk_type_equals(chunk, "zTXt")) { + /*compressed text chunk (zTXt)*/ + if(state->decoder.read_text_chunks) { + state->error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength); + if(state->error) break; + } + } else if(lodepng_chunk_type_equals(chunk, "iTXt")) { + /*international text chunk (iTXt)*/ + if(state->decoder.read_text_chunks) { + state->error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength); + if(state->error) break; + } + } else if(lodepng_chunk_type_equals(chunk, "tIME")) { + state->error = readChunk_tIME(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { + state->error = readChunk_pHYs(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "gAMA")) { + state->error = readChunk_gAMA(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "cHRM")) { + state->error = readChunk_cHRM(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "sRGB")) { + state->error = readChunk_sRGB(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "iCCP")) { + state->error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength); + if(state->error) break; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { + /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ + if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) { + CERROR_BREAK(state->error, 69); + } + + unknown = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->decoder.remember_unknown_chunks) { + state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], + &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + + if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ { + if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ + } + + if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize); + } + + if(!state->error && state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) { + state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */ + } + + if(!state->error) { + /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) { + size_t bpp = lodepng_get_bpp(&state->info_png.color); + expected_size = lodepng_get_raw_size_idat(*w, *h, bpp); + } else { + size_t bpp = lodepng_get_bpp(&state->info_png.color); + /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/ + expected_size = 0; + expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp); + if(*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp); + if(*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp); + if(*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp); + } + + state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings); + } + if(!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/ + lodepng_free(idat); + + if(!state->error) { + outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!*out) state->error = 83; /*alloc fail*/ + } + if(!state->error) { + lodepng_memset(*out, 0, outsize); + state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png); + } + lodepng_free(scanlines); +} + +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) { + *out = 0; + decodeGeneric(out, w, h, state, in, insize); + if(state->error) return state->error; + if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) { + /*same color type, no copying or converting of data needed*/ + /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype + the raw image has to the end user*/ + if(!state->decoder.color_convert) { + state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); + if(state->error) return state->error; + } + } else { /*color conversion needed*/ + unsigned char* data = *out; + size_t outsize; + + /*TODO: check if this works according to the statement in the documentation: "The converter can convert + from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/ + if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) + && !(state->info_raw.bitdepth == 8)) { + return 56; /*unsupported color mode conversion*/ + } + + outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!(*out)) { + state->error = 83; /*alloc fail*/ + } + else state->error = lodepng_convert(*out, data, &state->info_raw, + &state->info_png.color, *w, *h); + lodepng_free(data); + } + return state->error; +} + +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) { + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*disable reading things that this function doesn't output*/ + state.decoder.read_text_chunks = 0; + state.decoder.remember_unknown_chunks = 0; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + error = lodepng_decode(out, w, h, &state, in, insize); + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); +} + +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, + LodePNGColorType colortype, unsigned bitdepth) { + unsigned char* buffer = 0; + size_t buffersize; + unsigned error; + /* safe output values in case error happens */ + *out = 0; + *w = *h = 0; + error = lodepng_load_file(&buffer, &buffersize, filename); + if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { + return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); +} + +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { + return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) { + settings->color_convert = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->read_text_chunks = 1; + settings->remember_unknown_chunks = 0; + settings->max_text_size = 16777216; + settings->max_icc_size = 16777216; /* 16MB is much more than enough for any reasonable ICC profile */ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + settings->ignore_crc = 0; + settings->ignore_critical = 0; + settings->ignore_end = 0; + lodepng_decompress_settings_init(&settings->zlibsettings); +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) + +void lodepng_state_init(LodePNGState* state) { +#ifdef LODEPNG_COMPILE_DECODER + lodepng_decoder_settings_init(&state->decoder); +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + lodepng_encoder_settings_init(&state->encoder); +#endif /*LODEPNG_COMPILE_ENCODER*/ + lodepng_color_mode_init(&state->info_raw); + lodepng_info_init(&state->info_png); + state->error = 1; +} + +void lodepng_state_cleanup(LodePNGState* state) { + lodepng_color_mode_cleanup(&state->info_raw); + lodepng_info_cleanup(&state->info_png); +} + +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) { + lodepng_state_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->info_raw); + lodepng_info_init(&dest->info_png); + dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; + dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; +} + +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Encoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + + +static unsigned writeSignature(ucvector* out) { + size_t pos = out->size; + const unsigned char signature[] = {137, 80, 78, 71, 13, 10, 26, 10}; + /*8 bytes PNG signature, aka the magic bytes*/ + if(!ucvector_resize(out, out->size + 8)) return 83; /*alloc fail*/ + lodepng_memcpy(out->data + pos, signature, 8); + return 0; +} + +static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) { + unsigned char *chunk, *data; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 13, "IHDR")); + data = chunk + 8; + + lodepng_set32bitInt(data + 0, w); /*width*/ + lodepng_set32bitInt(data + 4, h); /*height*/ + data[8] = (unsigned char)bitdepth; /*bit depth*/ + data[9] = (unsigned char)colortype; /*color type*/ + data[10] = 0; /*compression method*/ + data[11] = 0; /*filter method*/ + data[12] = interlace_method; /*interlace method*/ + + lodepng_chunk_generate_crc(chunk); + return 0; +} + +/* only adds the chunk if needed (there is a key or palette with alpha) */ +static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) { + unsigned char* chunk; + size_t i, j = 8; + + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, info->palettesize * 3, "PLTE")); + + for(i = 0; i != info->palettesize; ++i) { + /*add all channels except alpha channel*/ + chunk[j++] = info->palette[i * 4 + 0]; + chunk[j++] = info->palette[i * 4 + 1]; + chunk[j++] = info->palette[i * 4 + 2]; + } + + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) { + unsigned char* chunk = 0; + + if(info->colortype == LCT_PALETTE) { + size_t i, amount = info->palettesize; + /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ + for(i = info->palettesize; i != 0; --i) { + if(info->palette[4 * (i - 1) + 3] != 255) break; + --amount; + } + if(amount) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, amount, "tRNS")); + /*add the alpha channel values from the palette*/ + for(i = 0; i != amount; ++i) chunk[8 + i] = info->palette[4 * i + 3]; + } + } else if(info->colortype == LCT_GREY) { + if(info->key_defined) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "tRNS")); + chunk[8] = (unsigned char)(info->key_r >> 8); + chunk[9] = (unsigned char)(info->key_r & 255); + } + } else if(info->colortype == LCT_RGB) { + if(info->key_defined) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "tRNS")); + chunk[8] = (unsigned char)(info->key_r >> 8); + chunk[9] = (unsigned char)(info->key_r & 255); + chunk[10] = (unsigned char)(info->key_g >> 8); + chunk[11] = (unsigned char)(info->key_g & 255); + chunk[12] = (unsigned char)(info->key_b >> 8); + chunk[13] = (unsigned char)(info->key_b & 255); + } + } + + if(chunk) lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, + LodePNGCompressSettings* zlibsettings) { + unsigned error = 0; + unsigned char* zlib = 0; + size_t zlibsize = 0; + + error = zlib_compress(&zlib, &zlibsize, data, datasize, zlibsettings); + if(!error) { + error = lodepng_chunk_createv(out, zlibsize, "IDAT", zlib); + } + lodepng_free(zlib); + return error; +} + +static unsigned addChunk_IEND(ucvector* out) { + return lodepng_chunk_createv(out, 0, "IEND", 0); +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) { + unsigned char* chunk = 0; + size_t keysize = lodepng_strlen(keyword), textsize = lodepng_strlen(textstring); + size_t size = keysize + 1 + textsize; + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, size, "tEXt")); + lodepng_memcpy(chunk + 8, keyword, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + lodepng_memcpy(chunk + 9 + keysize, textstring, textsize); + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, + LodePNGCompressSettings* zlibsettings) { + unsigned error = 0; + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t textsize = lodepng_strlen(textstring); + size_t keysize = lodepng_strlen(keyword); + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + + error = zlib_compress(&compressed, &compressedsize, + (const unsigned char*)textstring, textsize, zlibsettings); + if(!error) { + size_t size = keysize + 2 + compressedsize; + error = lodepng_chunk_init(&chunk, out, size, "zTXt"); + } + if(!error) { + lodepng_memcpy(chunk + 8, keyword, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + chunk[9 + keysize] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize); + lodepng_chunk_generate_crc(chunk); + } + + lodepng_free(compressed); + return error; +} + +static unsigned addChunk_iTXt(ucvector* out, unsigned compress, const char* keyword, const char* langtag, + const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) { + unsigned error = 0; + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t textsize = lodepng_strlen(textstring); + size_t keysize = lodepng_strlen(keyword), langsize = lodepng_strlen(langtag), transsize = lodepng_strlen(transkey); + + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + + if(compress) { + error = zlib_compress(&compressed, &compressedsize, + (const unsigned char*)textstring, textsize, zlibsettings); + } + if(!error) { + size_t size = keysize + 3 + langsize + 1 + transsize + 1 + (compress ? compressedsize : textsize); + error = lodepng_chunk_init(&chunk, out, size, "iTXt"); + } + if(!error) { + size_t pos = 8; + lodepng_memcpy(chunk + pos, keyword, keysize); + pos += keysize; + chunk[pos++] = 0; /*null termination char*/ + chunk[pos++] = (compress ? 1 : 0); /*compression flag*/ + chunk[pos++] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + pos, langtag, langsize); + pos += langsize; + chunk[pos++] = 0; /*null termination char*/ + lodepng_memcpy(chunk + pos, transkey, transsize); + pos += transsize; + chunk[pos++] = 0; /*null termination char*/ + if(compress) { + lodepng_memcpy(chunk + pos, compressed, compressedsize); + } else { + lodepng_memcpy(chunk + pos, textstring, textsize); + } + lodepng_chunk_generate_crc(chunk); + } + + lodepng_free(compressed); + return error; +} + +static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) { + unsigned char* chunk = 0; + if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "bKGD")); + chunk[8] = (unsigned char)(info->background_r >> 8); + chunk[9] = (unsigned char)(info->background_r & 255); + } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "bKGD")); + chunk[8] = (unsigned char)(info->background_r >> 8); + chunk[9] = (unsigned char)(info->background_r & 255); + chunk[10] = (unsigned char)(info->background_g >> 8); + chunk[11] = (unsigned char)(info->background_g & 255); + chunk[12] = (unsigned char)(info->background_b >> 8); + chunk[13] = (unsigned char)(info->background_b & 255); + } else if(info->color.colortype == LCT_PALETTE) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 1, "bKGD")); + chunk[8] = (unsigned char)(info->background_r & 255); /*palette index*/ + } + if(chunk) lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 7, "tIME")); + chunk[8] = (unsigned char)(time->year >> 8); + chunk[9] = (unsigned char)(time->year & 255); + chunk[10] = (unsigned char)time->month; + chunk[11] = (unsigned char)time->day; + chunk[12] = (unsigned char)time->hour; + chunk[13] = (unsigned char)time->minute; + chunk[14] = (unsigned char)time->second; + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 9, "pHYs")); + lodepng_set32bitInt(chunk + 8, info->phys_x); + lodepng_set32bitInt(chunk + 12, info->phys_y); + chunk[16] = info->phys_unit; + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 4, "gAMA")); + lodepng_set32bitInt(chunk + 8, info->gama_gamma); + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 32, "cHRM")); + lodepng_set32bitInt(chunk + 8, info->chrm_white_x); + lodepng_set32bitInt(chunk + 12, info->chrm_white_y); + lodepng_set32bitInt(chunk + 16, info->chrm_red_x); + lodepng_set32bitInt(chunk + 20, info->chrm_red_y); + lodepng_set32bitInt(chunk + 24, info->chrm_green_x); + lodepng_set32bitInt(chunk + 28, info->chrm_green_y); + lodepng_set32bitInt(chunk + 32, info->chrm_blue_x); + lodepng_set32bitInt(chunk + 36, info->chrm_blue_y); + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) { + unsigned char data = info->srgb_intent; + return lodepng_chunk_createv(out, 1, "sRGB", &data); +} + +static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) { + unsigned error = 0; + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t keysize = lodepng_strlen(info->iccp_name); + + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + error = zlib_compress(&compressed, &compressedsize, + info->iccp_profile, info->iccp_profile_size, zlibsettings); + if(!error) { + size_t size = keysize + 2 + compressedsize; + error = lodepng_chunk_init(&chunk, out, size, "iCCP"); + } + if(!error) { + lodepng_memcpy(chunk + 8, info->iccp_name, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + chunk[9 + keysize] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize); + lodepng_chunk_generate_crc(chunk); + } + + lodepng_free(compressed); + return error; +} + +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, + size_t length, size_t bytewidth, unsigned char filterType) { + size_t i; + switch(filterType) { + case 0: /*None*/ + for(i = 0; i != length; ++i) out[i] = scanline[i]; + break; + case 1: /*Sub*/ + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; + break; + case 2: /*Up*/ + if(prevline) { + for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; + } else { + for(i = 0; i != length; ++i) out[i] = scanline[i]; + } + break; + case 3: /*Average*/ + if(prevline) { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); + } else { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); + } + break; + case 4: /*Paeth*/ + if(prevline) { + /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ + for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); + for(i = bytewidth; i < length; ++i) { + out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); + } + } else { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ + for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); + } + break; + default: return; /*invalid filter type given*/ + } +} + +/* integer binary logarithm, max return value is 31 */ +static size_t ilog2(size_t i) { + size_t result = 0; + if(i >= 65536) { result += 16; i >>= 16; } + if(i >= 256) { result += 8; i >>= 8; } + if(i >= 16) { result += 4; i >>= 4; } + if(i >= 4) { result += 2; i >>= 2; } + if(i >= 2) { result += 1; /*i >>= 1;*/ } + return result; +} + +/* integer approximation for i * log2(i), helper function for LFS_ENTROPY */ +static size_t ilog2i(size_t i) { + size_t l; + if(i == 0) return 0; + l = ilog2(i); + /* approximate i*log2(i): l is integer logarithm, ((i - (1u << l)) << 1u) + linearly approximates the missing fractional part multiplied by i */ + return i * l + ((i - (1u << l)) << 1u); +} + +static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* color, const LodePNGEncoderSettings* settings) { + /* + For PNG filter method 0 + out must be a buffer with as size: h + (w * h * bpp + 7u) / 8u, because there are + the scanlines with 1 extra byte per scanline + */ + + unsigned bpp = lodepng_get_bpp(color); + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; + + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7u) / 8u; + const unsigned char* prevline = 0; + unsigned x, y; + unsigned error = 0; + LodePNGFilterStrategy strategy = settings->filter_strategy; + + /* + There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: + * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. + use fixed filtering, with the filter None). + * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is + not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply + all five filters and select the filter that produces the smallest sum of absolute values per row. + This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. + + If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, + but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum + heuristic is used. + */ + if(settings->filter_palette_zero && + (color->colortype == LCT_PALETTE || color->bitdepth < 8)) strategy = LFS_ZERO; + + if(bpp == 0) return 31; /*error: invalid color type*/ + + if(strategy >= LFS_ZERO && strategy <= LFS_FOUR) { + unsigned char type = (unsigned char)strategy; + for(y = 0; y != h; ++y) { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + out[outindex] = type; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); + prevline = &in[inindex]; + } + } else if(strategy == LFS_MINSUM) { + /*adaptive filtering*/ + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned char type, bestType = 0; + + for(type = 0; type != 5; ++type) { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) error = 83; /*alloc fail*/ + } + + if(!error) { + for(y = 0; y != h; ++y) { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) { + size_t sum = 0; + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + + /*calculate the sum of the result*/ + if(type == 0) { + for(x = 0; x != linebytes; ++x) sum += (unsigned char)(attempt[type][x]); + } else { + for(x = 0; x != linebytes; ++x) { + /*For differences, each byte should be treated as signed, values above 127 are negative + (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. + This means filtertype 0 is almost never chosen, but that is justified.*/ + unsigned char s = attempt[type][x]; + sum += s < 128 ? s : (255U - s); + } + } + + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum < smallest) { + bestType = type; + smallest = sum; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } else if(strategy == LFS_ENTROPY) { + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t bestSum = 0; + unsigned type, bestType = 0; + unsigned count[256]; + + for(type = 0; type != 5; ++type) { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) error = 83; /*alloc fail*/ + } + + if(!error) { + for(y = 0; y != h; ++y) { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) { + size_t sum = 0; + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + lodepng_memset(count, 0, 256 * sizeof(*count)); + for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; + ++count[type]; /*the filter type itself is part of the scanline*/ + for(x = 0; x != 256; ++x) { + sum += ilog2i(count[x]); + } + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum > bestSum) { + bestType = type; + bestSum = sum; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } else if(strategy == LFS_PREDEFINED) { + for(y = 0; y != h; ++y) { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + unsigned char type = settings->predefined_filters[y]; + out[outindex] = type; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); + prevline = &in[inindex]; + } + } else if(strategy == LFS_BRUTE_FORCE) { + /*brute force filter chooser. + deflate the scanline after every filter attempt to see which one deflates best. + This is very slow and gives only slightly smaller, sometimes even larger, result*/ + size_t size[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned type = 0, bestType = 0; + unsigned char* dummy; + LodePNGCompressSettings zlibsettings; + lodepng_memcpy(&zlibsettings, &settings->zlibsettings, sizeof(LodePNGCompressSettings)); + /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, + to simulate the true case where the tree is the same for the whole image. Sometimes it gives + better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare + cases better compression. It does make this a bit less slow, so it's worth doing this.*/ + zlibsettings.btype = 1; + /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG + images only, so disable it*/ + zlibsettings.custom_zlib = 0; + zlibsettings.custom_deflate = 0; + for(type = 0; type != 5; ++type) { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) error = 83; /*alloc fail*/ + } + if(!error) { + for(y = 0; y != h; ++y) /*try the 5 filter types*/ { + for(type = 0; type != 5; ++type) { + unsigned testsize = (unsigned)linebytes; + /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ + + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + size[type] = 0; + dummy = 0; + zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); + lodepng_free(dummy); + /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || size[type] < smallest) { + bestType = type; + smallest = size[type]; + } + } + prevline = &in[y * linebytes]; + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else return 88; /* unknown filter strategy */ + + return error; +} + +static void addPaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) { + /*The opposite of the removePaddingBits function + olinebits must be >= ilinebits*/ + unsigned y; + size_t diff = olinebits - ilinebits; + size_t obp = 0, ibp = 0; /*bit pointers*/ + for(y = 0; y != h; ++y) { + size_t x; + for(x = 0; x < ilinebits; ++x) { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + /*obp += diff; --> no, fill in some value in the padding bits too, to avoid + "Use of uninitialised value of size ###" warning from valgrind*/ + for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); + } +} + +/* +in: non-interlaced image with size w*h +out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with + no padding bits between scanlines, but between reduced images so that each + reduced image starts at a byte. +bpp: bits per pixel +there are no padding bits, not between scanlines, not between reduced images +in has the following size in bits: w * h * bpp. +out is possibly bigger due to padding bits between reduced images +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) { + for(i = 0; i != 7; ++i) { + unsigned x, y, b; + size_t bytewidth = bpp / 8u; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { + size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; + for(b = 0; b < bytewidth; ++b) { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { + for(i = 0; i != 7; ++i) { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { + ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + for(b = 0; b < bpp; ++b) { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + } + } + } +} + +/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. +return value is error**/ +static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, + unsigned w, unsigned h, + const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) { + /* + This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: + *) if no Adam7: 1) add padding bits (= possible extra bits per scanline if bpp < 8) 2) filter + *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + unsigned error = 0; + + if(info_png->interlace_method == 0) { + *outsize = h + (h * ((w * bpp + 7u) / 8u)); /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ + + if(!error) { + /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ + if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) { + unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7u) / 8u)); + if(!padded) error = 83; /*alloc fail*/ + if(!error) { + addPaddingBits(padded, in, ((w * bpp + 7u) / 8u) * 8u, w * bpp, h); + error = filter(*out, padded, w, h, &info_png->color, settings); + } + lodepng_free(padded); + } else { + /*we can immediately filter into the out buffer, no other steps needed*/ + error = filter(*out, in, w, h, &info_png->color, settings); + } + } + } else /*interlace_method is 1 (Adam7)*/ { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned char* adam7; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out)) error = 83; /*alloc fail*/ + + adam7 = (unsigned char*)lodepng_malloc(passstart[7]); + if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ + + if(!error) { + unsigned i; + + Adam7_interlace(adam7, in, w, h, bpp); + for(i = 0; i != 7; ++i) { + if(bpp < 8) { + unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); + if(!padded) ERROR_BREAK(83); /*alloc fail*/ + addPaddingBits(padded, &adam7[passstart[i]], + ((passw[i] * bpp + 7u) / 8u) * 8u, passw[i] * bpp, passh[i]); + error = filter(&(*out)[filter_passstart[i]], padded, + passw[i], passh[i], &info_png->color, settings); + lodepng_free(padded); + } else { + error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], + passw[i], passh[i], &info_png->color, settings); + } + + if(error) break; + } + } + + lodepng_free(adam7); + } + + return error; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) { + unsigned char* inchunk = data; + while((size_t)(inchunk - data) < datasize) { + CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); + out->allocsize = out->size; /*fix the allocsize again*/ + inchunk = lodepng_chunk_next(inchunk, data + datasize); + } + return 0; +} + +static unsigned isGrayICCProfile(const unsigned char* profile, unsigned size) { + /* + It is a gray profile if bytes 16-19 are "GRAY", rgb profile if bytes 16-19 + are "RGB ". We do not perform any full parsing of the ICC profile here, other + than check those 4 bytes to grayscale profile. Other than that, validity of + the profile is not checked. This is needed only because the PNG specification + requires using a non-gray color model if there is an ICC profile with "RGB " + (sadly limiting compression opportunities if the input data is grayscale RGB + data), and requires using a gray color model if it is "GRAY". + */ + if(size < 20) return 0; + return profile[16] == 'G' && profile[17] == 'R' && profile[18] == 'A' && profile[19] == 'Y'; +} + +static unsigned isRGBICCProfile(const unsigned char* profile, unsigned size) { + /* See comment in isGrayICCProfile*/ + if(size < 20) return 0; + return profile[16] == 'R' && profile[17] == 'G' && profile[18] == 'B' && profile[19] == ' '; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state) { + unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ + size_t datasize = 0; + ucvector outv = ucvector_init(NULL, 0); + LodePNGInfo info; + const LodePNGInfo* info_png = &state->info_png; + + lodepng_info_init(&info); + + /*provide some proper output values if error will happen*/ + *out = 0; + *outsize = 0; + state->error = 0; + + /*check input values validity*/ + if((info_png->color.colortype == LCT_PALETTE || state->encoder.force_palette) + && (info_png->color.palettesize == 0 || info_png->color.palettesize > 256)) { + state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/ + goto cleanup; + } + if(state->encoder.zlibsettings.btype > 2) { + state->error = 61; /*error: invalid btype*/ + goto cleanup; + } + if(info_png->interlace_method > 1) { + state->error = 71; /*error: invalid interlace mode*/ + goto cleanup; + } + state->error = checkColorValidity(info_png->color.colortype, info_png->color.bitdepth); + if(state->error) goto cleanup; /*error: invalid color type given*/ + state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); + if(state->error) goto cleanup; /*error: invalid color type given*/ + + /* color convert and compute scanline filter types */ + lodepng_info_copy(&info, &state->info_png); + if(state->encoder.auto_convert) { + LodePNGColorStats stats; + lodepng_color_stats_init(&stats); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(info_png->iccp_defined && + isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) { + /*the PNG specification does not allow to use palette with a GRAY ICC profile, even + if the palette has only gray colors, so disallow it.*/ + stats.allow_palette = 0; + } + if(info_png->iccp_defined && + isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) { + /*the PNG specification does not allow to use grayscale color with RGB ICC profile, so disallow gray.*/ + stats.allow_greyscale = 0; + } +#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ + state->error = lodepng_compute_color_stats(&stats, image, w, h, &state->info_raw); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(info_png->background_defined) { + /*the background chunk's color must be taken into account as well*/ + unsigned r = 0, g = 0, b = 0; + LodePNGColorMode mode16 = lodepng_color_mode_make(LCT_RGB, 16); + lodepng_convert_rgb(&r, &g, &b, info_png->background_r, info_png->background_g, info_png->background_b, &mode16, &info_png->color); + state->error = lodepng_color_stats_add(&stats, r, g, b, 65535); + if(state->error) goto cleanup; + } +#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ + state->error = auto_choose_color(&info.color, &state->info_raw, &stats); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*also convert the background chunk*/ + if(info_png->background_defined) { + if(lodepng_convert_rgb(&info.background_r, &info.background_g, &info.background_b, + info_png->background_r, info_png->background_g, info_png->background_b, &info.color, &info_png->color)) { + state->error = 104; + goto cleanup; + } + } +#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(info_png->iccp_defined) { + unsigned gray_icc = isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size); + unsigned rgb_icc = isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size); + unsigned gray_png = info.color.colortype == LCT_GREY || info.color.colortype == LCT_GREY_ALPHA; + if(!gray_icc && !rgb_icc) { + state->error = 100; /* Disallowed profile color type for PNG */ + goto cleanup; + } + if(gray_icc != gray_png) { + /*Not allowed to use RGB/RGBA/palette with GRAY ICC profile or vice versa, + or in case of auto_convert, it wasn't possible to find appropriate model*/ + state->error = state->encoder.auto_convert ? 102 : 101; + goto cleanup; + } + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) { + unsigned char* converted; + size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7u) / 8u; + + converted = (unsigned char*)lodepng_malloc(size); + if(!converted && size) state->error = 83; /*alloc fail*/ + if(!state->error) { + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); + } + if(!state->error) { + state->error = preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + } + lodepng_free(converted); + if(state->error) goto cleanup; + } else { + state->error = preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + if(state->error) goto cleanup; + } + + /* output all PNG chunks */ { +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + size_t i; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*write signature and chunks*/ + state->error = writeSignature(&outv); + if(state->error) goto cleanup; + /*IHDR*/ + state->error = addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*unknown chunks between IHDR and PLTE*/ + if(info.unknown_chunks_data[0]) { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); + if(state->error) goto cleanup; + } + /*color profile chunks must come before PLTE */ + if(info.iccp_defined) { + state->error = addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings); + if(state->error) goto cleanup; + } + if(info.srgb_defined) { + state->error = addChunk_sRGB(&outv, &info); + if(state->error) goto cleanup; + } + if(info.gama_defined) { + state->error = addChunk_gAMA(&outv, &info); + if(state->error) goto cleanup; + } + if(info.chrm_defined) { + state->error = addChunk_cHRM(&outv, &info); + if(state->error) goto cleanup; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*PLTE*/ + if(info.color.colortype == LCT_PALETTE) { + state->error = addChunk_PLTE(&outv, &info.color); + if(state->error) goto cleanup; + } + if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) { + /*force_palette means: write suggested palette for truecolor in PLTE chunk*/ + state->error = addChunk_PLTE(&outv, &info.color); + if(state->error) goto cleanup; + } + /*tRNS (this will only add if when necessary) */ + state->error = addChunk_tRNS(&outv, &info.color); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*bKGD (must come between PLTE and the IDAt chunks*/ + if(info.background_defined) { + state->error = addChunk_bKGD(&outv, &info); + if(state->error) goto cleanup; + } + /*pHYs (must come before the IDAT chunks)*/ + if(info.phys_defined) { + state->error = addChunk_pHYs(&outv, &info); + if(state->error) goto cleanup; + } + + /*unknown chunks between PLTE and IDAT*/ + if(info.unknown_chunks_data[1]) { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); + if(state->error) goto cleanup; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*IDAT (multiple IDAT chunks must be consecutive)*/ + state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*tIME*/ + if(info.time_defined) { + state->error = addChunk_tIME(&outv, &info.time); + if(state->error) goto cleanup; + } + /*tEXt and/or zTXt*/ + for(i = 0; i != info.text_num; ++i) { + if(lodepng_strlen(info.text_keys[i]) > 79) { + state->error = 66; /*text chunk too large*/ + goto cleanup; + } + if(lodepng_strlen(info.text_keys[i]) < 1) { + state->error = 67; /*text chunk too small*/ + goto cleanup; + } + if(state->encoder.text_compression) { + state->error = addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); + if(state->error) goto cleanup; + } else { + state->error = addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); + if(state->error) goto cleanup; + } + } + /*LodePNG version id in text chunk*/ + if(state->encoder.add_id) { + unsigned already_added_id_text = 0; + for(i = 0; i != info.text_num; ++i) { + const char* k = info.text_keys[i]; + /* Could use strcmp, but we're not calling or reimplementing this C library function for this use only */ + if(k[0] == 'L' && k[1] == 'o' && k[2] == 'd' && k[3] == 'e' && + k[4] == 'P' && k[5] == 'N' && k[6] == 'G' && k[7] == '\0') { + already_added_id_text = 1; + break; + } + } + if(already_added_id_text == 0) { + state->error = addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + if(state->error) goto cleanup; + } + } + /*iTXt*/ + for(i = 0; i != info.itext_num; ++i) { + if(lodepng_strlen(info.itext_keys[i]) > 79) { + state->error = 66; /*text chunk too large*/ + goto cleanup; + } + if(lodepng_strlen(info.itext_keys[i]) < 1) { + state->error = 67; /*text chunk too small*/ + goto cleanup; + } + state->error = addChunk_iTXt( + &outv, state->encoder.text_compression, + info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], + &state->encoder.zlibsettings); + if(state->error) goto cleanup; + } + + /*unknown chunks between IDAT and IEND*/ + if(info.unknown_chunks_data[2]) { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); + if(state->error) goto cleanup; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + state->error = addChunk_IEND(&outv); + if(state->error) goto cleanup; + } + +cleanup: + lodepng_info_cleanup(&info); + lodepng_free(data); + + /*instead of cleaning the vector up, give it to the output*/ + *out = outv.data; + *outsize = outv.size; + + return state->error; +} + +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, + unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + state.info_png.color.colortype = colortype; + state.info_png.color.bitdepth = bitdepth; + lodepng_encode(out, outsize, image, w, h, &state); + error = state.error; + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); + if(!error) error = lodepng_save_file(buffer, buffersize, filename); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { + return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { + return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) { + lodepng_compress_settings_init(&settings->zlibsettings); + settings->filter_palette_zero = 1; + settings->filter_strategy = LFS_MINSUM; + settings->auto_convert = 1; + settings->force_palette = 0; + settings->predefined_filters = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->add_id = 0; + settings->text_compression = 1; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/* +This returns the description of a numerical error code in English. This is also +the documentation of all the error codes. +*/ +const char* lodepng_error_text(unsigned code) { + switch(code) { + case 0: return "no error, everything went ok"; + case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ + case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ + case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ + case 13: return "problem while processing dynamic deflate block"; + case 14: return "problem while processing dynamic deflate block"; + case 15: return "problem while processing dynamic deflate block"; + /*this error could happen if there are only 0 or 1 symbols present in the huffman code:*/ + case 16: return "invalid code while processing dynamic deflate block"; + case 17: return "end of out buffer memory reached while inflating"; + case 18: return "invalid distance code while inflating"; + case 19: return "end of out buffer memory reached while inflating"; + case 20: return "invalid deflate block BTYPE encountered while decoding"; + case 21: return "NLEN is not ones complement of LEN in a deflate block"; + + /*end of out buffer memory reached while inflating: + This can happen if the inflated deflate data is longer than the amount of bytes required to fill up + all the pixels of the image, given the color depth and image dimensions. Something that doesn't + happen in a normal, well encoded, PNG image.*/ + case 22: return "end of out buffer memory reached while inflating"; + case 23: return "end of in buffer memory reached while inflating"; + case 24: return "invalid FCHECK in zlib header"; + case 25: return "invalid compression method in zlib header"; + case 26: return "FDICT encountered in zlib header while it's not used for PNG"; + case 27: return "PNG file is smaller than a PNG header"; + /*Checks the magic file header, the first 8 bytes of the PNG file*/ + case 28: return "incorrect PNG signature, it's no PNG or corrupted"; + case 29: return "first chunk is not the header chunk"; + case 30: return "chunk length too large, chunk broken off at end of file"; + case 31: return "illegal PNG color type or bpp"; + case 32: return "illegal PNG compression method"; + case 33: return "illegal PNG filter method"; + case 34: return "illegal PNG interlace method"; + case 35: return "chunk length of a chunk is too large or the chunk too small"; + case 36: return "illegal PNG filter type encountered"; + case 37: return "illegal bit depth for this color type given"; + case 38: return "the palette is too small or too big"; /*0, or more than 256 colors*/ + case 39: return "tRNS chunk before PLTE or has more entries than palette size"; + case 40: return "tRNS chunk has wrong size for grayscale image"; + case 41: return "tRNS chunk has wrong size for RGB image"; + case 42: return "tRNS chunk appeared while it was not allowed for this color type"; + case 43: return "bKGD chunk has wrong size for palette image"; + case 44: return "bKGD chunk has wrong size for grayscale image"; + case 45: return "bKGD chunk has wrong size for RGB image"; + case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; + case 49: return "jumped past memory while generating dynamic huffman tree"; + case 50: return "jumped past memory while generating dynamic huffman tree"; + case 51: return "jumped past memory while inflating huffman block"; + case 52: return "jumped past memory while inflating"; + case 53: return "size of zlib data too small"; + case 54: return "repeat symbol in tree while there was no value symbol yet"; + /*jumped past tree while generating huffman tree, this could be when the + tree will have more leaves than symbols after generating it out of the + given lengths. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ + case 55: return "jumped past tree while generating huffman tree"; + case 56: return "given output image colortype or bitdepth not supported for color conversion"; + case 57: return "invalid CRC encountered (checking CRC can be disabled)"; + case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; + case 59: return "requested color conversion not supported"; + case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; + case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; + /*LodePNG leaves the choice of RGB to grayscale conversion formula to the user.*/ + case 62: return "conversion from color to grayscale not supported"; + /*(2^31-1)*/ + case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; + /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ + case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; + case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; + case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; + case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; + case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; + case 71: return "invalid interlace mode given to encoder (must be 0 or 1)"; + case 72: return "while decoding, invalid compression method encountering in zTXt or iTXt chunk (it must be 0)"; + case 73: return "invalid tIME chunk size"; + case 74: return "invalid pHYs chunk size"; + /*length could be wrong, or data chopped off*/ + case 75: return "no null termination char found while decoding text chunk"; + case 76: return "iTXt chunk too short to contain required bytes"; + case 77: return "integer overflow in buffer size"; + case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ + case 79: return "failed to open file for writing"; + case 80: return "tried creating a tree of 0 symbols"; + case 81: return "lazy matching at pos 0 is impossible"; + case 82: return "color conversion to palette requested while a color isn't in palette, or index out of bounds"; + case 83: return "memory allocation failed"; + case 84: return "given image too small to contain all pixels to be encoded"; + case 86: return "impossible offset in lz77 encoding (internal bug)"; + case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; + case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; + case 89: return "text chunk keyword too short or long: must have size 1-79"; + /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ + case 90: return "windowsize must be a power of two"; + case 91: return "invalid decompressed idat size"; + case 92: return "integer overflow due to too many pixels"; + case 93: return "zero width or height is invalid"; + case 94: return "header chunk must have a size of 13 bytes"; + case 95: return "integer overflow with combined idat chunk size"; + case 96: return "invalid gAMA chunk size"; + case 97: return "invalid cHRM chunk size"; + case 98: return "invalid sRGB chunk size"; + case 99: return "invalid sRGB rendering intent"; + case 100: return "invalid ICC profile color type, the PNG specification only allows RGB or GRAY"; + case 101: return "PNG specification does not allow RGB ICC profile on gray color types and vice versa"; + case 102: return "not allowed to set grayscale ICC profile with colored pixels by PNG specification"; + case 103: return "invalid palette index in bKGD chunk. Maybe it came before PLTE chunk?"; + case 104: return "invalid bKGD color while encoding (e.g. palette index out of range)"; + case 105: return "integer overflow of bitsize"; + case 106: return "PNG file must have PLTE chunk if color type is palette"; + case 107: return "color convert from palette mode requested without setting the palette data in it"; + case 108: return "tried to add more than 256 values to a palette"; + /*this limit can be configured in LodePNGDecompressSettings*/ + case 109: return "tried to decompress zlib or deflate data larger than desired max_output_size"; + case 110: return "custom zlib or inflate decompression failed"; + case 111: return "custom zlib or deflate compression failed"; + /*max text size limit can be configured in LodePNGDecoderSettings. This error prevents + unreasonable memory consumption when decoding due to impossibly large text sizes.*/ + case 112: return "compressed text unreasonably large"; + /*max ICC size limit can be configured in LodePNGDecoderSettings. This error prevents + unreasonable memory consumption when decoding due to impossibly large ICC profile*/ + case 113: return "ICC profile unreasonably large"; + } + return "unknown error code"; +} +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // C++ Wrapper // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng { + +#ifdef LODEPNG_COMPILE_DISK +unsigned load_file(std::vector& buffer, const std::string& filename) { + long size = lodepng_filesize(filename.c_str()); + if(size < 0) return 78; + buffer.resize((size_t)size); + return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned save_file(const std::vector& buffer, const std::string& filename) { + return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); +} +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings) { + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_decompress(&buffer, &buffersize, 0, in, insize, &settings); + if(buffer) { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings) { + return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings) { + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); + if(buffer) { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings) { + return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ + + +#ifdef LODEPNG_COMPILE_PNG + +State::State() { + lodepng_state_init(this); +} + +State::State(const State& other) { + lodepng_state_init(this); + lodepng_state_copy(this, &other); +} + +State::~State() { + lodepng_state_cleanup(this); +} + +State& State::operator=(const State& other) { + lodepng_state_copy(this, &other); + return *this; +} + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) { + unsigned char* buffer = 0; + unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); + if(buffer && !error) { + State state; + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + } + lodepng_free(buffer); + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, LodePNGColorType colortype, unsigned bitdepth) { + return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize) { + unsigned char* buffer = NULL; + unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); + if(buffer && !error) { + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + } + lodepng_free(buffer); + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in) { + return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, + LodePNGColorType colortype, unsigned bitdepth) { + std::vector buffer; + /* safe output values in case error happens */ + w = h = 0; + unsigned error = load_file(buffer, filename); + if(error) return error; + return decode(out, w, h, buffer, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DECODER */ +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); + if(buffer) { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} + +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state) { + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); + if(buffer) { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state) { + if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, state); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + std::vector buffer; + unsigned error = encode(buffer, in, w, h, colortype, bitdepth); + if(!error) error = save_file(buffer, filename); + return error; +} + +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_PNG */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ diff --git a/src/screenshots/lodepng/lodepng.h b/src/screenshots/lodepng/lodepng.h new file mode 100644 index 000000000..6801cb789 --- /dev/null +++ b/src/screenshots/lodepng/lodepng.h @@ -0,0 +1,1977 @@ +/* +LodePNG version 20201017 + +Copyright (c) 2005-2020 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef LODEPNG_H +#define LODEPNG_H + +#include /*for size_t*/ + +extern const char* LODEPNG_VERSION_STRING; + +/* +The following #defines are used to create code sections. They can be disabled +to disable code sections, which can give faster compile time and smaller binary. +The "NO_COMPILE" defines are designed to be used to pass as defines to the +compiler command to disable them without modifying this header, e.g. +-DLODEPNG_NO_COMPILE_ZLIB for gcc. +In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to +allow implementing a custom lodepng_crc32. +*/ +/*deflate & zlib. If disabled, you must specify alternative zlib functions in +the custom_zlib field of the compress and decompress settings*/ +#ifndef LODEPNG_NO_COMPILE_ZLIB +#define LODEPNG_COMPILE_ZLIB +#endif + +/*png encoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_PNG +#define LODEPNG_COMPILE_PNG +#endif + +/*deflate&zlib decoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_DECODER +#define LODEPNG_COMPILE_DECODER +#endif + +/*deflate&zlib encoder and png encoder*/ +#ifndef LODEPNG_NO_COMPILE_ENCODER +#define LODEPNG_COMPILE_ENCODER +#endif + +/*the optional built in harddisk file loading and saving functions*/ +#ifndef LODEPNG_NO_COMPILE_DISK +#define LODEPNG_COMPILE_DISK +#endif + +/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ +#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS +#define LODEPNG_COMPILE_ANCILLARY_CHUNKS +#endif + +/*ability to convert error numerical codes to English text string*/ +#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT +#define LODEPNG_COMPILE_ERROR_TEXT +#endif + +/*Compile the default allocators (C's free, malloc and realloc). If you disable this, +you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your +source files with custom allocators.*/ +#ifndef LODEPNG_NO_COMPILE_ALLOCATORS +#define LODEPNG_COMPILE_ALLOCATORS +#endif + +/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ +#ifdef __cplusplus +#ifndef LODEPNG_NO_COMPILE_CPP +#define LODEPNG_COMPILE_CPP +#endif +#endif + +#ifdef LODEPNG_COMPILE_CPP +#include +#include +#endif /*LODEPNG_COMPILE_CPP*/ + +#ifdef LODEPNG_COMPILE_PNG +/*The PNG color types (also used for raw image).*/ +typedef enum LodePNGColorType { + LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/ + LCT_RGB = 2, /*RGB: 8,16 bit*/ + LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ + LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/ + LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/ + /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid + byte value from 0 to 255 that could be present in an invalid PNG file header. Do + not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use + the valid color type names above, or numeric values like 1 or 7 when checking for + particular disallowed color type byte values, or cast to integer to print it.*/ + LCT_MAX_OCTET_VALUE = 255 +} LodePNGColorType; + +#ifdef LODEPNG_COMPILE_DECODER +/* +Converts PNG data in memory to raw pixel data. +out: Output parameter. Pointer to buffer that will contain the raw pixel data. + After decoding, its size is w * h * (bytes per pixel) bytes larger than + initially. Bytes per pixel depends on colortype and bitdepth. + Must be freed after usage with free(*out). + Note: for 16-bit per channel colors, uses big endian format like PNG does. +w: Output parameter. Pointer to width of pixel data. +h: Output parameter. Pointer to height of pixel data. +in: Memory buffer with the PNG file. +insize: size of the in buffer. +colortype: the desired color type for the raw output image. See explanation on PNG color types. +bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +#ifdef LODEPNG_COMPILE_DISK +/* +Load PNG from disk, from file with given name. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); + +/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Converts raw pixel data into a PNG image in memory. The colortype and bitdepth + of the output PNG image cannot be chosen, they are automatically determined + by the colortype, bitdepth and content of the input pixel data. + Note: for 16-bit per channel colors, needs big endian format like PNG does. +out: Output parameter. Pointer to buffer that will contain the PNG image data. + Must be freed after usage with free(*out). +outsize: Output parameter. Pointer to the size in bytes of the out buffer. +image: The raw pixel data to encode. The size of this buffer should be + w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. +w: width of the raw pixel data in pixels. +h: height of the raw pixel data in pixels. +colortype: the color type of the raw input image. See explanation on PNG color types. +bitdepth: the bit depth of the raw input image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DISK +/* +Converts raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned lodepng_encode_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng { +#ifdef LODEPNG_COMPILE_DECODER +/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype +is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts PNG file from disk to raw pixel data in memory. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::string& filename, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype +is that of the raw input data. The output PNG color type will be auto chosen.*/ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts 32-bit RGBA raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/*Returns an English description of the numerical error code.*/ +const char* lodepng_error_text(unsigned code); +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Settings for zlib decompression*/ +typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; +struct LodePNGDecompressSettings { + /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */ + unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ + unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/ + + /*Maximum decompressed size, beyond this the decoder may (and is encouraged to) stop decoding, + return an error, output a data size > max_output_size and all the data up to that point. This is + not hard limit nor a guarantee, but can prevent excessive memory usage. This setting is + ignored by the PNG decoder, but is used by the deflate/zlib decoder and can be used by custom ones. + Set to 0 to impose no limit (the default).*/ + size_t max_output_size; + + /*use custom zlib decoder instead of built in one (default: null). + Should return 0 if success, any non-0 if error (numeric value not exposed).*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + /*use custom deflate decoder instead of built in one (default: null) + if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate). + Should return 0 if success, any non-0 if error (numeric value not exposed).*/ + unsigned (*custom_inflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGDecompressSettings lodepng_default_decompress_settings; +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Settings for zlib compression. Tweaking these settings tweaks the balance +between speed and compression ratio. +*/ +typedef struct LodePNGCompressSettings LodePNGCompressSettings; +struct LodePNGCompressSettings /*deflate = compress*/ { + /*LZ77 related settings*/ + unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ + unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ + unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ + unsigned minmatch; /*minimum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ + unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ + unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ + + /*use custom zlib encoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + /*use custom deflate encoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_deflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGCompressSettings lodepng_default_compress_settings; +void lodepng_compress_settings_init(LodePNGCompressSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_PNG +/* +Color mode of an image. Contains all information required to decode the pixel +bits to RGBA colors. This information is the same as used in the PNG file +format, and is used both for PNG and raw image data in LodePNG. +*/ +typedef struct LodePNGColorMode { + /*header (IHDR)*/ + LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ + unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ + + /* + palette (PLTE and tRNS) + + Dynamically allocated with the colors of the palette, including alpha. + This field may not be allocated directly, use lodepng_color_mode_init first, + then lodepng_palette_add per color to correctly initialize it (to ensure size + of exactly 1024 bytes). + + The alpha channels must be set as well, set them to 255 for opaque images. + + When decoding, by default you can ignore this palette, since LodePNG already + fills the palette colors in the pixels of the raw RGBA output. + + The palette is only supported for color type 3. + */ + unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/ + size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/ + + /* + transparent color key (tRNS) + + This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. + For grayscale PNGs, r, g and b will all 3 be set to the same. + + When decoding, by default you can ignore this information, since LodePNG sets + pixels with this key to transparent already in the raw RGBA output. + + The color key is only supported for color types 0 and 2. + */ + unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ + unsigned key_r; /*red/grayscale component of color key*/ + unsigned key_g; /*green component of color key*/ + unsigned key_b; /*blue component of color key*/ +} LodePNGColorMode; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_color_mode_init(LodePNGColorMode* info); +void lodepng_color_mode_cleanup(LodePNGColorMode* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); +/* Makes a temporary LodePNGColorMode that does not need cleanup (no palette) */ +LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth); + +void lodepng_palette_clear(LodePNGColorMode* info); +/*add 1 color to the palette*/ +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ +unsigned lodepng_get_bpp(const LodePNGColorMode* info); +/*get the amount of color channels used, based on colortype in the struct. +If a palette is used, it counts as 1 channel.*/ +unsigned lodepng_get_channels(const LodePNGColorMode* info); +/*is it a grayscale type? (only colortype 0 or 4)*/ +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); +/*has it got an alpha channel? (only colortype 2 or 6)*/ +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); +/*has it got a palette? (only colortype 3)*/ +unsigned lodepng_is_palette_type(const LodePNGColorMode* info); +/*only returns true if there is a palette and there is a value in the palette with alpha < 255. +Loops through the palette to check this.*/ +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); +/* +Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. +Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). +Returns false if the image can only have opaque pixels. +In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, +or if "key_defined" is true. +*/ +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); +/*Returns the byte size of a raw image buffer with given width, height and color mode*/ +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*The information of a Time chunk in PNG.*/ +typedef struct LodePNGTime { + unsigned year; /*2 bytes used (0-65535)*/ + unsigned month; /*1-12*/ + unsigned day; /*1-31*/ + unsigned hour; /*0-23*/ + unsigned minute; /*0-59*/ + unsigned second; /*0-60 (to allow for leap seconds)*/ +} LodePNGTime; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Information about the PNG image, except pixels, width and height.*/ +typedef struct LodePNGInfo { + /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ + unsigned compression_method;/*compression method of the original file. Always 0.*/ + unsigned filter_method; /*filter method of the original file*/ + unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/ + LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /* + Suggested background color chunk (bKGD) + + This uses the same color mode and bit depth as the PNG (except no alpha channel), + with values truncated to the bit depth in the unsigned integer. + + For grayscale and palette PNGs, the value is stored in background_r. The values + in background_g and background_b are then unused. + + So when decoding, you may get these in a different color mode than the one you requested + for the raw pixels. + + When encoding with auto_convert, you must use the color model defined in info_png.color for + these values. The encoder normally ignores info_png.color when auto_convert is on, but will + use it to interpret these values (and convert copies of them to its chosen color model). + + When encoding, avoid setting this to an expensive color, such as a non-gray value + when the image is gray, or the compression will be worse since it will be forced to + write the PNG with a more expensive color mode (when auto_convert is on). + + The decoder does not use this background color to edit the color of pixels. This is a + completely optional metadata feature. + */ + unsigned background_defined; /*is a suggested background color given?*/ + unsigned background_r; /*red/gray/palette component of suggested background color*/ + unsigned background_g; /*green component of suggested background color*/ + unsigned background_b; /*blue component of suggested background color*/ + + /* + Non-international text chunks (tEXt and zTXt) + + The char** arrays each contain num strings. The actual messages are in + text_strings, while text_keys are keywords that give a short description what + the actual text represents, e.g. Title, Author, Description, or anything else. + + All the string fields below including strings, keys, names and language tags are null terminated. + The PNG specification uses null characters for the keys, names and tags, and forbids null + characters to appear in the main text which is why we can use null termination everywhere here. + + A keyword is minimum 1 character and maximum 79 characters long (plus the + additional null terminator). It's discouraged to use a single line length + longer than 79 characters for texts. + + Don't allocate these text buffers yourself. Use the init/cleanup functions + correctly and use lodepng_add_text and lodepng_clear_text. + + Standard text chunk keywords and strings are encoded using Latin-1. + */ + size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ + char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ + char** text_strings; /*the actual text*/ + + /* + International text chunks (iTXt) + Similar to the non-international text chunks, but with additional strings + "langtags" and "transkeys", and the following text encodings are used: + keys: Latin-1, langtags: ASCII, transkeys and strings: UTF-8. + keys must be 1-79 characters (plus the additional null terminator), the other + strings are any length. + */ + size_t itext_num; /*the amount of international texts in this PNG*/ + char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ + char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ + char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ + char** itext_strings; /*the actual international text - UTF-8 string*/ + + /*time chunk (tIME)*/ + unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ + LodePNGTime time; + + /*phys chunk (pHYs)*/ + unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ + unsigned phys_x; /*pixels per unit in x direction*/ + unsigned phys_y; /*pixels per unit in y direction*/ + unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ + + /* + Color profile related chunks: gAMA, cHRM, sRGB, iCPP + + LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color + profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please + use these values with a color management library. + + See the PNG, ICC and sRGB specifications for more information about the meaning of these values. + */ + + /* gAMA chunk: optional, overridden by sRGB or iCCP if those are present. */ + unsigned gama_defined; /* Whether a gAMA chunk is present (0 = not present, 1 = present). */ + unsigned gama_gamma; /* Gamma exponent times 100000 */ + + /* cHRM chunk: optional, overridden by sRGB or iCCP if those are present. */ + unsigned chrm_defined; /* Whether a cHRM chunk is present (0 = not present, 1 = present). */ + unsigned chrm_white_x; /* White Point x times 100000 */ + unsigned chrm_white_y; /* White Point y times 100000 */ + unsigned chrm_red_x; /* Red x times 100000 */ + unsigned chrm_red_y; /* Red y times 100000 */ + unsigned chrm_green_x; /* Green x times 100000 */ + unsigned chrm_green_y; /* Green y times 100000 */ + unsigned chrm_blue_x; /* Blue x times 100000 */ + unsigned chrm_blue_y; /* Blue y times 100000 */ + + /* + sRGB chunk: optional. May not appear at the same time as iCCP. + If gAMA is also present gAMA must contain value 45455. + If cHRM is also present cHRM must contain respectively 31270,32900,64000,33000,30000,60000,15000,6000. + */ + unsigned srgb_defined; /* Whether an sRGB chunk is present (0 = not present, 1 = present). */ + unsigned srgb_intent; /* Rendering intent: 0=perceptual, 1=rel. colorimetric, 2=saturation, 3=abs. colorimetric */ + + /* + iCCP chunk: optional. May not appear at the same time as sRGB. + + LodePNG does not parse or use the ICC profile (except its color space header field for an edge case), a + separate library to handle the ICC data (not included in LodePNG) format is needed to use it for color + management and conversions. + + For encoding, if iCCP is present, gAMA and cHRM are recommended to be added as well with values that match the ICC + profile as closely as possible, if you wish to do this you should provide the correct values for gAMA and cHRM and + enable their '_defined' flags since LodePNG will not automatically compute them from the ICC profile. + + For encoding, the ICC profile is required by the PNG specification to be an "RGB" profile for non-gray + PNG color types and a "GRAY" profile for gray PNG color types. If you disable auto_convert, you must ensure + the ICC profile type matches your requested color type, else the encoder gives an error. If auto_convert is + enabled (the default), and the ICC profile is not a good match for the pixel data, this will result in an encoder + error if the pixel data has non-gray pixels for a GRAY profile, or a silent less-optimal compression of the pixel + data if the pixels could be encoded as grayscale but the ICC profile is RGB. + + To avoid this do not set an ICC profile in the image unless there is a good reason for it, and when doing so + make sure you compute it carefully to avoid the above problems. + */ + unsigned iccp_defined; /* Whether an iCCP chunk is present (0 = not present, 1 = present). */ + char* iccp_name; /* Null terminated string with profile name, 1-79 bytes */ + /* + The ICC profile in iccp_profile_size bytes. + Don't allocate this buffer yourself. Use the init/cleanup functions + correctly and use lodepng_set_icc and lodepng_clear_icc. + */ + unsigned char* iccp_profile; + unsigned iccp_profile_size; /* The size of iccp_profile in bytes */ + + /* End of color profile related chunks */ + + + /* + unknown chunks: chunks not known by LodePNG, passed on byte for byte. + + There are 3 buffers, one for each position in the PNG where unknown chunks can appear. + Each buffer contains all unknown chunks for that position consecutively. + The 3 positions are: + 0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND. + + For encoding, do not store critical chunks or known chunks that are enabled with a "_defined" flag + above in here, since the encoder will blindly follow this and could then encode an invalid PNG file + (such as one with two IHDR chunks or the disallowed combination of sRGB with iCCP). But do use + this if you wish to store an ancillary chunk that is not supported by LodePNG (such as sPLT or hIST), + or any non-standard PNG chunk. + + Do not allocate or traverse this data yourself. Use the chunk traversing functions declared + later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. + */ + unsigned char* unknown_chunks_data[3]; + size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGInfo; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_info_init(LodePNGInfo* info); +void lodepng_info_cleanup(LodePNGInfo* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ +void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ + +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ +void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ + +/*replaces if exists*/ +unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size); +void lodepng_clear_icc(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/* +Converts raw buffer from one color type to another color type, based on +LodePNGColorMode structs to describe the input and output color type. +See the reference manual at the end of this header file to see which color conversions are supported. +return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) +The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel +of the output color type (lodepng_get_bpp). +For < 8 bpp images, there should not be padding bits at the end of scanlines. +For 16-bit per channel colors, uses big endian format like PNG does. +Return value is LodePNG error code +*/ +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DECODER +/* +Settings for the decoder. This contains settings for the PNG and the Zlib +decoder, but not the Info settings from the Info structs. +*/ +typedef struct LodePNGDecoderSettings { + LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ + + /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */ + unsigned ignore_crc; /*ignore CRC checksums*/ + unsigned ignore_critical; /*ignore unknown critical chunks*/ + unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ + /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable + errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some + strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters + in string keys, etc... */ + + unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ + + /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ + unsigned remember_unknown_chunks; + + /* maximum size for decompressed text chunks. If a text chunk's text is larger than this, an error is returned, + unless reading text chunks is disabled or this limit is set higher or disabled. Set to 0 to allow any size. + By default it is a value that prevents unreasonably large strings from hogging memory. */ + size_t max_text_size; + + /* maximum size for compressed ICC chunks. If the ICC profile is larger than this, an error will be returned. Set to + 0 to allow any size. By default this is a value that prevents ICC profiles that would be much larger than any + legitimate profile could be to hog memory. */ + size_t max_icc_size; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGDecoderSettings; + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ +typedef enum LodePNGFilterStrategy { + /*every filter at zero*/ + LFS_ZERO = 0, + /*every filter at 1, 2, 3 or 4 (paeth), unlike LFS_ZERO not a good choice, but for testing*/ + LFS_ONE = 1, + LFS_TWO = 2, + LFS_THREE = 3, + LFS_FOUR = 4, + /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ + LFS_MINSUM, + /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending + on the image, this is better or worse than minsum.*/ + LFS_ENTROPY, + /* + Brute-force-search PNG filters by compressing each filter for each scanline. + Experimental, very slow, and only rarely gives better compression than MINSUM. + */ + LFS_BRUTE_FORCE, + /*use predefined_filters buffer: you specify the filter type for each scanline*/ + LFS_PREDEFINED +} LodePNGFilterStrategy; + +/*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...), +which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +typedef struct LodePNGColorStats { + unsigned colored; /*not grayscale*/ + unsigned key; /*image is not opaque and color key is possible instead of full alpha*/ + unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/ + unsigned short key_g; + unsigned short key_b; + unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/ + unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16 or allow_palette is disabled.*/ + unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order, only valid when numcolors is valid*/ + unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for grayscale only. 16 if 16-bit per channel required.*/ + size_t numpixels; + + /*user settings for computing/using the stats*/ + unsigned allow_palette; /*default 1. if 0, disallow choosing palette colortype in auto_choose_color, and don't count numcolors*/ + unsigned allow_greyscale; /*default 1. if 0, choose RGB or RGBA even if the image only has gray colors*/ +} LodePNGColorStats; + +void lodepng_color_stats_init(LodePNGColorStats* stats); + +/*Get a LodePNGColorStats of the image. The stats must already have been inited. +Returns error code (e.g. alloc fail) or 0 if ok.*/ +unsigned lodepng_compute_color_stats(LodePNGColorStats* stats, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); + +/*Settings for the encoder.*/ +typedef struct LodePNGEncoderSettings { + LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ + + unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ + + /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than + 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to + completely follow the official PNG heuristic, filter_palette_zero must be true and + filter_strategy must be LFS_MINSUM*/ + unsigned filter_palette_zero; + /*Which filter strategy to use when not using zeroes due to filter_palette_zero. + Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ + LodePNGFilterStrategy filter_strategy; + /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with + the same length as the amount of scanlines in the image, and each value must <= 5. You + have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero + must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ + const unsigned char* predefined_filters; + + /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). + If colortype is 3, PLTE is _always_ created.*/ + unsigned force_palette; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*add LodePNG identifier and version as a text chunk, for debugging*/ + unsigned add_id; + /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ + unsigned text_compression; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGEncoderSettings; + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) +/*The settings, state and information for extended encoding and decoding.*/ +typedef struct LodePNGState { +#ifdef LODEPNG_COMPILE_DECODER + LodePNGDecoderSettings decoder; /*the decoding settings*/ +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + LodePNGEncoderSettings encoder; /*the encoding settings*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ + LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ + unsigned error; +} LodePNGState; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_state_init(LodePNGState* state); +void lodepng_state_cleanup(LodePNGState* state); +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_DECODER +/* +Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and +getting much more information about the PNG image and color mode. +*/ +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); + +/* +Read the PNG header, but not the actual data. This returns only the information +that is in the IHDR chunk of the PNG, such as width, height and color type. The +information is placed in the info_png field of the LodePNGState. +*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* +Reads one metadata chunk (other than IHDR) of the PNG file and outputs what it +read in the state. Returns error code on failure. +Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const +to find the desired chunk type, and if non null use lodepng_inspect_chunk (with +chunk_pointer - start_of_file as pos). +Supports most metadata chunks from the PNG standard (gAMA, bKGD, tEXt, ...). +Ignores unsupported, unknown, non-metadata or IHDR chunks (without error). +Requirements: &in[pos] must point to start of a chunk, must use regular +lodepng_inspect first since format of most other chunks depends on IHDR, and if +there is a PLTE chunk, that one must be inspected before tRNS or bKGD. +*/ +unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos, + const unsigned char* in, size_t insize); + +#ifdef LODEPNG_COMPILE_ENCODER +/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* +The lodepng_chunk functions are normally not needed, except to traverse the +unknown chunks stored in the LodePNGInfo struct, or add new ones to it. +It also allows traversing the chunks of an encoded PNG file yourself. + +The chunk pointer always points to the beginning of the chunk itself, that is +the first byte of the 4 length bytes. + +In the PNG file format, chunks have the following format: +-4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer) +-4 bytes chunk type (ASCII a-z,A-Z only, see below) +-length bytes of data (may be 0 bytes if length was 0) +-4 bytes of CRC, computed on chunk name + data + +The first chunk starts at the 8th byte of the PNG file, the entire rest of the file +exists out of concatenated chunks with the above format. + +PNG standard chunk ASCII naming conventions: +-First byte: uppercase = critical, lowercase = ancillary +-Second byte: uppercase = public, lowercase = private +-Third byte: must be uppercase +-Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy +*/ + +/* +Gets the length of the data of the chunk. Total chunk length has 12 bytes more. +There must be at least 4 bytes to read from. If the result value is too large, +it may be corrupt data. +*/ +unsigned lodepng_chunk_length(const unsigned char* chunk); + +/*puts the 4-byte type in null terminated string*/ +void lodepng_chunk_type(char type[5], const unsigned char* chunk); + +/*check if the type is the given type*/ +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); + +/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); + +/*0: public, 1: private (see PNG standard)*/ +unsigned char lodepng_chunk_private(const unsigned char* chunk); + +/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); + +/*get pointer to the data of the chunk, where the input points to the header of the chunk*/ +unsigned char* lodepng_chunk_data(unsigned char* chunk); +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); + +/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ +unsigned lodepng_chunk_check_crc(const unsigned char* chunk); + +/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ +void lodepng_chunk_generate_crc(unsigned char* chunk); + +/* +Iterate to next chunks, allows iterating through all chunks of the PNG file. +Input must be at the beginning of a chunk (result of a previous lodepng_chunk_next call, +or the 8th byte of a PNG file which always has the first chunk), or alternatively may +point to the first byte of the PNG file (which is not a chunk but the magic header, the +function will then skip over it and return the first real chunk). +Will output pointer to the start of the next chunk, or at or beyond end of the file if there +is no more chunk after this or possibly if the chunk is corrupt. +Start this process at the 8th byte of the PNG file. +In a non-corrupt PNG file, the last chunk should have name "IEND". +*/ +unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end); +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end); + +/*Finds the first chunk with the given type in the range [chunk, end), or returns NULL if not found.*/ +unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]); +const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]); + +/* +Appends chunk to the data in out. The given chunk should already have its chunk header. +The out variable and outsize are updated to reflect the new reallocated buffer. +Returns error code (0 if it went ok) +*/ +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk); + +/* +Appends new chunk to out. The chunk to append is given by giving its length, type +and data separately. The type is a 4-letter string. +The out variable and outsize are updated to reflect the new reallocated buffer. +Returne error code (0 if it went ok) +*/ +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length, + const char* type, const unsigned char* data); + + +/*Calculate CRC32 of buffer*/ +unsigned lodepng_crc32(const unsigned char* buf, size_t len); +#endif /*LODEPNG_COMPILE_PNG*/ + + +#ifdef LODEPNG_COMPILE_ZLIB +/* +This zlib part can be used independently to zlib compress and decompress a +buffer. It cannot be used to create gzip files however, and it only supports the +part of zlib that is required for PNG, it does not support dictionaries. +*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); + +/* +Decompresses Zlib data. Reallocates the out buffer and appends the data. The +data must be according to the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Compresses data with Zlib. Reallocates the out buffer and appends the data. +Zlib adds a small header and trailer around the deflate data. +The data is output in the format of the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +/* +Find length-limited Huffman code for given frequencies. This function is in the +public interface only for tests, it's used internally by lodepng_deflate. +*/ +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen); + +/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into buffer. The function allocates the out buffer, and +after usage you should free it. +out: output parameter, contains pointer to loaded buffer. +outsize: output parameter, size of the allocated out buffer +filename: the path to the file to load +return value: error code (0 means ok) +*/ +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); + +/* +Save a file from buffer to disk. Warning, if it exists, this function overwrites +the file without warning! +buffer: the buffer to write +buffersize: size of the buffer to write +filename: the path to the file to save to +return value: error code (0 means ok) +*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ + +#ifdef LODEPNG_COMPILE_CPP +/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ +namespace lodepng { +#ifdef LODEPNG_COMPILE_PNG +class State : public LodePNGState { + public: + State(); + State(const State& other); + ~State(); + State& operator=(const State& other); +}; + +#ifdef LODEPNG_COMPILE_DECODER +/* Same as other lodepng::decode, but using a State for more settings and information. */ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Same as other lodepng::encode, but using a State for more settings and information. */ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into an std::vector. +return value: error code (0 means ok) +*/ +unsigned load_file(std::vector& buffer, const std::string& filename); + +/* +Save the binary data in an std::vector to a file on disk. The file is overwritten +without warning. +*/ +unsigned save_file(const std::vector& buffer, const std::string& filename); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_PNG */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +/* Zlib-decompress an unsigned char buffer */ +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); + +/* Zlib-decompress an std::vector */ +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Zlib-compress an unsigned char buffer */ +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); + +/* Zlib-compress an std::vector */ +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ + +/* +TODO: +[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often +[.] check compatibility with various compilers - done but needs to be redone for every newer version +[X] converting color to 16-bit per channel types +[X] support color profile chunk types (but never let them touch RGB values by default) +[ ] support all public PNG chunk types (almost done except sBIT, sPLT and hIST) +[ ] make sure encoder generates no chunks with size > (2^31)-1 +[ ] partial decoding (stream processing) +[X] let the "isFullyOpaque" function check color keys and transparent palettes too +[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" +[ ] allow treating some errors like warnings, when image is recoverable (e.g. 69, 57, 58) +[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... +[ ] error messages with line numbers (and version) +[ ] errors in state instead of as return code? +[ ] new errors/warnings like suspiciously big decompressed ztxt or iccp chunk +[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes +[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... +[ ] allow user to give data (void*) to custom allocator +[X] provide alternatives for C library functions not present on some platforms (memcpy, ...) +*/ + +#endif /*LODEPNG_H inclusion guard*/ + +/* +LodePNG Documentation +--------------------- + +0. table of contents +-------------------- + + 1. about + 1.1. supported features + 1.2. features not supported + 2. C and C++ version + 3. security + 4. decoding + 5. encoding + 6. color conversions + 6.1. PNG color types + 6.2. color conversions + 6.3. padding bits + 6.4. A note about 16-bits per channel and endianness + 7. error values + 8. chunks and PNG editing + 9. compiler support + 10. examples + 10.1. decoder C++ example + 10.2. decoder C example + 11. state settings reference + 12. changes + 13. contact information + + +1. about +-------- + +PNG is a file format to store raster images losslessly with good compression, +supporting different color types and alpha channel. + +LodePNG is a PNG codec according to the Portable Network Graphics (PNG) +Specification (Second Edition) - W3C Recommendation 10 November 2003. + +The specifications used are: + +*) Portable Network Graphics (PNG) Specification (Second Edition): + http://www.w3.org/TR/2003/REC-PNG-20031110 +*) RFC 1950 ZLIB Compressed Data Format version 3.3: + http://www.gzip.org/zlib/rfc-zlib.html +*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: + http://www.gzip.org/zlib/rfc-deflate.html + +The most recent version of LodePNG can currently be found at +http://lodev.org/lodepng/ + +LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds +extra functionality. + +LodePNG exists out of two files: +-lodepng.h: the header file for both C and C++ +-lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage + +If you want to start using LodePNG right away without reading this doc, get the +examples from the LodePNG website to see how to use it in code, or check the +smaller examples in chapter 13 here. + +LodePNG is simple but only supports the basic requirements. To achieve +simplicity, the following design choices were made: There are no dependencies +on any external library. There are functions to decode and encode a PNG with +a single function call, and extended versions of these functions taking a +LodePNGState struct allowing to specify or get more information. By default +the colors of the raw image are always RGB or RGBA, no matter what color type +the PNG file uses. To read and write files, there are simple functions to +convert the files to/from buffers in memory. + +This all makes LodePNG suitable for loading textures in games, demos and small +programs, ... It's less suitable for full fledged image editors, loading PNGs +over network (it requires all the image data to be available before decoding can +begin), life-critical systems, ... + +1.1. supported features +----------------------- + +The following features are supported by the decoder: + +*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, + or the same color type as the PNG +*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image +*) Adam7 interlace and deinterlace for any color type +*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk +*) support for alpha channels, including RGBA color model, translucent palettes and color keying +*) zlib decompression (inflate) +*) zlib compression (deflate) +*) CRC32 and ADLER32 checksums +*) colorimetric color profile conversions: currently experimentally available in lodepng_util.cpp only, + plus alternatively ability to pass on chroma/gamma/ICC profile information to other color management system. +*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. +*) the following chunks are supported by both encoder and decoder: + IHDR: header information + PLTE: color palette + IDAT: pixel data + IEND: the final chunk + tRNS: transparency for palettized images + tEXt: textual information + zTXt: compressed textual information + iTXt: international textual information + bKGD: suggested background color + pHYs: physical dimensions + tIME: modification time + cHRM: RGB chromaticities + gAMA: RGB gamma correction + iCCP: ICC color profile + sRGB: rendering intent + +1.2. features not supported +--------------------------- + +The following features are _not_ supported: + +*) some features needed to make a conformant PNG-Editor might be still missing. +*) partial loading/stream processing. All data must be available and is processed in one call. +*) The following public chunks are not (yet) supported but treated as unknown chunks by LodePNG: + sBIT + hIST + sPLT + + +2. C and C++ version +-------------------- + +The C version uses buffers allocated with alloc that you need to free() +yourself. You need to use init and cleanup functions for each struct whenever +using a struct from the C version to avoid exploits and memory leaks. + +The C++ version has extra functions with std::vectors in the interface and the +lodepng::State class which is a LodePNGState with constructor and destructor. + +These files work without modification for both C and C++ compilers because all +the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers +ignore it, and the C code is made to compile both with strict ISO C90 and C++. + +To use the C++ version, you need to rename the source file to lodepng.cpp +(instead of lodepng.c), and compile it with a C++ compiler. + +To use the C version, you need to rename the source file to lodepng.c (instead +of lodepng.cpp), and compile it with a C compiler. + + +3. Security +----------- + +Even if carefully designed, it's always possible that LodePNG contains possible +exploits. If you discover one, please let me know, and it will be fixed. + +When using LodePNG, care has to be taken with the C version of LodePNG, as well +as the C-style structs when working with C++. The following conventions are used +for all C-style structs: + +-if a struct has a corresponding init function, always call the init function when making a new one +-if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks +-if a struct has a corresponding copy function, use the copy function instead of "=". + The destination must also be inited already. + + +4. Decoding +----------- + +Decoding converts a PNG compressed image to a raw pixel buffer. + +Most documentation on using the decoder is at its declarations in the header +above. For C, simple decoding can be done with functions such as +lodepng_decode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_decode. For C++, all decoding can be done with the +various lodepng::decode functions, and lodepng::State can be used for advanced +features. + +When using the LodePNGState, it uses the following fields for decoding: +*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here +*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get +*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use + +LodePNGInfo info_png +-------------------- + +After decoding, this contains extra information of the PNG image, except the actual +pixels, width and height because these are already gotten directly from the decoder +functions. + +It contains for example the original color type of the PNG image, text comments, +suggested background color, etc... More details about the LodePNGInfo struct are +at its declaration documentation. + +LodePNGColorMode info_raw +------------------------- + +When decoding, here you can specify which color type you want +the resulting raw image to be. If this is different from the colortype of the +PNG, then the decoder will automatically convert the result. This conversion +always works, except if you want it to convert a color PNG to grayscale or to +a palette with missing colors. + +By default, 32-bit color is used for the result. + +LodePNGDecoderSettings decoder +------------------------------ + +The settings can be used to ignore the errors created by invalid CRC and Adler32 +chunks, and to disable the decoding of tEXt chunks. + +There's also a setting color_convert, true by default. If false, no conversion +is done, the resulting data will be as it was in the PNG (after decompression) +and you'll have to puzzle the colors of the pixels together yourself using the +color type information in the LodePNGInfo. + + +5. Encoding +----------- + +Encoding converts a raw pixel buffer to a PNG compressed image. + +Most documentation on using the encoder is at its declarations in the header +above. For C, simple encoding can be done with functions such as +lodepng_encode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_encode. For C++, all encoding can be done with the +various lodepng::encode functions, and lodepng::State can be used for advanced +features. + +Like the decoder, the encoder can also give errors. However it gives less errors +since the encoder input is trusted, the decoder input (a PNG image that could +be forged by anyone) is not trusted. + +When using the LodePNGState, it uses the following fields for encoding: +*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. +*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has +*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use + +LodePNGInfo info_png +-------------------- + +When encoding, you use this the opposite way as when decoding: for encoding, +you fill in the values you want the PNG to have before encoding. By default it's +not needed to specify a color type for the PNG since it's automatically chosen, +but it's possible to choose it yourself given the right settings. + +The encoder will not always exactly match the LodePNGInfo struct you give, +it tries as close as possible. Some things are ignored by the encoder. The +encoder uses, for example, the following settings from it when applicable: +colortype and bitdepth, text chunks, time chunk, the color key, the palette, the +background color, the interlace method, unknown chunks, ... + +When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. +If the palette contains any colors for which the alpha channel is not 255 (so +there are translucent colors in the palette), it'll add a tRNS chunk. + +LodePNGColorMode info_raw +------------------------- + +You specify the color type of the raw image that you give to the input here, +including a possible transparent color key and palette you happen to be using in +your raw image data. + +By default, 32-bit color is assumed, meaning your input has to be in RGBA +format with 4 bytes (unsigned chars) per pixel. + +LodePNGEncoderSettings encoder +------------------------------ + +The following settings are supported (some are in sub-structs): +*) auto_convert: when this option is enabled, the encoder will +automatically choose the smallest possible color mode (including color key) that +can encode the colors of all pixels without information loss. +*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, + 2 = dynamic huffman tree (best compression). Should be 2 for proper + compression. +*) use_lz77: whether or not to use LZ77 for compressed block types. Should be + true for proper compression. +*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value + 2048 by default, but can be set to 32768 for better, but slow, compression. +*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE + chunk if force_palette is true. This can used as suggested palette to convert + to by viewers that don't support more than 256 colors (if those still exist) +*) add_id: add text chunk "Encoder: LodePNG " to the image. +*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. + zTXt chunks use zlib compression on the text. This gives a smaller result on + large texts but a larger result on small texts (such as a single program name). + It's all tEXt or all zTXt though, there's no separate setting per text yet. + + +6. color conversions +-------------------- + +An important thing to note about LodePNG, is that the color type of the PNG, and +the color type of the raw image, are completely independent. By default, when +you decode a PNG, you get the result as a raw image in the color type you want, +no matter whether the PNG was encoded with a palette, grayscale or RGBA color. +And if you encode an image, by default LodePNG will automatically choose the PNG +color type that gives good compression based on the values of colors and amount +of colors in the image. It can be configured to let you control it instead as +well, though. + +To be able to do this, LodePNG does conversions from one color mode to another. +It can convert from almost any color type to any other color type, except the +following conversions: RGB to grayscale is not supported, and converting to a +palette when the palette doesn't have a required color is not supported. This is +not supported on purpose: this is information loss which requires a color +reduction algorithm that is beyond the scope of a PNG encoder (yes, RGB to gray +is easy, but there are multiple ways if you want to give some channels more +weight). + +By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB +color, no matter what color type the PNG has. And by default when encoding, +LodePNG automatically picks the best color model for the output PNG, and expects +the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control +the color format of the images yourself, you can skip this chapter. + +6.1. PNG color types +-------------------- + +A PNG image can have many color types, ranging from 1-bit color to 64-bit color, +as well as palettized color modes. After the zlib decompression and unfiltering +in the PNG image is done, the raw pixel data will have that color type and thus +a certain amount of bits per pixel. If you want the output raw image after +decoding to have another color type, a conversion is done by LodePNG. + +The PNG specification gives the following color types: + +0: grayscale, bit depths 1, 2, 4, 8, 16 +2: RGB, bit depths 8 and 16 +3: palette, bit depths 1, 2, 4 and 8 +4: grayscale with alpha, bit depths 8 and 16 +6: RGBA, bit depths 8 and 16 + +Bit depth is the amount of bits per pixel per color channel. So the total amount +of bits per pixel is: amount of channels * bitdepth. + +6.2. color conversions +---------------------- + +As explained in the sections about the encoder and decoder, you can specify +color types and bit depths in info_png and info_raw to change the default +behaviour. + +If, when decoding, you want the raw image to be something else than the default, +you need to set the color type and bit depth you want in the LodePNGColorMode, +or the parameters colortype and bitdepth of the simple decoding function. + +If, when encoding, you use another color type than the default in the raw input +image, you need to specify its color type and bit depth in the LodePNGColorMode +of the raw image, or use the parameters colortype and bitdepth of the simple +encoding function. + +If, when encoding, you don't want LodePNG to choose the output PNG color type +but control it yourself, you need to set auto_convert in the encoder settings +to false, and specify the color type you want in the LodePNGInfo of the +encoder (including palette: it can generate a palette if auto_convert is true, +otherwise not). + +If the input and output color type differ (whether user chosen or auto chosen), +LodePNG will do a color conversion, which follows the rules below, and may +sometimes result in an error. + +To avoid some confusion: +-the decoder converts from PNG to raw image +-the encoder converts from raw image to PNG +-the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image +-the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG +-when encoding, the color type in LodePNGInfo is ignored if auto_convert + is enabled, it is automatically generated instead +-when decoding, the color type in LodePNGInfo is set by the decoder to that of the original + PNG image, but it can be ignored since the raw image has the color type you requested instead +-if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion + between the color types is done if the color types are supported. If it is not + supported, an error is returned. If the types are the same, no conversion is done. +-even though some conversions aren't supported, LodePNG supports loading PNGs from any + colortype and saving PNGs to any colortype, sometimes it just requires preparing + the raw image correctly before encoding. +-both encoder and decoder use the same color converter. + +The function lodepng_convert does the color conversion. It is available in the +interface but normally isn't needed since the encoder and decoder already call +it. + +Non supported color conversions: +-color to grayscale when non-gray pixels are present: no error is thrown, but +the result will look ugly because only the red channel is taken (it assumes all +three channels are the same in this case so ignores green and blue). The reason +no error is given is to allow converting from three-channel grayscale images to +one-channel even if there are numerical imprecisions. +-anything to palette when the palette does not have an exact match for a from-color +in it: in this case an error is thrown + +Supported color conversions: +-anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA +-any gray or gray+alpha, to gray or gray+alpha +-anything to a palette, as long as the palette has the requested colors in it +-removing alpha channel +-higher to smaller bitdepth, and vice versa + +If you want no color conversion to be done (e.g. for speed or control): +-In the encoder, you can make it save a PNG with any color type by giving the +raw color mode and LodePNGInfo the same color mode, and setting auto_convert to +false. +-In the decoder, you can make it store the pixel data in the same color type +as the PNG has, by setting the color_convert setting to false. Settings in +info_raw are then ignored. + +6.3. padding bits +----------------- + +In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines +have a bit amount that isn't a multiple of 8, then padding bits are used so that each +scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. +The raw input image you give to the encoder, and the raw output image you get from the decoder +will NOT have these padding bits, e.g. in the case of a 1-bit image with a width +of 7 pixels, the first pixel of the second scanline will the 8th bit of the first byte, +not the first bit of a new byte. + +6.4. A note about 16-bits per channel and endianness +---------------------------------------------------- + +LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like +for any other color format. The 16-bit values are stored in big endian (most +significant byte first) in these arrays. This is the opposite order of the +little endian used by x86 CPU's. + +LodePNG always uses big endian because the PNG file format does so internally. +Conversions to other formats than PNG uses internally are not supported by +LodePNG on purpose, there are myriads of formats, including endianness of 16-bit +colors, the order in which you store R, G, B and A, and so on. Supporting and +converting to/from all that is outside the scope of LodePNG. + +This may mean that, depending on your use case, you may want to convert the big +endian output of LodePNG to little endian with a for loop. This is certainly not +always needed, many applications and libraries support big endian 16-bit colors +anyway, but it means you cannot simply cast the unsigned char* buffer to an +unsigned short* buffer on x86 CPUs. + + +7. error values +--------------- + +All functions in LodePNG that return an error code, return 0 if everything went +OK, or a non-zero code if there was an error. + +The meaning of the LodePNG error values can be retrieved with the function +lodepng_error_text: given the numerical error code, it returns a description +of the error in English as a string. + +Check the implementation of lodepng_error_text to see the meaning of each code. + +It is not recommended to use the numerical values to programmatically make +different decisions based on error types as the numbers are not guaranteed to +stay backwards compatible. They are for human consumption only. Programmatically +only 0 or non-0 matter. + + +8. chunks and PNG editing +------------------------- + +If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG +editor that should follow the rules about handling of unknown chunks, or if your +program is able to read other types of chunks than the ones handled by LodePNG, +then that's possible with the chunk functions of LodePNG. + +A PNG chunk has the following layout: + +4 bytes length +4 bytes type name +length bytes data +4 bytes CRC + +8.1. iterating through chunks +----------------------------- + +If you have a buffer containing the PNG image data, then the first chunk (the +IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the +signature of the PNG and are not part of a chunk. But if you start at byte 8 +then you have a chunk, and can check the following things of it. + +NOTE: none of these functions check for memory buffer boundaries. To avoid +exploits, always make sure the buffer contains all the data of the chunks. +When using lodepng_chunk_next, make sure the returned value is within the +allocated memory. + +unsigned lodepng_chunk_length(const unsigned char* chunk): + +Get the length of the chunk's data. The total chunk length is this length + 12. + +void lodepng_chunk_type(char type[5], const unsigned char* chunk): +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): + +Get the type of the chunk or compare if it's a certain type + +unsigned char lodepng_chunk_critical(const unsigned char* chunk): +unsigned char lodepng_chunk_private(const unsigned char* chunk): +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): + +Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). +Check if the chunk is private (public chunks are part of the standard, private ones not). +Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical +chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your +program doesn't handle that type of unknown chunk. + +unsigned char* lodepng_chunk_data(unsigned char* chunk): +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): + +Get a pointer to the start of the data of the chunk. + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk): +void lodepng_chunk_generate_crc(unsigned char* chunk): + +Check if the crc is correct or generate a correct one. + +unsigned char* lodepng_chunk_next(unsigned char* chunk): +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): + +Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these +functions do no boundary checking of the allocated data whatsoever, so make sure there is enough +data available in the buffer to be able to go to the next chunk. + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk): +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length, + const char* type, const unsigned char* data): + +These functions are used to create new chunks that are appended to the data in *out that has +length *outsize. The append function appends an existing chunk to the new data. The create +function creates a new chunk with the given parameters and appends it. Type is the 4-letter +name of the chunk. + +8.2. chunks in info_png +----------------------- + +The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 +buffers (each with size) to contain 3 types of unknown chunks: +the ones that come before the PLTE chunk, the ones that come between the PLTE +and the IDAT chunks, and the ones that come after the IDAT chunks. +It's necessary to make the distinction between these 3 cases because the PNG +standard forces to keep the ordering of unknown chunks compared to the critical +chunks, but does not force any other ordering rules. + +info_png.unknown_chunks_data[0] is the chunks before PLTE +info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT +info_png.unknown_chunks_data[2] is the chunks after IDAT + +The chunks in these 3 buffers can be iterated through and read by using the same +way described in the previous subchapter. + +When using the decoder to decode a PNG, you can make it store all unknown chunks +if you set the option settings.remember_unknown_chunks to 1. By default, this +option is off (0). + +The encoder will always encode unknown chunks that are stored in the info_png. +If you need it to add a particular chunk that isn't known by LodePNG, you can +use lodepng_chunk_append or lodepng_chunk_create to the chunk data in +info_png.unknown_chunks_data[x]. + +Chunks that are known by LodePNG should not be added in that way. E.g. to make +LodePNG add a bKGD chunk, set background_defined to true and add the correct +parameters there instead. + + +9. compiler support +------------------- + +No libraries other than the current standard C library are needed to compile +LodePNG. For the C++ version, only the standard C++ library is needed on top. +Add the files lodepng.c(pp) and lodepng.h to your project, include +lodepng.h where needed, and your program can read/write PNG files. + +It is compatible with C90 and up, and C++03 and up. + +If performance is important, use optimization when compiling! For both the +encoder and decoder, this makes a large difference. + +Make sure that LodePNG is compiled with the same compiler of the same version +and with the same settings as the rest of the program, or the interfaces with +std::vectors and std::strings in C++ can be incompatible. + +CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. + +*) gcc and g++ + +LodePNG is developed in gcc so this compiler is natively supported. It gives no +warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ +version 4.7.1 on Linux, 32-bit and 64-bit. + +*) Clang + +Fully supported and warning-free. + +*) Mingw + +The Mingw compiler (a port of gcc for Windows) should be fully supported by +LodePNG. + +*) Visual Studio and Visual C++ Express Edition + +LodePNG should be warning-free with warning level W4. Two warnings were disabled +with pragmas though: warning 4244 about implicit conversions, and warning 4996 +where it wants to use a non-standard function fopen_s instead of the standard C +fopen. + +Visual Studio may want "stdafx.h" files to be included in each source file and +give an error "unexpected end of file while looking for precompiled header". +This is not standard C++ and will not be added to the stock LodePNG. You can +disable it for lodepng.cpp only by right clicking it, Properties, C/C++, +Precompiled Headers, and set it to Not Using Precompiled Headers there. + +NOTE: Modern versions of VS should be fully supported, but old versions, e.g. +VS6, are not guaranteed to work. + +*) Compilers on Macintosh + +LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for +C and C++. + +*) Other Compilers + +If you encounter problems on any compilers, feel free to let me know and I may +try to fix it if the compiler is modern and standards compliant. + + +10. examples +------------ + +This decoder example shows the most basic usage of LodePNG. More complex +examples can be found on the LodePNG website. + +10.1. decoder C++ example +------------------------- + +#include "lodepng.h" +#include + +int main(int argc, char *argv[]) { + const char* filename = argc > 1 ? argv[1] : "test.png"; + + //load and decode + std::vector image; + unsigned width, height; + unsigned error = lodepng::decode(image, width, height, filename); + + //if there's an error, display it + if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; + + //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... +} + +10.2. decoder C example +----------------------- + +#include "lodepng.h" + +int main(int argc, char *argv[]) { + unsigned error; + unsigned char* image; + size_t width, height; + const char* filename = argc > 1 ? argv[1] : "test.png"; + + error = lodepng_decode32_file(&image, &width, &height, filename); + + if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); + + / * use image here * / + + free(image); + return 0; +} + +11. state settings reference +---------------------------- + +A quick reference of some settings to set on the LodePNGState + +For decoding: + +state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums +state.decoder.zlibsettings.custom_...: use custom inflate function +state.decoder.ignore_crc: ignore CRC checksums +state.decoder.ignore_critical: ignore unknown critical chunks +state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors +state.decoder.color_convert: convert internal PNG color to chosen one +state.decoder.read_text_chunks: whether to read in text metadata chunks +state.decoder.remember_unknown_chunks: whether to read in unknown chunks +state.info_raw.colortype: desired color type for decoded image +state.info_raw.bitdepth: desired bit depth for decoded image +state.info_raw....: more color settings, see struct LodePNGColorMode +state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo + +For encoding: + +state.encoder.zlibsettings.btype: disable compression by setting it to 0 +state.encoder.zlibsettings.use_lz77: use LZ77 in compression +state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize +state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match +state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching +state.encoder.zlibsettings.lazymatching: try one more LZ77 matching +state.encoder.zlibsettings.custom_...: use custom deflate function +state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png +state.encoder.filter_palette_zero: PNG filter strategy for palette +state.encoder.filter_strategy: PNG filter strategy to encode with +state.encoder.force_palette: add palette even if not encoding to one +state.encoder.add_id: add LodePNG identifier and version as a text chunk +state.encoder.text_compression: use compressed text chunks for metadata +state.info_raw.colortype: color type of raw input image you provide +state.info_raw.bitdepth: bit depth of raw input image you provide +state.info_raw: more color settings, see struct LodePNGColorMode +state.info_png.color.colortype: desired color type if auto_convert is false +state.info_png.color.bitdepth: desired bit depth if auto_convert is false +state.info_png.color....: more color settings, see struct LodePNGColorMode +state.info_png....: more PNG related settings, see struct LodePNGInfo + + +12. changes +----------- + +The version number of LodePNG is the date of the change given in the format +yyyymmdd. + +Some changes aren't backwards compatible. Those are indicated with a (!) +symbol. + +Not all changes are listed here, the commit history in github lists more: +https://github.com/lvandeve/lodepng + +*) 17 okt 2020: prevent decoding too large text/icc chunks by default. +*) 06 mar 2020: simplified some of the dynamic memory allocations. +*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct + overflow checks. +*) 14 aug 2019: around 25% faster decoding thanks to huffman lookup tables. +*) 15 jun 2019: (!) auto_choose_color API changed (for bugfix: don't use palette + if gray ICC profile) and non-ICC LodePNGColorProfile renamed to + LodePNGColorStats. +*) 30 dec 2018: code style changes only: removed newlines before opening braces. +*) 10 sep 2018: added way to inspect metadata chunks without full decoding. +*) 19 aug 2018: (!) fixed color mode bKGD is encoded with and made it use + palette index in case of palette. +*) 10 aug 2018: (!) added support for gAMA, cHRM, sRGB and iCCP chunks. This + change is backwards compatible unless you relied on unknown_chunks for those. +*) 11 jun 2018: less restrictive check for pixel size integer overflow +*) 14 jan 2018: allow optionally ignoring a few more recoverable errors +*) 17 sep 2017: fix memory leak for some encoder input error cases +*) 27 nov 2016: grey+alpha auto color model detection bugfix +*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). +*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within + the limits of pure C90). +*) 08 dec 2015: Made load_file function return error if file can't be opened. +*) 24 okt 2015: Bugfix with decoding to palette output. +*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. +*) 24 aug 2014: Moved to github +*) 23 aug 2014: Reduced needless memory usage of decoder. +*) 28 jun 2014: Removed fix_png setting, always support palette OOB for + simplicity. Made ColorProfile public. +*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. +*) 22 dec 2013: Power of two windowsize required for optimization. +*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. +*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). +*) 11 mar 2013: (!) Bugfix with custom free. Changed from "my" to "lodepng_" + prefix for the custom allocators and made it possible with a new #define to + use custom ones in your project without needing to change lodepng's code. +*) 28 jan 2013: Bugfix with color key. +*) 27 okt 2012: Tweaks in text chunk keyword length error handling. +*) 8 okt 2012: (!) Added new filter strategy (entropy) and new auto color mode. + (no palette). Better deflate tree encoding. New compression tweak settings. + Faster color conversions while decoding. Some internal cleanups. +*) 23 sep 2012: Reduced warnings in Visual Studio a little bit. +*) 1 sep 2012: (!) Removed #define's for giving custom (de)compression functions + and made it work with function pointers instead. +*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc + and free functions and toggle #defines from compiler flags. Small fixes. +*) 6 may 2012: (!) Made plugging in custom zlib/deflate functions more flexible. +*) 22 apr 2012: (!) Made interface more consistent, renaming a lot. Removed + redundant C++ codec classes. Reduced amount of structs. Everything changed, + but it is cleaner now imho and functionality remains the same. Also fixed + several bugs and shrunk the implementation code. Made new samples. +*) 6 nov 2011: (!) By default, the encoder now automatically chooses the best + PNG color model and bit depth, based on the amount and type of colors of the + raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. +*) 9 okt 2011: simpler hash chain implementation for the encoder. +*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. +*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. + A bug with the PNG filtertype heuristic was fixed, so that it chooses much + better ones (it's quite significant). A setting to do an experimental, slow, + brute force search for PNG filter types is added. +*) 17 aug 2011: (!) changed some C zlib related function names. +*) 16 aug 2011: made the code less wide (max 120 characters per line). +*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. +*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. +*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman + to optimize long sequences of zeros. +*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and + LodePNG_InfoColor_canHaveAlpha functions for convenience. +*) 7 nov 2010: added LodePNG_error_text function to get error code description. +*) 30 okt 2010: made decoding slightly faster +*) 26 okt 2010: (!) changed some C function and struct names (more consistent). + Reorganized the documentation and the declaration order in the header. +*) 08 aug 2010: only changed some comments and external samples. +*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. +*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. +*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could + read by ignoring the problem but windows apps couldn't. +*) 06 jun 2008: added more error checks for out of memory cases. +*) 26 apr 2008: added a few more checks here and there to ensure more safety. +*) 06 mar 2008: crash with encoding of strings fixed +*) 02 feb 2008: support for international text chunks added (iTXt) +*) 23 jan 2008: small cleanups, and #defines to divide code in sections +*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. +*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. +*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added + Also various fixes, such as in the deflate and the padding bits code. +*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved + filtering code of encoder. +*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A + C++ wrapper around this provides an interface almost identical to before. + Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code + are together in these files but it works both for C and C++ compilers. +*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks +*) 30 aug 2007: bug fixed which makes this Borland C++ compatible +*) 09 aug 2007: some VS2005 warnings removed again +*) 21 jul 2007: deflate code placed in new namespace separate from zlib code +*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images +*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing + invalid std::vector element [0] fixed, and level 3 and 4 warnings removed +*) 02 jun 2007: made the encoder add a tag with version by default +*) 27 may 2007: zlib and png code separated (but still in the same file), + simple encoder/decoder functions added for more simple usage cases +*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), + moved some examples from here to lodepng_examples.cpp +*) 12 may 2007: palette decoding bug fixed +*) 24 apr 2007: changed the license from BSD to the zlib license +*) 11 mar 2007: very simple addition: ability to encode bKGD chunks. +*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding + palettized PNG images. Plus little interface change with palette and texts. +*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. + Fixed a bug where the end code of a block had length 0 in the Huffman tree. +*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented + and supported by the encoder, resulting in smaller PNGs at the output. +*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. +*) 24 jan 2007: gave encoder an error interface. Added color conversion from any + greyscale type to 8-bit greyscale with or without alpha. +*) 21 jan 2007: (!) Totally changed the interface. It allows more color types + to convert to and is more uniform. See the manual for how it works now. +*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: + encode/decode custom tEXt chunks, separate classes for zlib & deflate, and + at last made the decoder give errors for incorrect Adler32 or Crc. +*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. +*) 29 dec 2006: Added support for encoding images without alpha channel, and + cleaned out code as well as making certain parts faster. +*) 28 dec 2006: Added "Settings" to the encoder. +*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. + Removed some code duplication in the decoder. Fixed little bug in an example. +*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. + Fixed a bug of the decoder with 16-bit per color. +*) 15 okt 2006: Changed documentation structure +*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the + given image buffer, however for now it's not compressed. +*) 08 sep 2006: (!) Changed to interface with a Decoder class +*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different + way. Renamed decodePNG to decodePNGGeneric. +*) 29 jul 2006: (!) Changed the interface: image info is now returned as a + struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. +*) 28 jul 2006: Cleaned the code and added new error checks. + Corrected terminology "deflate" into "inflate". +*) 23 jun 2006: Added SDL example in the documentation in the header, this + example allows easy debugging by displaying the PNG and its transparency. +*) 22 jun 2006: (!) Changed way to obtain error value. Added + loadFile function for convenience. Made decodePNG32 faster. +*) 21 jun 2006: (!) Changed type of info vector to unsigned. + Changed position of palette in info vector. Fixed an important bug that + happened on PNGs with an uncompressed block. +*) 16 jun 2006: Internally changed unsigned into unsigned where + needed, and performed some optimizations. +*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them + in LodePNG namespace. Changed the order of the parameters. Rewrote the + documentation in the header. Renamed files to lodepng.cpp and lodepng.h +*) 22 apr 2006: Optimized and improved some code +*) 07 sep 2005: (!) Changed to std::vector interface +*) 12 aug 2005: Initial release (C++, decoder only) + + +13. contact information +----------------------- + +Feel free to contact me with suggestions, problems, comments, ... concerning +LodePNG. If you encounter a PNG image that doesn't work properly with this +decoder, feel free to send it and I'll use it to find and fix the problem. + +My email address is (puzzle the account and domain together with an @ symbol): +Domain: gmail dot com. +Account: lode dot vandevenne. + + +Copyright (c) 2005-2020 Lode Vandevenne +*/ diff --git a/src/screenshots/screenshotMaker.cpp b/src/screenshots/screenshotMaker.cpp new file mode 100644 index 000000000..1332151cc --- /dev/null +++ b/src/screenshots/screenshotMaker.cpp @@ -0,0 +1,223 @@ +// +// Created by Deamon on 9/8/2020. +// +#include "screenshotMaker.h" +#include "lodepng/lodepng.h" +#include +#ifndef __EMSCRIPTEN__ +void saveScreenshot(const std::string &name, int width, int height, std::vector &rgbaBuff) { + FILE *fp = fopen(name.c_str(), "wb"); + if (!fp) { + return; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_ptr) { + fclose(fp); + return; + } + + png_infop png_info; + if (!(png_info = png_create_info_struct(png_ptr))) { + png_destroy_write_struct(&png_ptr, nullptr); + return; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, nullptr); + return; + } + + png_init_io(png_ptr, fp); + + png_set_IHDR(png_ptr, png_info, width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + std::shared_ptr> data = std::make_shared>(width*height*3); + std::shared_ptr> rows = std::make_shared>(height); + + for (int i = 0; i < height; ++i) { + (*rows)[i] = data->data() + (i*width*3); + for (int j = 0; j < width; ++j) { + int i1 = (i*width+j)*3; + int i2 = (i*width+j)*4; + + char r = rgbaBuff[i2++]; + char g = rgbaBuff[i2++]; + char b = rgbaBuff[i2++]; + char a = rgbaBuff[i2++]; + + (*data)[i1++] = r; + (*data)[i1++] = g; + (*data)[i1++] = b; + } + } + png_set_rows(png_ptr, png_info, rows->data()); + png_write_png(png_ptr, png_info, PNG_TRANSFORM_IDENTITY, nullptr); + png_write_end(png_ptr, png_info); + + + png_destroy_write_struct(&png_ptr, nullptr); + fclose(fp); +} +#endif + +//Lodepng part +void saveScreenshotLodePng(const std::string &name, int width, int height, std::vector &rgbaBuff){ + unsigned char* png; + size_t pngsize; + + unsigned error = lodepng_encode32(&png, &pngsize, rgbaBuff.data(), width, height); + if(!error) { + std::ofstream f(name,std::ofstream::binary); + f.write((const char *)(png), pngsize); + } + + /*if there's an error, display it*/ + if(error) printf("error %u: %s\n", error, lodepng_error_text(error)); + + free(png); +} + + + +//BMP part +const int bytesPerPixel = 4; /// red, green, blue +const int fileHeaderSize = 14; +const int infoHeaderSize = 52; +unsigned char* createBitmapFileHeader(int height, int width, int pitch, int paddingSize); +unsigned char* createBitmapInfoHeader (int height, int width); + +void generateBitmapImage(unsigned char *image, int height, int width, int pitch, const char* imageFileName) { + + unsigned char padding[3] = { 0, 0, 0 }; + int paddingSize = (4 - (/*width*bytesPerPixel*/ pitch) % 4) % 4; + + unsigned char* fileHeader = createBitmapFileHeader(height, width, pitch, paddingSize); + unsigned char* infoHeader = createBitmapInfoHeader(height, width); + + FILE* imageFile = fopen(imageFileName, "wb"); + + fwrite(fileHeader, 1, fileHeaderSize, imageFile); + fwrite(infoHeader, 1, infoHeaderSize, imageFile); + + int i; + for (i = 0; i < height; i++) { + fwrite(image + (i*pitch /*width*bytesPerPixel*/), bytesPerPixel, width, imageFile); + fwrite(padding, 1, paddingSize, imageFile); + } + + fclose(imageFile); + //free(fileHeader); + //free(infoHeader); +} + +unsigned char* createBitmapFileHeader(int height, int width, int pitch, int paddingSize) { + int fileSize = fileHeaderSize + infoHeaderSize + (/*bytesPerPixel*width*/pitch + paddingSize) * height; + + static unsigned char fileHeader[] = { + 0,0, /// signature + 0,0,0,0, /// image file size in bytes + 0,0,0,0, /// reserved + 0,0,0,0, /// start of pixel array + }; + + fileHeader[0] = (unsigned char)('B'); + fileHeader[1] = (unsigned char)('M'); + fileHeader[2] = (unsigned char)(fileSize); + fileHeader[3] = (unsigned char)(fileSize >> 8); + fileHeader[4] = (unsigned char)(fileSize >> 16); + fileHeader[5] = (unsigned char)(fileSize >> 24); + fileHeader[10] = (unsigned char)(fileHeaderSize + infoHeaderSize); + + return fileHeader; +} + +unsigned char* createBitmapInfoHeader(int height, int width) { + static unsigned char infoHeader[infoHeaderSize] = { + 0,0,0,0, /// header size + 0,0,0,0, /// image width + 0,0,0,0, /// image height + 0,0, /// number of color planes + 0,0, /// bits per pixel + 03,0,0,0, /// compression + 0,0,0,0, /// image size + 0,0,0,0, /// horizontal resolution + 0,0,0,0, /// vertical resolution + 0,0,0,0, /// colors in color table + 0,0,0,0, /// important color count + 0xFF,00,00,00, + 00,0xFF,00,00, + 00,00,0xFF,00, + }; + + infoHeader[0] = (unsigned char)(infoHeaderSize); + infoHeader[4] = (unsigned char)(width); + infoHeader[5] = (unsigned char)(width >> 8); + infoHeader[6] = (unsigned char)(width >> 16); + infoHeader[7] = (unsigned char)(width >> 24); + infoHeader[8] = (unsigned char)(height); + infoHeader[9] = (unsigned char)(height >> 8); + infoHeader[10] = (unsigned char)(height >> 16); + infoHeader[11] = (unsigned char)(height >> 24); + infoHeader[12] = (unsigned char)(1); + infoHeader[14] = (unsigned char)(bytesPerPixel * 8); + + return infoHeader; +} + +static bool endsWith3(std::string_view str, std::string_view suffix) +{ + return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix); +} + +void saveDataFromDrawStage(const HFrameBuffer& fb, + const std::string& screenshotFileName, + int screenshotWidth, int screenshotHeight, + std::vector &buffer) { + if (fb == nullptr) + return; + + std::vector internalBuffer = std::vector(screenshotWidth*screenshotHeight*4); + fb->readRGBAPixels( 0, 0, screenshotWidth, screenshotHeight, internalBuffer.data()); + + //In internalbuffer rn image is in bgra format. Let's turn it into RGBA and make alpha 256 + for (int j = 0; j < screenshotHeight; j++) { + for (int i = 0; i < screenshotWidth; i++) { + std::array ind = + { + screenshotWidth * 4 * (screenshotHeight - 1 - j) + (i * 4) + 0, + screenshotWidth * 4 * (screenshotHeight - 1 - j) + (i * 4) + 1, + screenshotWidth * 4 * (screenshotHeight - 1 - j) + (i * 4) + 2, + screenshotWidth * 4 * (screenshotHeight - 1 - j) + (i * 4) + 3, + }; + std::array ind2 = + { + screenshotWidth * 4 * (j) + (i * 4) + 0, + screenshotWidth * 4 * (j) + (i * 4) + 1, + screenshotWidth * 4 * (j) + (i * 4) + 2, + screenshotWidth * 4 * (j) + (i * 4) + 3, + }; + + + auto b = internalBuffer[ind[0]]; + auto g = internalBuffer[ind[1]]; + auto r = internalBuffer[ind[2]]; + auto a = internalBuffer[ind[3]]; + + buffer[ind2[0]] = b; + buffer[ind2[1]] = g; + buffer[ind2[2]] = r; + buffer[ind2[3]] = 255; + } + } + + std::string screenshotFileNamePath = screenshotFileName; + if (!endsWith3(screenshotFileName, "png")) { + screenshotFileNamePath = screenshotFileName + ".png"; + } +// generateBitmapImage((unsigned char*) buffer.data(), screenshotHeight, screenshotWidth, screenshotWidth*4, (screenshotFileName+".bmp").c_str()); + //saveScreenshot(screenshotFileName, screenshotWidth, screenshotHeight, buffer); + saveScreenshotLodePng(screenshotFileNamePath, screenshotWidth, screenshotHeight, buffer); +} diff --git a/src/screenshots/screenshotMaker.h b/src/screenshots/screenshotMaker.h new file mode 100644 index 000000000..dfead26f5 --- /dev/null +++ b/src/screenshots/screenshotMaker.h @@ -0,0 +1,28 @@ +// +// Created by Deamon on 9/8/2020. +// + +#ifndef AWEBWOWVIEWERCPP_SCREENSHOTMAKER_H +#define AWEBWOWVIEWERCPP_SCREENSHOTMAKER_H + +#include +#include +#include + +//Do not include libpng in Emscripten version +#ifndef __EMSCRIPTEN__ +#include +#endif + +#include "../../wowViewerLib/src/engine/ApiContainer.h" +#ifndef __EMSCRIPTEN__ +void saveScreenshot(const std::string& name, int width, int height, std::vector &rgbaBuff); +#endif + +void saveScreenshotLodePng(const std::string &name, int width, int height, std::vector &rgbaBuff); + +void saveDataFromDrawStage(const HFrameBuffer& fb, + const std::string& screenshotFileName, + int screenshotWidth, int screenshotHeight, + std::vector &buffer); +#endif //AWEBWOWVIEWERCPP_SCREENSHOTMAKER_H diff --git a/src/ui/FrontendUI.cpp b/src/ui/FrontendUI.cpp index 4a2a26f7c..fb523055d 100644 --- a/src/ui/FrontendUI.cpp +++ b/src/ui/FrontendUI.cpp @@ -2,16 +2,37 @@ // Created by deamon on 20.12.19. // -#include #include "FrontendUI.h" -#include +#ifndef __ANDROID_API__ +#include "imguiLib/imguiImpl/imgui_impl_glfw.h" +#else +#include +#endif + #include #include -#include "imguiLib/imguiImpl/imgui_impl_glfw.h" +#include +#include +#include +#include +#include #include "imguiLib/fileBrowser/imfilebrowser.h" #include "../../wowViewerLib/src/engine/shader/ShaderDefinitions.h" - +#include "childWindow/mapConstructionWindow.h" +#include "../persistance/CascRequestProcessor.h" +#include "../../wowViewerLib/src/engine/objects/scenes/map.h" +#include "../../wowViewerLib/src/engine/camera/firstPersonCamera.h" +#include "../../wowViewerLib/src/engine/objects/scenes/wmoScene.h" +#include "../../wowViewerLib/src/engine/objects/scenes/m2Scene.h" +#include "../screenshots/screenshotMaker.h" +#include "../persistance/HttpRequestProcessor.h" +#include "../exporters/gltfExporter/GLTFExporter.h" +#include "../../wowViewerLib/src/engine/objects/scenes/NullScene.h" +#include "../exporters/dataExporter/DataExporterClass.h" +#include "../database/CSqliteDB.h" +#include "../database/CEmptySqliteDB.h" +#include "../../wowViewerLib/src/gapi/UniformBufferStructures.h" static const GBufferBinding imguiBindings[3] = { {+imguiShader::Attribute::Position, 2, GBindingType::GFLOAT, false, sizeof(ImDrawVert), IM_OFFSETOF(ImDrawVert, pos)}, @@ -20,10 +41,6 @@ static const GBufferBinding imguiBindings[3] = { }; void FrontendUI::composeUI() { - m_api->getConfig()->setUseTimedGloabalLight(useTimedGlobalLight); - m_api->getConfig()->setUseM2AmbientLight(useM2AmbientLight); - m_api->getConfig()->setUseGaussBlur(useGaussBlur); - if (this->fontTexture == nullptr) return; @@ -62,21 +79,24 @@ void FrontendUI::composeUI() { if (fileDialog.HasSelected()) { std::cout << "Selected filename" << fileDialog.GetSelected().string() << std::endl; - if (openCascCallback) { - if (!openCascCallback(fileDialog.GetSelected().string())) { - ImGui::OpenPopup("Casc failed"); - cascOpened = false; - } else { - ImGui::OpenPopup("Casc succesed"); - cascOpened = true; - } + std::string cascPath = fileDialog.GetSelected().string(); + std::string product = fileDialog.getProductBuild(); + if (!product.empty()) + cascPath = cascPath + ":"+product; + + if (!openCascCallback(cascPath)) { + ImGui::OpenPopup("Casc failed"); + cascOpened = false; + } else { + ImGui::OpenPopup("Casc succesed"); + cascOpened = true; } fileDialog.ClearSelected(); } if (createFileDialog.HasSelected()) { - if (makeScreenshotCallback) { - makeScreenshotCallback(createFileDialog.GetSelected().string(), screenShotWidth, screenShotHeight); - } + screenshotFilename = createFileDialog.GetSelected().string(); + needToMakeScreenshot = true; + createFileDialog.ClearSelected(); } @@ -87,9 +107,11 @@ void FrontendUI::composeUI() { showSettingsDialog(); showQuickLinksDialog(); + showMapConstructionDialog(); showMapSelectionDialog(); showMakeScreenshotDialog(); showCurrentStatsDialog(); + showMinimapGenerationSettingsDialog(); // Rendering ImGui::Render(); @@ -103,19 +125,92 @@ void FrontendUI::showCurrentStatsDialog() { ImGui::Begin("Current stats", &showCurrentStats); // Create a window called "Hello, world!" and append into it. - static float cameraPosition[3] = {0, 0, 0}; - if (getCameraPos) { + if (ImGui::CollapsingHeader("Camera position")) { + static float cameraPosition[3] = {0, 0, 0}; getCameraPos(cameraPosition[0], cameraPosition[1], cameraPosition[2]); - } - ImGui::Text("Current camera position: (%.1f,%.1f,%.1f)", cameraPosition[0], cameraPosition[1], - cameraPosition[2]); // Display some text (you can use a format strings too) + ImGui::Text("Current camera position: (%.1f,%.1f,%.1f)", cameraPosition[0], cameraPosition[1], + cameraPosition[2]); + if (m_api->getConfig()->doubleCameraDebug) { + static float debugCameraPosition[3] = {0, 0, 0}; + getDebugCameraPos(debugCameraPosition[0], debugCameraPosition[1], debugCameraPosition[2]); + + ImGui::Text("Current debug camera position: (%.1f,%.1f,%.1f)", + debugCameraPosition[0], debugCameraPosition[1], debugCameraPosition[2]); + } + } ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); // if(getCurrentAreaName) { ImGui::Text("Current area name: %s", getCurrentAreaName().c_str()); -// } + + ImGui::Text("Uniform data for GPU: %.3f MB", m_api->hDevice->getUploadSize() / (1024.0f * 1024.0f)); + ImGui::Text("Current textures in use %d", m_api->hDevice->getCurrentTextureAllocated()); + + ImGui::NewLine(); + + if (ImGui::CollapsingHeader("Elapsed times")) { + ImGui::Text("Elapsed time on culling : %.3f ms", m_api->getConfig()->cullingTimePerFrame); + ImGui::Text("Elapsed time on update : %.3f ms", m_api->getConfig()->updateTimePerFrame); + ImGui::Text("Elapsed time on m2 update : %.3f ms", m_api->getConfig()->m2UpdateTime); + ImGui::Text("Elapsed time on wait for begin update: %.3f ms", m_api->hDevice->getWaitForUpdate()); + + ImGui::Text("Elapsed time on singleUpdateCNT: %.3f ms", m_api->getConfig()->singleUpdateCNT); + ImGui::Text("Elapsed time on meshesCollectCNT: %.3f ms", m_api->getConfig()->meshesCollectCNT); + ImGui::Text("Elapsed time on updateBuffersCNT: %.3f ms", m_api->getConfig()->updateBuffersCNT); + ImGui::Text("Elapsed time on updateBuffersDeviceCNT: %.3f ms", m_api->getConfig()->updateBuffersDeviceCNT); + ImGui::Text("Elapsed time on postLoadCNT: %.3f ms", m_api->getConfig()->postLoadCNT); + ImGui::Text("Elapsed time on textureUploadCNT: %.3f ms", m_api->getConfig()->textureUploadCNT); + ImGui::Text("Elapsed time on drawStageAndDepsCNT: %.3f ms", m_api->getConfig()->drawStageAndDepsCNT); + ImGui::Text("Elapsed time on endUpdateCNT: %.3f ms", m_api->getConfig()->endUpdateCNT); + } + + int currentFrame = m_api->hDevice->getDrawFrameNumber(); + auto &cullStageData = m_cullstages[currentFrame]; + + if (ImGui::CollapsingHeader("Objects Drawn")) { + int m2ObjectsDrawn = cullStageData!= nullptr ? cullStageData->m2Array.size() : 0; + int wmoObjectsDrawn = cullStageData!= nullptr ? cullStageData->wmoArray.size() : 0; + + ImGui::Text("M2 objects drawn: %s", std::to_string(m2ObjectsDrawn).c_str()); + ImGui::Text("WMO objects drawn: %s", std::to_string(wmoObjectsDrawn).c_str()); + } + + if (ImGui::CollapsingHeader("Current fog params")) { + if (cullStageData != nullptr && cullStageData->frameDepedantData != nullptr) { + ImGui::Text("Fog end: %.3f", cullStageData->frameDepedantData->FogEnd); + ImGui::Text("Fog Scalar: %.3f", cullStageData->frameDepedantData->FogScaler); + ImGui::Text("Fog Density: %.3f", cullStageData->frameDepedantData->FogDensity); + ImGui::Text("Fog Height: %.3f", cullStageData->frameDepedantData->FogHeight); + ImGui::Text("Fog Height Scaler: %.3f", cullStageData->frameDepedantData->FogHeightScaler); + ImGui::Text("Fog Height Density: %.3f", cullStageData->frameDepedantData->FogHeightDensity); + ImGui::Text("Sun Fog Angle: %.3f", cullStageData->frameDepedantData->SunFogAngle); + ImGui::Text("Fog Color: (%.3f, %.3f, %.3f)", + cullStageData->frameDepedantData->FogColor.x, + cullStageData->frameDepedantData->FogColor.y, + cullStageData->frameDepedantData->FogColor.z); + ImGui::Text("End Fog Color: (%.3f, %.3f, %.3f)", + cullStageData->frameDepedantData->EndFogColor.x, + cullStageData->frameDepedantData->EndFogColor.y, + cullStageData->frameDepedantData->EndFogColor.z); + ImGui::Text("End Fog Color Distance: %.3f", cullStageData->frameDepedantData->EndFogColorDistance); + ImGui::Text("Sun Fog Color: (%.3f, %.3f, %.3f)", + cullStageData->frameDepedantData->SunFogColor.x, + cullStageData->frameDepedantData->SunFogColor.y, + cullStageData->frameDepedantData->SunFogColor.z); + ImGui::Text("Sun Fog Strength: %.3f", cullStageData->frameDepedantData->SunFogStrength); + ImGui::Text("Fog Height Color: (%.3f, %.3f, %.3f)", + cullStageData->frameDepedantData->FogHeightColor.x, + cullStageData->frameDepedantData->FogHeightColor.y, + cullStageData->frameDepedantData->FogHeightColor.z); + ImGui::Text("Fog Height Coefficients: (%.3f, %.3f, %.3f)", + cullStageData->frameDepedantData->FogHeightCoefficients.x, + cullStageData->frameDepedantData->FogHeightCoefficients.y, + cullStageData->frameDepedantData->FogHeightCoefficients.z); + } + } + ImGui::End(); } } @@ -155,13 +250,20 @@ void FrontendUI::filterMapList(std::string text) { } } } +void FrontendUI::showMapConstructionDialog() { + if (!showMapConstruction) return; + if (m_mapConstructionWindow == nullptr) + m_mapConstructionWindow = std::make_shared(m_api); + + showMapConstruction = m_mapConstructionWindow->render(); + +} void FrontendUI::showMapSelectionDialog() { if (showSelectMap) { if (mapList.size() == 0) { getMapList(mapList); refilterIsNeeded = true; - } if (refilterIsNeeded) { filterMapList(std::string(&filterText[0])); @@ -226,13 +328,13 @@ void FrontendUI::showMapSelectionDialog() { } bool hovered = ImGui::IsItemHovered(); ImGui::NextColumn(); - ImGui::Text(mapListStringMap[i][1].c_str()); + ImGui::Text("%s", mapListStringMap[i][1].c_str()); ImGui::NextColumn(); - ImGui::Text(mapListStringMap[i][2].c_str()); + ImGui::Text("%s", mapListStringMap[i][2].c_str()); ImGui::NextColumn(); - ImGui::Text(mapListStringMap[i][3].c_str()); + ImGui::Text("%s", mapListStringMap[i][3].c_str()); ImGui::NextColumn(); - ImGui::Text(mapListStringMap[i][4].c_str()); + ImGui::Text("%s", mapListStringMap[i][4].c_str()); ImGui::NextColumn(); } ImGui::Columns(1); @@ -293,18 +395,15 @@ void FrontendUI::showAdtSelectionMinimap() { auto mousePos = ImGui::GetMousePos(); ImGuiStyle &style = ImGui::GetStyle(); - mousePos.x += ImGui::GetScrollX() - ImGui::GetWindowPos().x - style.WindowPadding.x; mousePos.y += ImGui::GetScrollY() - ImGui::GetWindowPos().y - style.WindowPadding.y; mousePos.x = ((mousePos.x / minimapZoom) / defaultImageDimension); mousePos.y = ((mousePos.y / minimapZoom) / defaultImageDimension); - mousePos.x = (32.0f - mousePos.x) * 533.33333f; - mousePos.y = (32.0f - mousePos.y) * 533.33333f; + worldPosX = AdtIndexToWorldCoordinate(mousePos.y); + worldPosY = AdtIndexToWorldCoordinate(mousePos.x); - worldPosX = mousePos.y; - worldPosY = mousePos.x; // if () ImGui::OpenPopup("AdtWorldCoordsTest"); std::cout << "world coords : x = " << worldPosX << " y = " << worldPosY @@ -327,10 +426,10 @@ void FrontendUI::showAdtSelectionMinimap() { if (ImGui::BeginPopup("AdtWorldCoordsTest", ImGuiWindowFlags_NoMove)) { ImGui::Text("Pos: (%.2f,%.2f,200)", worldPosX, worldPosY); if (ImGui::Button("Go")) { - if (openSceneByfdid) { - openSceneByfdid(prevMapId, prevMapRec.WdtFileID, worldPosX, worldPosY, 200); - showSelectMap = false; - } + + openSceneByfdid(prevMapId, prevMapRec.WdtFileID, worldPosX, worldPosY, 200); + showSelectMap = false; + ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); @@ -360,9 +459,7 @@ void FrontendUI::showMainMenu() { showSelectMap = true; } if (ImGui::MenuItem("Unload scene", "", false, cascOpened)) { - if (unloadScene) { - unloadScene(); - } + unloadScene(); } ImGui::EndMenu(); } @@ -372,6 +469,20 @@ void FrontendUI::showMainMenu() { ImGui::Separator(); if (ImGui::MenuItem("Open settings")) {showSettings = true;} if (ImGui::MenuItem("Open QuickLinks")) {showQuickLinks = true;} + if (ImGui::MenuItem("Open MapConstruction")) {showMapConstruction = true;} + if (ImGui::MenuItem("Open minimap generator")) { + showMinimapGeneratorSettings = true; + } + if (ImGui::MenuItem("Test export")) { + if (currentScene != nullptr) { + exporter = std::make_shared("./gltf/"); + currentScene->exportScene(exporter.get()); + exporterFramesReady = 0; + } + } + if (ImGui::MenuItem("Test data export")) { + dataExporter = new DataExporterClass(m_api); + } ImGui::Separator(); if (ImGui::MenuItem("Make screenshot")) { showMakeScreenshot = true; @@ -387,13 +498,37 @@ void FrontendUI::showMainMenu() { // -void FrontendUI::initImgui(GLFWwindow *window) { +void FrontendUI::initImgui( +#ifdef __ANDROID_API__ + ANativeWindow *window +#else + GLFWwindow *window +#endif +) { emptyMinimap(); // Setup Dear ImGui context IMGUI_CHECKVERSION(); - ImGui::CreateContext(); + auto context = ImGui::CreateContext(); + auto &fileDialog = this->fileDialog; + addIniCallback(context, + "Global Settings", + [&fileDialog](const char* line) -> void { + char lastCascDir[256]; + if (sscanf(line, "lastCascDir=%[^\n\r]", &lastCascDir) == 1) { + std::string s = std::string(&lastCascDir[0]); +// std::cout << " read string s = " << s << std::endl; + + fileDialog.SetPwd(s); + } + }, + [&fileDialog](ImGuiTextBuffer* buf) -> void { + std::string currPath = fileDialog.GetSelected(); + buf->appendf("lastCascDir=%s\n", currPath.c_str()); + } + ); + ImGuiIO &io = ImGui::GetIO(); (void) io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls @@ -404,18 +539,30 @@ void FrontendUI::initImgui(GLFWwindow *window) { //ImGui::StyleColorsClassic(); // Setup Platform/Renderer bindings +#ifdef __ANDROID_API__ + ImGui_ImplAndroid_Init(window); +#else ImGui_ImplGlfw_InitForOpenGL(window, true); +#endif } void FrontendUI::newFrame() { + // ImGui_ImplOpenGL3_NewFrame(); //Create Font image if (this->fontTexture == nullptr) return; - +#ifdef __ANDROID_API__ + ImGui_ImplAndroid_NewFrame(); +#else ImGui_ImplGlfw_NewFrame(); +#endif ImGui::NewFrame(); + + ImGuiIO& io = ImGui::GetIO(); + io.uiScale = this->uiScale; + io.DisplaySize = ImVec2((float)io.DisplaySize.x / io.uiScale, (float)io.DisplaySize.y / io.uiScale); } bool FrontendUI::getStopMouse() { @@ -428,233 +575,339 @@ bool FrontendUI::getStopKeyboard() { return io.WantCaptureKeyboard; } -void FrontendUI::setOpenCascStorageCallback(std::function callback) { - openCascCallback = callback; -} +std::shared_ptr setScene(const HApiContainer& apiContainer, int sceneType, const std::string& name, int cameraNum) { + apiContainer->camera = std::make_shared(); + if (sceneType == -1) { + return std::make_shared(); + } else if (sceneType == 0) { +// m_usePlanarCamera = cameraNum == -1; -void FrontendUI::setOpenSceneByfdidCallback(std::function callback) { - openSceneByfdid = callback; -} -void FrontendUI::setGetCameraPos(std::function callback) { - getCameraPos = callback; -} + return std::make_shared(apiContainer, name , cameraNum); + } else if (sceneType == 1) { + return std::make_shared(apiContainer, name); + } else if (sceneType == 2) { + auto &adtFileName = name; + + size_t lastSlashPos = adtFileName.find_last_of("/"); + size_t underscorePosFirst = adtFileName.find_last_of("_"); + size_t underscorePosSecond = adtFileName.find_last_of("_", underscorePosFirst-1); + std::string mapName = adtFileName.substr(lastSlashPos+1, underscorePosSecond-lastSlashPos-1); + + int i = std::stoi(adtFileName.substr(underscorePosSecond+1, underscorePosFirst-underscorePosSecond-1)); + int j = std::stoi(adtFileName.substr(underscorePosFirst+1, adtFileName.size()-underscorePosFirst-5)); + + float adt_x_min = AdtIndexToWorldCoordinate(j); + float adt_x_max = AdtIndexToWorldCoordinate(j+1); + float adt_y_min = AdtIndexToWorldCoordinate(i); + float adt_y_max = AdtIndexToWorldCoordinate(i+1); + + apiContainer->camera = std::make_shared(); + apiContainer->camera->setCameraPos( + (adt_x_min+adt_x_max) / 2.0, + (adt_y_min+adt_y_max) / 2.0, + 200 + ); + + return std::make_shared(apiContainer, adtFileName, i, j, mapName); + } + + return nullptr; +} void FrontendUI::showQuickLinksDialog() { if (!showQuickLinks) return; std::vector replacementTextureFDids = {}; ImGui::Begin("Quick Links", &showQuickLinks); + if (ImGui::Button("nightborne model", ImVec2(-1, 0))) { + openM2SceneByfdid(1810676, replacementTextureFDids); + } + if (ImGui::Button("Tomb of sargares hall", ImVec2(-1, 0))) { + openMapByIdAndWDTId(1676, 1532459, 6289, -801, 3028); + } + if (ImGui::Button("10.0 Raid WMO", ImVec2(-1, 0))) { + openWMOSceneByfdid(4282557); + } + if (ImGui::Button("(WMO) Model with broken portal culling", ImVec2(-1, 0))) { + openWMOSceneByfdid(4217818); + } + if (ImGui::Button("(WMO) NPE Ship with waterfall model", ImVec2(-1, 0))) { + openWMOSceneByfdid(3314067); + } if (ImGui::Button("Hearthstone Tavern", ImVec2(-1, 0))) { - if (openWMOSceneByfdid) { - openWMOSceneByfdid(2756726); - } + openWMOSceneByfdid(2756726); + } + if (ImGui::Button("Original WVF1 model", ImVec2(-1, 0))) { + openM2SceneByfdid(2445860, replacementTextureFDids); + } + if (ImGui::Button("Stormwind mage portal", ImVec2(-1, 0))) { + openM2SceneByfdid(2394711, replacementTextureFDids); + } + +// if (ImGui::Button("Azeroth map: Lion's Rest (Legion)", ImVec2(-1, 0))) { +// openMapByIdAndFilename(0, "azeroth", -8739, 944, 200); +// } + if (ImGui::Button("Nyalotha map", ImVec2(-1, 0))) { + openSceneByfdid(2217, 2842322, -11595, 9280, 260); + } + if (ImGui::Button("WMO 1247268", ImVec2(-1, 0))) { + openWMOSceneByfdid(1247268); + } + if (ImGui::Button("Ironforge.wmo", ImVec2(-1, 0))) { + openWMOSceneByfdid(113992); } if (ImGui::Button("Some item", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { replacementTextureFDids = std::vector(17); replacementTextureFDids[1] = 528801; for (auto &fdid: replacementTextureFDids) { fdid = 1029337; } openM2SceneByfdid(1029334, replacementTextureFDids); - } + } + if (ImGui::Button("IGC Anduin", ImVec2(-1, 0))) { + openM2SceneByfdid(3849312, replacementTextureFDids); + } + if (ImGui::Button("Steamscale mount", ImVec2(-1, 0))) { + openM2SceneByfdid(2843110, replacementTextureFDids); + } + if (ImGui::Button("Spline emitter", ImVec2(-1, 0))) { + openM2SceneByfdid(1536145, replacementTextureFDids); + } + if (ImGui::Button("Nether collector top", ImVec2(-1, 0))) { + openM2SceneByfdid(193157, replacementTextureFDids); + } + if (ImGui::Button("Сollector top", ImVec2(-1, 0))) { + openWMOSceneByfdid(113540); + } + if (ImGui::Button("10.0 unk model", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + + openM2SceneByfdid(4519090, replacementTextureFDids); + } + if (ImGui::Button("10.0 strange shoulders", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + replacementTextureFDids[2] = 4615508; +// replacementTextureFDids[3] = 4615508; + + + + openM2SceneByfdid(4614814, replacementTextureFDids); + } + if (ImGui::Button("DF chicken", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + replacementTextureFDids[11] = 4007136; + + openM2SceneByfdid(4005446, replacementTextureFDids); + } + if (ImGui::Button("Fox", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + replacementTextureFDids[11] = 3071379; + + openM2SceneByfdid(3071370, replacementTextureFDids); + } + if (ImGui::Button("COT hourglass", ImVec2(-1, 0))) { + openM2SceneByfdid(190850, replacementTextureFDids); + } + if (ImGui::Button("Gryphon roost", ImVec2(-1, 0))) { + openM2SceneByfdid(198261, replacementTextureFDids); } if (ImGui::Button("Northrend Human Inn", ImVec2(-1, 0))) { - if (openWMOSceneByfdid) { openWMOSceneByfdid(114998); - } + } + if (ImGui::Button("Strange WMO", ImVec2(-1, 0))) { + openWMOSceneByfdid(2342637); + } + if (ImGui::Button("Flyingsprite", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + + replacementTextureFDids[11] = 3059000; + openM2SceneByfdid(3024835, replacementTextureFDids); + } + if (ImGui::Button("maldraxxusflyer", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + replacementTextureFDids[11] = 3196375; + openM2SceneByfdid(3196372, replacementTextureFDids); + } + if (ImGui::Button("ridingphoenix", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + + openM2SceneByfdid(125644, replacementTextureFDids); + } + if (ImGui::Button("Upright Orc", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + replacementTextureFDids[1] = 3844710; + openM2SceneByfdid(1968587, replacementTextureFDids); + } + if (ImGui::Button("quillboarbrute.m2", ImVec2(-1, 0))) { + replacementTextureFDids = std::vector(17); + replacementTextureFDids[11] = 1786107; + openM2SceneByfdid(1784020, replacementTextureFDids); } if (ImGui::Button("WMO With Horde Symbol", ImVec2(-1, 0))) { - if (openWMOSceneByfdid) { openWMOSceneByfdid(1846142); - } } if (ImGui::Button("WMO 3565693", ImVec2(-1, 0))) { - if (openWMOSceneByfdid) { openWMOSceneByfdid(3565693); - } } + if (ImGui::Button("Vanilla login screen", ImVec2(-1, 0))) { + openM2SceneByfdid(131970, replacementTextureFDids); + } if (ImGui::Button("BC login screen", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(131982, replacementTextureFDids); // auto ambient = mathfu::vec4(0.3929412066936493f, 0.26823532581329346f, 0.3082353174686432f, 0); - m_api->getConfig()->setBCLightHack(true); - - } + m_api->getConfig()->BCLightHack = true; } if (ImGui::Button("Wrath login screen", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(236122, replacementTextureFDids); - } } if (ImGui::Button("Cataclysm login screen", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(466614, replacementTextureFDids); - } } if (ImGui::Button("Panda login screen", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(631713, replacementTextureFDids); - } } if (ImGui::Button("Draenor login screen", ImVec2(-1, 0))) { - if (openM2SceneByName) { openM2SceneByName("interface/glues/models/ui_mainmenu_warlords/ui_mainmenu_warlords.m2", replacementTextureFDids); - } } if (ImGui::Button("Legion Login Screen", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(1396280, replacementTextureFDids); // m_api->getConfig()->setBCLightHack(true); - } } if (ImGui::Button("BfA login screen", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(2021650, replacementTextureFDids); // m_api->getConfig()->setBCLightHack(true); - } + } + if (ImGui::Button("Shadowlands login screen", ImVec2(-1, 0))) { + openM2SceneByfdid(3846560, replacementTextureFDids); +// m_api->getConfig()->setBCLightHack(true); + } + + if (ImGui::Button("DragonLands login screen", ImVec2(-1, 0))) { + openM2SceneByfdid(4684877, replacementTextureFDids); +// m_api->getConfig()->setBCLightHack(true); } if (ImGui::Button("Shadowlands clouds", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(3445776, replacementTextureFDids); - } } if (ImGui::Button("Pink serpent", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { replacementTextureFDids = std::vector(17); replacementTextureFDids[11] = 2905480; replacementTextureFDids[12] = 2905481; replacementTextureFDids[13] = 577442; openM2SceneByfdid(577443, replacementTextureFDids); - } } if (ImGui::Button("Wolf", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { replacementTextureFDids = std::vector(17); replacementTextureFDids[11] = 126494; replacementTextureFDids[12] = 126495; replacementTextureFDids[13] = 0; openM2SceneByfdid(126487, replacementTextureFDids); - } } if (ImGui::Button("Aggramar", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { replacementTextureFDids = std::vector(17); replacementTextureFDids[11] = 1599776; openM2SceneByfdid(1599045, replacementTextureFDids); - } } if (ImGui::Button("M2 3087468", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { replacementTextureFDids = std::vector(17); replacementTextureFDids[11] = 3087540; openM2SceneByfdid(3087468, replacementTextureFDids); - } + } + + if (ImGui::Button("Nagrand skybox", ImVec2(-1, 0))) { + + openM2SceneByfdid(130575, replacementTextureFDids); + + } + if (ImGui::Button("Torghast raid skybox", ImVec2(-1, 0))) { + + openM2SceneByfdid(4001212, replacementTextureFDids); + + } + if (ImGui::Button("3445776 PBR cloud sky in Maw", ImVec2(-1, 0))) { + openM2SceneByfdid(3445776, replacementTextureFDids); } if (ImGui::Button("M2 3572296", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { - openM2SceneByfdid(3572296, replacementTextureFDids); - } + openM2SceneByfdid(3572296, replacementTextureFDids); } if (ImGui::Button("M2 3487959", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { - openM2SceneByfdid(3487959, replacementTextureFDids); - } + openM2SceneByfdid(3487959, replacementTextureFDids); } if (ImGui::Button("M2 1729717 waterfall", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { - openM2SceneByfdid(1729717, replacementTextureFDids); - } + openM2SceneByfdid(1729717, replacementTextureFDids); } if (ImGui::Button("Maw jailer", ImVec2(-1, 0))) { // 3096499,3096495 replacementTextureFDids = std::vector(17); replacementTextureFDids[11] = 3096499; replacementTextureFDids[12] = 3096495; - if (openM2SceneByfdid) { openM2SceneByfdid(3095966, replacementTextureFDids); - } } if (ImGui::Button("Creature with colors", ImVec2(-1, 0))) { // 3096499,3096495 - if (openM2SceneByfdid) { openM2SceneByfdid(1612576, replacementTextureFDids); - } } if (ImGui::Button("IC new sky", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(3159936, replacementTextureFDids); - } } if (ImGui::Button("vampire candle", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(3184581, replacementTextureFDids); - } } if (ImGui::Button("Bog Creature", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { replacementTextureFDids = std::vector(17); replacementTextureFDids[11] = 3732358; replacementTextureFDids[12] = 3732360; replacementTextureFDids[13] = 3732368; openM2SceneByfdid(3732303, replacementTextureFDids); - } } - + if (ImGui::Button("Bugged ADT (SL)", ImVec2(-1, 0))) { + currentScene = setScene(m_api, 2, "world/maps/2363/2363_31_31.adt", 0); + } ImGui::Separator(); ImGui::Text("Models for billboard checking"); ImGui::NewLine(); if (ImGui::Button("Dalaran dome", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(203598, replacementTextureFDids); - } } if (ImGui::Button("Gift of Nzoth", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(2432705, replacementTextureFDids); - } } if (ImGui::Button("Plagueheart Shoulderpad", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(143343, replacementTextureFDids); - } } if (ImGui::Button("Dalaran eye", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { openM2SceneByfdid(243044, replacementTextureFDids); - } } if (ImGui::Button("Hand weapon", ImVec2(-1, 0))) { - if (openM2SceneByfdid) { replacementTextureFDids = std::vector(17); replacementTextureFDids[1] = 528801; for (auto &fdid: replacementTextureFDids) { fdid = 528801; } openM2SceneByfdid(528797, replacementTextureFDids); - } } ImGui::End(); } +static HGTexture blpText = nullptr; + void FrontendUI::showSettingsDialog() { if(showSettings) { ImGui::Begin("Settings", &showSettings); - { std::string currentCamera; if (currentCameraNum == -1) { @@ -719,51 +972,122 @@ void FrontendUI::showSettingsDialog() { // } if (ImGui::SliderFloat("Far plane", &farPlane, 200, 2000)) { - m_api->getConfig()->setFarPlane(farPlane); - m_api->getConfig()->setFarPlaneForCulling(farPlane+50); + m_api->getConfig()->farPlane = farPlane; + m_api->getConfig()->farPlaneForCulling = farPlane+50; + } + + if (ImGui::Checkbox("Disable glow", &disableGlow)) { + m_api->getConfig()->disableGlow = disableGlow; + } + + bool disableFog = m_api->getConfig()->disableFog; + if (ImGui::Checkbox("Disable fog", &disableFog)) { + m_api->getConfig()->disableFog = disableFog; + } + + bool renderM2 = m_api->getConfig()->renderM2; + if (ImGui::Checkbox("Render M2", &renderM2)) { + m_api->getConfig()->renderM2 = renderM2; + } + + bool renderWMO = m_api->getConfig()->renderWMO; + if (ImGui::Checkbox("Render WMO", &renderWMO)) { + m_api->getConfig()->renderWMO = renderWMO; + } + + bool drawM2BB = m_api->getConfig()->drawM2BB; + if (ImGui::Checkbox("Render M2 Bounding Box", &drawM2BB)) { + m_api->getConfig()->drawM2BB = drawM2BB; + } + + bool renderPortals = m_api->getConfig()->renderPortals; + if (ImGui::Checkbox("Render portals", &renderPortals)) { + m_api->getConfig()->renderPortals = renderPortals; + } + if (renderPortals) { + bool renderPortalsIgnoreDepth = m_api->getConfig()->renderPortalsIgnoreDepth; + if (ImGui::Checkbox("Ignore depth test for rendering portals", &renderPortalsIgnoreDepth)) { + m_api->getConfig()->renderPortalsIgnoreDepth = renderPortalsIgnoreDepth; + } + } + + bool useDoubleCameraDebug = m_api->getConfig()->doubleCameraDebug; + if (ImGui::Checkbox("Enable second camera(for debug)", &useDoubleCameraDebug)) { + m_api->getConfig()->doubleCameraDebug = useDoubleCameraDebug; + } + + if (useDoubleCameraDebug) { + if (m_api->debugCamera == nullptr) { + m_api->debugCamera = std::make_shared(); + m_api->debugCamera->setMovementSpeed(movementSpeed); + float currentCameraPos[4] = {0, 0, 0, 0}; + m_api->camera->getCameraPosition(¤tCameraPos[0]); + + + m_api->debugCamera->setCameraPos(currentCameraPos[0], + currentCameraPos[1], + currentCameraPos[2]); + } + + bool controlSecondCamera = m_api->getConfig()->controlSecondCamera; + if (ImGui::Checkbox("Control debug camera", &controlSecondCamera)) { + m_api->getConfig()->controlSecondCamera = controlSecondCamera; + } + + bool swapMainAndDebug = m_api->getConfig()->swapMainAndDebug; + if (ImGui::Checkbox("Swap main and debug cameras", &swapMainAndDebug)) { + m_api->getConfig()->swapMainAndDebug = swapMainAndDebug; + } + } else { + m_api->debugCamera = nullptr; } - if (ImGui::Checkbox("Use gauss blur", &useGaussBlur)) { - m_api->getConfig()->setUseGaussBlur(useGaussBlur); + pauseAnimation = m_api->getConfig()->pauseAnimation; + if (ImGui::Checkbox("Pause animation", &pauseAnimation)) { + m_api->getConfig()->pauseAnimation = pauseAnimation; } if (ImGui::Button("Reset Animation")) { - if (resetAnimationCallback) { resetAnimationCallback(); - } } ImGui::Text("Time: %02d:%02d", (int)(currentTime/120), (int)((currentTime/2) % 60)); if (ImGui::SliderInt("Current time", ¤tTime, 0, 2880)) { - m_api->getConfig()->setCurrentTime(currentTime); + m_api->getConfig()->currentTime = currentTime; } - - - movementSpeed =m_api->getConfig()->getMovementSpeed(); - if (ImGui::SliderFloat("Movement Speed", &movementSpeed, 0.3, 10)) { - m_api->getConfig()->setMovementSpeed(movementSpeed); + if (ImGui::SliderFloat("Movement Speed", &movementSpeed, 0.3, 100)) { m_api->camera->setMovementSpeed(movementSpeed); } - if (ImGui::Checkbox("Use global timed light", &useTimedGlobalLight)) { - m_api->getConfig()->setUseTimedGloabalLight(useTimedGlobalLight); - if (useTimedGlobalLight) { - useM2AmbientLight = false; - m_api->getConfig()->setUseM2AmbientLight(useM2AmbientLight); + switch(m_api->getConfig()->globalLighting) { + case EParameterSource::eDatabase: { + lightSource = 0; + break; + } + case EParameterSource::eM2: { + lightSource = 1; + break; + } + case EParameterSource::eConfig: { + lightSource = 2; + break; } } - if (ImGui::Checkbox("Use ambient light from M2 (only for M2 scenes)", &useM2AmbientLight)) { - m_api->getConfig()->setUseM2AmbientLight(useM2AmbientLight); - if (useM2AmbientLight) { - useTimedGlobalLight = false; - m_api->getConfig()->setUseTimedGloabalLight(useTimedGlobalLight); - } + if (ImGui::RadioButton("Use global timed light", &lightSource, 0)) { + m_api->getConfig()->globalLighting = EParameterSource::eDatabase; + } + if (ImGui::RadioButton("Use ambient light from M2 (only for M2 scenes)", &lightSource, 1)) { + m_api->getConfig()->globalLighting = EParameterSource::eM2; + } + if (ImGui::RadioButton("Manual light", &lightSource, 2)) { + m_api->getConfig()->globalLighting = EParameterSource::eConfig; } - if (!useTimedGlobalLight && !useM2AmbientLight) { + + if (m_api->getConfig()->globalLighting == EParameterSource::eConfig) { { - auto ambient = m_api->getConfig()->getExteriorAmbientColor(); + auto ambient = m_api->getConfig()->exteriorAmbientColor; exteriorAmbientColor = {ambient.x, ambient.y, ambient.z}; ImVec4 col = ImVec4(ambient.x, ambient.y, ambient.z, 1.0); if (ImGui::ColorButton("ExteriorAmbientColor##3b", col)) { @@ -774,7 +1098,7 @@ void FrontendUI::showSettingsDialog() { if (ImGui::BeginPopup("Exterior Ambient picker")) { if (ImGui::ColorPicker3("Exterior Ambient", exteriorAmbientColor.data())) { - m_api->getConfig()->setExteriorAmbientColor( + m_api->getConfig()->exteriorAmbientColor = mathfu::vec4( exteriorAmbientColor[0], exteriorAmbientColor[1], exteriorAmbientColor[2], 1.0); } ImGui::EndPopup(); @@ -782,7 +1106,7 @@ void FrontendUI::showSettingsDialog() { } { - auto horizontAmbient = m_api->getConfig()->getExteriorHorizontAmbientColor(); + auto horizontAmbient = m_api->getConfig()->exteriorHorizontAmbientColor; exteriorHorizontAmbientColor = {horizontAmbient.x, horizontAmbient.y, horizontAmbient.z}; ImVec4 col = ImVec4(horizontAmbient.x, horizontAmbient.y, horizontAmbient.z, 1.0); if (ImGui::ColorButton("ExteriorHorizontAmbientColor##3b", col)) { @@ -793,7 +1117,7 @@ void FrontendUI::showSettingsDialog() { if (ImGui::BeginPopup("Exterior Horizont Ambient picker")) { if (ImGui::ColorPicker3("Exterior Horizont Ambient", exteriorHorizontAmbientColor.data())) { - m_api->getConfig()->setExteriorHorizontAmbientColor( + m_api->getConfig()->exteriorHorizontAmbientColor = mathfu::vec4 ( exteriorHorizontAmbientColor[0], exteriorHorizontAmbientColor[1], exteriorHorizontAmbientColor[2], 1.0); } @@ -801,7 +1125,7 @@ void FrontendUI::showSettingsDialog() { } } { - auto groundAmbient = m_api->getConfig()->getExteriorGroundAmbientColor(); + auto groundAmbient = m_api->getConfig()->exteriorGroundAmbientColor; exteriorGroundAmbientColor = {groundAmbient.x, groundAmbient.y, groundAmbient.z}; ImVec4 col = ImVec4(groundAmbient.x, groundAmbient.y, groundAmbient.z, 1.0); @@ -813,7 +1137,7 @@ void FrontendUI::showSettingsDialog() { if (ImGui::BeginPopup("Exterior Ground Ambient picker")) { if (ImGui::ColorPicker3("Exterior Ground Ambient", exteriorGroundAmbientColor.data())) { - m_api->getConfig()->setExteriorGroundAmbientColor( + m_api->getConfig()->exteriorGroundAmbientColor = mathfu::vec4( exteriorGroundAmbientColor[0], exteriorGroundAmbientColor[1], exteriorGroundAmbientColor[2], 1.0); } @@ -822,6 +1146,31 @@ void FrontendUI::showSettingsDialog() { } } + //Glow source + switch(m_api->getConfig()->glowSource) { + case EParameterSource::eDatabase: { + glowSource = 0; + break; + } + case EParameterSource::eConfig: { + glowSource = 1; + break; + } + } + + if (ImGui::RadioButton("Use glow from database", &glowSource, 0)) { + m_api->getConfig()->glowSource = EParameterSource::eDatabase; + } + if (ImGui::RadioButton("Manual glow", &glowSource, 1)) { + m_api->getConfig()->glowSource = EParameterSource::eConfig; + } + + if (m_api->getConfig()->glowSource == EParameterSource::eConfig) { + if (ImGui::SliderFloat("Custom glow", &customGlow, 0.0, 10)) { + m_api->getConfig()->currentGlow = customGlow; + } + } + // if (ImGui::SliderInt("Thread Count", &threadCount, 2, 16)) { // if (setThreadCount){ @@ -840,50 +1189,71 @@ void FrontendUI::showSettingsDialog() { ImGui::End(); } } - - +#define logExecution {} +//#define logExecution { \ +// std::cout << "Passed "<<__FUNCTION__<<" line " << __LINE__ << std::endl;\ +//} void FrontendUI::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, std::vector &additionalChunks) { auto m_device = m_api->hDevice; + logExecution if (this->fontTexture == nullptr) { + logExecution ImGuiIO& io = ImGui::GetIO(); + logExecution unsigned char* pixels; int width, height; + logExecution io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - + logExecution // Upload texture to graphics system - - this->fontTexture = m_device->createTexture(); + logExecution + this->fontTexture = m_device->createTexture(false, false); this->fontTexture->loadData(width, height, pixels, ITextureFormat::itRGBA); - + logExecution // Store our identifier + logExecution io.Fonts->TexID = this->fontTexture; + logExecution return; } - + logExecution + if (exporter != nullptr) { + if (m_processor->completedAllJobs() && !m_api->hDevice->wasTexturesUploaded()) { + exporterFramesReady++; + } + if (exporterFramesReady > 5) { + exporter->saveToFile("model.gltf"); + exporter = nullptr; + } + } + logExecution lastWidth = resultDrawStage->viewPortDimensions.maxs[0]; lastHeight = resultDrawStage->viewPortDimensions.maxs[1]; - resultDrawStage->meshesToRender = std::make_shared(); - + resultDrawStage->opaqueMeshes = std::make_shared(); + logExecution auto *draw_data = ImGui::GetDrawData(); + logExecution + if (draw_data == nullptr) + return; int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); if (fb_width <= 0 || fb_height <= 0) { return; } - - + logExecution ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) - + logExecution //Create projection matrix: - float L = draw_data->DisplayPos.x; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; - float T = draw_data->DisplayPos.y; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; - + auto uiScale = ImGui::GetIO().uiScale; + float L = draw_data->DisplayPos.x * uiScale; + float R = (draw_data->DisplayPos.x + draw_data->DisplaySize.x) * uiScale; + float T = draw_data->DisplayPos.y * uiScale; + float B = (draw_data->DisplayPos.y + draw_data->DisplaySize.y) * uiScale; + logExecution mathfu::mat4 ortho_projection = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, @@ -892,6 +1262,7 @@ void FrontendUI::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updat { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, }; + logExecution if (m_device->getIsVulkanAxisSystem()) { static const mathfu::mat4 vulkanMatrixFix1 = mathfu::mat4(1, 0, 0, 0, 0, -1, 0, 0, @@ -899,14 +1270,19 @@ void FrontendUI::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updat 0, 0, 0, 1).Transpose(); ortho_projection = vulkanMatrixFix1 * ortho_projection; } + logExecution + auto uboPart = m_device->createUniformBufferChunk(sizeof(ImgUI::modelWideBlockVS)); + - auto uboPart = m_device->createUniformBufferChunk(sizeof(mathfu::mat4)); - uboPart->setUpdateHandler([ortho_projection](IUniformBufferChunk* self) { - self->getObject() = ortho_projection; + uboPart->setUpdateHandler([ortho_projection,uiScale](IUniformBufferChunk* self, const HFrameDepedantData &frameDepedantData) { + auto &uni = self->getObject(); + uni.projectionMat = ortho_projection; + uni.scale[0] = uiScale; }); + logExecution auto shaderPermute = m_device->getShader("imguiShader", nullptr); - + logExecution // Render command lists for (int n = 0; n < draw_data->CmdListsCount; n++) { @@ -966,11 +1342,11 @@ void FrontendUI::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updat meshTemplate.scissorEnabled = true; //Vulkan has different clip offset compared to OGL if (!m_device->getIsVulkanAxisSystem()) { - meshTemplate.scissorOffset = {(int)clip_rect.x, (int)(fb_height - clip_rect.w)}; - meshTemplate.scissorSize = {(int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)}; + meshTemplate.scissorOffset = {(int)(clip_rect.x* uiScale), (int)((fb_height - clip_rect.w)* uiScale)}; + meshTemplate.scissorSize = {(int)((clip_rect.z - clip_rect.x) * uiScale), (int)((clip_rect.w - clip_rect.y)* uiScale)}; } else { - meshTemplate.scissorOffset = {(int)clip_rect.x, (int)(clip_rect.y)}; - meshTemplate.scissorSize = {(int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)}; + meshTemplate.scissorOffset = {(int)(clip_rect.x * uiScale), (int)((clip_rect.y) * uiScale)}; + meshTemplate.scissorSize = {(int)((clip_rect.z - clip_rect.x)* uiScale), (int)((clip_rect.w - clip_rect.y)* uiScale)}; } meshTemplate.ubo[1] = uboPart; @@ -980,41 +1356,36 @@ void FrontendUI::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updat meshTemplate.start = pcmd->IdxOffset * 2; meshTemplate.end = pcmd->ElemCount; - resultDrawStage->meshesToRender->meshes.push_back(m_device->createMesh(meshTemplate)); + resultDrawStage->opaqueMeshes->meshes.push_back(m_device->createMesh(meshTemplate)); } } } } -} -void FrontendUI::setOpenWMOSceneByfdidCallback(std::function callback) { - this->openWMOSceneByfdid = callback; -} + //1. Collect buffers + auto &bufferChunks = updateStage->uniformBufferChunks; + int renderIndex = 0; + for (const auto &mesh : resultDrawStage->opaqueMeshes->meshes) { + for (int i = 0; i < 5; i++ ) { + auto bufferChunk = mesh->getUniformBuffer(i); -void FrontendUI::setOpenM2SceneByfdidCallback(std::function&)> callback) { - this->openM2SceneByfdid = callback; -} + if (bufferChunk != nullptr) { + bufferChunks.push_back(bufferChunk); + } + } + } -void FrontendUI::setOpenM2SceneByFilenameCallback(std::function&)> callback) { - this->openM2SceneByName = callback; + std::sort( bufferChunks.begin(), bufferChunks.end()); + bufferChunks.erase( unique( bufferChunks.begin(), bufferChunks.end() ), bufferChunks.end() ); } + void FrontendUI::getMapList(std::vector &mapList) { if (m_api->databaseHandler == nullptr) return; m_api->databaseHandler->getMapArray(mapList); } -void FrontendUI::setGetCameraNum(std::function callback) { - getCameraNumCallback = callback; -} -void FrontendUI::setSelectNewCamera(std::function callback) { - setNewCameraCallback = callback; -} -void FrontendUI::setResetAnimation(std::function callback) { - resetAnimationCallback = callback; -} - bool FrontendUI::fillAdtSelectionminimap(std::array, 64> &minimap, bool &isWMOMap, bool &wdtFileExists) { if (m_wdtFile == nullptr) return false; @@ -1044,11 +1415,7 @@ bool FrontendUI::fillAdtSelectionminimap(std::array, 6 std::string FrontendUI::getCurrentAreaName() { auto conf = m_api->getConfig(); - return conf->getAreaName(); -} - -void FrontendUI::setMakeScreenshotCallback(std::function callback) { - makeScreenshotCallback = callback; + return conf->areaName; } void FrontendUI::showMakeScreenshotDialog() { @@ -1081,3 +1448,849 @@ void FrontendUI::showMakeScreenshotDialog() { } } + +void FrontendUI::produceUpdateStage(HUpdateStage updateStage) { + this->update(updateStage); + + +} + +mathfu::mat4 getInfZMatrix(float f, float aspect) { + return mathfu::mat4( + f / aspect, 0.0f, 0.0f, 0.0f, + 0.0f, f, 0.0f, 0.0f, + 0.0f, 0.0f, 1, -1.0f, + 0.0f, 0.0f, 1, 0.0f); +} + +HDrawStage createSceneDrawStage(HFrameScenario sceneScenario, int width, int height, double deltaTime, bool isScreenshot, + bool produceDoubleCamera, bool swapDebugCamera, + ApiContainer &apiContainer, const std::shared_ptr ¤tScene, HCullStage &cullStage) { + + + static const mathfu::mat4 vulkanMatrixFix2 = mathfu::mat4(1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1.0/2.0, 1.0/2.0, + 0, 0, 0, 1).Transpose(); + + float farPlaneRendering = apiContainer.getConfig()->farPlane; + float farPlaneCulling = apiContainer.getConfig()->farPlaneForCulling; + + float nearPlane = 1.0; + float fov = toRadian(45.0); + + float canvasAspect = (float)width / (float)height; + + HCameraMatrices cameraMatricesCulling = apiContainer.camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneCulling); + HCameraMatrices cameraMatricesUpdate = apiContainer.camera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneRendering); + HCameraMatrices cameraMatricesRendering = cameraMatricesUpdate; + HCameraMatrices cameraMatricesRenderingDebug = nullptr; + + if (produceDoubleCamera && apiContainer.debugCamera != nullptr) + cameraMatricesRenderingDebug = apiContainer.debugCamera->getCameraMatrices(fov, canvasAspect, nearPlane, farPlaneRendering); + + + //Frustum matrix with reversed Z + bool isInfZSupported = apiContainer.camera->isCompatibleWithInfiniteZ(); + if (isInfZSupported) + { + float f = 1.0f / tan(fov / 2.0f); + cameraMatricesRendering->perspectiveMat = getInfZMatrix(f, canvasAspect); + if (cameraMatricesRenderingDebug != nullptr) { + cameraMatricesRenderingDebug->perspectiveMat = cameraMatricesRendering->perspectiveMat; + } + } + + if (apiContainer.hDevice->getIsVulkanAxisSystem() ) { + auto &perspectiveMatrix = cameraMatricesRendering->perspectiveMat; + + perspectiveMatrix = vulkanMatrixFix2 * perspectiveMatrix; + } + + auto clearColor = apiContainer.getConfig()->clearColor; + + if (cameraMatricesRenderingDebug && swapDebugCamera) { + std::swap(cameraMatricesRendering, cameraMatricesRenderingDebug); + } + + if (currentScene != nullptr) { + ViewPortDimensions dimensions = {{0, 0}, {width, height}}; + + HFrameBuffer fb = nullptr; + if (isScreenshot) { + fb = apiContainer.hDevice->createFrameBuffer(width, height, + {ITextureFormat::itRGBA}, + ITextureFormat::itDepth32, + apiContainer.hDevice->getMaxSamplesCnt(), 4); + } + + cullStage = sceneScenario->addCullStage(cameraMatricesCulling, currentScene); + auto updateStage = sceneScenario->addUpdateStage(cullStage, deltaTime*(1000.0f), cameraMatricesUpdate); + std::vector drawStageDependencies = {}; + if (produceDoubleCamera) { + std::vector drawStageDependencies__ = {}; + HDrawStage sceneDrawStage = sceneScenario->addDrawStage(updateStage, currentScene, cameraMatricesRenderingDebug, drawStageDependencies__, + true, + dimensions, + true, isInfZSupported, clearColor, fb); + drawStageDependencies.push_back(sceneDrawStage); + + int newWidth = floor(dimensions.maxs[0]*0.25f); + int newHeight = floor((float)newWidth / canvasAspect); + + int newX = dimensions.maxs[0] - newWidth; + int newY = dimensions.maxs[1] - newHeight; + + dimensions = {{newX, newY}, {newWidth, newHeight}}; + } + + HDrawStage sceneDrawStage = sceneScenario->addDrawStage(updateStage, currentScene, cameraMatricesRendering, drawStageDependencies, + true, + dimensions, + true, isInfZSupported, clearColor, fb); + + + return sceneDrawStage; + } + + return nullptr; +} + +HFrameScenario FrontendUI::createFrameScenario(int canvWidth, int canvHeight, double deltaTime) { + if (minimapGenerator != nullptr && + ( + minimapGenerator->getCurrentMode() == EMGMode::eScreenshotGeneration || + minimapGenerator->getCurrentMode() == EMGMode::eBoundingBoxCalculation + ) + ) { + minimapGenerator->process(); + } + + if (dataExporter != nullptr) { + dataExporter->process(); + if (dataExporter->isDone()) { + delete dataExporter; + dataExporter = nullptr; + } + } + + if (screenshotDS != nullptr) { + if (screenshotFrame + 5 <= m_api->hDevice->getFrameNumber()) { + std::vector buffer = std::vector(screenShotWidth*screenShotHeight*4+1); + + saveDataFromDrawStage(screenshotDS->target, screenshotFilename, screenShotWidth, screenShotHeight, buffer); + + screenshotDS = nullptr; + } + } + + + HFrameScenario sceneScenario = std::make_shared(); + std::vector uiDependecies = {}; + + if (needToMakeScreenshot) + { + HCullStage tempCullStage = nullptr; + auto drawStage = createSceneDrawStage(sceneScenario, screenShotWidth, screenShotHeight, deltaTime, true, + false, false, *m_api, + currentScene,tempCullStage); + if (drawStage != nullptr) { + uiDependecies.push_back(drawStage); + screenshotDS = drawStage; + screenshotFrame = m_api->hDevice->getFrameNumber(); + } + needToMakeScreenshot = false; + } + if (minimapGenerator != nullptr && minimapGenerator->getCurrentMode() != EMGMode::eNone) { + uiDependecies.push_back(minimapGenerator->createSceneDrawStage(sceneScenario)); + } + + //DrawStage for current frame + bool clearOnUi = true; + if (currentScene != nullptr && m_api->camera != nullptr) + { + int currentFrame = m_api->hDevice->getDrawFrameNumber(); + auto &cullStageData = m_cullstages[currentFrame]; + cullStageData = nullptr; + + + + auto drawStage = createSceneDrawStage(sceneScenario, canvWidth, canvHeight, deltaTime, + false, + m_api->getConfig()->doubleCameraDebug, m_api->getConfig()->swapMainAndDebug, + *m_api, + currentScene, cullStageData); + if (drawStage != nullptr) { + uiDependecies.push_back(drawStage); + clearOnUi = false; + } + } + //DrawStage for UI + { + ViewPortDimensions dimension = { + {0, 0}, + {canvWidth, canvHeight} + }; + auto clearColor = m_api->getConfig()->clearColor; + + auto uiCullStage = sceneScenario->addCullStage(nullptr, getShared()); + auto uiUpdateStage = sceneScenario->addUpdateStage(uiCullStage, deltaTime * (1000.0f), nullptr); + HDrawStage frontUIDrawStage = sceneScenario->addDrawStage(uiUpdateStage, getShared(), nullptr, uiDependecies, + true, dimension, clearOnUi, false, clearColor, nullptr); + } + + return sceneScenario; +} + +bool FrontendUI::openCascCallback(std::string cascPath) { + HRequestProcessor newProcessor = nullptr; + std::shared_ptr newStorage = nullptr; + try { + newProcessor = std::make_shared(cascPath.c_str()); + newStorage = std::make_shared(newProcessor.get()); + newProcessor->setThreaded(true); + newProcessor->setFileRequester(newStorage.get()); + } catch (...){ + return false; + }; + + m_api->cacheStorage = newStorage; + m_processor = newProcessor; + + return true; +} + +void FrontendUI::openSceneByfdid(int mapId, int wdtFileId, float x, float y, float z) { + if (m_api->cacheStorage) { +// storage->actuallDropCache(); + } + + currentScene = std::make_shared(m_api, mapId, wdtFileId); + m_api->camera = std::make_shared(); + m_api->camera->setCameraPos(x, y, z); + m_api->camera->setMovementSpeed(movementSpeed); +} + +void FrontendUI::openWMOSceneByfdid(int WMOFdid) { + currentScene = std::make_shared(m_api, WMOFdid); + m_api->camera->setCameraPos(0, 0, 0); +} + +void FrontendUI::openMapByIdAndFilename(int mapId, std::string mapName, float x, float y, float z) { + currentScene = std::make_shared(m_api, mapId, mapName); + m_api->camera->setCameraPos(x,y,z); +} +void FrontendUI::openMapByIdAndWDTId(int mapId, int wdtFileId, float x, float y, float z) { + currentScene = std::make_shared(m_api, mapId, wdtFileId); + m_api->camera->setCameraPos(x,y,z); +} +void FrontendUI::openM2SceneByfdid(int m2Fdid, std::vector &replacementTextureIds) { + currentScene = std::make_shared(m_api, m2Fdid, -1); + currentScene->setReplaceTextureArray(replacementTextureIds); + + + m_api->camera = std::make_shared(); + m_api->camera->setMovementSpeed(movementSpeed); + m_api->getConfig()->BCLightHack = false; +// + m_api->camera->setCameraPos(0, 0, 0); +} +void FrontendUI::openM2SceneByName(std::string m2FileName, std::vector &replacementTextureIds) { + currentScene = std::make_shared(m_api, m2FileName, -1); + currentScene->setReplaceTextureArray(replacementTextureIds); + + m_api->camera = std::make_shared(); + m_api->camera->setCameraPos(0, 0, 0); + m_api->camera->setMovementSpeed(movementSpeed); +} + +void FrontendUI::unloadScene() { + if (m_api->cacheStorage) { + m_api->cacheStorage->actuallDropCache(); + } + currentScene = std::make_shared(); +} + +int FrontendUI::getCameraNumCallback() { + if (currentScene != nullptr) { + return currentScene->getCameraNum(); + } + + return 0; +} + +bool FrontendUI::setNewCameraCallback(int cameraNum) { + if (currentScene == nullptr) return false; + + auto newCamera = currentScene->createCamera(cameraNum); + if (newCamera == nullptr) { + m_api->camera = std::make_shared(); + m_api->camera->setMovementSpeed(movementSpeed); + return false; + } + + m_api->camera = newCamera; + return true; +} + +void FrontendUI::resetAnimationCallback() { + currentScene->resetAnimation(); +} + +void FrontendUI::getCameraPos(float &cameraX, float &cameraY, float &cameraZ) { + if (m_api->camera == nullptr) { + cameraX = 0; cameraY = 0; cameraZ = 0; + return; + } + float currentCameraPos[4] = {0,0,0,0}; + m_api->camera->getCameraPosition(¤tCameraPos[0]); + cameraX = currentCameraPos[0]; + cameraY = currentCameraPos[1]; + cameraZ = currentCameraPos[2]; +} + +void FrontendUI::getDebugCameraPos(float &cameraX, float &cameraY, float &cameraZ) { + if (m_api->debugCamera == nullptr) { + cameraX = 0; cameraY = 0; cameraZ = 0; + return; + } + float currentCameraPos[4] = {0,0,0,0}; + m_api->debugCamera->getCameraPosition(¤tCameraPos[0]); + cameraX = currentCameraPos[0]; + cameraY = currentCameraPos[1]; + cameraZ = currentCameraPos[2]; +} + +inline bool fileExistsNotNull (const std::string& name) { +#ifdef ANDROID + return false; +#endif + + ghc::filesystem::path p{name}; + std::error_code errorCode; + + bool fileExists = exists(p,errorCode) && ghc::filesystem::file_size(p) > 10; + if (errorCode) { + std::cout << "errorCode = " << errorCode.message() << std::endl; + } + + return fileExists; +} + + +void FrontendUI::createDefaultprocessor() { + + const char * url = "https://wow.tools/casc/file/fname?buildconfig=9a77c0cdef71f18aaee8ba081865b6fd&cdnconfig=dd2c07aa3d4621529a93921750262d28&filename="; + const char * urlFileId = "https://wow.tools/casc/file/fdid?buildconfig=9a77c0cdef71f18aaee8ba081865b6fd&cdnconfig=dd2c07aa3d4621529a93921750262d28&filename=data&filedataid="; +// +//Classics +// const char * url = "https://wow.tools/casc/file/fname?buildconfig=bf24b9d67a4a9c7cc0ce59d63df459a8&cdnconfig=2b5b60cdbcd07c5f88c23385069ead40&filename="; +// const char * urlFileId = "https://wow.tools/casc/file/fdid?buildconfig=bf24b9d67a4a9c7cc0ce59d63df459a8&cdnconfig=2b5b60cdbcd07c5f88c23385069ead40&filename=data&filedataid="; +// processor = new HttpZipRequestProcessor(url); +//// processor = new ZipRequestProcessor(filePath); +//// processor = new MpqRequestProcessor(filePath); + m_processor = std::make_shared(url, urlFileId); +// m_processor = std::make_shared("e:\\games\\wow beta\\World of Warcraft Beta\\:wowt"); +//// processor->setThreaded(false); +//// + m_processor->setThreaded(true); + m_api->cacheStorage = std::make_shared(m_processor.get()); + m_processor->setFileRequester(m_api->cacheStorage.get()); + overrideCascOpened(true); +} + +auto FrontendUI::createMinimapGenerator() { + boundingBoxHolder = std::make_shared(); + riverColorOverrides = std::make_shared(); + + if (sceneDef != nullptr) { + m_minimapDB->getAdtBoundingBoxes(sceneDef->mapId, *boundingBoxHolder); + m_minimapDB->getRiverColorOverrides(sceneDef->mapId, *riverColorOverrides); + } + + minimapGenerator = std::make_shared( + m_api->cacheStorage, + m_api->hDevice, + m_processor, + m_api->databaseHandler, + boundingBoxHolder + ); + + minimapGenerator->getConfig()->colorOverrideHolder = riverColorOverrides; + + minimapGenerator->setZoom(previewZoom); + minimapGenerator->setLookAtPoint(previewX, previewY); + + +// sceneDef = { +// EMGMode::eScreenshotGeneration, +// 0, +// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +// mathfu::vec2(0, 0), +// mathfu::vec2(MathHelper::TILESIZE*2, MathHelper::TILESIZE*2), +// 1024, +// 1024, +// 1.0f, +// false, +// ScenarioOrientation::so45DegreeTick3, +// "azeroth/topDown1" +// }; + + sceneDefList = { + { + -1, + 530, + "Netherstorm", + mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), + mathfu::vec2(1627, -1175), + mathfu::vec2(6654 , 4689 ), + 1024, + 1024, + 1.0f, + ScenarioOrientation::so45DegreeTick0, + "outland/netherstorm" + }, + { + -1, + 1, + "Kalimdor, rot 0", + mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), + mathfu::vec2(-12182, -8803 ), + mathfu::vec2(12058, 4291), + 1024, + 1024, + 1.0f, + ScenarioOrientation::so45DegreeTick0, + "kalimdor/rotation0" + } + }; + + return minimapGenerator; + +// std::vector scenarios = { +//// { +//// 2222, +//// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +//// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +//// mathfu::vec2(-9750, -8001 ), +//// mathfu::vec2(8333, 9500 ), +//// ScenarioOrientation::so45DegreeTick0, +//// "shadowlands/orient0" +//// } +// { +// 1643, +// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +// mathfu::vec2(291 , 647 ), +// mathfu::vec2(2550, 2895), +// 256, +// 256, +// 4.0f, +// ScenarioOrientation::so45DegreeTick0, +// "kultiras/orient0" +// }, +//// { +//// 530, +//// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +//// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +//// mathfu::vec2(-5817, -1175), +//// mathfu::vec2(1758, 10491), +//// ScenarioOrientation::so45DegreeTick0, +//// "outland/topDown1" +//// } +//// { +//// 0, +//// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +//// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +//// mathfu::vec2(-9081, -20), +//// mathfu::vec2(-8507, 1296), +//// ScenarioOrientation::soTopDownOrtho, +//// "azeroth/topDown" +//// } +//// }; +// +//// std::vector scenarios = { +//// { +//// 1, +//// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +//// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +//// mathfu::vec2(-12182, -8803 ), +//// mathfu::vec2(12058, 4291), +//// ScenarioOrientation::so45DegreeTick0, +//// "kalimdor/rotation0" +//// }, +// { +// 1, +// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +// mathfu::vec2(-12182, -8803), +// mathfu::vec2(12058, 4291), +// 256, +// 256, +// 4.0f, +// ScenarioOrientation::so45DegreeTick1, +// "kalimdor/rotation1_new" +// } +// }; +//// { +//// 1, +//// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +//// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +//// mathfu::vec2(-12182, -8803 ), +//// mathfu::vec2(12058, 4291), +//// ScenarioOrientation::so45DegreeTick2, +//// "kalimdor/rotation2" +//// }, +//// { +//// 1, +//// mathfu::vec4(0.0671968088, 0.294095874, 0.348881632, 0), +//// mathfu::vec4(0.345206976, 0.329288304, 0.270450264, 0), +//// mathfu::vec2(-12182, -8803 ), +//// mathfu::vec2(12058, 4291), +//// ScenarioOrientation::so45DegreeTick3, +//// "kalimdor/rotation3" +//// }, +//// }; +} + +void FrontendUI::editComponentsForConfig(Config * config) { + if (config == nullptr) return; + + ImGui::BeginGroupPanel("Exterior Lighting"); + + { + ImGui::CompactColorPicker("Exterior Ambient", config->exteriorAmbientColor); + ImGui::CompactColorPicker("Exterior Horizon Ambient", config->exteriorHorizontAmbientColor); + ImGui::CompactColorPicker("Exterior Ground Ambient", config->exteriorGroundAmbientColor); + ImGui::CompactColorPicker("Exterior Direct Color", config->exteriorDirectColor); + } + + ImGui::EndGroupPanel(); +} + +void FrontendUI::restartMinimapGenPreview() { + minimapGenerator->stopPreview(); + minimapGenerator->startPreview(*sceneDef); + minimapGenerator->setZoom(previewZoom); + minimapGenerator->setLookAtPoint(previewX, previewY); +} + +void FrontendUI::showMinimapGenerationSettingsDialog() { + if(showMinimapGeneratorSettings) { + if (m_minimapDB == nullptr) { + m_minimapDB = std::make_shared("minimapdb.sqlite"); + m_minimapDB->getScenarios(sceneDefList); + } + if (minimapGenerator == nullptr) { + createMinimapGenerator(); + } + + + ImGui::Begin("Minimap Generator settings", &showMinimapGeneratorSettings); + ImGui::Columns(2, NULL, true); + //Left panel + ImGui::BeginTabBar("MinimapGenTabs"); + { + bool listOpened = this->sceneDef == nullptr; + bool staticAlwaysTrue = true; + if (ImGui::BeginTabItem("List")) + { + //The table + ImGui::BeginChild("Scenario List"); + ImGui::Columns(3, "scenarioListcolumns"); // 3-ways, with border + ImGui::Separator(); + ImGui::Text(""); + ImGui::NextColumn(); + ImGui::Text("Name"); + ImGui::NextColumn(); + ImGui::Text("Actions"); + ImGui::NextColumn(); + ImGui::Separator(); + + for (int i = 0; i < this->sceneDefList.size(); i++) { + auto &l_sceneDef = sceneDefList[i]; + bool checked = true; + ImGui::Checkbox("", &checked); + ImGui::NextColumn(); + ImGui::Text("%s", l_sceneDef.name.c_str()); + ImGui::NextColumn(); + if (ImGui::Button(("Edit##" + std::to_string(i)).c_str())) { + this->sceneDef = &l_sceneDef; + editTabOpened = true; + createMinimapGenerator(); + } + ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::Separator(); + ImGui::EndChild(); + + ImGui::EndTabItem(); + } + + if (sceneDef != nullptr) { + if (editTabOpened && ImGui::BeginTabItem("Edit", &editTabOpened, ImGuiTabItemFlags_SetSelected)) { + { + ImGui::InputInt("Map Id", &sceneDef->mapId); + auto scenarioName = std::array(); + if (sceneDef->name.size() > 128) sceneDef->name.resize(128); + std::copy(sceneDef->name.begin(), sceneDef->name.end(), scenarioName.data()); + if (ImGui::InputText("Scenario name", scenarioName.data(), 128)) { + sceneDef->name = std::string(std::begin(scenarioName), std::end(scenarioName)); + } + ImGui::BeginGroupPanel("Orientation"); + { + if (ImGui::RadioButton("Ortho projection", &sceneDef->orientation, ScenarioOrientation::soTopDownOrtho)) { + if (minimapGenerator->getCurrentMode() == EMGMode::ePreview) { + restartMinimapGenPreview(); + } + } + if (ImGui::RadioButton("At 45° tick 0", &sceneDef->orientation, ScenarioOrientation::so45DegreeTick0)) { + if (minimapGenerator->getCurrentMode() == EMGMode::ePreview) { + restartMinimapGenPreview(); + } + } + if (ImGui::RadioButton("At 45° tick 1", &sceneDef->orientation, ScenarioOrientation::so45DegreeTick1)) { + if (minimapGenerator->getCurrentMode() == EMGMode::ePreview) { + restartMinimapGenPreview(); + } + } + if (ImGui::RadioButton("At 45° tick 2", &sceneDef->orientation, ScenarioOrientation::so45DegreeTick2)) { + if (minimapGenerator->getCurrentMode() == EMGMode::ePreview) { + restartMinimapGenPreview(); + } + } + if (ImGui::RadioButton("At 45° tick 3", &sceneDef->orientation, ScenarioOrientation::so45DegreeTick3)) { + if (minimapGenerator->getCurrentMode() == EMGMode::ePreview) { + restartMinimapGenPreview(); + } + } + } + ImGui::EndGroupPanel(); + ImGui::SameLine(); + ImGui::BeginGroupPanel("Generation boundaries"); + { + ImGui::Text("In world coordinates"); + ImGui::InputFloat("Min x", &sceneDef->minWowWorldCoord.x); + ImGui::InputFloat("Min y", &sceneDef->minWowWorldCoord.y); + ImGui::InputFloat("Max x", &sceneDef->maxWowWorldCoord.x); + ImGui::InputFloat("Max y", &sceneDef->maxWowWorldCoord.y); + ImGui::EndGroupPanel(); + } + ImGui::BeginGroupPanel("Ocean color override"); + { + ImGui::CompactColorPicker("Close Ocean Color", sceneDef->closeOceanColor); + ImGui::EndGroupPanel(); + } + ImGui::BeginGroupPanel("Image settings"); + { + ImGui::PushItemWidth(100); + ImGui::InputInt("Image Width", &sceneDef->imageWidth); + ImGui::InputInt("Image Height", &sceneDef->imageHeight); + ImGui::PopItemWidth(); + ImGui::EndGroupPanel(); + } + + + + ImGui::BeginGroupPanel("Global map settings"); + { + ImGui::BeginGroupPanel("River color overrides"); + { + for (int i = 0; i < riverColorOverrides->size(); i++) { + auto &riverColorOverride = riverColorOverrides->operator[](i); + + ImGui::PushID(i); + if (ImGui::Button("Copy from current")) { + int areaId, parentAreaId; + mathfu::vec4 riverColor; + + minimapGenerator->getCurrentFDData(areaId, parentAreaId, riverColor); + riverColorOverride.areaId = areaId; + riverColorOverride.color = riverColor; + } + ImGui::SameLine(); + ImGui::CompactColorPicker("River Color Override", riverColorOverride.color); + ImGui::SameLine(); + ImGui::PushItemWidth(100); + ImGui::InputInt("Area Id", &riverColorOverride.areaId); + ImGui::PopItemWidth(); + + + ImGui::PopID(); + } + + if (ImGui::Button("Add override")) { + riverColorOverrides->push_back({}); + } + } + ImGui::EndGroupPanel(); + } + ImGui::EndGroupPanel(); + + ImGui::BeginGroupPanel("Current stats"); + { + int areaId, parentAreaId; + mathfu::vec4 riverColor; + + minimapGenerator->getCurrentFDData(areaId, parentAreaId, riverColor); + ImGui::Text("Current areaId %d", areaId); + ImGui::Text("Current parent areaId %d", parentAreaId); + ImGui::CompactColorPicker("Current River Color", riverColor); + + ImGui::EndGroupPanel(); + } + + auto currentTime = minimapGenerator->getConfig()->currentTime; + ImGui::Text("Time: %02d:%02d", (int)(currentTime/120), (int)((currentTime/2) % 60)); + if (ImGui::SliderInt("Current time", ¤tTime, 0, 2880)) { + minimapGenerator->getConfig()->currentTime = currentTime; + } + + editComponentsForConfig(minimapGenerator->getConfig()); + + if (minimapGenerator->getCurrentMode() != EMGMode::eScreenshotGeneration) { + bool isDisabled = minimapGenerator->getCurrentMode() != EMGMode::eNone; + if (ImGui::ButtonDisablable("Start Screenshot Gen", isDisabled)) { + std::vector list = {*sceneDef}; + + minimapGenerator->startScenarios(list); + } + } else { + if (ImGui::Button("Stop Screenshot Gen")) { + minimapGenerator->stopPreview(); + } + } + ImGui::SameLine(); + if (minimapGenerator->getCurrentMode() != EMGMode::ePreview) { + bool isDisabled = minimapGenerator->getCurrentMode() != EMGMode::eNone; + if (ImGui::ButtonDisablable("Start Preview", isDisabled)) { + minimapGenerator->startPreview(*sceneDef); + minimapGenerator->setZoom(previewZoom); + minimapGenerator->setLookAtPoint(previewX, previewY); + } + } else { + if (ImGui::Button("Stop Preview")) { + minimapGenerator->stopPreview(); + } + } + ImGui::SameLine(); + if (minimapGenerator->getCurrentMode() != EMGMode::eBoundingBoxCalculation) { + bool isDisabled = minimapGenerator->getCurrentMode() != EMGMode::eNone; + if (ImGui::ButtonDisablable("Start BBox calc", isDisabled)) { + minimapGenerator->startBoundingBoxCalc(*sceneDef); + } + } else { + if (ImGui::Button("Stop BBox calc")) { + minimapGenerator->stopBoundingBoxCalc(); + } + } + + if (minimapGenerator->getCurrentMode() != EMGMode::eNone && minimapGenerator->getCurrentMode() != EMGMode::ePreview) { + int x, y, maxX, maxY; + minimapGenerator->getCurrentTileCoordinates(x, y, maxX, maxY); + + ImGui::Text("X: % 03d out of % 03d", x, maxX); + ImGui::Text("Y: % 03d out of % 03d", y, maxY); + + } + + if (ImGui::Button("Save")) { + m_minimapDB->saveScenario(*sceneDef); + m_minimapDB->saveRiverColorOverrides(sceneDef->mapId, *riverColorOverrides); + m_minimapDB->saveAdtBoundingBoxes(sceneDef->mapId, *boundingBoxHolder); + } + } + + ImGui::EndTabItem(); + + } else { + //sceneDef = nullptr; + } + } + + + ImGui::EndTabBar(); + } + + //Right panel + ImGui::NextColumn(); + { + ImGui::BeginChild("Minimap Gen Preview", ImVec2(0, 0)); + { + bool changed = false; + bool readOnly = minimapGenerator->getCurrentMode() != EMGMode::ePreview; + + const char * fmt = "%.3f"; + changed |= ImGui::InputFloat("x", &previewX, 0.0f, 0.0f, fmt, readOnly ? ImGuiInputTextFlags_::ImGuiInputTextFlags_ReadOnly: 0); + changed |= ImGui::InputFloat("y", &previewY, 0.0f, 0.0f, fmt, readOnly ? ImGuiInputTextFlags_::ImGuiInputTextFlags_ReadOnly: 0); + + if (minimapGenerator->getCurrentMode() == EMGMode::ePreview) { + minimapGenerator->setLookAtPoint(previewX, previewY); + } + if (ImGui::SliderFloat("Zoom", &previewZoom, 0.1, 10)) { + if (minimapGenerator->getCurrentMode() == EMGMode::ePreview) { + minimapGenerator->setZoom(previewZoom); + } + } + if (ImGui::Button("Reload")) { + minimapGenerator->reload(); + } + + ImGui::BeginChild("Minimap Gen Preview image", ImVec2(0, 0), + true, ImGuiWindowFlags_AlwaysHorizontalScrollbar | + ImGuiWindowFlags_AlwaysVerticalScrollbar); + + auto drawStage = minimapGenerator->getLastDrawStage(); + if (drawStage != nullptr) { + auto texture = drawStage->target->getAttachment(0); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,1.0)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0,0,0,1.0)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0,0,0,1.0)); + + const int imageSize = 512; + + if (ImGui::ImageButton2(texture, "previewImage", ImVec2(imageSize, imageSize))) { + auto mousePos = ImGui::GetMousePos(); + ImGuiStyle &style = ImGui::GetStyle(); + + mousePos.x += -ImGui::GetWindowPos().x - style.WindowPadding.x; + mousePos.y += -ImGui::GetWindowPos().y - style.WindowPadding.y; + + previewX = (0.5f - (mousePos.y / (float)imageSize)) * minimapGenerator->GetOrthoDimension() + previewX; + previewY = (0.5f - (mousePos.x / (float)imageSize)) * minimapGenerator->GetOrthoDimension() + previewY; + minimapGenerator->setLookAtPoint(previewX, previewY); + }; + + ImGui::PopStyleColor(3); + ImGui::PopStyleVar(3); + } + + ImGui::EndChild(); + + } + ImGui::EndChild(); + + + } + ImGui::Columns(1); + + ImGui::End(); + } else { + if (minimapGenerator != nullptr && minimapGenerator->getCurrentMode() == EMGMode::eNone) { + minimapGenerator = nullptr; + } + } +} + +void FrontendUI::createDatabaseHandler() { + if (fileExistsNotNull("./export.db3")) { + m_api->databaseHandler = std::make_shared("./export.db3"); + } else { + m_api->databaseHandler = std::make_shared(); + } +} diff --git a/src/ui/FrontendUI.h b/src/ui/FrontendUI.h index 93c8baca7..6bce8cf0d 100644 --- a/src/ui/FrontendUI.h +++ b/src/ui/FrontendUI.h @@ -5,45 +5,103 @@ #ifndef AWEBWOWVIEWERCPP_FRONTENDUI_H #define AWEBWOWVIEWERCPP_FRONTENDUI_H +#ifdef __ANDROID_API__ +#include +#else +#include +#endif #include "imguiLib/imgui.h" #include #include "../../wowViewerLib/src/include/database/dbStructs.h" #include "../../wowViewerLib/src/engine/objects/iScene.h" +#include "childWindow/mapConstructionWindow.h" +#include "../minimapGenerator/minimapGenerator.h" +#include "../persistance/CascRequestProcessor.h" +#include "../minimapGenerator/storage/CMinimapDataDB.h" +#include "../exporters/dataExporter/DataExporterClass.h" -class FrontendUI : public IScene { +class FrontendUI : public IScene, public std::enable_shared_from_this { //Implementation of iInnerSceneApi public: - FrontendUI(ApiContainer *api) { + void createDefaultprocessor(); + void createDatabaseHandler(); + + FrontendUI(HApiContainer api, HRequestProcessor processor) { m_api = api; + m_processor = processor; + + this->createDatabaseHandler(); + //this->createDefaultprocessor(); + } ~FrontendUI() override {}; + std::shared_ptr getShared() + { + return shared_from_this(); + } + + HRequestProcessor getProcessor() { + return m_processor; + } + void setReplaceTextureArray(std::vector &replaceTextureArray) override {}; + void setMeshIdArray(std::vector &meshIds) override {}; void setAnimationId(int animationId) override {}; + void setMeshIds(std::vector &meshIds) override {}; void produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, std::vector &additionalChunks) override; + void produceUpdateStage(HUpdateStage updateStage) override; void checkCulling(HCullStage cullStage) override {}; void doPostLoad(HCullStage cullStage) override {}; - void update(HUpdateStage updateStage) override {}; - void updateBuffers(HCullStage cullStage) override {}; + + void update(HUpdateStage updateStage) {}; + void updateBuffers(HUpdateStage updateStage) override {}; + int getCameraNum() override {return 0;}; - std::shared_ptr createCamera(int cameraNum) {return nullptr;}; - void resetAnimation() override {} + std::shared_ptr createCamera(int cameraNum) override {return nullptr;}; + void resetAnimation() override {}; + + HFrameScenario createFrameScenario(int canvWidth, int canvHeight, double deltaTime); + + void setUIScale(float scale) { + uiScale = scale; + } private: - std::function openCascCallback = nullptr; + std::array m_cullstages = {}; + + std::shared_ptr m_minimapDB; + + HMinimapGenerator minimapGenerator; + HADTBoundingBoxHolder boundingBoxHolder; + std::vector sceneDefList; + HRiverColorOverrideHolder riverColorOverrides; + ScenarioDef *sceneDef = nullptr; + bool editTabOpened; + float previewX = 0; + float previewY = 0; + float previewZoom = 1; + + float uiScale = 1; + - std::function openSceneByfdid = nullptr; - std::function openWMOSceneByfdid = nullptr; - std::function &replacementTextureIds)> openM2SceneByfdid = nullptr; - std::function &replacementTextureIds)> openM2SceneByName = nullptr; + std::shared_ptr currentScene = nullptr; - std::function getCameraPos = nullptr; - std::function makeScreenshotCallback = nullptr; + bool openCascCallback(std::string cascPath); + + void openSceneByfdid(int mapId, int wdtFileId, float x, float y, float z); + void openWMOSceneByfdid(int WMOFdid); + void openM2SceneByfdid(int m2Fdid, std::vector &replacementTextureIds); + void openM2SceneByName(std::string m2FileName, std::vector &replacementTextureIds); + + void getCameraPos(float &cameraX,float &cameraY,float &cameraZ); + void getDebugCameraPos(float &cameraX,float &cameraY,float &cameraZ); + void makeScreenshotCallback(std::string fileName, int width, int height); void getAdtSelectionMinimap(int wdtFileDataId) { m_wdtFile = m_api->cacheStorage->getWdtFileCache()->getFileId(wdtFileDataId); @@ -53,10 +111,10 @@ class FrontendUI : public IScene { bool fillAdtSelectionminimap(std::array, 64> &minimap, bool &isWMOMap, bool &wdtFileExists); - std::function unloadScene; - std::function getCameraNumCallback; - std::function setNewCameraCallback; - std::function resetAnimationCallback; + void unloadScene(); + int getCameraNumCallback(); + bool setNewCameraCallback(int cameraNum); + void resetAnimationCallback(); std::array, 64> adtSelectionMinimap; @@ -68,12 +126,13 @@ class FrontendUI : public IScene { } } + auto createMinimapGenerator(); std::array filterText = {0}; bool refilterIsNeeded = false; void filterMapList(std::string text); - ImGui::FileBrowser fileDialog = ImGui::FileBrowser(ImGuiFileBrowserFlags_SelectDirectory); + ImGui::FileBrowser fileDialog = ImGui::FileBrowser(ImGuiFileBrowserFlags_SelectDirectory, true); ImGui::FileBrowser createFileDialog = ImGui::FileBrowser(ImGuiFileBrowserFlags_EnterNewFilename); bool show_demo_window = true; @@ -84,8 +143,11 @@ class FrontendUI : public IScene { bool showSettings = false; bool showQuickLinks = false; bool showAboutWindow = false; + bool showMinimapGeneratorSettings = false; // c bool showWorldPosTooltip = false; + bool showMapConstruction = false; + bool cascOpened = false; bool mapCanBeOpened = true; bool adtMinimapFilled = false; @@ -99,10 +161,14 @@ class FrontendUI : public IScene { float prevMinimapZoom = 1; int prevMapId = -1; bool isWmoMap = false; - bool useGaussBlur = true; - bool useTimedGlobalLight = true; - bool useM2AmbientLight = false; + bool pauseAnimation = true; + + int lightSource = 0; + + bool disableGlow = false; + int glowSource = 0; + float customGlow = 0.0; int currentCameraNum = -1; @@ -117,7 +183,8 @@ class FrontendUI : public IScene { float worldPosY = 0; float worldPosZ = 0; - ApiContainer * m_api; + HApiContainer m_api; + HRequestProcessor m_processor; HGTexture fontTexture; HWdtFile m_wdtFile = nullptr; @@ -129,14 +196,38 @@ class FrontendUI : public IScene { int lastWidth = 100; int lastHeight = 100; + bool needToMakeScreenshot = false; + std::string screenshotFilename = ""; + HDrawStage screenshotDS = nullptr; int screenShotWidth = 100; int screenShotHeight = 100; + int screenshotFrame = -1; + + std::shared_ptr exporter; + int exporterFramesReady = 0; + + std::shared_ptr m_mapConstructionWindow = nullptr; + + +//Test export + DataExporterClass *dataExporter = nullptr; public: void overrideCascOpened(bool value) { cascOpened = value; } - void initImgui(GLFWwindow* window); + +#ifdef __ANDROID_API__ + void initImgui(ANativeWindow* window); +#else + void initImgui( +#ifdef __ANDROID_API__ + ANativeWindow *window +#else + GLFWwindow *window +#endif +); +#endif void composeUI(); void newFrame(); @@ -144,24 +235,11 @@ class FrontendUI : public IScene { bool getStopMouse(); bool getStopKeyboard(); - void setOpenCascStorageCallback(std::function callback); - void setOpenSceneByfdidCallback(std::function callback); - void setOpenWMOSceneByfdidCallback(std::function callback); - void setOpenM2SceneByfdidCallback(std::function &replacementTextureIds)> callback); - void setOpenM2SceneByFilenameCallback(std::function&)> callback); - void setGetCameraPos( std::function callback); - void setMakeScreenshotCallback( std::function callback); - - void setUnloadScene( std::function callback) { - unloadScene = callback; - }; - - void setGetCameraNum(std::function callback); - void setSelectNewCamera(std::function callback); - void setResetAnimation(std::function callback); + void setExperimentCallback(std::function callback); void showMainMenu(); void showMapSelectionDialog(); + void showMapConstructionDialog(); void showMakeScreenshotDialog(); void showAdtSelectionMinimap(); @@ -169,6 +247,14 @@ class FrontendUI : public IScene { void showQuickLinksDialog(); void showCurrentStatsDialog(); + + void restartMinimapGenPreview(); + void showMinimapGenerationSettingsDialog(); + + void openMapByIdAndFilename(int mapId, std::string mapName, float x, float y, float z); + void openMapByIdAndWDTId(int mapId, int wdtFileId, float x, float y, float z); + + void editComponentsForConfig(Config *config); }; diff --git a/src/ui/childWindow/mapConstructionWindow.cpp b/src/ui/childWindow/mapConstructionWindow.cpp new file mode 100644 index 000000000..f6c0ab935 --- /dev/null +++ b/src/ui/childWindow/mapConstructionWindow.cpp @@ -0,0 +1,264 @@ +// +// Created by Deamon on 10/4/2020. +// + +#include +#include +#include "mapConstructionWindow.h" +#include "../../../wowViewerLib/src/engine/algorithms/mathHelper.h" + +MapConstructionWindow::MapConstructionWindow(HApiContainer mApi) : m_api(mApi) {} + +bool MapConstructionWindow::render() { + bool isNotClosed = true; + if (mapList.size() == 0) { + m_api->databaseHandler->getMapArray(mapList); + } + + if (mapCanBeOpened) { + if (!adtMinimapFilled && fillAdtSelectionminimap(adtSelectionMinimap, isWmoMap, mapCanBeOpened )) { +// fillAdtSelectionminimap = nullptr; + adtMinimapFilled = true; + } + } + + if (refilterIsNeeded) { + filterMapList(std::string(&filterText[0])); + mapListStringMap = {}; + for (int i = 0; i < filteredMapList.size(); i++) { + auto mapRec = filteredMapList[i]; + + std::vector mapStrRec; + mapStrRec.push_back(std::to_string(mapRec.ID)); + mapStrRec.push_back(mapRec.MapName); + mapStrRec.push_back(mapRec.MapDirectory); + mapStrRec.push_back(std::to_string(mapRec.WdtFileID)); + mapStrRec.push_back(std::to_string(mapRec.MapType)); + + mapListStringMap.push_back(mapStrRec); + } + + refilterIsNeeded = false; + } + + ImGui::Begin("Map construction dialog", &isNotClosed); + { + ImGui::Columns(2, NULL, true); + { + ImGui::BeginChild("Map Select Dialog for map constr", ImVec2(0, 0)); + { + if (!mapCanBeOpened) { + ImGui::Text("Cannot open this map."); + ImGui::Text("WDT file either does not exist in CASC repository or is encrypted"); + } else if (!isWmoMap) { + ImGui::SliderFloat("Zoom", &minimapZoom, 0.1, 10); + // ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); + showAdtSelectionMinimap(); + } else { + ImGui::Text("This is WMO map."); + } + + } + ImGui::EndChild(); + } + + ImGui::NextColumn(); + + { + //Filter + if (ImGui::InputText("Filter: ", filterText.data(), filterText.size(), ImGuiInputTextFlags_AlwaysInsertMode)) { + refilterIsNeeded = true; + } + //The table + ImGui::BeginChild("Map Select Dialog Left panel"); + ImGui::Columns(5, "mycolumns"); // 5-ways, with border + ImGui::Separator(); + ImGui::Text("ID"); + ImGui::NextColumn(); + ImGui::Text("MapName"); + ImGui::NextColumn(); + ImGui::Text("MapDirectory"); + ImGui::NextColumn(); + ImGui::Text("WdtFileID"); + ImGui::NextColumn(); + ImGui::Text("MapType"); + ImGui::NextColumn(); + ImGui::Separator(); + static int selected = -1; + for (int i = 0; i < filteredMapList.size(); i++) { + auto mapRec = filteredMapList[i]; + + if (ImGui::Selectable(mapListStringMap[i][0].c_str(), selected == i, ImGuiSelectableFlags_SpanAllColumns)) { + if (mapRec.ID != prevMapId) { + mapCanBeOpened = true; + adtMinimapFilled = false; + prevMapRec = mapRec; + + isWmoMap = false; + adtSelectionMinimap = {}; + currentlySelectedWdtFile = m_api->cacheStorage->getWdtFileCache()->getFileId(mapRec.WdtFileID); + } + prevMapId = mapRec.ID; + selected = i; + } + bool hovered = ImGui::IsItemHovered(); + ImGui::NextColumn(); + ImGui::Text("%s", mapListStringMap[i][1].c_str()); + ImGui::NextColumn(); + ImGui::Text("%s", mapListStringMap[i][2].c_str()); + ImGui::NextColumn(); + ImGui::Text("%s", mapListStringMap[i][3].c_str()); + ImGui::NextColumn(); + ImGui::Text("%s", mapListStringMap[i][4].c_str()); + ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::Separator(); + ImGui::EndChild(); + } + } + ImGui::End(); + + return isNotClosed; +} + +bool MapConstructionWindow::fillAdtSelectionminimap(std::array, 64> &minimap, bool &isWMOMap, + bool &wdtFileExists) { + if (currentlySelectedWdtFile == nullptr) return false; + + if (currentlySelectedWdtFile->getStatus() == FileStatus::FSRejected) { + wdtFileExists = false; + isWMOMap = false; + return false; + } + + if (currentlySelectedWdtFile->getStatus() != FileStatus::FSLoaded) return false; + + isWMOMap = currentlySelectedWdtFile->mphd->flags.wdt_uses_global_map_obj != 0; + + for (int i = 0; i < 64; i++) { + for (int j = 0; j < 64; j++) { + if (currentlySelectedWdtFile->mapFileDataIDs[i*64 + j].minimapTexture > 0) { + auto texture = m_api->cacheStorage->getTextureCache()->getFileId(currentlySelectedWdtFile->mapFileDataIDs[i*64 + j].minimapTexture); + minimap[i][j] = m_api->hDevice->createBlpTexture(texture, false, false); + } else { + minimap[i][j] = nullptr; + } + } + } + return true; +} + +template +struct my_equal1 { + my_equal1( const std::locale& loc ) : loc_(loc) {} + bool operator()(charT ch1, charT ch2) { + return std::toupper(ch1, loc_) == std::toupper(ch2, loc_); + } +private: + const std::locale& loc_; +}; + +template +int ci_find_substr_1( const T& str1, const T& str2, const std::locale& loc = std::locale() ) +{ + typename T::const_iterator it = std::search( str1.begin(), str1.end(), + str2.begin(), str2.end(), my_equal1(loc) ); + if ( it != str1.end() ) return it - str1.begin(); + else return -1; // not found +} + +void MapConstructionWindow::filterMapList(std::string text) { + filteredMapList = {}; + for (int i = 0; i < mapList.size(); i++) { + auto ¤tRec = mapList[i]; + if (text == "" || + ( + (ci_find_substr_1(currentRec.MapName, text) != std::string::npos) || + (ci_find_substr_1(currentRec.MapDirectory, text) != std::string::npos) + ) + ) { + filteredMapList.push_back(currentRec); + } + } +} + +void MapConstructionWindow::showAdtSelectionMinimap() { + ImGui::BeginChild("Adt selection minimap", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysHorizontalScrollbar | + ImGuiWindowFlags_AlwaysVerticalScrollbar); + + if (minimapZoom < 0.001) + minimapZoom = 0.001f; + + float oldRealScrollX = ImGui::GetScrollX() + (ImGui::GetCurrentWindow()->InnerRect.GetWidth() / 2.0f); + float oldRealScrollY = ImGui::GetScrollY() + (ImGui::GetCurrentWindow()->InnerRect.GetHeight() / 2.0f); + + float oldScrollPercX = oldRealScrollX / (std::ceil(64*100*prevMinimapZoom) + (ImGui::GetCurrentWindow()->WindowPadding.x * 2.0f)); + float oldScrollPercY = oldRealScrollY / (std::ceil(64*100*prevMinimapZoom) + (ImGui::GetCurrentWindow()->WindowPadding.y * 2.0f)); + + if (prevMinimapZoom != minimapZoom) { + auto windowSize = ImGui::GetWindowSize(); +// ImGui::SetScrollX((windowSize.x / 2.0f) * ((ImGui::GetScrollX() + 1.0f) * minimapZoom / prevMinimapZoom - 1.0f)); +// ImGui::SetScrollY((windowSize.y / 2.0f) * ((ImGui::GetScrollY() + 1.0f) * minimapZoom / prevMinimapZoom - 1.0f)); + +// ImGui::SetScrollX((minimapZoom / prevMinimapZoom) * (ImGui::GetScrollX()/ImGui::GetScrollMaxX())); +// ImGui::SetScrollY((minimapZoom / prevMinimapZoom) * (ImGui::GetScrollY()/ImGui::GetScrollMaxY())); + + } + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); +// ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); + + const float defaultImageDimension = 100; + for (int i = 0; i < 64; i++) { + for (int j = 0; j < 64; j++) { + if (adtSelectionMinimap[i][j] != nullptr) { + if (ImGui::ImageButton(adtSelectionMinimap[i][j], + ImVec2(defaultImageDimension * minimapZoom, defaultImageDimension * minimapZoom))) { + auto mousePos = ImGui::GetMousePos(); + ImGuiStyle &style = ImGui::GetStyle(); + + + mousePos.x += ImGui::GetScrollX() - ImGui::GetWindowPos().x - style.WindowPadding.x; + mousePos.y += ImGui::GetScrollY() - ImGui::GetWindowPos().y - style.WindowPadding.y; + + mousePos.x = ((mousePos.x / minimapZoom) / defaultImageDimension); + mousePos.y = ((mousePos.y / minimapZoom) / defaultImageDimension); + + mousePos.x = (32.0f - mousePos.x) * MathHelper::TILESIZE; + mousePos.y = (32.0f - mousePos.y) * MathHelper::TILESIZE; + +// worldPosX = mousePos.y; +// worldPosY = mousePos.x; + } + } else { + ImGui::Dummy(ImVec2(100 * minimapZoom, 100 * minimapZoom)); + } + + ImGui::SameLine(0, 0); + } + ImGui::NewLine(); + } + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + + if (prevMinimapZoom != minimapZoom) { +// ImGui::BeginChild("Adt selection minimap", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysHorizontalScrollbar | +// ImGuiWindowFlags_AlwaysVerticalScrollbar); + + float newScrollX = oldScrollPercX * (std::ceil(64*100*minimapZoom) + (ImGui::GetCurrentWindow()->WindowPadding.x * 2.0f)) - (ImGui::GetCurrentWindow()->InnerRect.GetWidth() / 2.0f); + float newScrollY = oldScrollPercY * (std::ceil(64*100*minimapZoom) + (ImGui::GetCurrentWindow()->WindowPadding.y * 2.0f)) - (ImGui::GetCurrentWindow()->InnerRect.GetHeight() / 2.0f); + + ImGui::SetScrollX(newScrollX); + ImGui::SetScrollY(newScrollY); + prevMinimapZoom = minimapZoom; + +// ImGui::EndChild(); + } + + ImGui::EndChild(); + +} \ No newline at end of file diff --git a/src/ui/childWindow/mapConstructionWindow.h b/src/ui/childWindow/mapConstructionWindow.h new file mode 100644 index 000000000..67e613fe8 --- /dev/null +++ b/src/ui/childWindow/mapConstructionWindow.h @@ -0,0 +1,48 @@ +// +// Created by Deamon on 10/4/2020. +// + +#ifndef AWEBWOWVIEWERCPP_MAPCONSTRUCTIONWINDOW_H +#define AWEBWOWVIEWERCPP_MAPCONSTRUCTIONWINDOW_H + + +#include "../../../wowViewerLib/src/engine/ApiContainer.h" + +class MapConstructionWindow { +private: + HApiContainer m_api; + + std::vector mapList = {}; + std::vector filteredMapList = {}; + std::vector> mapListStringMap = {}; + + bool showMapConstruction = false; + + + bool mapCanBeOpened = true; + bool adtMinimapFilled = false; + bool isWmoMap = false; + + int prevMapId = -1; + MapRecord prevMapRec; + + std::shared_ptr currentlySelectedWdtFile = nullptr; + std::array, 64> adtSelectionMinimap; + + float minimapZoom = 1; + float prevMinimapZoom = 1; + + std::array filterText = {0}; + bool refilterIsNeeded = false; + void filterMapList(std::string text); + + bool fillAdtSelectionminimap(std::array, 64> &minimap, bool &isWMOMap, bool &wdtFileExists); + void showAdtSelectionMinimap(); +public: + MapConstructionWindow(HApiContainer mApi); + + bool render(); +}; + + +#endif //AWEBWOWVIEWERCPP_MAPCONSTRUCTIONWINDOW_H diff --git a/src/ui/imguiLib/compactColorPicker/compactColorPicker.cpp b/src/ui/imguiLib/compactColorPicker/compactColorPicker.cpp new file mode 100644 index 000000000..00c127be0 --- /dev/null +++ b/src/ui/imguiLib/compactColorPicker/compactColorPicker.cpp @@ -0,0 +1,21 @@ +// +// Created by Deamon on 1/23/2021. +// + +#include "compactColorPicker.h" + +void ImGui::CompactColorPicker(const std::string colorName, mathfu::vec4 &color) { + ImVec4 col = ImVec4(color.x, color.y, color.z, 1.0); + if (ImGui::ColorButton((colorName+"##3b").c_str(), col)) { + ImGui::OpenPopup((colorName+"picker").c_str()); + } + ImGui::SameLine(); + ImGui::Text("%s", colorName.c_str()); + + if (ImGui::BeginPopup((colorName + "picker").c_str())) { + if (ImGui::ColorPicker3(colorName.c_str(), color.data_)) { + + } + ImGui::EndPopup(); + } +} \ No newline at end of file diff --git a/src/ui/imguiLib/compactColorPicker/compactColorPicker.h b/src/ui/imguiLib/compactColorPicker/compactColorPicker.h new file mode 100644 index 000000000..4dce40dc5 --- /dev/null +++ b/src/ui/imguiLib/compactColorPicker/compactColorPicker.h @@ -0,0 +1,14 @@ +// +// Created by Deamon on 1/23/2021. +// + +#ifndef AWEBWOWVIEWERCPP_COMPACTCOLORPICKER_H +#define AWEBWOWVIEWERCPP_COMPACTCOLORPICKER_H + +#include +namespace ImGui { + void CompactColorPicker(const std::string colorName, mathfu::vec4 &color); +} + + +#endif //AWEBWOWVIEWERCPP_COMPACTCOLORPICKER_H diff --git a/src/ui/imguiLib/disablableButton/disablableButton.cpp b/src/ui/imguiLib/disablableButton/disablableButton.cpp new file mode 100644 index 000000000..87169a896 --- /dev/null +++ b/src/ui/imguiLib/disablableButton/disablableButton.cpp @@ -0,0 +1,20 @@ +// +// Created by Deamon on 1/16/2021. +// + +#include "disablableButton.h" +#include +#include + +bool ImGui::ButtonDisablable(const char* label, bool disabled, const struct ImVec2& size_arg) { + bool result; + if (disabled) { + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f,0.5f,0.5f,1.0)); + result = ImGui::ButtonEx(label, size_arg, ImGuiButtonFlags_Disabled); + ImGui::PopStyleColor(1); + } else { + result = ImGui::Button(label, size_arg); + } + + return result; +} \ No newline at end of file diff --git a/src/ui/imguiLib/disablableButton/disablableButton.h b/src/ui/imguiLib/disablableButton/disablableButton.h new file mode 100644 index 000000000..bd67ebd1b --- /dev/null +++ b/src/ui/imguiLib/disablableButton/disablableButton.h @@ -0,0 +1,14 @@ +// +// Created by Deamon on 1/16/2021. +// + +#ifndef AWEBWOWVIEWERCPP_DISABLABLEBUTTON_H +#define AWEBWOWVIEWERCPP_DISABLABLEBUTTON_H + +#include +namespace ImGui { + bool ButtonDisablable(const char* label, bool disabled, const struct ImVec2& size = ImVec2(0,0)); +} + + +#endif //AWEBWOWVIEWERCPP_DISABLABLEBUTTON_H diff --git a/src/ui/imguiLib/fileBrowser/imfilebrowser.h b/src/ui/imguiLib/fileBrowser/imfilebrowser.h index 22075d372..0cda108a6 100644 --- a/src/ui/imguiLib/fileBrowser/imfilebrowser.h +++ b/src/ui/imguiLib/fileBrowser/imfilebrowser.h @@ -2,15 +2,22 @@ #include #include -#include +#include +//#include +#include #include #include #include +#include "../../../../3rdparty/filesystem_impl/include/ghc/filesystem.hpp" + #ifndef IMGUI_VERSION # error "include imgui.h before this header" #endif +#include "../../database/csvtest/csv.h" +#include "../../../../wowViewerLib/src/include/string_utils.h" + using ImGuiFileBrowserFlags = int; enum ImGuiFileBrowserFlags_ @@ -31,7 +38,7 @@ namespace ImGui public: // pwd is set to current working directory by default - explicit FileBrowser(ImGuiFileBrowserFlags flags = 0); + explicit FileBrowser(ImGuiFileBrowserFlags flags = 0, bool cascOpenMode = false ); FileBrowser(const FileBrowser ©From); @@ -56,10 +63,10 @@ namespace ImGui bool HasSelected() const noexcept; // set current browsing directory - bool SetPwd(const std::filesystem::path &pwd = std::filesystem::current_path()); + bool SetPwd(const ghc::filesystem::path &pwd = ghc::filesystem::current_path()); // returns selected filename. make sense only when HasSelected returns true - std::filesystem::path GetSelected() const; + ghc::filesystem::path GetSelected() const; // set selected filename to empty void ClearSelected(); @@ -67,8 +74,17 @@ namespace ImGui // set file type filters. eg. { ".h", ".cpp", ".hpp", ".cc", ".inl" } void SetTypeFilters(const std::vector &typeFilters); + bool isCascOpenMode() { return m_cascOpenMode;}; + std::string getProductBuild() { return currentBuild;}; private: + void loadBuildsFromBuildInfo(); + + bool m_cascOpenMode = false; + ghc::filesystem::path m_last_pwd_forBuildInfo; + std::vector availableBuilds; + std::string currentBuild = ""; + class ScopeGuard { std::function func_; @@ -80,7 +96,7 @@ namespace ImGui ~ScopeGuard() { func_(); } }; - void SetPwdUncatched(const std::filesystem::path &pwd); + void SetPwdUncatched(const ghc::filesystem::path &pwd); #ifdef _WIN32 static std::uint32_t GetDrivesBitMask(); @@ -101,7 +117,7 @@ namespace ImGui std::vector typeFilters_; int typeFilterIndex_; - std::filesystem::path pwd_; + ghc::filesystem::path pwd_; std::string selectedFilename_; struct FileRecord @@ -126,17 +142,23 @@ namespace ImGui }; } // namespace ImGui -inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags) +inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags, bool cascOpenMode) : flags_(flags), openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false), + m_cascOpenMode(cascOpenMode), inputNameBuf_(std::make_unique>()) { if(flags_ & ImGuiFileBrowserFlags_CreateNewDir) newDirNameBuf_ = std::make_unique>(); inputNameBuf_->at(0) = '\0'; - SetTitle("file browser"); - SetPwd(std::filesystem::current_path()); + if (m_cascOpenMode) { + SetTitle("Select WoW Directory"); + } else { + SetTitle("file browser"); + } + + SetPwd(ghc::filesystem::current_path()); typeFilters_.clear(); typeFilterIndex_ = 0; @@ -161,7 +183,7 @@ inline ImGui::FileBrowser &ImGui::FileBrowser::operator=(const FileBrowser © closeFlag_ = copyFrom.closeFlag_; isOpened_ = copyFrom.isOpened_; ok_ = copyFrom.ok_; - + statusStr_ = ""; pwd_ = copyFrom.pwd_; selectedFilename_ = copyFrom.selectedFilename_; @@ -207,6 +229,51 @@ inline bool ImGui::FileBrowser::IsOpened() const noexcept return isOpened_; } +inline bool fileExistsNotNull1 (const std::string& name) { + ghc::filesystem::path p{name}; + + return exists(p) && ghc::filesystem::file_size(p) > 10; +} + +inline void ImGui::FileBrowser::loadBuildsFromBuildInfo() { + availableBuilds.clear(); + currentBuild = ""; + std::string buildFile = GetSelected() / ".build.info"; +// std::cout< headerNames; + tokenize(header, "|", headerNames); + + int productIndex = -1; + for (int i = 0; i < headerNames.size(); i++) { + if (startsWith(headerNames[i], "Product")) { + productIndex = i; + break; + } + } + if (productIndex == -1) return; + + while(char*line = lineReader.next_line()){ + std::string content = line; + std::vector values; + tokenize(content, "|", values); + if (productIndex < values.size()) { + availableBuilds.push_back(values[productIndex]); + } + } + } + + + std::sort(availableBuilds.begin(), availableBuilds.end()); + availableBuilds.erase(std::unique(availableBuilds.begin(), availableBuilds.end()), availableBuilds.end()); + + if (availableBuilds.size()>0) { + currentBuild = availableBuilds[0]; + } +} + inline void ImGui::FileBrowser::Display() { PushID(this); @@ -286,7 +353,7 @@ inline void ImGui::FileBrowser::Display() if(newPwdLastSecIdx >= 0) { int i = 0; - std::filesystem::path newPwd; + ghc::filesystem::path newPwd; for(auto &sec : pwd_) { if(i++ > newPwdLastSecIdx) @@ -333,7 +400,7 @@ inline void ImGui::FileBrowser::Display() // browse files in a child window float reserveHeight = GetItemsLineHeightWithSpacing(); - std::filesystem::path newPwd; bool setNewPwd = false; + ghc::filesystem::path newPwd; bool setNewPwd = false; if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) && (flags_ & ImGuiFileBrowserFlags_EnterNewFilename)) reserveHeight += GetItemsLineHeightWithSpacing(); { @@ -366,7 +433,8 @@ inline void ImGui::FileBrowser::Display() { selectedFilename_ = rsc.name; if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory)) - std::strcpy(inputNameBuf_->data(), selectedFilename_.c_str()); + std::strncpy(inputNameBuf_->data(), selectedFilename_.c_str(), + std::min(std::strlen(selectedFilename_.c_str()), INPUT_NAME_BUF_SIZE)); } } } @@ -422,6 +490,35 @@ inline void ImGui::FileBrowser::Display() ((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) && IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && escIdx >= 0 && IsKeyPressed(escIdx))) CloseCurrentPopup(); + if (m_cascOpenMode) { + if (pwd_ != m_last_pwd_forBuildInfo) { + loadBuildsFromBuildInfo(); + m_last_pwd_forBuildInfo = pwd_; + } + + SameLine(); + ImGui::Text("Select product build:"); + SameLine(); + if (ImGui::BeginCombo("##buildSelect", + currentBuild.c_str())) // The second parameter is the label previewed before opening the combo. + { + if (availableBuilds.empty()) { + availableBuilds.push_back(""); + } + for (int n = 0; n < availableBuilds.size(); n++) + { + bool is_selected = (availableBuilds[n] == currentBuild); + if (ImGui::Selectable(availableBuilds[n].c_str(), is_selected)) { + currentBuild = availableBuilds[n]; + } + + if (is_selected) + ImGui::SetItemDefaultFocus(); // You may set the initial focus when opening the combo (scrolling + for keyboard navigation support) + } + ImGui::EndCombo(); + } + } + if(!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar)) { SameLine(); @@ -442,7 +539,7 @@ inline bool ImGui::FileBrowser::HasSelected() const noexcept return ok_; } -inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd) +inline bool ImGui::FileBrowser::SetPwd(const ghc::filesystem::path &pwd) { try { @@ -458,11 +555,11 @@ inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd) statusStr_ = "last error: unknown"; } - SetPwdUncatched(std::filesystem::current_path()); + SetPwdUncatched(ghc::filesystem::current_path()); return false; } -inline std::filesystem::path ImGui::FileBrowser::GetSelected() const +inline ghc::filesystem::path ImGui::FileBrowser::GetSelected() const { return pwd_ / selectedFilename_; } @@ -480,11 +577,11 @@ inline void ImGui::FileBrowser::SetTypeFilters(const std::vector &t typeFilterIndex_ = 0; } -inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd) +inline void ImGui::FileBrowser::SetPwdUncatched(const ghc::filesystem::path &pwd) { fileRecords_ = { FileRecord{ true, "..", "[D] .." } }; - for(auto &p : std::filesystem::directory_iterator(pwd)) + for(auto &p : ghc::filesystem::directory_iterator(pwd)) { FileRecord rcd; diff --git a/src/ui/imguiLib/groupPanel/groupPanel.cpp b/src/ui/imguiLib/groupPanel/groupPanel.cpp new file mode 100644 index 000000000..fcf1ecfcc --- /dev/null +++ b/src/ui/imguiLib/groupPanel/groupPanel.cpp @@ -0,0 +1,131 @@ +// +// Created by Deamon on 1/9/2021. +// + +#include "groupPanel.h" +#define IMGUI_DEFINE_MATH_OPERATORS +#include "imgui_internal.h" + +static ImVector s_GroupPanelLabelStack; + +void ImGui::BeginGroupPanel(const char* name, const ImVec2& size) +{ + ImGui::BeginGroup(); + + auto cursorPos = ImGui::GetCursorScreenPos(); + auto itemSpacing = ImGui::GetStyle().ItemSpacing; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + + auto frameHeight = ImGui::GetFrameHeight(); + ImGui::BeginGroup(); + + ImVec2 effectiveSize = size; + if (size.x < 0.0f) + effectiveSize.x = ImGui::GetContentRegionAvailWidth(); + else + effectiveSize.x = size.x; + ImGui::Dummy(ImVec2(effectiveSize.x, 0.0f)); + + ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f)); + ImGui::SameLine(0.0f, 0.0f); + ImGui::BeginGroup(); + ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f)); + ImGui::SameLine(0.0f, 0.0f); + ImGui::TextUnformatted(name); + auto labelMin = ImGui::GetItemRectMin(); + auto labelMax = ImGui::GetItemRectMax(); + ImGui::SameLine(0.0f, 0.0f); + ImGui::Dummy(ImVec2(0.0, frameHeight + itemSpacing.y)); + ImGui::BeginGroup(); + + //ImGui::GetWindowDrawList()->AddRect(labelMin, labelMax, IM_COL32(255, 0, 255, 255)); + + ImGui::PopStyleVar(2); + +#if IMGUI_VERSION_NUM >= 17301 + ImGui::GetCurrentWindow()->ContentRegionRect.Max.x -= frameHeight * 0.5f; + ImGui::GetCurrentWindow()->WorkRect.Max.x -= frameHeight * 0.5f; + ImGui::GetCurrentWindow()->InnerRect.Max.x -= frameHeight * 0.5f; +#else + ImGui::GetCurrentWindow()->ContentsRegionRect.Max.x -= frameHeight * 0.5f; +#endif + ImGui::GetCurrentWindow()->Size.x -= frameHeight; + + auto itemWidth = ImGui::CalcItemWidth(); + ImGui::PushItemWidth(ImMax(0.0f, itemWidth - frameHeight)); + + s_GroupPanelLabelStack.push_back(ImRect(labelMin, labelMax)); +} + +void ImGui::EndGroupPanel() +{ + ImGui::PopItemWidth(); + + auto itemSpacing = ImGui::GetStyle().ItemSpacing; + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + + auto frameHeight = ImGui::GetFrameHeight(); + + ImGui::EndGroup(); + + //ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(0, 255, 0, 64), 4.0f); + + ImGui::EndGroup(); + + ImGui::SameLine(0.0f, 0.0f); + ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f)); + ImGui::Dummy(ImVec2(0.0, frameHeight - frameHeight * 0.5f - itemSpacing.y)); + + ImGui::EndGroup(); + + auto itemMin = ImGui::GetItemRectMin(); + auto itemMax = ImGui::GetItemRectMax(); + //ImGui::GetWindowDrawList()->AddRectFilled(itemMin, itemMax, IM_COL32(255, 0, 0, 64), 4.0f); + + auto labelRect = s_GroupPanelLabelStack.back(); + s_GroupPanelLabelStack.pop_back(); + + ImVec2 halfFrame = ImVec2(frameHeight * 0.25f, frameHeight) * 0.5f; + ImRect frameRect = ImRect(itemMin + halfFrame, itemMax - ImVec2(halfFrame.x, 0.0f)); + labelRect.Min.x -= itemSpacing.x; + labelRect.Max.x += itemSpacing.x; + for (int i = 0; i < 4; ++i) + { + switch (i) + { + // left half-plane + case 0: ImGui::PushClipRect(ImVec2(-FLT_MAX, -FLT_MAX), ImVec2(labelRect.Min.x, FLT_MAX), true); break; + // right half-plane + case 1: ImGui::PushClipRect(ImVec2(labelRect.Max.x, -FLT_MAX), ImVec2(FLT_MAX, FLT_MAX), true); break; + // top + case 2: ImGui::PushClipRect(ImVec2(labelRect.Min.x, -FLT_MAX), ImVec2(labelRect.Max.x, labelRect.Min.y), true); break; + // bottom + case 3: ImGui::PushClipRect(ImVec2(labelRect.Min.x, labelRect.Max.y), ImVec2(labelRect.Max.x, FLT_MAX), true); break; + } + + ImGui::GetWindowDrawList()->AddRect( + frameRect.Min, frameRect.Max, + ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Border)), + halfFrame.x); + + ImGui::PopClipRect(); + } + + ImGui::PopStyleVar(2); + +#if IMGUI_VERSION_NUM >= 17301 + ImGui::GetCurrentWindow()->ContentRegionRect.Max.x += frameHeight * 0.5f; + ImGui::GetCurrentWindow()->WorkRect.Max.x += frameHeight * 0.5f; + ImGui::GetCurrentWindow()->InnerRect.Max.x += frameHeight * 0.5f; +#else + ImGui::GetCurrentWindow()->ContentsRegionRect.Max.x += frameHeight * 0.5f; +#endif + ImGui::GetCurrentWindow()->Size.x += frameHeight; + + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + + ImGui::EndGroup(); +} \ No newline at end of file diff --git a/src/ui/imguiLib/groupPanel/groupPanel.h b/src/ui/imguiLib/groupPanel/groupPanel.h new file mode 100644 index 000000000..96c9472cd --- /dev/null +++ b/src/ui/imguiLib/groupPanel/groupPanel.h @@ -0,0 +1,16 @@ +// +// Created by Deamon on 1/9/2021. +// + +#ifndef AWEBWOWVIEWERCPP_GROUPPANEL_H +#define AWEBWOWVIEWERCPP_GROUPPANEL_H + +#include + +namespace ImGui { + void BeginGroupPanel(const char *name, const ImVec2 &size = ImVec2(0.0f, 0.0f)); + + void EndGroupPanel(); +} + +#endif //AWEBWOWVIEWERCPP_GROUPPANEL_H diff --git a/src/ui/imguiLib/imageButton2/imageButton2.cpp b/src/ui/imguiLib/imageButton2/imageButton2.cpp new file mode 100644 index 000000000..579c50ee0 --- /dev/null +++ b/src/ui/imguiLib/imageButton2/imageButton2.cpp @@ -0,0 +1,45 @@ +// +// Created by Deamon on 1/27/2021. +// + +#include "imageButton2.h" +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include + +bool ImGui::ImageButton2(ImTextureID user_texture_id, char *idText, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + // Default to using texture ID as ID. User can still push string/integer prefixes. + // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. + PushID(idText); + const ImGuiID id = window->GetID("#image"); + PopID(); + + const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2); + const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); + ItemSize(bb); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); + window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); + + return pressed; +} \ No newline at end of file diff --git a/src/ui/imguiLib/imageButton2/imageButton2.h b/src/ui/imguiLib/imageButton2/imageButton2.h new file mode 100644 index 000000000..f0c31a6f2 --- /dev/null +++ b/src/ui/imguiLib/imageButton2/imageButton2.h @@ -0,0 +1,16 @@ +// +// Created by Deamon on 1/27/2021. +// + +#ifndef AWEBWOWVIEWERCPP_IMAGEBUTTON2_H +#define AWEBWOWVIEWERCPP_IMAGEBUTTON2_H + +#include + +namespace ImGui { + //The standart imgui button ties to user_texture_id as id. Thus it cant detect clicks, + // when user_texture_id is different in every frame (like when that texture is from framebuffer) + IMGUI_API bool ImageButton2(ImTextureID user_texture_id, char *idText, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding +} + +#endif //AWEBWOWVIEWERCPP_IMAGEBUTTON2_H diff --git a/src/ui/imguiLib/imgui.cpp b/src/ui/imguiLib/imgui.cpp index 9cec56f69..eea4edbe8 100644 --- a/src/ui/imguiLib/imgui.cpp +++ b/src/ui/imguiLib/imgui.cpp @@ -3774,9 +3774,15 @@ void ImGui::Initialize(ImGuiContext* context) ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Window"; ini_handler.TypeHash = ImHashStr("Window"); - ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; - ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; - ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; + ini_handler.ReadOpenFn = [](ImGuiContext* g, ImGuiSettingsHandler* gg, const char* name) -> void* { + return WindowSettingsHandler_ReadOpen(g, gg, name); + }; + ini_handler.ReadLineFn = [](ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line) -> void { + WindowSettingsHandler_ReadLine(ctx, handler, entry, line); + }; + ini_handler.WriteAllFn = [](ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -> void { + WindowSettingsHandler_WriteAll(ctx, handler, buf); + }; g.SettingsHandlers.push_back(ini_handler); } @@ -9357,7 +9363,7 @@ ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) { ImGuiContext& g = *GImGui; const ImGuiID type_hash = ImHashStr(type_name); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + for (int handler_n = 0; handler_n < g.SettingsHandlers.size(); handler_n++) if (g.SettingsHandlers[handler_n].TypeHash == type_hash) return &g.SettingsHandlers[handler_n]; return NULL; @@ -9442,7 +9448,7 @@ const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) g.SettingsDirtyTimer = 0.0f; g.SettingsIniData.Buf.resize(0); g.SettingsIniData.Buf.push_back(0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + for (int handler_n = 0; handler_n < g.SettingsHandlers.size(); handler_n++) { ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; handler->WriteAllFn(&g, handler, &g.SettingsIniData); diff --git a/src/ui/imguiLib/imgui.h b/src/ui/imguiLib/imgui.h index b707cf668..09762fd48 100644 --- a/src/ui/imguiLib/imgui.h +++ b/src/ui/imguiLib/imgui.h @@ -416,6 +416,13 @@ namespace ImGui IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; } IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer + template + IMGUI_API bool RadioButton(const char* label, T* v, T v_button) { + const bool pressed = RadioButton(label, *v == v_button); + if (pressed) + *v = v_button; + return pressed; + } // shortcut to handle the above pattern when value is an integer IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1,0), const char* overlay = NULL); IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses @@ -1429,6 +1436,7 @@ struct ImGuiIO bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). + float uiScale = 1.0f; // Functions IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input diff --git a/src/ui/imguiLib/imguiImpl/imgui_impl_android.cpp b/src/ui/imguiLib/imguiImpl/imgui_impl_android.cpp new file mode 100644 index 000000000..4127210d1 --- /dev/null +++ b/src/ui/imguiLib/imguiImpl/imgui_impl_android.cpp @@ -0,0 +1,196 @@ +// dear imgui: Platform Binding for Android native app +// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) + +// Implemented features: +// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). +// Missing features: +// [ ] Platform: Clipboard support. +// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. +// Important: +// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) +// - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2021-03-04: Initial version. + +#include "imgui.h" +#include "imgui_impl_android.h" +#include +#include +#include +#include +#include +#include +#include + +// Android data +static double g_Time = 0.0; +static ANativeWindow* g_Window; +static char g_LogTag[] = "ImGuiExample"; +static std::map> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. + +int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) +{ + ImGuiIO& io = ImGui::GetIO(); + int32_t event_type = AInputEvent_getType(input_event); + switch (event_type) + { + case AINPUT_EVENT_TYPE_KEY: + { + int32_t event_key_code = AKeyEvent_getKeyCode(input_event); + int32_t event_action = AKeyEvent_getAction(input_event); + int32_t event_meta_state = AKeyEvent_getMetaState(input_event); + + io.KeyCtrl = ((event_meta_state & AMETA_CTRL_ON) != 0); + io.KeyShift = ((event_meta_state & AMETA_SHIFT_ON) != 0); + io.KeyAlt = ((event_meta_state & AMETA_ALT_ON) != 0); + + switch (event_action) + { + // FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer + // goes up from a key. We use a simple key event queue/ and process one event per key per frame in + // ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787 + case AKEY_EVENT_ACTION_DOWN: + case AKEY_EVENT_ACTION_UP: + g_KeyEventQueues[event_key_code].push(event_action); + break; + default: + break; + } + break; + } + case AINPUT_EVENT_TYPE_MOTION: + { + int32_t event_action = AMotionEvent_getAction(input_event); + int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + event_action &= AMOTION_EVENT_ACTION_MASK; + switch (event_action) + { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_UP: + // Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP, + // but we have to process them separately to identify the actual button pressed. This is done below via + // AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback). + if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER) + || (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) + { + io.MouseDown[0] = (event_action == AMOTION_EVENT_ACTION_DOWN) ? true : false; + io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); + auto uiScale = ImGui::GetIO().uiScale; + io.MousePos = ImVec2((float)io.MousePos.x / uiScale, (float)io.MousePos.y / uiScale); + + } + break; + case AMOTION_EVENT_ACTION_BUTTON_PRESS: + case AMOTION_EVENT_ACTION_BUTTON_RELEASE: + { + int32_t button_state = AMotionEvent_getButtonState(input_event); + io.MouseDown[0] = (button_state & AMOTION_EVENT_BUTTON_PRIMARY) ? true : false; + io.MouseDown[1] = (button_state & AMOTION_EVENT_BUTTON_SECONDARY) ? true : false; + io.MouseDown[2] = (button_state & AMOTION_EVENT_BUTTON_TERTIARY) ? true : false; + } + break; + case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse) + case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN + { + io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), + AMotionEvent_getY(input_event, event_pointer_index)); + + auto uiScale = ImGui::GetIO().uiScale; + io.MousePos = ImVec2((float) io.MousePos.x / uiScale, (float) io.MousePos.y / uiScale); + } + break; + case AMOTION_EVENT_ACTION_SCROLL: + io.MouseWheel = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index); + io.MouseWheelH = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index); + break; + default: + break; + } + } + return 1; + default: + break; + } + + return 0; +} + +bool ImGui_ImplAndroid_Init(ANativeWindow* window) +{ + g_Window = window; + g_Time = 0.0; + + // Setup backend capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendPlatformName = "imgui_impl_android"; + + // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. + io.KeyMap[ImGuiKey_Tab] = AKEYCODE_TAB; + io.KeyMap[ImGuiKey_LeftArrow] = AKEYCODE_DPAD_LEFT; // also covers physical keyboard arrow key + io.KeyMap[ImGuiKey_RightArrow] = AKEYCODE_DPAD_RIGHT; // also covers physical keyboard arrow key + io.KeyMap[ImGuiKey_UpArrow] = AKEYCODE_DPAD_UP; // also covers physical keyboard arrow key + io.KeyMap[ImGuiKey_DownArrow] = AKEYCODE_DPAD_DOWN; // also covers physical keyboard arrow key + io.KeyMap[ImGuiKey_PageUp] = AKEYCODE_PAGE_UP; + io.KeyMap[ImGuiKey_PageDown] = AKEYCODE_PAGE_DOWN; + io.KeyMap[ImGuiKey_Home] = AKEYCODE_MOVE_HOME; + io.KeyMap[ImGuiKey_End] = AKEYCODE_MOVE_END; + io.KeyMap[ImGuiKey_Insert] = AKEYCODE_INSERT; + io.KeyMap[ImGuiKey_Delete] = AKEYCODE_FORWARD_DEL; + io.KeyMap[ImGuiKey_Backspace] = AKEYCODE_DEL; + io.KeyMap[ImGuiKey_Space] = AKEYCODE_SPACE; + io.KeyMap[ImGuiKey_Enter] = AKEYCODE_ENTER; + io.KeyMap[ImGuiKey_Escape] = AKEYCODE_ESCAPE; + io.KeyMap[ImGuiKey_KeyPadEnter] = AKEYCODE_NUMPAD_ENTER; + io.KeyMap[ImGuiKey_A] = AKEYCODE_A; + io.KeyMap[ImGuiKey_C] = AKEYCODE_C; + io.KeyMap[ImGuiKey_V] = AKEYCODE_V; + io.KeyMap[ImGuiKey_X] = AKEYCODE_X; + io.KeyMap[ImGuiKey_Y] = AKEYCODE_Y; + io.KeyMap[ImGuiKey_Z] = AKEYCODE_Z; + + return true; +} + +void ImGui_ImplAndroid_Shutdown() +{ +} + +void ImGui_ImplAndroid_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); + + // Process queued key events + // FIXME: This is a workaround for multiple key event actions occurring at once (see above) and can be removed once we use upcoming input queue. + for (auto& key_queue : g_KeyEventQueues) + { + if (key_queue.second.empty()) + continue; + io.KeysDown[key_queue.first] = (key_queue.second.front() == AKEY_EVENT_ACTION_DOWN); + key_queue.second.pop(); + } + + // Setup display size (every frame to accommodate for window resizing) + int32_t window_width = ANativeWindow_getWidth(g_Window); + int32_t window_height = ANativeWindow_getHeight(g_Window); + int display_width = window_width; + int display_height = window_height; + + io.DisplaySize = ImVec2((float)window_width, (float)window_height); + if (window_width > 0 && window_height > 0) + io.DisplayFramebufferScale = ImVec2((float)display_width / window_width, (float)display_height / window_height); + + // Setup time step + struct timespec current_timespec; + clock_gettime(CLOCK_MONOTONIC, ¤t_timespec); + double current_time = (double)(current_timespec.tv_sec) + (current_timespec.tv_nsec / 1000000000.0); + io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); + g_Time = current_time; +} diff --git a/src/ui/imguiLib/imguiImpl/imgui_impl_android.h b/src/ui/imguiLib/imguiImpl/imgui_impl_android.h new file mode 100644 index 000000000..e2f175736 --- /dev/null +++ b/src/ui/imguiLib/imguiImpl/imgui_impl_android.h @@ -0,0 +1,26 @@ +// dear imgui: Platform Binding for Android native app +// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) + +// Implemented features: +// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). +// Missing features: +// [ ] Platform: Clipboard support. +// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. +// Important: +// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) +// - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +#pragma once + +struct ANativeWindow; +struct AInputEvent; + +IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window); +IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event); +IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame(); diff --git a/src/ui/imguiLib/imguiImpl/imgui_impl_glfw.cpp b/src/ui/imguiLib/imguiImpl/imgui_impl_glfw.cpp index a28634e20..f93f03e43 100644 --- a/src/ui/imguiLib/imguiImpl/imgui_impl_glfw.cpp +++ b/src/ui/imguiLib/imguiImpl/imgui_impl_glfw.cpp @@ -254,7 +254,9 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() { double mouse_x, mouse_y; glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + auto uiScale = ImGui::GetIO().uiScale; + + io.MousePos = ImVec2((float)mouse_x / uiScale, (float)mouse_y / uiScale); } } } diff --git a/src/ui/imguiLib/imguiImpl/imgui_impl_opengl3.cpp b/src/ui/imguiLib/imguiImpl/imgui_impl_opengl3.cpp index 4f3334b23..0d95e395c 100644 --- a/src/ui/imguiLib/imguiImpl/imgui_impl_opengl3.cpp +++ b/src/ui/imguiLib/imguiImpl/imgui_impl_opengl3.cpp @@ -164,7 +164,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) glsl_version = "#version 100"; #elif defined(IMGUI_IMPL_OPENGL_ES3) if (glsl_version == NULL) - glsl_version = "#version 300 es"; + glsl_version = "#version 310 es"; #else if (glsl_version == NULL) glsl_version = "#version 130"; diff --git a/src/ui/imguiLib/imgui_internal.h b/src/ui/imguiLib/imgui_internal.h index 738800982..9350cfd9f 100644 --- a/src/ui/imguiLib/imgui_internal.h +++ b/src/ui/imguiLib/imgui_internal.h @@ -776,9 +776,9 @@ struct ImGuiSettingsHandler { const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' ImGuiID TypeHash; // == ImHashStr(TypeName) - void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" - void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry - void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' + std::function ReadOpenFn; // Read: Called when entering into a new ini entry e.g. "[Window][Name]" + std::function ReadLineFn; // Read: Called for every line of text within an ini entry + std::function WriteAllFn; // Write: Output every entries into 'out_buf' void* UserData; ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } @@ -1136,7 +1136,7 @@ struct ImGuiContext bool SettingsLoaded; float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero ImGuiTextBuffer SettingsIniData; // In memory .ini settings - ImVector SettingsHandlers; // List of .ini settings handlers + std::vector SettingsHandlers; // List of .ini settings handlers ImChunkStream SettingsWindows; // ImGuiWindow .ini settings entries // Capture/Logging diff --git a/src/ui/imguiLib/stateSaver/stateSaver.cpp b/src/ui/imguiLib/stateSaver/stateSaver.cpp new file mode 100644 index 000000000..8d72a0700 --- /dev/null +++ b/src/ui/imguiLib/stateSaver/stateSaver.cpp @@ -0,0 +1,34 @@ +#include +#include + +void addIniCallback(ImGuiContext* context, const std::string& sectionName, + std::function readFunction, + std::function writeFunction + ) +{ + ImGuiContext& g = *context; + + // Add .ini handle for UserData type + ImGuiSettingsHandler ini_handler; + ini_handler.TypeName = sectionName.c_str(); + ini_handler.TypeHash = ImHashStr(sectionName.c_str()); + ini_handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) -> void* { + if (std::string("global").compare(name) != 0 ) + return nullptr; + + return reinterpret_cast(1); + }; + ini_handler.ReadLineFn = [readFunction](ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line) -> void { + readFunction(line); + }; + + ini_handler.WriteAllFn = [writeFunction, sectionName](ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -> void { + buf->appendf("[%s][%s]\n", sectionName.c_str(), "global"); + writeFunction(buf); +// buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); +// buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); +// buf->appendf("Collapsed=%d\n", settings->Collapsed); + buf->append("\n"); + }; + g.SettingsHandlers.push_back(ini_handler); +} \ No newline at end of file diff --git a/src/ui/imguiLib/stateSaver/stateSaver.h b/src/ui/imguiLib/stateSaver/stateSaver.h new file mode 100644 index 000000000..3317dcd9e --- /dev/null +++ b/src/ui/imguiLib/stateSaver/stateSaver.h @@ -0,0 +1,14 @@ +// +// Created by Deamon on 8/3/2021. +// + +#ifndef AWEBWOWVIEWERCPP_STATESAVER_H +#define AWEBWOWVIEWERCPP_STATESAVER_H + +#include +void addIniCallback(ImGuiContext* context, const std::string& sectionName, + std::function readFunction, + std::function writeFunction +); + +#endif //AWEBWOWVIEWERCPP_STATESAVER_H diff --git a/tileCutter.py b/tileCutter.py new file mode 100644 index 000000000..97bd13fd6 --- /dev/null +++ b/tileCutter.py @@ -0,0 +1,73 @@ +from PIL import Image +import sys, os, math +from multiprocessing import Pool +from multiprocessing import cpu_count + +maxZoom = 10 + +def pasteIntoResult(result, filename, x, y): + try: + i = Image.open(filename) + result.paste(i, (x, y)) + except Exception, e: + print "could not open %s" % (filename) + # print "-- %s, removing %s" % (e, filename) + # trash_dst = os.path.expanduser("~/.Trash/%s" % filename) + # os.rename(filename, trash_dst) + +def getTargetDirectory(zoom): + if (zoom == maxZoom): + return sys.argv[3] + + return sys.argv[3]+("%d" % (zoom)) + +def getFileNameForZoom(x,y,zoom): + directory = getTargetDirectory(zoom) + + return (directory+"/"+sys.argv[4]+"%d_%d.png") % (x, y) + +class ZoomCreator: + def __init__(self, constX, constZoom): + self.constX = constX + self.constZoom = constZoom + + def __call__(self, constY): + self.createZoomLvl(self.constX, constY, self.constZoom) + + def createZoomLvl(self, x, y, currentZoom): + result = Image.new("RGBA", (2*1024, 2*1024)) + pasteIntoResult(result, getFileNameForZoom(2*x, 2*y, currentZoom+1), 0, 0) + pasteIntoResult(result, getFileNameForZoom(2*x+1, 2*y, currentZoom+1), 1024, 0) + pasteIntoResult(result, getFileNameForZoom(2*x, 2*y+1, currentZoom+1), 0, 1024) + pasteIntoResult(result, getFileNameForZoom(2*x+1, 2*y+1, currentZoom+1), 1024, 1024) + result = result.resize((1024, 1024), Image.LANCZOS) + result.save(getFileNameForZoom(x, y, currentZoom), optimize=True, quality=95) + + +if __name__ == '__main__': + print "cpu_count() = %d" % (cpu_count()) + + maxX = int(sys.argv[1]) + maxY = int(sys.argv[2]) + + print "maxX", maxX + print "maxY", maxY + + for currentZoom in reversed(xrange(1, maxZoom)): + # Create directory if not exists + targetDir = getTargetDirectory(currentZoom) + if not os.path.exists(targetDir): + os.makedirs(targetDir) + + pool = Pool(cpu_count()) + + xRange = xrange(0, int(math.ceil(float(maxX+1) / pow(2.0, maxZoom - currentZoom)))) + yRange = xrange(0, int(math.ceil(float(maxY+1) / pow(2.0, maxZoom - currentZoom)))) + + for x in xRange: + results = pool.map(ZoomCreator(x, currentZoom), yRange) + + pool.close() + pool.join() + + diff --git a/wowViewerLib/3rdparty/tbb b/wowViewerLib/3rdparty/tbb new file mode 160000 index 000000000..214e50bdc --- /dev/null +++ b/wowViewerLib/3rdparty/tbb @@ -0,0 +1 @@ +Subproject commit 214e50bdca046b291f13389946756ef7264a8b90 diff --git a/wowViewerLib/3rdparty/tinygltf b/wowViewerLib/3rdparty/tinygltf new file mode 160000 index 000000000..2c521b343 --- /dev/null +++ b/wowViewerLib/3rdparty/tinygltf @@ -0,0 +1 @@ +Subproject commit 2c521b34327ca8fe893244c854544d60941e7388 diff --git a/wowViewerLib/CMakeLists.txt b/wowViewerLib/CMakeLists.txt index 88c2d2321..f51b9e8cf 100644 --- a/wowViewerLib/CMakeLists.txt +++ b/wowViewerLib/CMakeLists.txt @@ -1,27 +1,46 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.6) project(WoWViewerLib) include(CheckCXXCompilerFlag) -#set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_VERBOSE_MAKEFILE ON) set(LINK_GLEW 1) set(LINK_OGL2 1) set(LINK_OGL3 1) set(LINK_OGL4 0) -set(LINK_VULKAN 0) -set(LINK_OPENMP 1) +set(LINK_EGL 1 PARENT_SCOPE) +set(LINK_EGL 1) +option(LINK_VULKAN "Enable Vulkan" ON) +set(LINK_OPENMP 0) set(ENABLE_SIMD 1) -set(EMSCRIPTEN_SIMD 0) +option(EMSCRIPTEN_SIMD "Enable SIMD for Emscripten " OFF) + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LINK_GLEW 0) +endif() +#MacOS system +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(LINK_OGL2 0) + set(LINK_GLEW 0) +endif() + if (CMAKE_NDK_BUILD) set(LINK_GLEW 0) ENDIF() +if (NOT EGL_PATH) + set(LINK_EGL 0 PARENT_SCOPE) + set(LINK_EGL 0) +endif() + + if (EMSCRIPTEN) message(This is EMSCRIPTEN) set(LINK_GLEW 0) - set(LINK_VULKAN 0) + set(LINK_VULKAN OFF) set(LINK_OPENMP 0) set(LINK_OGL4 0) if (NOT EMSCRIPTEN_SIMD) @@ -32,20 +51,26 @@ ENDIF() if (LINK_OGL2) add_definitions(-DLINK_OGL2) endif() +if (LINK_EGL) + add_definitions(-DLINK_EGL) + add_definitions(-DWITH_GLESv2) +endif() if (LINK_OGL3) add_definitions(-DLINK_OGL3) endif() if (LINK_OGL4) add_definitions(-DLINK_OGL4) endif() -if (LINK_VULKAN) - add_definitions(-DLINK_VULKAN) -endif() +set(My_Vectorize_Compile_Options "") +message("ENABLE_SIMD = ${ENABLE_SIMD}") +SET(TBB_BUILD_SHARED 0) +add_subdirectory(${PROJECT_SOURCE_DIR}/3rdparty/tbb EXCLUDE_FROM_ALL) + option(BUILD_WITHOUT_CULLED_PORTAL_DRAWING "Build without drawing culled portals" ON) if (BUILD_WITHOUT_CULLED_PORTAL_DRAWING MATCHES ON) @@ -55,20 +80,23 @@ endif(BUILD_WITHOUT_CULLED_PORTAL_DRAWING MATCHES ON) #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDODEBUGTHREADS" ) #set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DDODEBUGTHREADS") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC") +#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC") +#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC") if (EMSCRIPTEN) + message("EMSCRIPTEN ENABLE_SIMD = ${ENABLE_SIMD}") + if (ENABLE_SIMD) + set(My_Vectorize_Compile_Options "${My_Vectorize_Compile_Options};-msimd128;-msse2;-msse;-ftree-vectorize") + endif() else() - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-multichar -g -O3 -msse3 -mavx2 -ftree-vectorize" ) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-multichar -g -O3 -msse3 -mavx2 -ftree-vectorize") -endif() + set(My_Vectorize_Compile_Options "${My_Vectorize_Compile_Options};-msse3;-mavx2;-ftree-vectorize") -if (EMSCRIPTEN_SIMD) - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -msimd128") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -msimd128") + if (NOT MSVC) + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-multichar") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-multichar") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -msimd128" ) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -msimd128") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -g -O3" ) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O3") + endif() endif() @@ -85,19 +113,24 @@ set(mathfu_build_benchmarks OFF CACHE BOOL "") set(mathfu_build_tests OFF CACHE BOOL "") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") -add_subdirectory(${PROJECT_SOURCE_DIR}/3rdparty/mathfu) +add_subdirectory(${PROJECT_SOURCE_DIR}/3rdparty/mathfu EXCLUDE_FROM_ALL) -message(${CMAKE_SYSTEM_NAME}) +message(CMAKE_SYSTEM_NAME = ${CMAKE_SYSTEM_NAME}) +string(TOUPPER ${CMAKE_SYSTEM_NAME} CMAKE_SYSTEM_NAME_LOWERCASE) if (LINK_GLEW) if (WIN32) MESSAGE(IT IS WINDOWS!!!!) + set(GLEW_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/3rdparty/glew_win/include) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(GLEW_LIBRARIES ${PROJECT_SOURCE_DIR}/3rdparty/glew_win/lib/Release/x64/glew32.lib ) + set(GLEW_DLL "${PROJECT_SOURCE_DIR}/3rdparty/glew_win/bin/Release/x64/glew32.dll") elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) set(GLEW_LIBRARIES ${PROJECT_SOURCE_DIR}/3rdparty/glew_win/lib/Release/Win32/glew32.lib ) + set(GLEW_DLL "${PROJECT_SOURCE_DIR}/3rdparty/glew_win/bin/Release/Win32/glew32.dll") endif() - else() + elseif(NOT CMAKE_SYSTEM_NAME_LOWERCASE MATCHES "ANDROID") + find_package(GLEW REQUIRED) if (GLEW_FOUND) set(glew_include ${GLEW_INCLUDE_DIRS}) @@ -115,7 +148,6 @@ endif(LINK_GLEW) set(SOURCE_FILES - src/engine/wowScene.h src/engine/shader/ShaderRuntimeData.cpp src/engine/shader/ShaderRuntimeData.h src/engine/opengl/header.h @@ -123,6 +155,10 @@ set(SOURCE_FILES src/engine/camera/firstPersonCamera.h src/engine/camera/firstPersonOrthoCamera.cpp src/engine/camera/firstPersonOrthoCamera.h + src/engine/camera/firstPersonOrthoStaticCamera.cpp + src/engine/camera/firstPersonOrthoStaticCamera.h + src/engine/camera/firstPersonOrthoStaticTopDownCamera.cpp + src/engine/camera/firstPersonOrthoStaticTopDownCamera.h src/include/config.h src/engine/camera/CameraInterface.h src/engine/persistance/header/commonFileStructs.h @@ -163,8 +199,6 @@ set(SOURCE_FILES src/engine/objects/scenes/m2Scene.cpp # src/engine/objects/scenes/creatureScene.cpp # src/engine/objects/scenes/creatureScene.h - src/engine/algorithms/PortalCullingAlgo.cpp - src/engine/algorithms/PortalCullingAlgo.h src/engine/objects/scenes/wmoScene.cpp src/engine/objects/scenes/wmoScene.h src/engine/objects/objectCache.h @@ -187,7 +221,6 @@ set(SOURCE_FILES src/engine/managers/particles/generators/CPlaneGenerator.h src/engine/texture/DxtDecompress.cpp src/engine/texture/DxtDecompress.h - src/engine/androidLogSupport.h src/engine/persistance/texFile.cpp src/engine/persistance/texFile.h src/engine/persistance/header/texFileHeader.h src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.cpp @@ -239,8 +272,18 @@ set(SOURCE_FILES src/engine/DrawStage.h src/include/iostuff.h src/engine/ApiContainer.cpp - src/engine/ApiContainer.h src/engine/CameraMatrices.h src/engine/persistance/header/wdlHeader.h src/engine/camera/m2TiedCamera.cpp src/engine/camera/m2TiedCamera.h) + src/engine/ApiContainer.h + src/engine/CameraMatrices.h + src/engine/persistance/header/wdlHeader.h + src/engine/camera/m2TiedCamera.cpp + src/engine/camera/m2TiedCamera.h + src/engine/persistance/header/M2FileHeader.cpp + src/engine/objects/m2/m2Helpers/CBoneMasterData.cpp + src/engine/objects/m2/m2Helpers/CBoneMasterData.h + src/exporters/IExporter.h + src/engine/objects/iScene.cpp src/engine/algorithms/C3Spline.cpp src/engine/algorithms/C3Spline.h src/engine/algorithms/C3Spline_Bezier3.cpp src/engine/algorithms/C3Spline_Bezier3.h src/include/string_utils.h src/include/string_utlis.cpp) +if (LINK_OGL2) set(OPENGL20_IMPLEMENTATION src/gapi/ogl2.0/textures/GBlpTextureGL20.cpp src/gapi/ogl2.0/textures/GBlpTextureGL20.h @@ -278,6 +321,7 @@ set(OPENGL20_IMPLEMENTATION src/gapi/ogl2.0/shaders/GAdtShaderPermutationGL20.h src/gapi/ogl2.0/GOcclusionQueryGL20.cpp src/gapi/ogl2.0/GOcclusionQueryGL20.h) +endif() set(OPENGL33_IMPLEMENTATION src/gapi/ogl3.3/textures/GBlpTextureGL33.cpp @@ -323,7 +367,11 @@ set(OPENGL33_IMPLEMENTATION src/gapi/ogl3.3/shaders/GSkyConus.cpp src/gapi/ogl3.3/shaders/GFFXgauss4.h src/gapi/ogl3.3/shaders/GFFXGlow.cpp - src/gapi/ogl3.3/shaders/GFFXGlow.h) + src/gapi/ogl3.3/shaders/GFFXGlow.h + src/gapi/ogl3.3/shaders/GWaterShaderGL33.cpp + src/gapi/ogl3.3/shaders/GWaterShaderGL33.h + src/gapi/ogl3.3/shaders/GWaterfallShaderGL33.cpp + src/gapi/ogl3.3/shaders/GWaterfallShaderGL33.h) if (LINK_OGL4) set(OPENGL4x_IMPLEMENTATION src/gapi/ogl4.x/textures/GBlpTextureGL4x.cpp @@ -363,6 +411,7 @@ set(OPENGL4x_IMPLEMENTATION src/gapi/ogl4.x/textures/GBlpTextureGL4x.cpp src/gapi/ogl4.x/GOcclusionQueryGL4x.h src/gapi/ogl4.x/syncronization/GPUFenceGL44.cpp src/gapi/ogl4.x/syncronization/GPUFenceGL44.h src/engine/persistance/header/skelFileHeader.h) endif() +set(Vulkan_INCLUDE_DIR "") if (LINK_VULKAN) set(VULKAN_IMPLEMENTATION src/include/vulkancontext.h @@ -375,7 +424,6 @@ if (LINK_VULKAN) src/gapi/vulkan/shaders/GM2ShaderPermutationVLK.cpp src/gapi/vulkan/shaders/GM2ParticleShaderPermutationVLK.cpp src/gapi/vulkan/shaders/GM2RibbonShaderPermutationVLK.cpp - src/gapi/vulkan/shaders/GWMOWaterShaderVLK.cpp src/gapi/vulkan/shaders/GWMOShaderPermutationVLK.cpp src/gapi/vulkan/meshes/GM2MeshVLK.cpp src/gapi/vulkan/meshes/GMeshVLK.cpp @@ -392,70 +440,122 @@ if (LINK_VULKAN) src/gapi/vulkan/descriptorSets/GDescriptorSet.h src/gapi/vulkan/shaders/GImguiShaderPermutation.cpp src/gapi/vulkan/shaders/GImguiShaderPermutation.h - src/gapi/vulkan/shaders/GAdtWaterShaderPermutation.cpp - src/gapi/vulkan/shaders/GAdtWaterShaderPermutation.h) - include_directories(${Vulkan_INCLUDE_DIR}) + src/gapi/vulkan/shaders/GWaterShaderPermutation.cpp + src/gapi/vulkan/shaders/GWaterShaderPermutation.h + src/gapi/vulkan/shaders/GSkyConusShaderVLK.cpp + src/gapi/vulkan/shaders/GSkyConusShaderVLK.h + src/gapi/vulkan/GFrameBufferVLK.cpp + src/gapi/vulkan/GFrameBufferVLK.h + src/gapi/vulkan/shaders/GFFXgauss4VLK.cpp + src/gapi/vulkan/shaders/GFFXgauss4VLK.h + src/gapi/vulkan/shaders/GFFXGlowVLK.cpp + src/gapi/vulkan/shaders/GFFXGlowVLK.h + src/gapi/vulkan/GRenderPassVLK.cpp + src/gapi/vulkan/GRenderPassVLK.h + src/gapi/vulkan/shaders/GDrawBoundingBoxVLK.cpp + src/gapi/vulkan/shaders/GDrawBoundingBoxVLK.h + src/gapi/vulkan/shaders/GWaterfallShaderVLK.cpp + src/gapi/vulkan/shaders/GWaterfallShaderVLK.h) + set(Vulkan_INCLUDE_DIR $ENV{VULKAN_SDK}/Include) IF(WIN32) IF (NOT Vulkan_FOUND) find_library(Vulkan_LIBRARY NAMES vulkan-1 vulkan PATHS "$ENV{VULKAN_SDK}/lib") IF (Vulkan_LIBRARY) - set(Vulkan_FOUND ON) - MESSAGE("Using bundled Vulkan library version win32") + set(Vulkan_FOUND ON PARENT_SCOPE) + MESSAGE("Using bundled Vulkan library version win32, Vulkan_LIBRARY= ${Vulkan_LIBRARY}") + ELSE() + set(LINK_VULKAN OFF) + set(VULKAN_IMPLEMENTATION "") ENDIF() ENDIF() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_WIN32_KHR") ELSE(WIN32) IF (NOT Vulkan_FOUND) - find_library(Vulkan_LIBRARY NAMES vulkan HINTS "$ENV{VULKAN_SDK}/lib" "${CMAKE_SOURCE_DIR}/libs/vulkan" REQUIRED) + find_library(Vulkan_LIBRARY NAMES vulkan HINTS "$ENV{VULKAN_SDK}/lib" "${CMAKE_SOURCE_DIR}/libs/vulkan") IF (Vulkan_LIBRARY) - set(Vulkan_FOUND ON) + set(Vulkan_FOUND ON PARENT_SCOPE) MESSAGE("Using bundled Vulkan library version non-win32") + ELSE() + set(LINK_VULKAN OFF) + set(VULKAN_IMPLEMENTATION "") ENDIF() ENDIF() find_package(Threads REQUIRED) ENDIF() + + if (LINK_VULKAN) + add_definitions(-DLINK_VULKAN) + endif() endif() -check_cxx_compiler_flag(-std=c++17 HAVE_FLAG_STD_CXX17) -if(HAVE_FLAG_STD_CXX17) - # Have -std=c++17, use it - message("WOWLIB HAVE_FLAG_STD_CXX17 is supported") -# set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++17" ) -# set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++17" ) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++17" ) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++17" ) -else() - check_cxx_compiler_flag(-std=c++1z HAVE_FLAG_STD_CXX1Z) - if(HAVE_FLAG_STD_CXX1Z) -# set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++1z" ) -# set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++1z" ) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++1z") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++1z") +if (NOT MSVC) + check_cxx_compiler_flag(-std=c++17 HAVE_FLAG_STD_CXX17) + if(HAVE_FLAG_STD_CXX17) + # Have -std=c++17, use it + message("WOWLIB HAVE_FLAG_STD_CXX17 is supported") + # set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++17" ) + # set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++17" ) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++17" ) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++17" ) else() - message(ERROR "No supported flags") + check_cxx_compiler_flag(-std=c++1z HAVE_FLAG_STD_CXX1Z) + if(HAVE_FLAG_STD_CXX1Z) + # set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++1z" ) + # set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++1z" ) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++1z") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++1z") + else() + message(ERROR "No supported flags") + endif() + endif() +endif() +if (MSVC) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("/std:c++17" _cpp_17_flag_supported) + message("MSVC Is on") + if (_cpp_17_flag_supported) + message("/std:c++17 is supported") + #target_compile_options(AWebWoWViewerCpp "/std:c++17") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /std:c++17") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /std:c++17") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /std:c++17") + #target_compile_options(AWebWoWViewerCpp /std:c++17) endif() endif() + add_library(WoWViewerLib STATIC ${SOURCE_FILES} ${OPENGL20_IMPLEMENTATION} ${OPENGL33_IMPLEMENTATION} ${OPENGL4x_IMPLEMENTATION} ${VULKAN_IMPLEMENTATION}) -target_compile_options(WoWViewerLib PUBLIC -Werror=return-type) +if (NOT MSVC) + target_compile_options(WoWViewerLib PUBLIC -Werror=return-type) +endif() + +foreach(X IN LISTS My_Vectorize_Compile_Options) + target_compile_options(WoWViewerLib PRIVATE ${X}) +endforeach() target_compile_features(WoWViewerLib PRIVATE cxx_std_17) set_property(TARGET WoWViewerLib PROPERTY CXX_STANDARD 17) if (LINK_OPENMP) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - target_compile_options(WoWViewerLib PRIVATE -fopenmp=libgomp) - target_link_libraries(WoWViewerLib -fopenmp=libgomp) + MESSAGE("OPENMP CLANG ROUTE WAS TAKEN") + target_compile_options(WoWViewerLib PRIVATE -fopenmp=libomp) + target_link_libraries(WoWViewerLib -fopenmp=libomp) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") MESSAGE("OPENMP GNU ROUTE WAS TAKEN") target_compile_options(WoWViewerLib PRIVATE -fopenmp) target_link_libraries(WoWViewerLib -fopenmp) endif() endif() -if (LINK_VULKAN) - target_link_libraries(WoWViewerLib ${Vulkan_LIBRARY}) -endif() target_link_libraries(WoWViewerLib ${GLEW_LIBRARIES}) +target_link_libraries(WoWViewerLib tbb_static) +#target_link_options(WoWViewerLib PUBLIC "SHELL: -lstdc++") + +if (WIN32) + install(FILES ${GLEW_DLL} + DESTINATION ${CMAKE_INSTALL_BINDIR} + ) +endif() if (ENABLE_SIMD) mathfu_configure_flags(WoWViewerLib TRUE) @@ -464,18 +564,22 @@ else() endif() if (LINK_VULKAN) - #Vulkan stuff - add_custom_target( - Shaders - DEPENDS ${SPIRV_BINARY_FILES} - ) - add_dependencies(WoWViewerLib Shaders) - target_include_directories(WoWViewerLib INTERFACE ${Vulkan_INCLUDE_DIR}) + message("Include vulkan include dir ${Vulkan_INCLUDE_DIR}") + target_include_directories(WoWViewerLib PUBLIC ${Vulkan_INCLUDE_DIR}) + target_compile_definitions(WoWViewerLib PUBLIC -DLINK_VULKAN) + target_link_libraries(WoWViewerLib ${Vulkan_LIBRARY}) #Vulkan stuff end else() target_compile_definitions(WoWViewerLib PUBLIC -DSKIP_VULKAN) endif() +if (LINK_EGL) + target_compile_definitions(WoWViewerLib PUBLIC -DWITH_GLESv2) + target_include_directories(WoWViewerLib PUBLIC ${EGL_PATH}/include/) + target_link_libraries(WoWViewerLib ${EGL_PATH}/libGLESv2.lib) + target_link_libraries(WoWViewerLib ${EGL_PATH}/libEGL.lib) +endif() + if (1) add_subdirectory(shaders) diff --git a/wowViewerLib/shaders/CMakeLists.txt b/wowViewerLib/shaders/CMakeLists.txt index a57f50d96..92c8c11ab 100644 --- a/wowViewerLib/shaders/CMakeLists.txt +++ b/wowViewerLib/shaders/CMakeLists.txt @@ -1,4 +1,4 @@ -if (CMAKE_NDK_BUILD MATCHES 1) +if (ANDROID) set(GLSL_TARGET_FOLDER ${ANDROID_ASSETSFOLDER}) else() set(GLSL_TARGET_FOLDER ${CMAKE_BINARY_DIR}) @@ -6,43 +6,76 @@ endif() set(APP_RUN_PREFIX "") if (EMSCRIPTEN) - set(APP_RUN_PREFIX node ) + set(APP_RUN_PREFIX node --experimental-wasm-simd) endif() message(INFO "GLSL_TARGET_FOLDER = ${GLSL_TARGET_FOLDER}") -add_subdirectory(${PROJECT_SOURCE_DIR}/shaders/3rdparty/spirv-cross) -set(BUILD_EXTERNAL OFF) -add_subdirectory(${PROJECT_SOURCE_DIR}/shaders/3rdparty/glsllang) -if(EMSCRIPTEN) - target_link_options(glslangValidator PUBLIC -s NODERAWFS=1) -endif() -set(BUILD_EXTERNAL OFF) - -set(SPIRV_REF_SOURCES - src/spirv/spirv_refl_main.cpp - src/spirv/webGLSLCompiler.cpp - src/spirv/webGLSLCompiler.h - src/spirv/dumpShaderFields.h src/spirv/dumpShaderMetaData.h src/spirv/dumpGLSLShader.h) set(SPIRV_META_FILE ${PROJECT_SOURCE_DIR}/src/engine/shader/ShaderDefinitions.h) -set_target_properties(glslangValidator PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - -add_custom_target(SPIRV_EXTRACT_META ALL) -add_executable(spirv_reflection ${SPIRV_REF_SOURCES}) -set_target_properties(spirv_reflection PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") -target_link_libraries(spirv_reflection spirv-cross-c) -target_include_directories(spirv_reflection PUBLIC ${PROJECT_SOURCE_DIR}/3rdparty/spirv-cross/) -if(EMSCRIPTEN) - target_link_options(spirv_reflection PUBLIC -s NODERAWFS=1) +if(NOT ANDROID) + #GLSL Compiler + add_subdirectory(${PROJECT_SOURCE_DIR}/shaders/3rdparty/spirv-cross EXCLUDE_FROM_ALL) + set(BUILD_EXTERNAL OFF) + add_subdirectory(${PROJECT_SOURCE_DIR}/shaders/3rdparty/glsllang EXCLUDE_FROM_ALL) + if(EMSCRIPTEN) + target_link_options(glslangValidator PUBLIC -s NODERAWFS=1) + endif() + set(BUILD_EXTERNAL OFF) + set_target_properties(glslangValidator PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + + #SPIRV_Reflections + set(SPIRV_REF_SOURCES + src/spirv/spirv_refl_main.cpp + src/spirv/webGLSLCompiler.cpp + src/spirv/webGLSLCompiler.h + src/spirv/dumpShaderFields.h src/spirv/dumpShaderMetaData.h src/spirv/dumpGLSLShader.h) + + add_executable(spirv_reflection ${SPIRV_REF_SOURCES}) + set_target_properties(spirv_reflection PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") + target_link_libraries(spirv_reflection spirv-cross-c) + target_include_directories(spirv_reflection PUBLIC ${PROJECT_SOURCE_DIR}/3rdparty/spirv-cross/) + if(EMSCRIPTEN) + target_link_options(spirv_reflection PUBLIC -s NODERAWFS=1) + endif() + set_target_properties(spirv_reflection PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +# set(PATH_TO_NATIVE_GLSL_VALIDATOR "${CMAKE_BINARY_DIR}/glslangValidator${CMAKE_EXECUTABLE_SUFFIX}") +# set(PATH_TO_NATIVE_SPIRV_REFLECTION "${CMAKE_BINARY_DIR}/spirv_reflection${CMAKE_EXECUTABLE_SUFFIX}") + set(PATH_TO_NATIVE_GLSL_VALIDATOR $) + set(PATH_TO_NATIVE_SPIRV_REFLECTION $) +else() + MESSAGE(WARNING "PYTHON_EXECUTABLE = ${PYTHON_EXECUTABLE}") + MESSAGE(WARNING "PATH_TO_NATIVE_GLSL_VALIDATOR = ${PATH_TO_NATIVE_GLSL_VALIDATOR}") + MESSAGE(WARNING "PATH_TO_NATIVE_SPIRV_REFLECTION = ${PATH_TO_NATIVE_SPIRV_REFLECTION}") + if (NOT EXISTS "${PATH_TO_NATIVE_GLSL_VALIDATOR}") + MESSAGE(ERROR "Native precompiled GLSL Validator is required for Android") + return() + endif() + if (NOT EXISTS "${PATH_TO_NATIVE_SPIRV_REFLECTION}") + MESSAGE(ERROR "Native precompiled Spirv Reflection is required for Android") + return() + endif() + + add_custom_target(glslangValidator) + add_custom_target(spirv_reflection) endif() -set_target_properties(spirv_reflection PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -macro(configure_filesVLK srcDir destDir destDirGL20 destDirGL33 ) + +macro(configure_filesVLK srcDir srcCommonDir destDir destDirGL20 destDirGL33 ) message(STATUS "Configuring directory ${destDir}") make_directory(${destDir}) file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*.frag ${srcDir}/*.vert) + file(GLOB commonFilesRelative RELATIVE ${srcCommonDir} ${srcCommonDir}/*.glsl) + + foreach(commonFileRelative ${commonFilesRelative}) + set(commonFileFullPath ${srcCommonDir}/${commonFileRelative}) + if(NOT IS_DIRECTORY ${commonFileFullPath}) + list(APPEND commonFiles ${commonFileFullPath}) + endif() + endforeach(commonFileRelative) + foreach(templateFile ${templateFiles}) set(srcTemplatePath ${srcDir}/${templateFile}) if(NOT IS_DIRECTORY ${srcTemplatePath}) @@ -55,8 +88,8 @@ macro(configure_filesVLK srcDir destDir destDirGL20 destDirGL33 ) add_custom_command( OUTPUT ${SPIRV_NonOpt} COMMAND ${CMAKE_COMMAND} -E make_directory "${destDir}" - COMMAND ${APP_RUN_PREFIX} ${CMAKE_BINARY_DIR}/glslangValidator${CMAKE_EXECUTABLE_SUFFIX} -V ${srcTemplatePath} -o ${SPIRV_NonOpt} - DEPENDS ${srcTemplatePath} glslangValidator) + COMMAND ${APP_RUN_PREFIX} ${PATH_TO_NATIVE_GLSL_VALIDATOR} -V ${srcTemplatePath} -o ${SPIRV_NonOpt} + DEPENDS ${srcTemplatePath} glslangValidator ${commonFiles}) # if(SPIRV_OPT_APP) # add_custom_command( @@ -78,11 +111,6 @@ macro(configure_filesVLK srcDir destDir destDirGL20 destDirGL33 ) # DEPENDS ${srcTemplatePath} ${SPIRV_NonOpt}) # endif() - add_custom_command( - TARGET SPIRV_EXTRACT_META - PRE_BUILD - DEPENDS ${SPIRV_NonOpt}) - list(APPEND SPIRV_BINARY_FILES_NON_OPT ${SPIRV_NonOpt}) list(APPEND SPIRV_BINARY_FILES_OPT ${SPIRVOpt}) @@ -93,11 +121,28 @@ macro(configure_filesVLK srcDir destDir destDirGL20 destDirGL33 ) get_filename_component(GLSL20_FILE_FULLPATH "${GLSL20_FILE}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") + SET(OGL3_GLSL_OPTION "") + if (ANDROID) + SET(OGL3_GLSL_OPTION "-glslEs310") + elseif(EMSCRIPTEN) + SET(OGL3_GLSL_OPTION "-glslEs300") + else() + SET(OGL3_GLSL_OPTION "-glsl330") + endif() + + SET(OGL2_GLSL_OPTION "") + if (ANDROID OR EMSCRIPTEN) + SET(OGL2_GLSL_OPTION "-glsl100") + else() + SET(OGL2_GLSL_OPTION "-glsl100") + endif() + + add_custom_command( OUTPUT ${GLSL20_FILE} COMMAND ${CMAKE_COMMAND} -E make_directory "${destDirGL20}" - COMMAND ${APP_RUN_PREFIX} ${CMAKE_BINARY_DIR}/spirv_reflection${CMAKE_EXECUTABLE_SUFFIX} -glsl100 ${SPIRV_NonOpt} > ${GLSL20_FILE_FULLPATH} - DEPENDS ${SPIRV_NonOpt}) + COMMAND ${APP_RUN_PREFIX} ${PATH_TO_NATIVE_SPIRV_REFLECTION} ${OGL2_GLSL_OPTION} ${SPIRV_NonOpt} > ${GLSL20_FILE_FULLPATH} + DEPENDS ${SPIRV_NonOpt} spirv_reflection) list(APPEND GLSL20Files ${GLSL20_FILE}) @@ -105,11 +150,13 @@ macro(configure_filesVLK srcDir destDir destDirGL20 destDirGL33 ) get_filename_component(GLSL30_FILE_FULLPATH "${GLSL33_FILE}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") + + add_custom_command( OUTPUT ${GLSL33_FILE} COMMAND ${CMAKE_COMMAND} -E make_directory "${destDirGL33}" - COMMAND ${APP_RUN_PREFIX} ${CMAKE_BINARY_DIR}/spirv_reflection${CMAKE_EXECUTABLE_SUFFIX} -glsl330 ${SPIRV_NonOpt} > ${GLSL30_FILE_FULLPATH} - DEPENDS ${SPIRV_NonOpt}) + COMMAND ${APP_RUN_PREFIX} ${PATH_TO_NATIVE_SPIRV_REFLECTION} ${OGL3_GLSL_OPTION} ${SPIRV_NonOpt} > ${GLSL30_FILE_FULLPATH} + DEPENDS ${SPIRV_NonOpt} spirv_reflection) list(APPEND GLSL30Files ${GLSL33_FILE}) @@ -123,21 +170,30 @@ macro(configure_filesVLK srcDir destDir destDirGL20 destDirGL33 ) # endif() endmacro(configure_filesVLK) -configure_filesVLK(${PROJECT_SOURCE_DIR}/shaders/glsl/vulkan ${GLSL_TARGET_FOLDER}/spirv - ${GLSL_TARGET_FOLDER}/glsl/glsl20/ ${GLSL_TARGET_FOLDER}/glsl/glsl3.3/) +configure_filesVLK( + ${PROJECT_SOURCE_DIR}/shaders/glsl/vulkan + ${PROJECT_SOURCE_DIR}/shaders/glsl/common + ${GLSL_TARGET_FOLDER}/spirv + ${GLSL_TARGET_FOLDER}/glsl/glsl20/ + ${GLSL_TARGET_FOLDER}/glsl/glsl3.3/) add_custom_command( - TARGET SPIRV_EXTRACT_META - COMMAND ${APP_RUN_PREFIX} ${CMAKE_BINARY_DIR}/spirv_reflection${CMAKE_EXECUTABLE_SUFFIX} -sf ${SPIRV_BINARY_FILES} > ${SPIRV_META_FILE} - DEPENDS ${SPIRV_BINARY_FILES_NON_OPT} - VERBATIM) + OUTPUT ${SPIRV_META_FILE} + COMMAND ${APP_RUN_PREFIX} ${PATH_TO_NATIVE_SPIRV_REFLECTION} -sf ${SPIRV_BINARY_FILES} > ${SPIRV_META_FILE} + DEPENDS ${SPIRV_BINARY_FILES} spirv_reflection glslangValidator ShadersVulkanNonOpt +) + +add_custom_target(SPIRV_EXTRACT_META DEPENDS ${SPIRV_META_FILE}) -add_dependencies(SPIRV_EXTRACT_META spirv_reflection glslangValidator ${SPIRV_NonOpt}) add_custom_target( ShadersGLSL20 DEPENDS ${GLSL20Files} ) +add_custom_target( + ShadersVulkanNonOpt + DEPENDS ${SPIRV_BINARY_FILES_NON_OPT} +) add_dependencies(ShadersGLSL20 spirv_reflection) @@ -151,4 +207,23 @@ add_custom_target( GenerateForWebgl DEPENDS ${GLSL20Files} DEPENDS ${GLSL30Files} -) \ No newline at end of file +) + +#Add shaders on install +if (NOT EMSCRIPTEN) + if(LINK_OGL2) + install(FILES ${GLSL20Files} + DESTINATION ${CMAKE_INSTALL_BINDIR}/glsl/glsl20/ + ) + endif() + if(LINK_OGL3) + install(FILES ${GLSL30Files} + DESTINATION ${CMAKE_INSTALL_BINDIR}/glsl/glsl3.3/ + ) + endif() + if(LINK_VULKAN) + install(FILES ${SPIRV_BINARY_FILES_NON_OPT} + DESTINATION ${CMAKE_INSTALL_BINDIR}/spirv + ) + endif() +endif() \ No newline at end of file diff --git a/wowViewerLib/shaders/glsl/common/commonFogFunctions.glsl b/wowViewerLib/shaders/glsl/common/commonFogFunctions.glsl index 6ffafd81f..52ffe8d78 100644 --- a/wowViewerLib/shaders/glsl/common/commonFogFunctions.glsl +++ b/wowViewerLib/shaders/glsl/common/commonFogFunctions.glsl @@ -9,7 +9,52 @@ struct PSFog vec4 sunPercentage; }; -vec3 makeFog(const in PSFog fogData, in vec3 final, in vec3 vertexInViewSpace, in vec3 sunDirInViewSpace) { +const vec3 fogColors[14] = vec3[14]( + vec3(0.0, 0.0, 0.0), //0 GxBlend_Opaque + vec3(0.0, 0.0, 0.0), //1 GxBlend_AlphaKey + vec3(0.0, 0.0, 0.0), //2 GxBlend_Alpha + vec3(0.0, 0.0, 0.0), //3 GxBlend_Add + vec3(1.0, 1.0, 1.0), //4 GxBlend_Mod + vec3(0.5, 0.5, 0.5), //5 GxBlend_Mod2x + vec3(0.5, 0.5, 0.5), //6 GxBlend_ModAdd + vec3(0.0, 0.0, 0.0), //7 GxBlend_InvSrcAlphaAdd + vec3(0.0, 0.0, 0.0), //8 GxBlend_InvSrcAlphaOpaque + vec3(0.5, 0.5, 0.5), //9 GxBlend_SrcAlphaOpaque + vec3(0.0, 0.0, 0.0), //10 GxBlend_NoAlphaAdd + vec3(0.0, 0.0, 0.0), //11 GxBlend_ConstantAlpha + vec3(0.0, 0.0, 0.0), //12 GxBlend_Screen + vec3(0.0, 0.0, 0.0) //13 GxBlend_BlendAdd +); +const float fogMix[14] = float[14]( + 0.0, //0 GxBlend_Opaque + 0.0, //1 GxBlend_AlphaKey + 0.0, //2 GxBlend_Alpha + 1.0, //3 GxBlend_Add + 1.0, //4 GxBlend_Mod + 1.0, //5 GxBlend_Mod2x + 0.0, //6 GxBlend_ModAdd + 0.0, //7 GxBlend_InvSrcAlphaAdd + 0.0, //8 GxBlend_InvSrcAlphaOpaque + 0.0, //9 GxBlend_SrcAlphaOpaque + 1.0, //10 GxBlend_NoAlphaAdd + 0.0, //11 GxBlend_ConstantAlpha + 0.0, //12 GxBlend_Screen + 1.0 //13 GxBlend_BlendAdd +); + + + +vec3 validateFogColor(in vec3 fogColor, int blendMode) { + return mix(fogColor, fogColors[blendMode], fogMix[blendMode]); +} + +vec4 makeFog(const in PSFog fogData, in vec4 final, in vec3 vertexInViewSpace, in vec3 sunDirInViewSpace, in int blendMode) { +// //The best solution so far is to not apply fog to blendmode GxBlend_BlendAdd +// if (blendMode == 13) +// return final; + + + vec4 l_densityParams = fogData.densityParams; vec4 l_heightPlane = fogData.heightPlane; vec4 l_color_and_heightRate = fogData.color_and_heightRate; @@ -27,17 +72,23 @@ vec3 makeFog(const in PSFog fogData, in vec3 final, in vec3 vertexInViewSpace, i float heightFog = clamp((height * l_color_and_heightRate.w), 0, 1); float finalFog = mix(expFog, expFogHeight, heightFog); float endFadeFog = clamp((1.42857146 * (1.0 - (vLength / end))), 0, 1); - vec3 endColor = l_heightDensity_and_endColor.yzw; + + float alpha = 1.0; + if (blendMode == 13) { + alpha = min(finalFog, endFadeFog); + } + + vec3 endColor = validateFogColor(l_heightDensity_and_endColor.yzw, blendMode); vec4 l_heightColor_and_endFogDistance = fogData.heightColor_and_endFogDistance; float end2 = (vLength / l_heightColor_and_endFogDistance.w); float end2_cube = (end2 * (end2 * end2)); - vec3 heightColor = mix(l_heightColor_and_endFogDistance.xyz, endColor, vec3(clamp(end2, 0, 1))); - vec3 fogFinal = mix(l_color_and_heightRate.xyz, endColor, vec3(clamp(end2_cube, 0, 1))); + vec3 heightColor = mix(validateFogColor(l_heightColor_and_endFogDistance.xyz, blendMode), endColor, vec3(clamp(end2, 0, 1))); + vec3 fogFinal = mix(validateFogColor(l_color_and_heightRate.xyz, blendMode), endColor, vec3(clamp(end2_cube, 0, 1))); fogFinal = mix(fogFinal, heightColor, vec3(heightFog)); float nDotSun = dot(normalize(vertexInViewSpace), sunDirInViewSpace.xyz); - vec3 sunColor = mix(fogFinal, fogData.sunAngle_and_sunColor.yzw, vec3(fogData.sunPercentage.x)); + vec3 sunColor = mix(fogFinal, validateFogColor(fogData.sunAngle_and_sunColor.yzw, blendMode), vec3(fogData.sunPercentage.x)); nDotSun = clamp((nDotSun - fogData.sunAngle_and_sunColor.x), 0, 1); if ((nDotSun > 0.0)) { @@ -47,6 +98,6 @@ vec3 makeFog(const in PSFog fogData, in vec3 final, in vec3 vertexInViewSpace, i fogFinal = mix(fogFinal, final.xyz, vec3(min(finalFog, endFadeFog))); - return fogFinal; + return vec4(fogFinal, final.a * alpha); } diff --git a/wowViewerLib/shaders/glsl/common/commonLightFunctions.glsl b/wowViewerLib/shaders/glsl/common/commonLightFunctions.glsl index 31b6755c5..6d0b3ea17 100644 --- a/wowViewerLib/shaders/glsl/common/commonLightFunctions.glsl +++ b/wowViewerLib/shaders/glsl/common/commonLightFunctions.glsl @@ -4,6 +4,7 @@ struct SceneExteriorLight { vec4 uExteriorGroundAmbientColor; vec4 uExteriorDirectColor; vec4 uExteriorDirectColorDir; + vec4 adtSpecMult; }; @@ -27,7 +28,8 @@ vec3 calcLight( float interiorExteriorBlend, readonly SceneWideParams sceneParams, readonly InteriorLightParam intLight, - readonly vec3 accumLight, const vec3 precomputedLight, const vec3 specular) { + readonly vec3 accumLight, const vec3 precomputedLight, const vec3 specular, + readonly const vec3 emissive) { vec3 currColor; vec3 localDiffuse = accumLight; @@ -83,8 +85,39 @@ vec3 calcLight( //Specular term vec3 specTerm = specular; //Emission term - vec3 emTerm = vec3(0,0,0); + vec3 emTerm = emissive; return sqrt(gammaDiffTerm*gammaDiffTerm + linearDiffTerm) + specTerm + emTerm; + + +// vec3 normalDirection = normalize(vNormal); +// +// vec3 viewDirection = normalize(_WorldSpaceCameraPos - vec3(position)); +// vec3 lightDirection; +// float attenuation; +// +// if (0.0 == _WorldSpaceLightPos0.w) // directional light? +// { +// attenuation = 1.0; // no attenuation +// lightDirection = normalize(vec3(_WorldSpaceLightPos0)); +// } +// else // point or spot light +// { +// vec3 vertexToLightSource = +// vec3(_WorldSpaceLightPos0 - position); +// float distance = length(vertexToLightSource); +// attenuation = 1.0 / distance; // linear attenuation +// lightDirection = normalize(vertexToLightSource); +// } +// +// vec4 fragmentColor = vec4(0.0, 0.0, 0.0, 0.0); +// if (dot(normalDirection, lightDirection) > 0.0 +// // light source on the right side? +// && attenuation * pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), _Shininess) > 0.5) +// // more than half highlight intensity? +// { +// fragmentColor = vec4(_LightColor0.rgb, 1.0) * _SpecColor; +// } + } \ No newline at end of file diff --git a/wowViewerLib/shaders/glsl/vulkan/adtLodShader.frag b/wowViewerLib/shaders/glsl/vulkan/adtLodShader.frag index 66558a8b9..340f8bd44 100644 --- a/wowViewerLib/shaders/glsl/vulkan/adtLodShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/adtLodShader.frag @@ -5,6 +5,7 @@ #include "../common/commonLightFunctions.glsl" precision highp float; +precision highp int; layout(location = 0) in vec2 vChunkCoords; layout(location = 1) in vec3 vPosition; diff --git a/wowViewerLib/shaders/glsl/vulkan/adtLodShader.vert b/wowViewerLib/shaders/glsl/vulkan/adtLodShader.vert index b81069e5a..3f751ddd8 100644 --- a/wowViewerLib/shaders/glsl/vulkan/adtLodShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/adtLodShader.vert @@ -2,6 +2,9 @@ #extension GL_GOOGLE_include_directive: require +precision highp float; +precision highp int; + #include "../common/commonLightFunctions.glsl" layout(location = 0) in float aHeight; diff --git a/wowViewerLib/shaders/glsl/vulkan/adtShader.frag b/wowViewerLib/shaders/glsl/vulkan/adtShader.frag index d2a75229e..f2c5e7d59 100644 --- a/wowViewerLib/shaders/glsl/vulkan/adtShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/adtShader.frag @@ -3,6 +3,7 @@ #extension GL_GOOGLE_include_directive: require precision highp float; +precision highp int; #include "../common/commonLightFunctions.glsl" #include "../common/commonFogFunctions.glsl" @@ -29,8 +30,8 @@ layout(std140, set=0, binding=0) uniform sceneWideBlockVSPS { }; layout(std140, set=0, binding=3) uniform modelWideBlockPS { - vec4 uFogStartAndFogEnd; - vec4 uFogColor; + ivec4 uUseHeightMixFormula; + }; layout(std140, set=0, binding=4) uniform meshWideBlockPS { @@ -46,6 +47,10 @@ const InteriorLightParam intLight = { vec4(0,0,0,1) }; +vec4 mixTextures(vec4 tex0, vec4 tex1, float alpha) { + return (alpha*(tex1-tex0)+tex0); +} + void main() { vec2 vTexCoord = vChunkCoords; const float threshold = 1.5; @@ -58,34 +63,44 @@ void main() { vec2 tcLayer2 = (animationMat[2]*vec4(vTexCoord, 0, 1)).xy; vec2 tcLayer3 = (animationMat[3]*vec4(vTexCoord, 0, 1)).xy; - float minusAlphaBlendSum = (1.0 - clamp(dot(alphaBlend, vec3(1.0)), 0.0, 1.0)); - vec4 weightsVector = vec4(minusAlphaBlendSum, alphaBlend); - float weightedTexture_x = (minusAlphaBlendSum * ((texture(uLayerHeight0, tcLayer0).w * uHeightScale[0]) + uHeightOffset[0])); - float weightedTexture_y = (weightsVector.y * ((texture(uLayerHeight1, tcLayer1).w * uHeightScale[1]) + uHeightOffset[1])); - float weightedTexture_z = (weightsVector.z * ((texture(uLayerHeight2, tcLayer2).w * uHeightScale[2]) + uHeightOffset[2])); - float weightedTexture_w = (weightsVector.w * ((texture(uLayerHeight3, tcLayer3).w * uHeightScale[3]) + uHeightOffset[3])); - vec4 weights = vec4(weightedTexture_x, weightedTexture_y, weightedTexture_z, weightedTexture_w); - vec4 weights_temp = (weights * (vec4(1.0) - clamp((vec4(max(max(weightedTexture_x, weightedTexture_y), max(weightedTexture_z, weightedTexture_w))) - weights), 0.0, 1.0))); - vec4 weightsNormalized = (weights_temp / vec4(dot(vec4(1.0), weights_temp))); - - - vec4 weightedLayer_0 = (texture(uLayer0, tcLayer0) * weightsNormalized.x); - vec3 matDiffuse_0 = weightedLayer_0.xyz; - float specBlend_0 = weightedLayer_0.w; - - vec4 weightedLayer_1 = (texture(uLayer1, tcLayer1) * weightsNormalized.y); - vec3 matDiffuse_1 = (matDiffuse_0 + weightedLayer_1.xyz); - float specBlend_1 = (specBlend_0 + weightedLayer_1.w); - - vec4 weightedLayer_2 = (texture(uLayer2, tcLayer1) * weightsNormalized.z); - vec3 matDiffuse_2 = (matDiffuse_1 + weightedLayer_2.xyz); - float specBlend_2 = (specBlend_1 + weightedLayer_2.w); - - vec4 weightedLayer_3 = (texture(uLayer3, tcLayer1) * weightsNormalized.w); - vec3 matDiffuse_3 = (matDiffuse_2 + weightedLayer_3.xyz); - float specBlend_3 = (specBlend_2 + weightedLayer_3.w); - - vec4 final = vec4(matDiffuse_3, specBlend_3); + vec4 final; + if (uUseHeightMixFormula.r > 0) { + float minusAlphaBlendSum = (1.0 - clamp(dot(alphaBlend, vec3(1.0)), 0.0, 1.0)); + vec4 weightsVector = vec4(minusAlphaBlendSum, alphaBlend); + float weightedTexture_x = (minusAlphaBlendSum * ((texture(uLayerHeight0, tcLayer0).w * uHeightScale[0]) + uHeightOffset[0])); + float weightedTexture_y = (weightsVector.y * ((texture(uLayerHeight1, tcLayer1).w * uHeightScale[1]) + uHeightOffset[1])); + float weightedTexture_z = (weightsVector.z * ((texture(uLayerHeight2, tcLayer2).w * uHeightScale[2]) + uHeightOffset[2])); + float weightedTexture_w = (weightsVector.w * ((texture(uLayerHeight3, tcLayer3).w * uHeightScale[3]) + uHeightOffset[3])); + vec4 weights = vec4(weightedTexture_x, weightedTexture_y, weightedTexture_z, weightedTexture_w); + vec4 weights_temp = (weights * (vec4(1.0) - clamp((vec4(max(max(weightedTexture_x, weightedTexture_y), max(weightedTexture_z, weightedTexture_w))) - weights), 0.0, 1.0))); + vec4 weightsNormalized = (weights_temp / vec4(dot(vec4(1.0), weights_temp))); + + + vec4 weightedLayer_0 = (texture(uLayer0, tcLayer0) * weightsNormalized.x); + vec3 matDiffuse_0 = weightedLayer_0.xyz; + float specBlend_0 = weightedLayer_0.w; + + vec4 weightedLayer_1 = (texture(uLayer1, tcLayer1) * weightsNormalized.y); + vec3 matDiffuse_1 = (matDiffuse_0 + weightedLayer_1.xyz); + float specBlend_1 = (specBlend_0 + weightedLayer_1.w); + + vec4 weightedLayer_2 = (texture(uLayer2, tcLayer2) * weightsNormalized.z); + vec3 matDiffuse_2 = (matDiffuse_1 + weightedLayer_2.xyz); + float specBlend_2 = (specBlend_1 + weightedLayer_2.w); + + vec4 weightedLayer_3 = (texture(uLayer3, tcLayer3) * weightsNormalized.w); + vec3 matDiffuse_3 = (matDiffuse_2 + weightedLayer_3.xyz); + float specBlend_3 = (specBlend_2 + weightedLayer_3.w); + + final = vec4(matDiffuse_3, specBlend_3); + } else { + vec4 tex1 = texture(uLayer0, tcLayer0).rgba; + vec4 tex2 = texture(uLayer1, tcLayer1).rgba; + vec4 tex3 = texture(uLayer2, tcLayer2).rgba; + vec4 tex4 = texture(uLayer3, tcLayer3).rgba; + + final = mixTextures(mixTextures(mixTextures(tex1, tex2, alphaBlend.r), tex3, alphaBlend.g), tex4, alphaBlend.b); + } vec3 matDiffuse = final.rgb * 2.0 * vColor.rgb; @@ -100,7 +115,8 @@ void main() { intLight, vVertexLighting.rgb, /* accumLight */ vec3(0.0), /*precomputedLight*/ - vec3(0.0) /* specular */ + vec3(0.0), /* specular */ + vec3(0.0) /* emissive */ ), 1.0 ); @@ -109,10 +125,10 @@ void main() { float specBlend = final.a; vec3 halfVec = -(normalize((scene.extLight.uExteriorDirectColorDir.xyz + normalize(vPosition)))); vec3 lSpecular = ((scene.extLight.uExteriorDirectColor.xyz * pow(max(0.0, dot(halfVec, vNormal)), 20.0))); - vec3 specTerm = (vec3(specBlend) * lSpecular); + vec3 specTerm = (vec3(specBlend) * lSpecular) * scene.extLight.adtSpecMult.x; finalColor.rgb += specTerm; - finalColor.rgb = makeFog(fogData, finalColor.rgb, vPosition.xyz, scene.extLight.uExteriorDirectColorDir.xyz); + finalColor = makeFog(fogData, finalColor, vPosition.xyz, scene.extLight.uExteriorDirectColorDir.xyz, 0); finalColor.a = 1.0; outColor = finalColor; diff --git a/wowViewerLib/shaders/glsl/vulkan/adtShader.vert b/wowViewerLib/shaders/glsl/vulkan/adtShader.vert index a7291b832..72266831e 100644 --- a/wowViewerLib/shaders/glsl/vulkan/adtShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/adtShader.vert @@ -2,6 +2,9 @@ #extension GL_GOOGLE_include_directive: require +precision highp float; +precision highp int; + #include "../common/commonLightFunctions.glsl" #include "../common/commonFogFunctions.glsl" diff --git a/wowViewerLib/shaders/glsl/vulkan/adtWater.frag b/wowViewerLib/shaders/glsl/vulkan/adtWater.frag deleted file mode 100644 index b7670b0bc..000000000 --- a/wowViewerLib/shaders/glsl/vulkan/adtWater.frag +++ /dev/null @@ -1,39 +0,0 @@ -#version 450 - -#extension GL_GOOGLE_include_directive: require - -#include "../common/commonLightFunctions.glsl" - -precision highp float; - -layout(location=0) in vec3 vPosition; -layout(location=1) in vec2 vTextCoords; - - - -layout(location=0) out vec4 outputColor; - -layout(set=1,binding=5) uniform sampler2D uTexture; - -//Individual meshes -layout(std140, binding=4) uniform meshWideBlockPS { - //ivec4 waterTypeV; - vec4 color; -}; - -void main() { -// int waterType = int(waterTypeV.x); -// if (waterType == 13) { // LIQUID_WMO_Water -// outputColor = vec4(0.0, 0, 0.3, 0.5); -// } else if (waterType == 14) { //LIQUID_WMO_Ocean -// outputColor = vec4(0, 0, 0.8, 0.8); -// } else if (waterType == 19) { //LIQUID_WMO_Magma -// outputColor = vec4(0.3, 0, 0, 0.5); -// } else if (waterType == 20) { //LIQUID_WMO_Slime -// outputColor = vec4(0.0, 0.5, 0, 0.5); -// } else { -// outputColor = vec4(0.5, 0.5, 0.5, 0.5); -// } - - outputColor = vec4(color.rgb*texture(uTexture, vTextCoords).rgb, 0.7); -} diff --git a/wowViewerLib/shaders/glsl/vulkan/adtWater.vert b/wowViewerLib/shaders/glsl/vulkan/adtWater.vert deleted file mode 100644 index 655e10319..000000000 --- a/wowViewerLib/shaders/glsl/vulkan/adtWater.vert +++ /dev/null @@ -1,31 +0,0 @@ -#version 450 - -#extension GL_GOOGLE_include_directive: require - -#include "../common/commonLightFunctions.glsl" - -layout(location=0) in vec4 aPositionTransp; -layout(location=1) in vec2 aTexCoord; - - -layout(std140, set=0, binding=0) uniform sceneWideBlockVSPS { - SceneWideParams scene; -}; - -//out vec2 vTexCoord; -layout(location=0) out vec3 vPosition; -layout(location=1) out vec2 vTextCoords; - -void main() { - vec4 aPositionVec4 = vec4(aPositionTransp.xyz, 1); - mat4 cameraMatrix = scene.uLookAtMat; - - vec4 cameraPoint = cameraMatrix * aPositionVec4; - - vTextCoords = aPositionVec4.xy / (1600.0/3.0/16); - - gl_Position = scene.uPMatrix * cameraPoint; -// vTexCoord = aTexCoord; - vPosition = cameraPoint.xyz; - -} diff --git a/wowViewerLib/shaders/glsl/vulkan/drawBBShader.frag b/wowViewerLib/shaders/glsl/vulkan/drawBBShader.frag index 60e1651af..e8b6bdb71 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawBBShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/drawBBShader.frag @@ -1,8 +1,15 @@ #version 450 +#extension GL_GOOGLE_include_directive: require + precision highp float; +precision highp int; + +#include "../common/commonLightFunctions.glsl" +#include "../common/commonFogFunctions.glsl" + -layout(std140, binding=0) uniform modelWideBlockVS { +layout(std140, set=0, binding=1) uniform modelWideBlockVS { mat4 uPlacementMat; vec4 uBBScale; diff --git a/wowViewerLib/shaders/glsl/vulkan/drawBBShader.vert b/wowViewerLib/shaders/glsl/vulkan/drawBBShader.vert index bc2ef13c4..0e8498491 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawBBShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/drawBBShader.vert @@ -1,15 +1,23 @@ #version 450 +#extension GL_GOOGLE_include_directive: require + +precision highp float; +precision highp int; + +#include "../common/commonLightFunctions.glsl" +#include "../common/commonFogFunctions.glsl" + /* vertex shader code */ layout(location = 0) in vec3 aPosition; -layout(std140, binding=0) uniform sceneWideBlockVSPS { - mat4 uLookAtMat; - mat4 uPMatrix; +layout(std140, set=0, binding=0) uniform sceneWideBlockVSPS { + SceneWideParams scene; + PSFog fogData; }; // Whole model -layout(std140, binding=1) uniform modelWideBlockVS { +layout(std140, set=0, binding=1) uniform modelWideBlockVS { mat4 uPlacementMat; vec4 uBBScale; @@ -24,5 +32,5 @@ void main() { aPosition.z*uBBScale.z + uBBCenter.z, 1); - gl_Position = uPMatrix * uLookAtMat * uPlacementMat * worldPoint; + gl_Position = scene.uPMatrix * scene.uLookAtMat * uPlacementMat * worldPoint; } \ No newline at end of file diff --git a/wowViewerLib/shaders/glsl/vulkan/drawDepthShader.frag b/wowViewerLib/shaders/glsl/vulkan/drawDepthShader.frag index 23d2c36d8..f8d0d5d8f 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawDepthShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/drawDepthShader.frag @@ -1,6 +1,8 @@ #version 450 precision highp float; +precision highp int; + layout(binding=3) uniform sampler2D diffuse; layout(std140, binding=2) uniform meshWideBlockPS { diff --git a/wowViewerLib/shaders/glsl/vulkan/drawFrustumShader.frag b/wowViewerLib/shaders/glsl/vulkan/drawFrustumShader.frag index 7b1730bc5..ce8634219 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawFrustumShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/drawFrustumShader.frag @@ -1,9 +1,10 @@ #version 450 precision highp float; +precision highp int; layout(std140, binding=2) uniform meshWideBlockPS { - uniform vec3 uColor; + vec3 uColor; }; layout(location = 0) out vec4 fragColor; diff --git a/wowViewerLib/shaders/glsl/vulkan/drawFrustumShader.vert b/wowViewerLib/shaders/glsl/vulkan/drawFrustumShader.vert index 10fc13b98..ded4a8220 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawFrustumShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/drawFrustumShader.vert @@ -1,5 +1,8 @@ #version 450 +precision highp float; +precision highp int; + /* vertex shader code */ layout(location = 0) in vec3 aPosition; diff --git a/wowViewerLib/shaders/glsl/vulkan/drawLinesShader.frag b/wowViewerLib/shaders/glsl/vulkan/drawLinesShader.frag index c8a28be93..e9d2f9bce 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawLinesShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/drawLinesShader.frag @@ -1,9 +1,10 @@ #version 450 precision highp float; +precision highp int; layout(std140, binding=1) uniform modelWideBlockPS { -uniform vec3 uColor; + vec3 uColor; }; layout(location = 0) out vec4 fragColor; diff --git a/wowViewerLib/shaders/glsl/vulkan/drawLinesShader.vert b/wowViewerLib/shaders/glsl/vulkan/drawLinesShader.vert index c0d1e6f38..ae290ad39 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawLinesShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/drawLinesShader.vert @@ -1,5 +1,8 @@ #version 450 +precision highp float; +precision highp int; + /* vertex shader code */ layout(location = 0) in vec2 aPosition; diff --git a/wowViewerLib/shaders/glsl/vulkan/drawPoints.frag b/wowViewerLib/shaders/glsl/vulkan/drawPoints.frag index b2eea3f01..3cdad8b6c 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawPoints.frag +++ b/wowViewerLib/shaders/glsl/vulkan/drawPoints.frag @@ -1,6 +1,7 @@ #version 450 precision highp float; +precision highp int; layout(location = 0) in vec4 vPos; diff --git a/wowViewerLib/shaders/glsl/vulkan/drawPoints.vert b/wowViewerLib/shaders/glsl/vulkan/drawPoints.vert index 9f162fc8e..920a640a4 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawPoints.vert +++ b/wowViewerLib/shaders/glsl/vulkan/drawPoints.vert @@ -1,5 +1,8 @@ #version 450 +precision highp float; +precision highp int; + /* vertex shader code */ layout(location = 0) in vec3 aPosition; diff --git a/wowViewerLib/shaders/glsl/vulkan/drawPortalShader.frag b/wowViewerLib/shaders/glsl/vulkan/drawPortalShader.frag index 0a439ef20..e967f262e 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawPortalShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/drawPortalShader.frag @@ -1,10 +1,11 @@ #version 450 precision highp float; +precision highp int; //Individual mesh -layout(std140, binding=1) uniform modelWideBlockVS { - uniform vec4 uColor; +layout(std140, set=0, binding=4) uniform meshWideBlockPS { + vec4 uColor; }; layout(location = 0) out vec4 fragColor; diff --git a/wowViewerLib/shaders/glsl/vulkan/drawPortalShader.vert b/wowViewerLib/shaders/glsl/vulkan/drawPortalShader.vert index 69be9f519..d8705404b 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawPortalShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/drawPortalShader.vert @@ -1,5 +1,8 @@ #version 450 +precision highp float; +precision highp int; + /* vertex shader code */ layout(location = 0) in vec3 aPosition; @@ -9,13 +12,8 @@ layout(std140, binding=0) uniform sceneWideBlockVSPS { mat4 uPMatrix; }; -layout(std140, binding=1) uniform modelWideBlockVS { - mat4 uPlacementMat; -}; - - void main() { vec4 worldPoint = vec4(aPosition.xyz, 1); - gl_Position = uPMatrix * uLookAtMat * uPlacementMat * worldPoint; + gl_Position = uPMatrix * uLookAtMat * worldPoint; } \ No newline at end of file diff --git a/wowViewerLib/shaders/glsl/vulkan/drawQuad.vert b/wowViewerLib/shaders/glsl/vulkan/drawQuad.vert index bb485199f..e5aa19760 100644 --- a/wowViewerLib/shaders/glsl/vulkan/drawQuad.vert +++ b/wowViewerLib/shaders/glsl/vulkan/drawQuad.vert @@ -1,17 +1,22 @@ #version 450 +precision highp float; +precision highp int; + layout (location = 0) in vec2 position; layout(std140, binding=2) uniform meshWideBlockVS { - float uWidth; - float uHeight; - float uX; - float uY; + vec4 uWidth_uHeight_uX_uY; }; layout(location = 0) out vec2 texCoord; void main() { + float uWidth = uWidth_uHeight_uX_uY.x; + float uHeight = uWidth_uHeight_uX_uY.y; + float uX = uWidth_uHeight_uX_uY.z; + float uY = uWidth_uHeight_uX_uY.w; + //texCoord = texture; texCoord = position.xy * 0.5 + 0.5; diff --git a/wowViewerLib/shaders/glsl/vulkan/ffxgauss4.frag b/wowViewerLib/shaders/glsl/vulkan/ffxgauss4.frag index 58b555ec4..a37e3d628 100644 --- a/wowViewerLib/shaders/glsl/vulkan/ffxgauss4.frag +++ b/wowViewerLib/shaders/glsl/vulkan/ffxgauss4.frag @@ -1,4 +1,7 @@ -#version 420 +#version 450 + +precision highp float; +precision highp int; layout(location = 0) in vec2 texCoord; layout(location = 0) out vec4 out_result; diff --git a/wowViewerLib/shaders/glsl/vulkan/ffxglow.frag b/wowViewerLib/shaders/glsl/vulkan/ffxglow.frag index 3c6a0e201..808b5602e 100644 --- a/wowViewerLib/shaders/glsl/vulkan/ffxglow.frag +++ b/wowViewerLib/shaders/glsl/vulkan/ffxglow.frag @@ -1,6 +1,9 @@ -#version 420 +#version 450 -layout(location = 1) in vec2 texCoord; +precision highp float; +precision highp int; + +layout(location = 0) in vec2 texCoord; layout(std140, binding=4) uniform meshWideBlockPS { vec4 blurAmount; diff --git a/wowViewerLib/shaders/glsl/vulkan/imguiShader.frag b/wowViewerLib/shaders/glsl/vulkan/imguiShader.frag index 94af42f69..f3549a2d7 100644 --- a/wowViewerLib/shaders/glsl/vulkan/imguiShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/imguiShader.frag @@ -1,6 +1,7 @@ #version 450 precision highp float; +precision highp int; layout(location=0) in vec2 Frag_UV; layout(location=1) in vec4 Frag_Color; diff --git a/wowViewerLib/shaders/glsl/vulkan/imguiShader.vert b/wowViewerLib/shaders/glsl/vulkan/imguiShader.vert index ec1c020d2..1daa3a78e 100644 --- a/wowViewerLib/shaders/glsl/vulkan/imguiShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/imguiShader.vert @@ -1,11 +1,15 @@ #version 450 +precision highp float; +precision highp int; + layout (location = 0) in vec2 Position; layout (location = 1) in vec2 UV; layout (location = 2) in vec4 Color; layout(std140, binding=1) uniform modelWideBlockVS { - uniform mat4 ProjMtx; + mat4 ProjMtx; + vec4 uiScale; }; layout(location=0) out vec2 Frag_UV; @@ -14,5 +18,6 @@ layout(location=1) out vec4 Frag_Color; void main() { Frag_UV = UV; Frag_Color = Color; - gl_Position = ProjMtx * vec4(Position.xy,0,1); + gl_Position = ProjMtx * vec4(Position.xy * uiScale[0],0,1); +// gl_Position.xyz/= uiScale[0]; } \ No newline at end of file diff --git a/wowViewerLib/shaders/glsl/vulkan/m2ParticleShader.frag b/wowViewerLib/shaders/glsl/vulkan/m2ParticleShader.frag index ac6cb4ff1..34a5e34e4 100644 --- a/wowViewerLib/shaders/glsl/vulkan/m2ParticleShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/m2ParticleShader.frag @@ -3,6 +3,8 @@ #extension GL_GOOGLE_include_directive: require precision highp float; +precision highp int; + layout(location = 0) in vec3 vPosition; layout(location = 1) in vec4 vColor; layout(location = 2) in vec2 vTexcoord0; @@ -20,7 +22,7 @@ layout(std140, binding=0) uniform sceneWideBlockVSPS { //Individual meshes layout(std140, binding=4) uniform meshWideBlockPS { vec4 uAlphaTestv; - ivec4 uPixelShaderv; + ivec4 uPixelShaderBlendModev; }; layout(set=1,binding=5) uniform sampler2D uTexture; @@ -40,7 +42,7 @@ void main() { discard; vec4 finalColor = vec4((tex * vColor ).rgb, tex.a*vColor.a ); - int uNonOptPixelShader = uPixelShaderv.x; + int uNonOptPixelShader = uPixelShaderBlendModev.x; if (uNonOptPixelShader == 0) { //particle_mod vec3 matDiffuse = vColor.xyz * tex.rgb; @@ -70,7 +72,15 @@ void main() { vec3 matDiffuse = vColor.xyz * textureMod.rgb; finalColor = vec4(matDiffuse.rgb, opacity); } else if (uNonOptPixelShader == 4) { //Refraction - //TODO:! + float t0_973 = tex.x; + float t1_978 = tex2.y; + float t2_983 = tex3.z; + float textureMod_986 = (((t0_973 * t1_978) * t2_983) * 4.0); + float depthScale_991 = (1.0 - clamp((vPosition.z * 0.00999999978), 0, 1)); + float textureMod_992 = (textureMod_986 * depthScale_991); + float height_995 = (textureMod_992 * vColor.x); + float alpha_997 = (textureMod_992 * vColor.w); + finalColor = vec4(height_995, 0.0, 0.0, alpha_997); } if(finalColor.a < uAlphaTest) @@ -85,7 +95,7 @@ void main() { // .xyz; vec3 sunDir =scene.extLight.uExteriorDirectColorDir.xyz; - finalColor.rgb = makeFog(fogData, finalColor.rgb, vPosition.xyz, sunDir.xyz); + finalColor = makeFog(fogData, finalColor, vPosition.xyz, sunDir.xyz, uPixelShaderBlendModev.y); outputColor.rgba = finalColor ; } diff --git a/wowViewerLib/shaders/glsl/vulkan/m2ParticleShader.vert b/wowViewerLib/shaders/glsl/vulkan/m2ParticleShader.vert index 8bc859b25..dc3f247aa 100644 --- a/wowViewerLib/shaders/glsl/vulkan/m2ParticleShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/m2ParticleShader.vert @@ -1,7 +1,10 @@ #version 450 #extension GL_GOOGLE_include_directive: require + precision highp float; +precision highp int; + layout(location = 0) in vec3 aPosition; layout(location = 1) in vec4 aColor; layout(location = 2) in vec2 aTexcoord0; diff --git a/wowViewerLib/shaders/glsl/vulkan/m2Shader.frag b/wowViewerLib/shaders/glsl/vulkan/m2Shader.frag index bd32c1fc5..7f03decac 100644 --- a/wowViewerLib/shaders/glsl/vulkan/m2Shader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/m2Shader.frag @@ -6,8 +6,8 @@ #define MAX_MATRIX_NUM 220 #endif - precision highp float; +precision highp int; #include "../common/commonLightFunctions.glsl" #include "../common/commonFogFunctions.glsl" @@ -48,8 +48,10 @@ layout(std140, set=0, binding=3) uniform modelWideBlockPS { //Individual meshes layout(std140, set=0, binding=4) uniform meshWideBlockPS { - ivec4 PixelShader_UnFogged_IsAffectedByLight; + ivec4 PixelShader_UnFogged_IsAffectedByLight_blendMode; vec4 uFogColorAndAlphaTest; + vec4 uTexSampleAlpha; + vec4 uPcColor; }; @@ -88,10 +90,8 @@ void main() { vec4 finalColor = vec4(0); vec4 meshResColor = vDiffuseColor; -// if(meshResColor.a < uAlphaTest) -// discard; vec3 accumLight; - if ((PixelShader_UnFogged_IsAffectedByLight.z == 1)) { + if ((PixelShader_UnFogged_IsAffectedByLight_blendMode.z == 1)) { vec3 vPos3 = vPosition.xyz; vec3 vNormal3 = normalize(vNormal.xyz); vec3 lightColor = vec3(0.0); @@ -129,176 +129,143 @@ void main() { genericParams[0] = vec4( 1.0, 1.0, 1.0, 1.0 ); genericParams[1] = vec4( 1.0, 1.0, 1.0, 1.0 ); genericParams[2] = vec4( 1.0, 1.0, 1.0, 1.0 ); - - int uPixelShader = PixelShader_UnFogged_IsAffectedByLight.x; - - - if ( uPixelShader == 0 ) {//Combiners_Opaque - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 1 ) {//Combiners_Mod - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = tex.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - - } else if ( uPixelShader == 2 ) {//Combiners_Opaque_Mod - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb; - opacity = tex2.a * vDiffuseColor.a; - opacity = tex2.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 3 ) {//Combiners_Opaque_Mod2x - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb * 2.000000; - opacity = tex2.a * 2.000000 * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 4 ) {//Combiners_Opaque_Mod2xNA - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb * 2.000000; - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 5 ) {//Combiners_Opaque_Opaque - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb; - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 6 ) {//Combiners_Mod_Mod - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb; - opacity = tex.a * tex2.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 7 ) {//Combiners_Mod_Mod2x - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb * 2.000000; - opacity = tex.a * tex2.a * 2.000000 * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 8 ) {//Combiners_Mod_Add - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = (tex.a + tex2.a) * vDiffuseColor.a; + bool canDiscard = false; + float discardAlpha = 1.0; + + int uPixelShader = PixelShader_UnFogged_IsAffectedByLight_blendMode.x; + + if ( uPixelShader == 0 ) { //Combiners_Opaque + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + } else if ( uPixelShader == 1 ) { //Combiners_Mod + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + discardAlpha = tex.a; + canDiscard = true; + } else if ( uPixelShader == 2 ) { //Combiners_Opaque_Mod + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb; + discardAlpha = tex2.a; + canDiscard = true; + } else if ( uPixelShader == 3 ) { //Combiners_Opaque_Mod2x + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb * 2.0; + discardAlpha = tex2.a * 2.0; + canDiscard = true; + } else if ( uPixelShader == 4 ) { //Combiners_Opaque_Mod2xNA + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb * 2.0; + } else if ( uPixelShader == 5 ) { //Combiners_Opaque_Opaque + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb; + } else if ( uPixelShader == 6 ) { //Combiners_Mod_Mod + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb; + discardAlpha = tex.a * tex2.a; + canDiscard = true; + } else if ( uPixelShader == 7 ) { //Combiners_Mod_Mod2x + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb * 2.0; + discardAlpha = tex.a * tex2.a * 2.0; + canDiscard = true; + } else if ( uPixelShader == 8 ) { //Combiners_Mod_Add + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + discardAlpha = tex.a + tex2.a; + canDiscard = true; specular = tex2.rgb; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 9 ) {//Combiners_Mod_Mod2xNA - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb * 2.000000; - opacity = tex.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 10 ) {//Combiners_Mod_AddNA - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = tex.a * vDiffuseColor.a; + } else if ( uPixelShader == 9 ) { //Combiners_Mod_Mod2xNA + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb * 2.0; + discardAlpha = tex.a; + canDiscard = true; + } else if ( uPixelShader == 10 ) { //Combiners_Mod_AddNA + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + discardAlpha = tex.a; + canDiscard = true; specular = tex2.rgb; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 11 ) {//Combiners_Mod_Opaque - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb; - opacity = tex.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 12 ) {//Combiners_Opaque_Mod2xNA_Alpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb * tex2.rgb * 2.000000, tex.rgb, vec3(tex.a)); - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 13 ) {//Combiners_Opaque_AddAlpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; + } else if ( uPixelShader == 11 ) { //Combiners_Mod_Opaque + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb; + discardAlpha = tex.a; + canDiscard = true; + } else if ( uPixelShader == 12 ) { //Combiners_Opaque_Mod2xNA_Alpha + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb * tex2.rgb * 2.0, tex.rgb, vec3(tex.a)); + } else if ( uPixelShader == 13 ) { //Combiners_Opaque_AddAlpha + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; specular = tex2.rgb * tex2.a; - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 14 ) {//Combiners_Opaque_AddAlpha_Alpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - specular = tex2.rgb * tex2.a * (1.000000 - tex.a); - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 15 ) {//Combiners_Opaque_Mod2xNA_Alpha_Add - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb * tex2.rgb * 2.000000, tex.rgb, vec3(tex.a)); - specular = tex3.rgb * tex3.a * genericParams[0].b; - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 16 ) {//Combiners_Mod_AddAlpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = tex.a * vDiffuseColor.a; + } else if ( uPixelShader == 14 ) { //Combiners_Opaque_AddAlpha_Alpha + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + specular = tex2.rgb * tex2.a * (1.0 - tex.a); + } else if ( uPixelShader == 15 ) { //Combiners_Opaque_Mod2xNA_Alpha_Add + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb * tex2.rgb * 2.0, tex.rgb, vec3(tex.a)); + specular = tex3.rgb * tex3.a * uTexSampleAlpha.b; + } else if ( uPixelShader == 16 ) { //Combiners_Mod_AddAlpha + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + discardAlpha = tex.a; + canDiscard = true; specular = tex2.rgb * tex2.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 17 ) {//Combiners_Mod_AddAlpha_Alpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = (tex.a + tex2.a * (0.300000 * tex2.r + 0.590000 * tex2.g + 0.110000 * tex2.b)) * vDiffuseColor.a; - specular = tex2.rgb * tex2.a * (1.000000 - tex.a); - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 18 ) {//Combiners_Opaque_Alpha_Alpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(mix(tex.rgb, tex2.rgb, vec3(tex2.a)), tex.rgb, vec3(tex.a)); - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 19 ) {//Combiners_Opaque_Mod2xNA_Alpha_3s - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb * tex2.rgb * 2.000000, tex3.rgb, vec3(tex3.a)); - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 20 ) {//Combiners_Opaque_AddAlpha_Wgt - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - specular = tex2.rgb * tex2.a * genericParams[0].g; - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 21 ) {//Combiners_Mod_Add_Alpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = (tex.a + tex2.a) * vDiffuseColor.a; - specular = tex2.rgb * (1.000000 - tex.a); - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 22 ) {//Combiners_Opaque_ModNA_Alpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb * tex2.rgb, tex.rgb, vec3(tex.a)); - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 23 ) {//Combiners_Mod_AddAlpha_Wgt - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = tex.a * vDiffuseColor.a; - specular = tex2.rgb * tex2.a * genericParams[0].g; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 24 ) {//Combiners_Opaque_Mod_Add_Wgt - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb, tex2.rgb, vec3(tex2.a)); - specular = tex.rgb * tex.a * genericParams[0].r; - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 25 ) {//Combiners_Opaque_Mod2xNA_Alpha_UnshAlpha - float glowOpacity = clamp((tex3.a * genericParams[0].z), 0.0, 1.0); - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb * tex2.rgb * 2.000000, tex.rgb, vec3(tex.a)) * (1.000000 - glowOpacity); + } else if ( uPixelShader == 17 ) { //Combiners_Mod_AddAlpha_Alpha + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + discardAlpha = tex.a + tex2.a * (0.3 * tex2.r + 0.59 * tex2.g + 0.11 * tex2.b); + canDiscard = true; + specular = tex2.rgb * tex2.a * (1.0 - tex.a); + } else if ( uPixelShader == 18 ) { //Combiners_Opaque_Alpha_Alpha + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(mix(tex.rgb, tex2.rgb, vec3(tex2.a)), tex.rgb, vec3(tex.a)); + } else if ( uPixelShader == 19 ) { //Combiners_Opaque_Mod2xNA_Alpha_3s + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb * tex2.rgb * 2.0, tex3.rgb, vec3(tex3.a)); + } else if ( uPixelShader == 20 ) { //Combiners_Opaque_AddAlpha_Wgt + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + specular = tex2.rgb * tex2.a * uTexSampleAlpha.g; + } else if ( uPixelShader == 21 ) { //Combiners_Mod_Add_Alpha + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + discardAlpha = tex.a + tex2.a; + canDiscard = true; + specular = tex2.rgb * (1.0 - tex.a); + } else if ( uPixelShader == 22 ) { //Combiners_Opaque_ModNA_Alpha + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb * tex2.rgb, tex.rgb, vec3(tex.a)); + } else if ( uPixelShader == 23 ) { //Combiners_Mod_AddAlpha_Wgt + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + discardAlpha = tex.a; + canDiscard = true; + specular = tex2.rgb * tex2.a * uTexSampleAlpha.g; + } else if ( uPixelShader == 24 ) { //Combiners_Opaque_Mod_Add_Wgt + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb, tex2.rgb, vec3(tex2.a)); + specular = tex.rgb * tex.a * uTexSampleAlpha.r; + } else if ( uPixelShader == 25 ) { //Combiners_Opaque_Mod2xNA_Alpha_UnshAlpha + float glowOpacity = clamp(tex3.a * uTexSampleAlpha.b, 0.0, 1.0); + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb * tex2.rgb * 2.0, tex.rgb, vec3(tex.a)) * (1.0 - glowOpacity); specular = tex3.rgb * glowOpacity; - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 26 ) {//Combiners_Mod_Dual_Crossfade - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].g, 0.000000, 1.000000))), tex3WithTextCoord1, vec4(clamp(genericParams[0].b, 0.000000, 1.000000))).rgb; - opacity = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].g, 0.000000, 1.000000))), tex3WithTextCoord1, vec4(clamp(genericParams[0].b, 0.000000, 1.000000))).a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 27 ) {//Combiners_Opaque_Mod2xNA_Alpha_Alpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(mix(tex.rgb * tex2.rgb * 2.000000, tex3.rgb, vec3(tex3.a)), tex.rgb, vec3(tex.a)); - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 28 ) {//Combiners_Mod_Masked_Dual_Crossfade - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].g, 0.000000, 1.000000))), tex3WithTextCoord1, vec4(clamp(genericParams[0].b, 0.000000, 1.000000))).rgb; - opacity = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(genericParams[0].g, 0.000000, 1.000000))), tex3WithTextCoord1, vec4(clamp(genericParams[0].b, 0.000000, 1.000000))).a * tex4WithTextCoord2.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 29 ) {//Combiners_Opaque_Alpha - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb, tex2.rgb, vec3(tex2.a)); - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 30 ) {//Guild - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb * mix(genericParams[0].rgb, tex2.rgb * genericParams[1].rgb, vec3(tex2.a)), tex3.rgb * genericParams[2].rgb, vec3(tex3.a)); - opacity = tex.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 31 ) {//Guild_NoBorder - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * mix(genericParams[0].rgb, tex2.rgb * genericParams[1].rgb, vec3(tex2.a)); - opacity = tex.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 32 ) {//Guild_Opaque - matDiffuse = vDiffuseColor.rgb * 2.000000 * mix(tex.rgb * mix(genericParams[0].rgb, tex2.rgb * genericParams[1].rgb, vec3(tex2.a)), tex3.rgb * genericParams[2].rgb, vec3(tex3.a)); - opacity = vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 33 ) {//Combiners_Mod_Depth - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb; - opacity = tex.a * vDiffuseColor.a * visParams.r; - finalOpacity = opacity * visParams.r; + } else if ( uPixelShader == 26 ) { //Combiners_Mod_Dual_Crossfade + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(uTexSampleAlpha.g, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(uTexSampleAlpha.b, 0.0, 1.0))).rgb; + discardAlpha = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(uTexSampleAlpha.g, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(uTexSampleAlpha.b, 0.0, 1.0))).a; + canDiscard = true; + } else if ( uPixelShader == 27 ) { //Combiners_Opaque_Mod2xNA_Alpha_Alpha + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(mix(tex.rgb * tex2.rgb * 2.0, tex3.rgb, vec3(tex3.a)), tex.rgb, vec3(tex.a)); + } else if ( uPixelShader == 28 ) { //Combiners_Mod_Masked_Dual_Crossfade + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(mix(tex, tex2WithTextCoord1, vec4(clamp(uTexSampleAlpha.g, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(uTexSampleAlpha.b, 0.0, 1.0))).rgb; + discardAlpha = mix(mix(tex, tex2WithTextCoord1, vec4(clamp(uTexSampleAlpha.g, 0.0, 1.0))), tex3WithTextCoord1, vec4(clamp(uTexSampleAlpha.b, 0.0, 1.0))).a * tex4WithTextCoord2.a; + canDiscard = true; + } else if ( uPixelShader == 29 ) { //Combiners_Opaque_Alpha + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb, tex2.rgb, vec3(tex2.a)); + } else if ( uPixelShader == 30 ) { //Guild + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb * mix(genericParams[0].rgb, tex2.rgb * genericParams[1].rgb, vec3(tex2.a)), tex3.rgb * genericParams[2].rgb, vec3(tex3.a)); + discardAlpha = tex.a; + canDiscard = true; + } else if ( uPixelShader == 31 ) { //Guild_NoBorder + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * mix(genericParams[0].rgb, tex2.rgb * genericParams[1].rgb, vec3(tex2.a)); + discardAlpha = tex.a; + canDiscard = true; + } else if ( uPixelShader == 32 ) { //Guild_Opaque + matDiffuse = vDiffuseColor.rgb * 2.0 * mix(tex.rgb * mix(genericParams[0].rgb, tex2.rgb * genericParams[1].rgb, vec3(tex2.a)), tex3.rgb * genericParams[2].rgb, vec3(tex3.a)); + } else if ( uPixelShader == 33 ) { //Combiners_Mod_Depth + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb; + discardAlpha = tex.a; + canDiscard = true; } else if ( uPixelShader == 34 ) { //Illum - finalColor = vec4(1.0,1.0,1.0, 1.0); - - //Unusued - } else if ( uPixelShader == 35 ) {//Combiners_Mod_Mod_Mod_Const - matDiffuse = vDiffuseColor.rgb * 2.000000 * (tex * tex2 * tex3 * genericParams[0]).rgb; - opacity = (tex * tex2 * tex3 * genericParams[0]).a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - } else if ( uPixelShader == 36 ) {//unk shader combiner - matDiffuse = vDiffuseColor.rgb * 2.000000 * tex.rgb * tex2.rgb; - opacity = tex.a * tex2.a * vDiffuseColor.a; - finalOpacity = opacity * visParams.r; - /* - WOTLK DEPRECATED SHADERS! - */ + discardAlpha = tex.a; + canDiscard = true; + } else if ( uPixelShader == 35 ) { //Combiners_Mod_Mod_Mod_Const + matDiffuse = vDiffuseColor.rgb * 2.0 * (tex * tex2 * tex3 * genericParams[0]).rgb; + discardAlpha = (tex * tex2 * tex3 * genericParams[0]).a; + canDiscard = true; + } else if ( uPixelShader == 36 ) { //unk shader combiner + matDiffuse = vDiffuseColor.rgb * 2.0 * tex.rgb * tex2.rgb; + discardAlpha = tex.a * tex2.a; + canDiscard = true; + + /* + WOTLK DEPRECATED SHADERS! + */ /* } else if (uPixelShader == -1) { // Combiners_Decal finalColor.rgb = (meshResColor.rgb - tex.rgb) * meshResColor.a + tex.rgb; @@ -325,25 +292,39 @@ void main() { */ } + int blendMode = PixelShader_UnFogged_IsAffectedByLight_blendMode.w; + + if (blendMode == 13) { + finalOpacity = discardAlpha * vDiffuseColor.a; + } else if (blendMode == 1) { + finalOpacity = vDiffuseColor.a; + if (canDiscard && discardAlpha < 0.501960814) + discard; + + finalOpacity = vDiffuseColor.a; + } else if (blendMode == 0) { + finalOpacity = vDiffuseColor.a; + } else { + finalOpacity = discardAlpha * vDiffuseColor.a; + } + finalColor = vec4( calcLight( matDiffuse, vNormal, - PixelShader_UnFogged_IsAffectedByLight.z > 0, + PixelShader_UnFogged_IsAffectedByLight_blendMode.z > 0, interiorExteriorBlend.x, scene, intLight, accumLight, vec3(0.0), - specular + specular, + vec3(0.0) /* emissive */ ) , finalOpacity ); - if(finalColor.a < uFogColorAndAlphaTest.w) - discard; - - int uUnFogged = PixelShader_UnFogged_IsAffectedByLight.y; + int uUnFogged = PixelShader_UnFogged_IsAffectedByLight_blendMode.y; if (uUnFogged == 0) { vec3 sunDir = mix( @@ -353,7 +334,7 @@ void main() { ) .xyz; - finalColor.rgb = makeFog(fogData, finalColor.rgb, vPosition.xyz, sunDir.xyz); + finalColor = makeFog(fogData, finalColor, vPosition.xyz, sunDir.xyz, PixelShader_UnFogged_IsAffectedByLight_blendMode.w); } // finalColor.rgb = finalColor.rgb; diff --git a/wowViewerLib/shaders/glsl/vulkan/m2Shader.vert b/wowViewerLib/shaders/glsl/vulkan/m2Shader.vert index 639207637..0f47de6fb 100644 --- a/wowViewerLib/shaders/glsl/vulkan/m2Shader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/m2Shader.vert @@ -6,6 +6,9 @@ #define MAX_MATRIX_NUM 220 #endif +precision highp float; +precision highp int; + #include "../common/commonLightFunctions.glsl" #include "../common/commonFogFunctions.glsl" diff --git a/wowViewerLib/shaders/glsl/vulkan/renderFrameBufferShader.frag b/wowViewerLib/shaders/glsl/vulkan/renderFrameBufferShader.frag index afba45062..98f1d9aa7 100644 --- a/wowViewerLib/shaders/glsl/vulkan/renderFrameBufferShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/renderFrameBufferShader.frag @@ -1,6 +1,8 @@ #version 450 precision highp float; +precision highp int; + layout(location = 0) in vec2 v_texcoord; layout(std140, binding=2) uniform meshWideBlockPS { diff --git a/wowViewerLib/shaders/glsl/vulkan/renderFrameBufferShader.vert b/wowViewerLib/shaders/glsl/vulkan/renderFrameBufferShader.vert index e84996cdd..df391cecc 100644 --- a/wowViewerLib/shaders/glsl/vulkan/renderFrameBufferShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/renderFrameBufferShader.vert @@ -1,5 +1,9 @@ #version 450 + +precision highp float; +precision highp int; + layout(location = 0) in vec4 a_position; layout(location = 0) out vec2 v_texcoord; diff --git a/wowViewerLib/shaders/glsl/vulkan/ribbonShader.frag b/wowViewerLib/shaders/glsl/vulkan/ribbonShader.frag index f85fb2654..bda57d969 100644 --- a/wowViewerLib/shaders/glsl/vulkan/ribbonShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/ribbonShader.frag @@ -2,6 +2,9 @@ #extension GL_GOOGLE_include_directive: require +precision highp float; +precision highp int; + #include "../common/commonLightFunctions.glsl" #include "../common/commonFogFunctions.glsl" @@ -14,15 +17,23 @@ layout(std140, binding=0) uniform sceneWideBlockVSPS { SceneWideParams scene; PSFog fogData; }; +layout(std140, binding=4) uniform meshWideBlockPS { + vec4 uAlphaTestScalev; + ivec4 uPixelShaderv; + vec4 uTextureTranslate; +}; layout(set=1, binding=5) uniform sampler2D uTexture; layout(location = 0) out vec4 outputColor; void main() { - vec4 tex = texture(uTexture, vTexcoord0).rgba; + vec2 textCoordScale = uAlphaTestScalev.yz; + vec2 texcoord = (vTexcoord0 * textCoordScale) + uTextureTranslate.xy; + + vec4 tex = texture(uTexture, texcoord).rgba; - vec4 finalColor = vec4((vColor.rgb*tex.rgb), tex.a * vColor.a ); + vec4 finalColor = vec4((vColor.rgb*tex.rgb), tex.a * vColor.a); // vec3 sunDir = @@ -34,7 +45,7 @@ void main() { // .xyz; vec3 sunDir =scene.extLight.uExteriorDirectColorDir.xyz; - finalColor.rgb = makeFog(fogData, finalColor.rgb, vPosition.xyz, sunDir.xyz); + finalColor = makeFog(fogData, finalColor, vPosition.xyz, sunDir.xyz, uPixelShaderv.y); outputColor = finalColor; } diff --git a/wowViewerLib/shaders/glsl/vulkan/ribbonShader.vert b/wowViewerLib/shaders/glsl/vulkan/ribbonShader.vert index be87d056e..532197da7 100644 --- a/wowViewerLib/shaders/glsl/vulkan/ribbonShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/ribbonShader.vert @@ -2,6 +2,9 @@ #extension GL_GOOGLE_include_directive: require +precision highp float; +precision highp int; + #include "../common/commonLightFunctions.glsl" #include "../common/commonFogFunctions.glsl" diff --git a/wowViewerLib/shaders/glsl/vulkan/skyConus.frag b/wowViewerLib/shaders/glsl/vulkan/skyConus.frag index 233e02b8e..3cccdbefc 100644 --- a/wowViewerLib/shaders/glsl/vulkan/skyConus.frag +++ b/wowViewerLib/shaders/glsl/vulkan/skyConus.frag @@ -2,11 +2,15 @@ #extension GL_GOOGLE_include_directive: require - +precision highp float; +precision highp int; layout(location = 0) in vec4 vColor; layout(location = 0) out vec4 outputColor; void main() { - outputColor = vec4(vColor.xyz, 1.0); +// if (vColor.a < 1.0) +// discard; + + outputColor = vec4(vColor.xyz, vColor.a); } diff --git a/wowViewerLib/shaders/glsl/vulkan/skyConus.vert b/wowViewerLib/shaders/glsl/vulkan/skyConus.vert index f4d7d34cb..a522edec8 100644 --- a/wowViewerLib/shaders/glsl/vulkan/skyConus.vert +++ b/wowViewerLib/shaders/glsl/vulkan/skyConus.vert @@ -2,12 +2,17 @@ #extension GL_GOOGLE_include_directive: require +precision highp float; +precision highp int; + #include "../common/commonLightFunctions.glsl" +#include "../common/commonFogFunctions.glsl" precision highp float; layout(std140, set=0, binding=0) uniform sceneWideBlockVSPS { SceneWideParams scene; + PSFog fogData; }; layout(std140, set=0, binding=2) uniform meshWideBlockVS { vec4 skyColor[6]; @@ -24,14 +29,16 @@ void main() { vec3 inputPos = aPosition.xyz; //Correction of conus - inputPos.xy = inputPos.xy / 0.6875; - inputPos.z = inputPos.z > 0 ? (inputPos.z / 0.2928): inputPos.z; + inputPos = inputPos * 33.333; // inputPos.z = -1.0-inputPos.z; vec4 cameraPos = scene.uLookAtMat * vec4(inputPos.xyz, 1.0); cameraPos.xyz = cameraPos.xyz - scene.uLookAtMat[3].xyz; cameraPos.z = cameraPos.z ; - vColor = vec4(skyColor[int(aPosition.w)].xyz, 1.0); + vec4 vertPosInNDC = scene.uPMatrix * cameraPos; + + + vColor = skyColor[int(aPosition.w)].xyzw; gl_Position = scene.uPMatrix * cameraPos; } diff --git a/wowViewerLib/shaders/glsl/vulkan/waterShader.frag b/wowViewerLib/shaders/glsl/vulkan/waterShader.frag index 92a569118..428dc1c20 100644 --- a/wowViewerLib/shaders/glsl/vulkan/waterShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/waterShader.frag @@ -1,29 +1,37 @@ - #version 450 +#version 450 + +#extension GL_GOOGLE_include_directive: require + +#include "../common/commonLightFunctions.glsl" +#include "../common/commonFogFunctions.glsl" precision highp float; +precision highp int; layout(location=0) in vec3 vPosition; - -layout(binding=3) uniform sampler2D uTexture; +layout(location=1) in vec2 vTextCoords; layout(location=0) out vec4 outputColor; +layout(set=1,binding=5) uniform sampler2D uTexture; + +layout(std140, set=0, binding=0) uniform sceneWideBlockVSPS { + SceneWideParams scene; + PSFog fogData; +}; + //Individual meshes layout(std140, binding=4) uniform meshWideBlockPS { - ivec4 waterTypeV; +//ivec4 waterTypeV; + vec4 color; }; void main() { - int waterType = int(waterTypeV.x); - if (waterType == 13) { // LIQUID_WMO_Water - outputColor = vec4(0.0, 0, 0.3, 0.5); - } else if (waterType == 14) { //LIQUID_WMO_Ocean - outputColor = vec4(0, 0, 0.8, 0.8); - } else if (waterType == 19) { //LIQUID_WMO_Magma - outputColor = vec4(0.3, 0, 0, 0.5); - } else if (waterType == 20) { //LIQUID_WMO_Slime - outputColor = vec4(0.0, 0.5, 0, 0.5); - } else { - outputColor = vec4(0.5, 0.5, 0.5, 0.5); - } + vec3 finalColor = color.rgb+texture(uTexture, vTextCoords).rgb; + + vec3 sunDir =scene.extLight.uExteriorDirectColorDir.xyz; + + //BlendMode is always GxBlend_Alpha + finalColor = makeFog(fogData, vec4(finalColor, 1.0), vPosition.xyz, sunDir.xyz, 2).rgb; + outputColor = vec4(finalColor, 0.7); } diff --git a/wowViewerLib/shaders/glsl/vulkan/waterShader.vert b/wowViewerLib/shaders/glsl/vulkan/waterShader.vert index b5982afe3..871ce363d 100644 --- a/wowViewerLib/shaders/glsl/vulkan/waterShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/waterShader.vert @@ -1,31 +1,41 @@ #version 450 -layout(location=0) in vec3 aPosition; -//layout(location=1) in float aDepth; -//layout(location=2) in vec2 aTexCoord; +#extension GL_GOOGLE_include_directive: require +precision highp float; +precision highp int; -layout(std140, binding=0) uniform sceneWideBlockVSPS { - mat4 uLookAtMat; - mat4 uPMatrix; +#include "../common/commonLightFunctions.glsl" +#include "../common/commonFogFunctions.glsl" + +layout(location=0) in vec4 aPositionTransp; +layout(location=1) in vec2 aTexCoord; + + +layout(std140, set=0, binding=0) uniform sceneWideBlockVSPS { + SceneWideParams scene; + PSFog fogData; }; layout(std140, binding=1) uniform modelWideBlockVS { mat4 uPlacementMat; }; - //out vec2 vTexCoord; layout(location=0) out vec3 vPosition; +layout(location=1) out vec2 vTextCoords; void main() { - vec4 aPositionVec4 = vec4(aPosition, 1); - mat4 cameraMatrix = uLookAtMat * uPlacementMat ; + vec4 aPositionVec4 = vec4(aPositionTransp.xyz, 1); + mat4 cameraMatrix = scene.uLookAtMat * uPlacementMat; + + vec4 cameraPoint = cameraMatrix * aPositionVec4; - vec4 cameraPoint = cameraMatrix * aPositionVec4; + const float posToTextPos = 1.0 / (1600.0/3.0/16.0); + vTextCoords = aPositionVec4.xy * posToTextPos; - gl_Position = uPMatrix * cameraPoint; -// vTexCoord = aTexCoord; - vPosition = cameraPoint.xyz; + gl_Position = scene.uPMatrix * cameraPoint; + // vTexCoord = aTexCoord; + vPosition = cameraPoint.xyz; } diff --git a/wowViewerLib/shaders/glsl/vulkan/waterfallShader.frag b/wowViewerLib/shaders/glsl/vulkan/waterfallShader.frag new file mode 100644 index 000000000..26438dc80 --- /dev/null +++ b/wowViewerLib/shaders/glsl/vulkan/waterfallShader.frag @@ -0,0 +1,126 @@ +#version 450 + +#extension GL_GOOGLE_include_directive: require + +#ifndef MAX_MATRIX_NUM +#define MAX_MATRIX_NUM 220 +#endif + +precision highp float; +precision highp int; + +#include "../common/commonFunctions.glsl" +#include "../common/commonLightFunctions.glsl" +#include "../common/commonFogFunctions.glsl" + +layout(location=0) in vec2 vTexCoord; +layout(location=1) in vec2 vTexCoord2; +layout(location=2) in vec2 vTexCoord2_animated; +layout(location=3) in vec3 vNormal; +layout(location=4) in vec3 vPosition; + +layout(set=1,binding=5) uniform sampler2D uMask; +layout(set=1,binding=6) uniform sampler2D uWhiteWater; +layout(set=1,binding=7) uniform sampler2D uNoise; +layout(set=1,binding=9) uniform sampler2D uNormalTex; + +layout(location=0) out vec4 outputColor; + +layout(std140, set=0, binding=0) uniform sceneWideBlockVSPS { + SceneWideParams scene; + PSFog fogData; +}; + +layout(std140, set=0, binding=1) uniform modelWideBlockVS { + mat4 uPlacementMat; + mat4 uBoneMatrixes[MAX_MATRIX_NUM]; +}; + +layout(std140, set=0, binding=4) uniform meshWideBlockPS { + vec4 values0; + vec4 values1; + vec4 values2; + vec4 values3; + vec4 values4; + vec4 baseColor; +}; + +const InteriorLightParam intLight = { + vec4(0,0,0,0), + vec4(0,0,0,1) +}; + +// For references: +// http://mmikkelsen3d.blogspot.com/2011/07/derivative-maps.html +// http://www.thetenthplanet.de/archives/1180 +// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf + +vec3 PerturbNormal ( vec3 surf_pos, vec3 surf_norm ) +{ + vec2 dBdUV = (texture(uNormalTex, vTexCoord2_animated).xy*2.0f - 1.0f) * (values3.x * 100); + + vec2 duv1 = dFdx( vTexCoord2_animated ).xy; + vec2 duv2 = dFdy( vTexCoord2_animated ).xy; + + vec3 vSigmaS = dFdx ( surf_pos ); + vec3 vSigmaT = dFdy ( surf_pos ); + vec3 vN = surf_norm ; // normalized + vec3 vR1 = cross ( vSigmaT , vN ); + vec3 vR2 = cross (vN , vSigmaS ); + float fDet = dot ( vSigmaS , vR1 ); + float dBs = dBdUV.x * duv1.x + dBdUV.y * duv1.y; + float dBt = dBdUV.x * duv2.x + dBdUV.y * duv2.y; + vec3 vSurfGrad = sign ( fDet ) * ( dBs * vR1 + dBt * vR2 ); + return normalize ( abs ( fDet )*vN - vSurfGrad ); +} + +void main() { + vec3 perturbedNormal = PerturbNormal(vPosition, normalize(vNormal)); + + vec2 vTexCoordNorm = vTexCoord / values1.x; + + float noise0 = texture(uNoise, vec2(vTexCoordNorm.x - values1.z, vTexCoordNorm.y-values1.z - values2.z)).x; + float noise1 = texture(uNoise, vec2(vTexCoordNorm.x - values1.z + 0.418f, vTexCoordNorm.y + 0.355f + values1.z - values2.z)).x; + float noise2 = texture(uNoise, vec2(vTexCoordNorm.x + values1.z + 0.865f, vTexCoordNorm.y + 0.148f - values1.z - values2.z)).x; + float noise3 = texture(uNoise, vec2(vTexCoordNorm.x + values1.z + 0.651, vTexCoordNorm.y + 0.752f + values1.z - values2.z)).x; + + float noise_avr = abs(noise0 + noise1 + noise2 + noise3) * 0.25f; + float noiseFinal = clamp(exp(values0.x * log2(noise_avr) * 2.2f) * values0.y, 0.0f, 1.0f); + + vec4 whiteWater_val = texture(uWhiteWater, vTexCoord2_animated); + vec4 mask_val_0 = texture(uMask, vTexCoord); + vec4 mask_val_1 = texture(uMask, vec2(vTexCoord.x, vTexCoord.y +values3.z)); + + float mix_alpha = clamp( + ((whiteWater_val.w * noiseFinal - mask_val_1.y * mask_val_0.x) * 2.0f + values0.z) * + (values0.w * 2.0f + 1.0f) - + values0.w, 0.0f, 1.0f); + + vec4 whiteWater_val_baseColor_mix = mix(baseColor, whiteWater_val, mix_alpha); + + vec3 colorAfterLight = calcLight( + whiteWater_val_baseColor_mix.rgb, + perturbedNormal, + true, + 0.0, + scene, + intLight, + vec3(0.0), /* accumLight */ + vec3(0.0), /*precomputedLight*/ + vec3(0.0), /* specular */ + vec3(0.0) /* emissive */ + ); + + + + float w_clamped = clamp((1.0f - mask_val_0.w) * values1.w, 0.0f, 1.0f); + float w_alpha_combined = clamp(w_clamped + mix_alpha, 0.0f, 1.0f); + + vec4 finalColor = vec4( + mix(colorAfterLight.rgb, whiteWater_val_baseColor_mix.rgb, values3.w), + w_alpha_combined +// whiteWater_val.a+0.2 + ); + + outputColor = finalColor; +} diff --git a/wowViewerLib/shaders/glsl/vulkan/waterfallShader.vert b/wowViewerLib/shaders/glsl/vulkan/waterfallShader.vert new file mode 100644 index 000000000..b0da765fb --- /dev/null +++ b/wowViewerLib/shaders/glsl/vulkan/waterfallShader.vert @@ -0,0 +1,84 @@ +#version 450 + +#extension GL_GOOGLE_include_directive: require + +#ifndef MAX_MATRIX_NUM +#define MAX_MATRIX_NUM 220 +#endif + +precision highp float; +precision highp int; + +#include "../common/commonFunctions.glsl" +#include "../common/commonLightFunctions.glsl" +#include "../common/commonFogFunctions.glsl" + +precision highp float; + +/* vertex shader code */ +layout(location=0) in vec3 aPosition; +layout(location=1) in vec3 aNormal; +layout(location=2) in uvec4 bones; +layout(location=3) in vec4 boneWeights; +layout(location=4) in vec2 aTexCoord; +layout(location=5) in vec2 aTexCoord2; + +//Whole scene +layout(std140, set=0, binding=0) uniform sceneWideBlockVSPS { + SceneWideParams scene; + PSFog fogData; +}; + +// Whole model +layout(std140, set=0, binding=1) uniform modelWideBlockVS { + mat4 uPlacementMat; + mat4 uBoneMatrixes[MAX_MATRIX_NUM]; +}; + +//Individual meshes +layout(std140, set=0, binding=2) uniform meshWideBlockVS { + vec4 bumpScale; + mat4 uTextMat[2]; +}; + +//Shader output +layout(location=0) out vec2 vTexCoord; +layout(location=1) out vec2 vTexCoord2; +layout(location=2) out vec2 vTexCoord2_animated; +layout(location=3) out vec3 vNormal; +layout(location=4) out vec3 vPosition; + + +layout(set=1,binding=8) uniform sampler2D uBumpTexture; + +void main() { + + vec2 texCoord2 = (uTextMat[0] * vec4(aTexCoord2, 0, 1)).xy; + + vec4 bumpValue = texture(uBumpTexture, texCoord2); + vec3 pos = (aNormal * bumpScale.x) * bumpValue.z + aPosition; + + mat4 boneTransformMat = mat4(0.0); + + boneTransformMat += (boneWeights.x ) * uBoneMatrixes[bones.x]; + boneTransformMat += (boneWeights.y ) * uBoneMatrixes[bones.y]; + boneTransformMat += (boneWeights.z ) * uBoneMatrixes[bones.z]; + boneTransformMat += (boneWeights.w ) * uBoneMatrixes[bones.w]; + + mat4 cameraMatrix = scene.uLookAtMat * uPlacementMat * boneTransformMat ; + vec4 cameraPoint = cameraMatrix * vec4(pos, 1.0); + + mat3 viewModelMatTransposed = + blizzTranspose(scene.uLookAtMat) * + blizzTranspose(uPlacementMat) * + blizzTranspose(boneTransformMat); + + vNormal = (scene.uLookAtMat * uPlacementMat * vec4(aNormal, 0)).xyz; + vPosition = pos; + + vTexCoord = aTexCoord; + vTexCoord2_animated = texCoord2; + vTexCoord2 = aTexCoord2; + + gl_Position = scene.uPMatrix * cameraPoint; +} diff --git a/wowViewerLib/shaders/glsl/vulkan/wmoShader.frag b/wowViewerLib/shaders/glsl/vulkan/wmoShader.frag index 9c937c106..5c80d1e35 100644 --- a/wowViewerLib/shaders/glsl/vulkan/wmoShader.frag +++ b/wowViewerLib/shaders/glsl/vulkan/wmoShader.frag @@ -2,6 +2,7 @@ #extension GL_GOOGLE_include_directive: require precision highp float; +precision highp int; #include "../common/commonLightFunctions.glsl" #include "../common/commonFogFunctions.glsl" @@ -25,12 +26,55 @@ layout(std140, set=0, binding=3) uniform modelWideBlockPS { }; layout(std140, set=0, binding=4) uniform meshWideBlockPS { - ivec4 UseLitColor_EnableAlpha_PixelShader; + ivec4 UseLitColor_EnableAlpha_PixelShader_BlendMode; vec4 FogColor_AlphaTest; }; +vec3 Slerp(vec3 p0, vec3 p1, float t) +{ + float dotp = dot(normalize(p0), normalize(p1)); + if ((dotp > 0.9999) || (dotp<-0.9999)) + { + if (t<=0.5) + return p0; + return p1; + } + float theta = acos(dotp); + vec3 P = ((p0*sin((1-t)*theta) + p1*sin(t*theta)) / sin(theta)); + + return P; +} + +vec3 calcSpec(float texAlpha) { + vec3 normal = normalize(vNormal); + + vec3 sunDir = vec3(0); + vec3 sunColor = vec3(0); + if (intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0) { + sunDir = -(scene.extLight.uExteriorDirectColorDir.xyz); + sunColor = scene.extLight.uExteriorDirectColor.xyz; + } + + if (intLight.uInteriorAmbientColorAndApplyInteriorLight.w > 0) { + sunDir = -(scene.uInteriorSunDir.xyz); + sunColor = intLight.uInteriorDirectColorAndApplyExteriorLight.xyz; + + if (intLight.uInteriorDirectColorAndApplyExteriorLight.w > 0) { + sunDir = Slerp(sunDir, -(scene.extLight.uExteriorDirectColorDir.xyz), vColor.a); + sunColor = mix(sunColor, scene.extLight.uExteriorDirectColor.xyz, vColor.a); + } + } + vec3 t849 = normalize((sunDir + normalize(-(vPosition.xyz)))); + float dirAtten_956 = clamp(dot(normal, sunDir), 0, 1); + float spec = (1.25 * pow(clamp(dot(normal, t849), 0, 1), 8.0)); + vec3 specTerm = ((((vec3(mix(pow((1.0 - clamp(dot(sunDir, t849), 0, 1)), 5.0), 1.0, texAlpha)) * spec) * sunColor) * dirAtten_956)); +// float distFade = clamp(((vPosition * pc_miscParams.z) + pc_miscParams.w), 0, 1); + float distFade = 1.0; + specTerm = (specTerm * distFade); + return specTerm; +} layout(set=1, binding=5) uniform sampler2D uTexture; layout(set=1, binding=6) uniform sampler2D uTexture2; @@ -38,191 +82,159 @@ layout(set=1, binding=7) uniform sampler2D uTexture3; layout (location = 0) out vec4 outputColor; -//vec3 makeDiffTerm(vec3 matDiffuse) { -// vec3 currColor; -// vec3 lDiffuse = vec3(0.0, 0.0, 0.0); -// if (UseLitColor_EnableAlpha_PixelShader.x == 1) { -// //vec3 viewUp = normalize(vec3(0, 0.9, 0.1)); -// vec3 normalizedN = normalize(vNormal); -// float nDotL = dot(normalizedN, -(uSunDir_FogStart.xyz)); -// float nDotUp = dot(normalizedN, uViewUp.xyz); -// -// vec3 precomputed = vColor2.rgb; -// -// vec3 ambientColor = uAmbientLight.rgb; -// vec3 directColor = uSunColor_uFogEnd.xyz; -// if (uAmbientLight2AndIsBatchA.w > 0.0) { -// ambientColor = mix(uAmbientLight2AndIsBatchA.rgb, uAmbientLight.rgb, vec3(vPosition.w)); -// directColor = mix(vec3(0), directColor.rgb, vec3(vPosition.w)); -// } -// -// vec3 adjAmbient = (ambientColor.rgb + precomputed); -// vec3 adjHorizAmbient = (ambientColor.rgb + precomputed); -// vec3 adjGroundAmbient = (ambientColor.rgb + precomputed); -// -// if ((nDotUp >= 0.0)) -// { -// currColor = mix(adjHorizAmbient, adjAmbient, vec3(nDotUp)); -// } -// else -// { -// currColor= mix(adjHorizAmbient, adjGroundAmbient, vec3(-(nDotUp))); -// } -// -// vec3 skyColor = (currColor * 1.10000002); -// vec3 groundColor = (currColor* 0.699999988); -// currColor = mix(groundColor, skyColor, vec3((0.5 + (0.5 * nDotL)))); -// lDiffuse = (directColor * clamp(nDotL, 0.0, 1.0)); -// } else { -// currColor = vec3 (1.0, 1.0, 1.0) * uAmbientLight.rgb; -// } -// -// vec3 gammaDiffTerm = matDiffuse * (currColor + lDiffuse); -// vec3 linearDiffTerm = (matDiffuse * matDiffuse) * vec3(0.0); -// return sqrt(gammaDiffTerm*gammaDiffTerm + linearDiffTerm) ; -// -//// return matDiffuse * currColor.rgb ; -//} - void main() { vec4 tex = texture(uTexture, vTexCoord).rgba ; vec4 tex2 = texture(uTexture2, vTexCoord2).rgba; vec4 tex3 = texture(uTexture3, vTexCoord3).rgba; - - if (UseLitColor_EnableAlpha_PixelShader.y == 1) { + if (UseLitColor_EnableAlpha_PixelShader_BlendMode.y == 1) { if ((tex.a - 0.501960814) < 0.0) { discard; } } - int uPixelShader = UseLitColor_EnableAlpha_PixelShader.z; + int uPixelShader = UseLitColor_EnableAlpha_PixelShader_BlendMode.z; vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0); vec3 matDiffuse = vec3(0.0); - vec3 env = vec3(0.0); + vec3 spec = vec3(0.0); + vec3 emissive = vec3(0.0); float finalOpacity = 0.0; + // float distFade_1070 = clamp(((in_vpos.z * pc_miscParams.z) + pc_miscParams.w), 0, 1); + float distFade = 1.0; + if ( uPixelShader == -1 ) { - matDiffuse = tex.rgb * vColor.rgb + tex2.rgb*vColor2.bgr; + matDiffuse = tex.rgb * tex2.rgb; finalOpacity = tex.a; } else if (uPixelShader == 0) { //MapObjDiffuse - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + matDiffuse = tex.rgb; + finalOpacity = tex.a; } else if (uPixelShader == 1) { //MapObjSpecular - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + matDiffuse = tex.rgb; + spec = calcSpec(tex.a); + finalOpacity = tex.a; } else if (uPixelShader == 2) { //MapObjMetal - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + matDiffuse = tex.rgb ; + spec = calcSpec(((tex * 4.0) * tex.a).x); + finalOpacity = tex.a; } else if (uPixelShader == 3) { //MapObjEnv + matDiffuse = tex.rgb ; - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - env = tex2.rgb * tex.a; - finalOpacity = vColor.a; + emissive = tex2.rgb * tex.a * distFade; + finalOpacity = 1.0; } else if (uPixelShader == 4) { //MapObjOpaque - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + matDiffuse = tex.rgb ; + finalOpacity = 1.0; } else if (uPixelShader == 5) { //MapObjEnvMetal - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - env = (tex.rgb * tex.a) * tex2.rgb; + matDiffuse = tex.rgb ; + emissive = (((tex.rgb * tex.a) * tex2.rgb) * distFade); - finalOpacity = vColor.a; + finalOpacity = 1.0; } else if (uPixelShader == 6) { //MapObjTwoLayerDiffuse vec3 layer1 = tex.rgb; vec3 layer2 = mix(layer1, tex2.rgb, tex2.a); - matDiffuse = (vColor.rgb * 2.0) * mix(layer2, layer1, vColor2.a); + matDiffuse = mix(layer2, layer1, vColor2.a); - finalOpacity = vColor.a; + finalOpacity = tex.a; } else if (uPixelShader == 7) { //MapObjTwoLayerEnvMetal vec4 colorMix = mix(tex2, tex, vColor2.a); - matDiffuse = colorMix.rgb * (2.0 * vColor.rgb); - env = (colorMix.rgb * colorMix.a) * tex3.rgb; + matDiffuse = colorMix.rgb ; + emissive = (colorMix.rgb * colorMix.a) * tex3.rgb * distFade; - finalOpacity = vColor.a; + finalOpacity = tex.a; } else if (uPixelShader == 8) { //MapObjTwoLayerTerrain vec3 layer1 = tex.rgb; vec3 layer2 = tex2.rgb; - matDiffuse = ((vColor.rgb * 2.0) * mix(layer2, layer1, vColor2.a)); - finalOpacity = vColor.a; + matDiffuse = mix(layer2, layer1, vColor2.a); + spec = calcSpec(tex2.a * (1.0 - vColor2.a)); + + finalOpacity = tex.a; } else if (uPixelShader == 9) { //MapObjDiffuseEmissive - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - env = tex2.rgb * tex2.a * vColor2.a; + matDiffuse = tex.rgb ; + emissive = tex2.rgb * tex2.a * vColor2.a; - finalOpacity = vColor.a; + finalOpacity = tex.a; } else if (uPixelShader == 10) { //MapObjMaskedEnvMetal float mixFactor = clamp((tex3.a * vColor2.a), 0.0, 1.0); matDiffuse = - (vColor.rgb * 2.0) * mix(mix(((tex.rgb * tex2.rgb) * 2.0), tex3.rgb, mixFactor), tex.rgb, tex.a); - finalOpacity = vColor.a; + finalOpacity = tex.a; } else if (uPixelShader == 11) { //MapObjEnvMetalEmissive - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - env = + matDiffuse = tex.rgb ; + emissive = ( ((tex.rgb * tex.a) * tex2.rgb) + ((tex3.rgb * tex3.a) * vColor2.a) ); - finalOpacity = vColor.a; + finalOpacity = tex.a; } else if (uPixelShader == 12) { //MapObjTwoLayerDiffuseOpaque - matDiffuse = - (vColor.rgb * 2.0) * - mix(tex2.rgb, tex.rgb, vColor2.a); + matDiffuse = mix(tex2.rgb, tex.rgb, vColor2.a); - finalOpacity = vColor.a; + finalOpacity = 1.0; } else if (uPixelShader == 13) { //MapObjTwoLayerDiffuseEmissive vec3 t1diffuse = (tex2.rgb * (1.0 - tex2.a)); - matDiffuse = - ((vColor.rgb * 2.0) * - mix(t1diffuse, tex.rgb, vColor2.a)); + matDiffuse = mix(t1diffuse, tex.rgb, vColor2.a); - env = ((tex2.rgb * tex2.a) * (1.0 - vColor2.a)); - finalOpacity = vColor.a; - } else if (uPixelShader == 13) { //MapObjTwoLayerDiffuseEmissive - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + emissive = (tex2.rgb * tex2.a) * (1.0 - vColor2.a); + + finalOpacity = tex.a; } else if (uPixelShader == 14) { //MapObjAdditiveMaskedEnvMetal - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + matDiffuse = mix( + (tex.rgb * tex2.rgb * 2.0) + (tex3.rgb * clamp(tex3.a * vColor2.a, 0.0, 1.0)), + tex.rgb, + vec3(tex.a) + ); + finalOpacity = 1.0; } else if (uPixelShader == 15) { //MapObjTwoLayerDiffuseMod2x - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + vec3 layer1 = tex.rgb; + vec3 layer2 = mix(layer1, tex2.rbg, vec3(tex2.a)); + vec3 layer3 = mix(layer2, layer1, vec3(vColor2.a)); + + matDiffuse = layer3 * tex3.rgb * 2.0; + finalOpacity = tex.a; } if (uPixelShader == 16) { //MapObjTwoLayerDiffuseMod2xNA - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + vec3 layer1 = ((tex.rgb * tex2.rgb) * 2.0); + + matDiffuse = mix(tex.rgb, layer1, vec3(vColor2.a)) ; + finalOpacity = tex.a; } if (uPixelShader == 17) { //MapObjTwoLayerDiffuseAlpha - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + vec3 layer1 = tex.rgb; + vec3 layer2 = mix(layer1, tex2.rgb, vec3(tex2.a)); + vec3 layer3 = mix(layer2, layer1, vec3(tex3.a)); + + matDiffuse = ((layer3 * tex3.rgb) * 2.0); + finalOpacity = tex.a; } if (uPixelShader == 18) { //MapObjLod - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + matDiffuse = tex.rgb ; + finalOpacity = tex.a; } if (uPixelShader == 19) { //MapObjParallax - matDiffuse = tex.rgb * (2.0 * vColor.rgb); - finalOpacity = vColor.a; + matDiffuse = tex.rgb ; + finalOpacity = tex.a; } finalColor = vec4( @@ -230,22 +242,20 @@ void main() { matDiffuse, vNormal, true, - vPosition.w, + vColor.w, scene, intLight, vec3(0.0) /*accumLight*/, - vColor2.rgb, - vec3(0.0) /* specular */ + vColor.rgb, + spec, /* specular */ + emissive ), finalOpacity ); - if(finalColor.a < FogColor_AlphaTest.w) - discard; - - finalColor.rgb = makeFog(fogData, finalColor.rgb, vPosition.xyz, scene.extLight.uExteriorDirectColorDir.xyz); + finalColor = makeFog(fogData, finalColor, vPosition.xyz, scene.extLight.uExteriorDirectColorDir.xyz, UseLitColor_EnableAlpha_PixelShader_BlendMode.w); - finalColor.a = 1.0; //do I really need it now? +// finalColor.a = 1.0; //do I really need it now? outputColor = finalColor; } diff --git a/wowViewerLib/shaders/glsl/vulkan/wmoShader.vert b/wowViewerLib/shaders/glsl/vulkan/wmoShader.vert index b04ac2318..0333dd25e 100644 --- a/wowViewerLib/shaders/glsl/vulkan/wmoShader.vert +++ b/wowViewerLib/shaders/glsl/vulkan/wmoShader.vert @@ -2,6 +2,9 @@ #extension GL_GOOGLE_include_directive: require +precision highp float; +precision highp int; + #include "../common/commonLightFunctions.glsl" #include "../common/commonFogFunctions.glsl" @@ -49,11 +52,11 @@ void main() { gl_Position = scene.uPMatrix * cameraPoint; - vPosition = vec4(cameraPoint.xyz, aColor.w); + vPosition = vec4(cameraPoint.xyz, 0); vNormal = normalize(viewModelMatTransposed * aNormal); - vColor.rgba = vec4(vec3(0.5, 0.499989986, 0.5), 1.0); - vColor2 = vec4((aColor.bgr * 2.0), aColor2.a); + vColor = aColor.bgra; + vColor2 = aColor2; int uVertexShader = VertexShader_UseLitColor.x; if ( uVertexShader == -1 ) { vTexCoord = aTexCoord; @@ -97,14 +100,4 @@ void main() { vTexCoord2 = vPosition.xy * -0.239999995; vTexCoord3 = aTexCoord3; //not used } - - -// -// vs_out.vTexCoord = vTexCoord; -// vs_out.vTexCoord2 = vTexCoord2; -// vs_out.vTexCoord3 = vTexCoord3; -// vs_out.vColor = vColor; -// vs_out.vColor2 = vColor2; -// vs_out.vPosition = vPosition; -// vs_out.vNormal = vNormal; } diff --git a/wowViewerLib/shaders/src/spirv/dumpGLSLShader.h b/wowViewerLib/shaders/src/spirv/dumpGLSLShader.h index ce4c37ad6..9db53b3d2 100644 --- a/wowViewerLib/shaders/src/spirv/dumpGLSLShader.h +++ b/wowViewerLib/shaders/src/spirv/dumpGLSLShader.h @@ -9,11 +9,17 @@ #include "webGLSLCompiler.h" #include "fileHelpers.h" -void dumpGLSLText(std::vector &shaderFilePaths, int glslVersion) { +void dumpGLSLText(std::vector &shaderFilePaths, int glslVersion, bool isES) { // Read SPIR-V from disk or similar. spirv_cross::WebGLSLCompiler::Options options; + if (glslVersion < 300) { + options.force_flattened_io_blocks = true; + options.webgl10 = true; +// glsl.flatten_buffer_block(resource.id); + } + for (auto &filePath : shaderFilePaths) { std::vector spirv_binary = readFile(filePath); @@ -58,19 +64,25 @@ void dumpGLSLText(std::vector &shaderFilePaths, int glslVersion) { // Some arbitrary remapping if we want. - if (glslVersion >= 300) { - glsl.unset_decoration(resource.id, spv::DecorationBinding); + if (isES) { + options.enable_420pack_extension = false; } else { - glsl.unset_decoration(resource.id, spv::DecorationBinding); - options.enable_420pack_extension = false; + if (glslVersion > 300) { + glsl.unset_decoration(resource.id, spv::DecorationBinding); + options.enable_420pack_extension = false; + } else { + glsl.unset_decoration(resource.id, spv::DecorationBinding); + options.enable_420pack_extension = false; + } } } // Set some options. options.version = glslVersion; - options.es = false; + options.es = isES; + glsl.set_common_options(options); diff --git a/wowViewerLib/shaders/src/spirv/dumpShaderFields.h b/wowViewerLib/shaders/src/spirv/dumpShaderFields.h index 699129b22..c06047173 100644 --- a/wowViewerLib/shaders/src/spirv/dumpShaderFields.h +++ b/wowViewerLib/shaders/src/spirv/dumpShaderFields.h @@ -78,7 +78,7 @@ void dumpMembers(spirv_cross::WebGLSLCompiler &glsl, std::vector &f auto memberName = glsl.get_member_name(parentTypeId, k); auto memberSize = glsl.get_declared_struct_member_size(memberType, k); auto memberOffset = glsl.type_struct_member_offset(memberType, k); - dumpMembers(glsl, fieldDefines, memberType.member_types[k], namePrefix + "." + memberName, offset+memberOffset, memberSize); + dumpMembers(glsl, fieldDefines, memberType.member_types[k], namePrefix + "_" + memberName, offset+memberOffset, memberSize); } } } else { @@ -110,7 +110,8 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { "#include \n" "\n" "template \n" - "inline constexpr const uint32_t operator+ (T const val) { return static_cast(val); };" << std::endl; + "inline constexpr const uint32_t operator+ (T const val) { return static_cast(val); };" + << std::endl; std::cout << "struct fieldDefine {\n" @@ -139,11 +140,16 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { "\n" "//Per file\n" "extern const std::unordered_map shaderMetaInfo;" << std::endl << - "extern const std::unordered_map> attributesPerShaderName;" << std::endl << - "extern const std::unordered_map>> fieldDefMapPerShaderName;" << std::endl; + "extern const std::unordered_map> attributesPerShaderName;" + << std::endl << + "extern const std::unordered_map>> fieldDefMapPerShaderNameVert;" + << std::endl << + "extern const std::unordered_map>> fieldDefMapPerShaderNameFrag;" + << std::endl; - std::unordered_map>> fieldDefMapPerShaderName; + std::unordered_map>> fieldDefMapPerShaderNameVert; + std::unordered_map>> fieldDefMapPerShaderNameFrag; std::unordered_map> attributesPerShaderName; //1. Collect data @@ -156,6 +162,15 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { std::string fileName = basename(filePath); auto tokens = split(fileName, '.'); + spirv_cross::WebGLSLCompiler glsl(std::move(spirv_binary)); + + auto fieldDefMapPerShaderNameRef = std::ref(fieldDefMapPerShaderNameVert); + if (glsl.get_entry_points_and_stages()[0].execution_model == spv::ExecutionModel::ExecutionModelFragment) { + fieldDefMapPerShaderNameRef = std::ref(fieldDefMapPerShaderNameFrag); + } + auto &fieldDefMapPerShaderName = fieldDefMapPerShaderNameRef.get(); + + //Find or create new record for shader { auto it = fieldDefMapPerShaderName.find(tokens[0]); @@ -173,9 +188,6 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { auto &perSetMap = fieldDefMapPerShaderName.at(tokens[0]); auto &metaInfo = shaderMetaInfo.at(fileName); - - spirv_cross::WebGLSLCompiler glsl(std::move(spirv_binary)); - if (glsl.get_entry_points_and_stages()[0].execution_model == spv::ExecutionModel::ExecutionModelVertex) { auto it = attributesPerShaderName.find(tokens[0]); if (it == attributesPerShaderName.end()) { @@ -193,9 +205,10 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { shaderAttributeVector.push_back({attribute.name, location}); } - std::sort(shaderAttributeVector.begin(), shaderAttributeVector.end(), [](const attributeDefine &a, const attributeDefine &b) -> bool { - return a.location < b.location; - }); + std::sort(shaderAttributeVector.begin(), shaderAttributeVector.end(), + [](const attributeDefine &a, const attributeDefine &b) -> bool { + return a.location < b.location; + }); } // The SPIR-V is now parsed, and we can perform reflection on it. @@ -211,7 +224,7 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { metaInfo.uboBindings.push_back({set, binding, typeId_size}); if (perSetMap.find(binding) != perSetMap.end()) { - perSetMap[binding]={}; + perSetMap[binding] = {}; } auto &fieldVectorDef = perSetMap[binding]; @@ -224,7 +237,8 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { auto memberName = glsl.get_member_name(uboType.parent_type, j); - dumpMembers(glsl, fieldVectorDef, uboType.member_types[j], "_" +std::to_string(resource.id) + "."+memberName, offset, memberSize); + dumpMembers(glsl, fieldVectorDef, uboType.member_types[j], + "_" + std::to_string(resource.id) + "_" + memberName, offset, memberSize); } } @@ -232,8 +246,8 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { //2.1 Create attribute enums for (auto it = attributesPerShaderName.begin(); it != attributesPerShaderName.end(); it++) { - std::cout << "struct "<< it->first << " {\n" - " enum class Attribute {" << std::endl; + std::cout << "struct " << it->first << " {\n" + " enum class Attribute {" << std::endl; std::cout << " "; for (auto &attributeInfo : it->second) { @@ -251,7 +265,8 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { std::cout << "#ifdef SHADERDATACPP" << std::endl; //3.2 Dump attribute info - std::cout << "const std::unordered_map> attributesPerShaderName = {" << std::endl; + std::cout << "const std::unordered_map> attributesPerShaderName = {" + << std::endl; for (auto it = attributesPerShaderName.begin(); it != attributesPerShaderName.end(); it++) { std::cout << "{\"" << it->first << "\", " << " {" << std::endl; @@ -285,32 +300,43 @@ void dumpShaderUniformOffsets(std::vector &shaderFilePaths) { //Dump unfform bufffers info - std::cout << "const std::unordered_map>> fieldDefMapPerShaderName = {" << std::endl; - for (auto it = fieldDefMapPerShaderName.begin(); it != fieldDefMapPerShaderName.end(); it++) { - std::cout << " {\"" << it->first << "\", " << " {" << std::endl; - - for (auto subIt = it->second.begin(); subIt != it->second.end(); subIt++) { - std::cout << " {" << std::endl; - - std::cout << " " << subIt->first <<", {" << std::endl; - - for (auto &fieldDef : subIt->second) { - std::cout << " {" - << "\"" << fieldDef.name << ((fieldDef.arraySize > 0) ? "[0]" : "") << "\", " - << (fieldDef.isFloat ? "true" : "false") << ", " - << fieldDef.offset << ", " - << fieldDef.columns << ", " - << fieldDef.vecSize << ", " - << fieldDef.arraySize << "}," << std::endl; - } + auto dumpLambda = [](auto fieldDefMapPerShaderName) { + for (auto it = fieldDefMapPerShaderName.begin(); it != fieldDefMapPerShaderName.end(); it++) { + std::cout << " {\"" << it->first << "\", " << " {" << std::endl; + + for (auto subIt = it->second.begin(); subIt != it->second.end(); subIt++) { + std::cout << " {" << std::endl; + + std::cout << " " << subIt->first << ", {" << std::endl; + + for (auto &fieldDef : subIt->second) { + std::cout << " {" + << "\"" << fieldDef.name << ((fieldDef.arraySize > 0) ? "[0]" : "") << "\", " + << (fieldDef.isFloat ? "true" : "false") << ", " + << fieldDef.offset << ", " + << fieldDef.columns << ", " + << fieldDef.vecSize << ", " + << fieldDef.arraySize << "}," << std::endl; + } - std::cout << " }" << std::endl; + std::cout << " }" << std::endl; - std::cout << " }," << std::endl; + std::cout << " }," << std::endl; + } + + std::cout << " }}," << std::endl; } + }; + std::cout + << "const std::unordered_map>> fieldDefMapPerShaderNameVert = {" + << std::endl; + dumpLambda(fieldDefMapPerShaderNameVert); + std::cout << "};" << std::endl; - std::cout << " }}," << std::endl; - } + std::cout + << "const std::unordered_map>> fieldDefMapPerShaderNameFrag = {" + << std::endl; + dumpLambda(fieldDefMapPerShaderNameFrag); std::cout << "};" << std::endl; std::cout << "#endif" << std::endl << std::endl; diff --git a/wowViewerLib/shaders/src/spirv/spirv_refl_main.cpp b/wowViewerLib/shaders/src/spirv/spirv_refl_main.cpp index fe8e3202a..1ce3d4547 100644 --- a/wowViewerLib/shaders/src/spirv/spirv_refl_main.cpp +++ b/wowViewerLib/shaders/src/spirv/spirv_refl_main.cpp @@ -73,10 +73,20 @@ int main(int argc, char **argv) if (mode == "-sf") { dumpShaderUniformOffsets(filePaths); } else if (mode == "-glsl100") { - dumpGLSLText(filePaths, 100); + dumpGLSLText(filePaths, 100, false); + } else if (mode == "-glsl120Es") { + dumpGLSLText(filePaths, 120, true); + } else if (mode == "-glsl120") { + dumpGLSLText(filePaths, 120, false); } else if (mode == "-glsl330") { - dumpGLSLText(filePaths, 330); + dumpGLSLText(filePaths, 330, false); + } else if (mode == "-glslEs310") { + dumpGLSLText(filePaths, 310, true); + } else if (mode == "-glslEs300") { + dumpGLSLText(filePaths, 300, true); } + + return 0; } \ No newline at end of file diff --git a/wowViewerLib/shaders/src/spirv/webGLSLCompiler.cpp b/wowViewerLib/shaders/src/spirv/webGLSLCompiler.cpp index 4c3856056..67793be20 100644 --- a/wowViewerLib/shaders/src/spirv/webGLSLCompiler.cpp +++ b/wowViewerLib/shaders/src/spirv/webGLSLCompiler.cpp @@ -836,7 +836,7 @@ void WebGLSLCompiler::emit_header() } case ExecutionModelFragment: - if (options.es) + if (options.es || options.webgl10) { switch (options.fragment.default_float_precision) { @@ -3009,14 +3009,20 @@ void WebGLSLCompiler::emit_resources() c.specialization_constant_macro_name = constant_value_macro_name(get_decoration(c.self, DecorationSpecId)); } - emit_constant(c); - emitted = true; + //Webgl 1.0 do not support const arrays. Sad, but true + if (!options.webgl10) { + emit_constant(c); + emitted = true; + } } } else if (id.get_type() == TypeConstantOp) { - emit_specialization_constant_op(id.get()); - emitted = true; + //Webgl 1.0 do not support const arrays. Sad, but true + if (!options.webgl10) { + emit_specialization_constant_op(id.get()); + emitted = true; + } } else if (id.get_type() == TypeType) { @@ -3036,7 +3042,7 @@ void WebGLSLCompiler::emit_resources() is_natural_struct = true; } - if (is_natural_struct) + if (is_natural_struct ) { if (emitted) statement(""); @@ -3102,7 +3108,7 @@ void WebGLSLCompiler::emit_resources() } // Output UBOs and SSBOs - ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + ir.for_each_typed_id([&](uint32_t r_id, SPIRVariable &var) { auto &type = this->get(var.basetype); bool is_block_storage = type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform || @@ -3113,7 +3119,11 @@ void WebGLSLCompiler::emit_resources() if (var.storage != StorageClassFunction && type.pointer && is_block_storage && !is_hidden_variable(var) && has_block_flags) { - emit_buffer_block(var); + if (options.webgl10) { + emit_ubo_as_uniforms(type.parent_type, "_" +std::to_string(r_id)); + } else { + emit_buffer_block(var); + } } }); @@ -7492,6 +7502,8 @@ string WebGLSLCompiler::access_chain_internal(uint32_t base, const uint32_t *ind access_chain_internal_append_index(expr, base, type, mod_flags, access_chain_is_arrayed, index); }; + bool doPossibleUBODereference = true; + for (uint32_t i = 0; i < count; i++) { uint32_t index = indices[i]; @@ -7554,6 +7566,7 @@ string WebGLSLCompiler::access_chain_internal(uint32_t base, const uint32_t *ind // Arrays else if (!type->array.empty()) { + doPossibleUBODereference = false; // If we are flattening multidimensional arrays, only create opening bracket on first // array index. if (options.flatten_multidimensional_arrays && !pending_array_enclose) @@ -7660,8 +7673,11 @@ string WebGLSLCompiler::access_chain_internal(uint32_t base, const uint32_t *ind expr = qual_mbr_name; else if (flatten_member_reference) expr += join("_", to_member_name(*type, index)); - else + else if (options.webgl10 && doPossibleUBODereference) { + expr += to_member_reference_webgl10(base, *type, index, ptr_chain); + } else { expr += to_member_reference(base, *type, index, ptr_chain); + } } if (has_member_decoration(type->self, index, DecorationInvariant)) @@ -7679,6 +7695,7 @@ string WebGLSLCompiler::access_chain_internal(uint32_t base, const uint32_t *ind // Matrix -> Vector else if (type->columns > 1) { + doPossibleUBODereference = false; // If we have a row-major matrix here, we need to defer any transpose in case this access chain // is used to store a column. We can resolve it right here and now if we access a scalar directly, // by flipping indexing order of the matrix. @@ -7696,6 +7713,8 @@ string WebGLSLCompiler::access_chain_internal(uint32_t base, const uint32_t *ind // Vector -> Scalar else if (type->vecsize > 1) { + doPossibleUBODereference = false; + string deferred_index; if (row_major_matrix_needs_conversion) { @@ -11414,11 +11433,82 @@ string WebGLSLCompiler::to_member_name(const SPIRType &type, uint32_t index) return join("_m", index); } -string WebGLSLCompiler::to_member_reference(uint32_t, const SPIRType &type, uint32_t index, bool) +string WebGLSLCompiler::to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool) { return join(".", to_member_name(type, index)); } +static bool endsWith(std::string_view str, std::string_view suffix) +{ + return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix); +} + +bool WebGLSLCompiler::canDoFlatteningForMember(uint32_t base) { + auto *var = maybe_get(base); + if (!var) { + auto *expr = maybe_get(base); + auto *exprType = &get_pointee_type(expr->expression_type); + if (expr) { + if (0 != expr->loaded_from) { + if (expr->expression_dependencies.size() > 0) { + auto depId = expr->expression_dependencies[expr->expression_dependencies.size()-1]; + auto *depExpr = maybe_get(depId); +// + if (depExpr != nullptr && endsWith(depExpr->expression, "]")) { + return false; + } + } + + + return canDoFlatteningForMember(expr->loaded_from); + } else { + if (expr->expression_dependencies.size() > 1) { + return canDoFlatteningForMember(expr->expression_dependencies[1]); + } + + } + } + auto *chain = maybe_get(base); + if (chain) { + join(""); + } + + return false; + } +// auto *varType = &get_pointee_type(var->basetype); +// if (var->dependees.size() > 1) { +// for (auto depId : var->dependees) { +// auto *depVar = maybe_get(depId); +// auto *depExpr = maybe_get(depId); +// +// if (depExpr != nullptr) { +// uint32_t type_id = expression_type_id(depId); +// const auto *type = &get_pointee_type(type_id); +// if (!type->array.empty()) { +// return false; +// } +// } +// join(""); +// } +// } + return var && var->storage == StorageClassUniform; +} + +std::string WebGLSLCompiler::to_member_reference_webgl10(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain) { + if (to_member_name(type, index) == "attenuation") { + join(""); + } + bool doFlattening = canDoFlatteningForMember(base); + + auto *chain = maybe_get(base); + if (doFlattening && options.webgl10) { + return join("_", to_member_name(type, index)); + } else { + return join(".", to_member_name(type, index)); + } +} + + string WebGLSLCompiler::to_multi_member_reference(const SPIRType &type, const SmallVector &indices) { string ret; @@ -12405,6 +12495,14 @@ void WebGLSLCompiler::emit_function(SPIRFunction &func, const Bitset &return_fla return; func.active = true; + //Hack for one single function, that uses consts + if (options.webgl10 && to_name(func.self) == "validateFogColor") { + statement("vec3 validateFogColor(in vec3 fogColor, int blendMode) {\n" + " return fogColor;\n" + "}"); + return; + } + // If we depend on a function, emit that function before we emit our own function. for (auto block : func.blocks) { @@ -14381,3 +14479,66 @@ bool WebGLSLCompiler::variable_is_depth_or_compare(VariableID id) const { return image_is_comparison(get(get(id).basetype), id); } + +void WebGLSLCompiler::emit_ubo_as_uniforms(spirv_cross::TypeID type, std::string namePrefix) { + auto memberType = get_type(type); + int arraySize = memberType.array.size() > 0 ? memberType.array[0] : 0; + bool arrayLiteral = memberType.array_size_literal.size() > 0 ? memberType.array_size_literal[0] : 0; + int vecSize = memberType.vecsize; + int width = memberType.width; + int columns = memberType.columns; + + + bool isStruct = memberType.basetype == spirv_cross::SPIRType::Struct; + + if (isStruct) { + auto parentTypeId = memberType.parent_type; + if (parentTypeId == spirv_cross::TypeID(0)) { + parentTypeId = memberType.type_alias; + } + if (parentTypeId == spirv_cross::TypeID(0)) { + parentTypeId = memberType.self; + } +// +// auto submemberType = glsl.get_type(submemberTypeId); +// int structSize = submemberType.vecsize * submemberType.columns*(submemberType.width/8); + + if (arrayLiteral) { + statement("uniform ",variable_decl(memberType, namePrefix, 0), ";"); +// for (int j = 0; j < arraySize; j++) { +// +// for (int k = 0; k < memberType.member_types.size(); k++) { +// auto memberName = get_member_name(parentTypeId, k); +// +// emit_ubo_as_uniforms(memberType.member_types[k], +// namePrefix + "[" + std::to_string(j) + "]" + "." + memberName); +// } +// } + } else { + for (int k = 0; k < memberType.member_types.size(); k++) { + auto memberName = get_member_name(parentTypeId, k); + + emit_ubo_as_uniforms(memberType.member_types[k], namePrefix + "_" + memberName); + } + } + } else { + bool isFloat = (memberType.basetype == spirv_cross::SPIRType::Float); +// std::cout << "{\"" <(type); + + statement("uniform ",variable_decl(membertype, namePrefix, 0), ";"); + + + +// std::cout << +// namePrefix << +// ", columns = " << columns << +// ", isFloat = " << (memberType.basetype == spirv_cross::SPIRType::Float) << +// ", memberSize = " << currmemberSize << +// ", vecSize = " << vecSize << +// ", width = " << width << +// ", arraySize = " << arraySize << +// ", arrayLiteral = " << arrayLiteral << +// " offset = " << offset << std::endl; + } +} \ No newline at end of file diff --git a/wowViewerLib/shaders/src/spirv/webGLSLCompiler.h b/wowViewerLib/shaders/src/spirv/webGLSLCompiler.h index 95560cbd1..fb0689bd5 100644 --- a/wowViewerLib/shaders/src/spirv/webGLSLCompiler.h +++ b/wowViewerLib/shaders/src/spirv/webGLSLCompiler.h @@ -81,6 +81,8 @@ namespace SPIRV_CROSS_NAMESPACE // what happens on legacy GLSL targets for blocks and structs. bool force_flattened_io_blocks = false; + bool webgl10 = false; + enum Precision { DontCare, @@ -158,6 +160,9 @@ namespace SPIRV_CROSS_NAMESPACE void set_common_options(const Options &opts) { options = opts; + if (opts.version < 100) { + backend.explicit_struct_type = true; + } } std::string compile() override; @@ -283,6 +288,7 @@ namespace SPIRV_CROSS_NAMESPACE virtual void emit_buffer_block(const SPIRVariable &type); virtual void emit_push_constant_block(const SPIRVariable &var); virtual void emit_uniform(const SPIRVariable &var); + virtual void emit_ubo_as_uniforms(spirv_cross::TypeID type, std::string namePrefix); virtual std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, bool packed_type, bool row_major); @@ -572,6 +578,8 @@ namespace SPIRV_CROSS_NAMESPACE void strip_enclosed_expression(std::string &expr); std::string to_member_name(const SPIRType &type, uint32_t index); virtual std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain); + bool canDoFlatteningForMember(uint32_t base); + virtual std::string to_member_reference_webgl10(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain); std::string to_multi_member_reference(const SPIRType &type, const SmallVector &indices); std::string type_to_glsl_constructor(const SPIRType &type); std::string argument_decl(const SPIRFunction::Parameter &arg); diff --git a/wowViewerLib/src/engine/ApiContainer.h b/wowViewerLib/src/engine/ApiContainer.h index fdfebc407..f936c6c30 100644 --- a/wowViewerLib/src/engine/ApiContainer.h +++ b/wowViewerLib/src/engine/ApiContainer.h @@ -5,20 +5,24 @@ #ifndef AWEBWOWVIEWERCPP_APICONTAINER_H #define AWEBWOWVIEWERCPP_APICONTAINER_H +#include class ApiContainer; +typedef std::shared_ptr HApiContainer; +#include #include "WowFilesCacheStorage.h" #include "camera/CameraInterface.h" #include "../include/config.h" #include "../include/databaseHandler.h" + class ApiContainer { private: Config config; public: HWoWFilesCacheStorage cacheStorage = nullptr; - std::shared_ptr hDevice = nullptr; - IClientDatabase *databaseHandler = nullptr; + HGDevice hDevice = nullptr; + std::shared_ptr databaseHandler = nullptr; std::shared_ptr camera = nullptr; std::shared_ptr debugCamera = nullptr; @@ -27,5 +31,9 @@ class ApiContainer { } }; +typedef std::shared_ptr HApiContainer; + +typedef std::array, 64> ADTBoundingBoxHolder; +typedef std::shared_ptr HADTBoundingBoxHolder; #endif //AWEBWOWVIEWERCPP_APICONTAINER_H diff --git a/wowViewerLib/src/engine/CameraMatrices.h b/wowViewerLib/src/engine/CameraMatrices.h index 9286d146f..4f8697a38 100644 --- a/wowViewerLib/src/engine/CameraMatrices.h +++ b/wowViewerLib/src/engine/CameraMatrices.h @@ -5,7 +5,11 @@ #ifndef AWEBWOWVIEWERCPP_CAMERAMATRICES_H #define AWEBWOWVIEWERCPP_CAMERAMATRICES_H -#include +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#include +#include "mathfu/glsl_mappings.h" struct CameraMatrices { mathfu::mat4 perspectiveMat; diff --git a/wowViewerLib/src/engine/DrawStage.h b/wowViewerLib/src/engine/DrawStage.h index d9d307b20..a85aa9c98 100644 --- a/wowViewerLib/src/engine/DrawStage.h +++ b/wowViewerLib/src/engine/DrawStage.h @@ -5,13 +5,21 @@ #ifndef AWEBWOWVIEWERCPP_DRAWSTAGE_H #define AWEBWOWVIEWERCPP_DRAWSTAGE_H +#include + struct DrawStage; struct CameraMatrices; struct ViewPortDimensions; +struct FrameDepedantData; + typedef std::shared_ptr HDrawStage; +typedef std::shared_ptr HFrameDepedantData; #include +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif #include #include "../gapi/interface/IDevice.h" #include "CameraMatrices.h" @@ -25,13 +33,70 @@ struct ViewPortDimensions{ struct MeshesToRender {std::vector meshes;} ; typedef std::shared_ptr HMeshesToRender; +struct FrameDepedantData { + //Glow + float currentGlow; + + //Ambient + mathfu::vec4 exteriorAmbientColor = {1, 1, 1, 1}; + mathfu::vec4 exteriorHorizontAmbientColor = {1, 1, 1, 1}; + mathfu::vec4 exteriorGroundAmbientColor = {1, 1, 1, 1}; + mathfu::vec4 exteriorDirectColor = {0.3,0.3,0.3, 0.3}; + mathfu::vec3 exteriorDirectColorDir; + + mathfu::vec4 interiorAmbientColor; + mathfu::vec4 interiorSunColor; + mathfu::vec3 interiorSunDir; + +//Sky params + bool overrideValuesWithFinalFog = false; + + mathfu::vec4 SkyTopColor; + mathfu::vec4 SkyMiddleColor; + mathfu::vec4 SkyBand1Color; + mathfu::vec4 SkyBand2Color; + mathfu::vec4 SkySmogColor; + mathfu::vec4 SkyFogColor; + +//Fog params + bool FogDataFound = false; + + float FogEnd = 0; + float FogScaler = 0; + float FogDensity = 0; + float FogHeight = 0; + float FogHeightScaler = 0; + float FogHeightDensity = 0; + float SunFogAngle = 0; + mathfu::vec3 FogColor = mathfu::vec3(0,0,0); + mathfu::vec3 EndFogColor = mathfu::vec3(0,0,0); + float EndFogColorDistance = 0; + mathfu::vec3 SunFogColor = mathfu::vec3(0,0,0); + float SunFogStrength = 0; + mathfu::vec3 FogHeightColor = mathfu::vec3(0,0,0); + mathfu::vec4 FogHeightCoefficients = mathfu::vec4(0,0,0,0); + +//Water params + bool useMinimapWaterColor; + bool useCloseRiverColorForDB; + mathfu::vec4 closeRiverColor = mathfu::vec4(0,0,0,0); + mathfu::vec4 farRiverColor = mathfu::vec4(0,0,0,0); + + mathfu::vec4 closeOceanColor = mathfu::vec4(0,0,0,0); + mathfu::vec4 farOceanColor = mathfu::vec4(0,0,0,0); +}; + + struct DrawStage { HCameraMatrices matricesForRendering; HGUniformBufferChunk sceneWideBlockVSPSChunk; - HMeshesToRender meshesToRender; + HFrameDepedantData frameDepedantData; + HMeshesToRender opaqueMeshes; + HMeshesToRender transparentMeshes = nullptr; std::vector drawStageDependencies; + bool invertedZ = false; bool setViewPort = false; ViewPortDimensions viewPortDimensions; @@ -39,7 +104,7 @@ struct DrawStage { bool clearScreen = false; mathfu::vec4 clearColor; - HFrameBuffer target; + HFrameBuffer target = nullptr; }; typedef std::shared_ptr HDrawStage; diff --git a/wowViewerLib/src/engine/SceneComposer.cpp b/wowViewerLib/src/engine/SceneComposer.cpp index fcc4e0415..b42a42d32 100644 --- a/wowViewerLib/src/engine/SceneComposer.cpp +++ b/wowViewerLib/src/engine/SceneComposer.cpp @@ -7,6 +7,9 @@ #include "SceneComposer.h" #include "algorithms/FrameCounter.h" #include "../gapi/UniformBufferStructures.h" +#ifdef __EMSCRIPTEN__ +#include +#endif void SceneComposer::processCaches(int limit) { // std::cout << "WoWSceneImpl::processCaches called " << std::endl; @@ -16,11 +19,15 @@ void SceneComposer::processCaches(int limit) { } } -SceneComposer::SceneComposer(ApiContainer *apiContainer) : m_apiContainer(apiContainer) { +SceneComposer::SceneComposer(HApiContainer apiContainer) : m_apiContainer(apiContainer) { #ifdef __EMSCRIPTEN__ m_supportThreads = false; +// m_supportThreads = emscripten_run_script_int("(SharedArrayBuffer != null) ? 1 : 0") == 1; #endif - nextDeltaTime = std::promise(); + nextDeltaTime[0] = std::promise(); + nextDeltaTime[1] = std::promise(); + nextDeltaTimeForUpdate[0] = std::promise(); + nextDeltaTimeForUpdate[1] = std::promise(); if (m_supportThreads) { loadingResourcesThread = std::thread([&]() { @@ -46,21 +53,30 @@ SceneComposer::SceneComposer(ApiContainer *apiContainer) : m_apiContainer(apiCon using namespace std::chrono_literals; FrameCounter frameCounter; + + //NOTE: it's required to have separate counting process here with getPromiseInd/getNextPromiseInd, + //cause it's not known if the frameNumber will be updated from main thread before + //new currIndex is calculated (and if that doenst happen, desync in numbers happens) + auto currIndex = getPromiseInd(); + while (!this->m_isTerminating) { - auto future = nextDeltaTime.get_future(); + //std::cout << "cullingThread " << currIndex << std::endl; + auto future = nextDeltaTime[currIndex].get_future(); future.wait(); + auto nextIndex = getNextPromiseInd(); // std::cout << "update frame = " << getDevice()->getUpdateFrameNumber() << std::endl; int currentFrame = m_apiContainer->hDevice->getCullingFrameNumber(); - nextDeltaTime = std::promise(); frameCounter.beginMeasurement(); DoCulling(); - frameCounter.endMeasurement("Culling thread "); + m_apiContainer->getConfig()->cullingTimePerFrame = frameCounter.getTimePerFrame(); this->cullingFinished.set_value(true); + currIndex = nextIndex; + } // } catch(const std::exception &e) { // std::cerr << e.what() << std::endl; @@ -73,13 +89,25 @@ SceneComposer::SceneComposer(ApiContainer *apiContainer) : m_apiContainer(apiCon if (m_apiContainer->hDevice->getIsAsynBuffUploadSupported()) { updateThread = std::thread(([&]() { // try { + this->m_apiContainer->hDevice->initUploadThread(); + FrameCounter frameCounter; + //NOTE: Refer to comment in cullingThread code + auto currIndex = getPromiseInd(); while (!this->m_isTerminating) { - auto future = nextDeltaTimeForUpdate.get_future(); + + //std::cout << "updateThread " << currIndex << std::endl; + auto future = nextDeltaTimeForUpdate[currIndex].get_future(); future.wait(); - nextDeltaTimeForUpdate = std::promise(); + auto nextIndex = getNextPromiseInd(); + + frameCounter.beginMeasurement(); DoUpdate(); + frameCounter.endMeasurement("Update thread "); + + m_apiContainer->getConfig()->updateTimePerFrame = frameCounter.getTimePerFrame(); updateFinished.set_value(true); + currIndex = nextIndex; } // } catch(const std::exception &e) { // std::cerr << e.what() << std::endl; @@ -106,21 +134,23 @@ void SceneComposer::DoCulling() { for (int i = 0; i < frameScenario->cullStages.size(); i++) { auto cullStage = frameScenario->cullStages[i]; - auto config = m_apiContainer->getConfig(); - - float farPlane = config->getFarPlane(); - float nearPlane = 1.0; - float fov = toRadian(45.0); cullStage->scene->checkCulling(cullStage); } } void collectMeshes(HDrawStage drawStage, std::vector &meshes) { - if (drawStage->meshesToRender != nullptr) { + if (drawStage->opaqueMeshes != nullptr) { std::copy( - drawStage->meshesToRender->meshes.begin(), - drawStage->meshesToRender->meshes.end(), + drawStage->opaqueMeshes->meshes.begin(), + drawStage->opaqueMeshes->meshes.end(), + std::back_inserter(meshes) + ); + } + if (drawStage->transparentMeshes != nullptr) { + std::copy( + drawStage->transparentMeshes->meshes.begin(), + drawStage->transparentMeshes->meshes.end(), std::back_inserter(meshes) ); } @@ -128,75 +158,150 @@ void collectMeshes(HDrawStage drawStage, std::vector &meshes) { collectMeshes(deps, meshes); } } +#define logExecution {} +//#define logExecution { \ +// std::cout << "Passed "<<__FUNCTION__<<" line " << __LINE__ << std::endl;\ +//} void SceneComposer::DoUpdate() { - FrameCounter frameCounter; - - FrameCounter singleUpdateCNT; - FrameCounter meshesCollectCNT; - - frameCounter.beginMeasurement(); - + logExecution auto device = m_apiContainer->hDevice; + logExecution int updateObjFrame = device->getUpdateFrameNumber(); - + logExecution auto frameScenario = m_frameScenarios[updateObjFrame]; if (frameScenario == nullptr) return; - + logExecution device->startUpdateForNextFrame(); - + logExecution singleUpdateCNT.beginMeasurement(); + logExecution for (auto updateStage : frameScenario->updateStages) { - updateStage->cullResult->scene->update(updateStage); + updateStage->cullResult->scene->produceUpdateStage(updateStage); } + logExecution singleUpdateCNT.endMeasurement("single update "); - + logExecution meshesCollectCNT.beginMeasurement(); + logExecution std::vector additionalChunks; + logExecution for (auto &link : frameScenario->drawStageLinks) { + logExecution link.scene->produceDrawStage(link.drawStage, link.updateStage, additionalChunks); - + logExecution } std::vector meshes; + logExecution collectMeshes(frameScenario->getDrawStage(), meshes); + logExecution meshesCollectCNT.endMeasurement("collectMeshes "); + logExecution - for (auto cullStage : frameScenario->cullStages) { - cullStage->scene->updateBuffers(cullStage); + updateBuffersCNT.beginMeasurement(); + logExecution + for (auto updateStage : frameScenario->updateStages) { + logExecution + updateStage->cullResult->scene->updateBuffers(updateStage); + logExecution } - device->updateBuffers(meshes, additionalChunks); - + updateBuffersCNT.endMeasurement(""); + logExecution + + updateBuffersDeviceCNT.beginMeasurement(); + logExecution + std::vector frameDepDataVec = {}; + logExecution + std::vector*> uniformChunkVec = {}; + logExecution + for (auto updateStage : frameScenario->updateStages) { + frameDepDataVec.push_back(updateStage->cullResult->frameDepedantData); + uniformChunkVec.push_back(&updateStage->uniformBufferChunks); + } + logExecution + device->updateBuffers(uniformChunkVec, frameDepDataVec); + logExecution + updateBuffersDeviceCNT.endMeasurement(""); + logExecution + postLoadCNT.beginMeasurement(); for (auto cullStage : frameScenario->cullStages) { + logExecution cullStage->scene->doPostLoad(cullStage); //Do post load after rendering is done! + logExecution } + postLoadCNT.endMeasurement(""); + logExecution + textureUploadCNT.beginMeasurement(); + logExecution device->uploadTextureForMeshes(meshes); + logExecution + textureUploadCNT.endMeasurement(""); + drawStageAndDepsCNT.beginMeasurement(); if (device->getIsVulkanAxisSystem()) { if (frameScenario != nullptr) { m_apiContainer->hDevice->drawStageAndDeps(frameScenario->getDrawStage()); } } - + drawStageAndDepsCNT.endMeasurement(""); + logExecution + endUpdateCNT.beginMeasurement(); device->endUpdateForNextFrame(); - frameCounter.endMeasurement("Update Thread"); + logExecution + endUpdateCNT.endMeasurement(""); + logExecution + + auto config = m_apiContainer->getConfig(); + config->singleUpdateCNT = singleUpdateCNT.getTimePerFrame(); + config->meshesCollectCNT = meshesCollectCNT.getTimePerFrame();; + config->updateBuffersCNT = updateBuffersCNT.getTimePerFrame();; + config->updateBuffersDeviceCNT = updateBuffersDeviceCNT.getTimePerFrame();; + config->postLoadCNT = postLoadCNT.getTimePerFrame();; + config->textureUploadCNT = textureUploadCNT.getTimePerFrame();; + config->drawStageAndDepsCNT = drawStageAndDepsCNT.getTimePerFrame();; + config->endUpdateCNT = endUpdateCNT.getTimePerFrame();; + logExecution + } +#define logExecution {} +//#define logExecution { \ +// std::cout << "Passed "<<__FUNCTION__<<" line " << __LINE__ << std::endl;\ +//} + void SceneComposer::draw(HFrameScenario frameScenario) { +// std::cout << __FILE__ << ":" << __LINE__<< "m_apiContainer == nullptr :" << ((m_apiContainer == nullptr) ? "true" : "false") << std::endl; +// std::cout << __FILE__ << ":" << __LINE__<< "hdevice == nullptr :" << ((m_apiContainer->hDevice == nullptr) ? "true" : "false") << std::endl; + std::future cullingFuture; std::future updateFuture; + logExecution if (m_supportThreads) { cullingFuture = cullingFinished.get_future(); - - nextDeltaTime.set_value(1.0f); + logExecution + nextDeltaTime[getNextPromiseInd()] = std::promise(); + logExecution + nextDeltaTime[getPromiseInd()].set_value(1.0f); + //std::cout << "set new nextDeltaTime value for " << getPromiseInd() << "frame " << std::endl; + //std::cout << "set new nextDeltaTime promise for " << getNextPromiseInd() << "frame " << std::endl; + logExecution if (m_apiContainer->hDevice->getIsAsynBuffUploadSupported()) { - nextDeltaTimeForUpdate.set_value(1.0f); + logExecution + nextDeltaTimeForUpdate[getNextPromiseInd()] = std::promise(); + logExecution + nextDeltaTimeForUpdate[getPromiseInd()].set_value(1.0f); + + //std::cout << "set new nextDeltaTime value for " << getPromiseInd() << "frame " << std::endl; + //std::cout << "set new nextDeltaTimeForUpdate promise for " << getNextPromiseInd() << "frame " << std::endl; + logExecution updateFuture = updateFinished.get_future(); + logExecution } } - if (frameScenario == nullptr) return; + //if (frameScenario == nullptr) return; // if (needToDropCache) { // if (cacheStorage) { @@ -207,36 +312,48 @@ void SceneComposer::draw(HFrameScenario frameScenario) { m_apiContainer->hDevice->reset(); + logExecution int currentFrame = m_apiContainer->hDevice->getDrawFrameNumber(); auto thisFrameScenario = m_frameScenarios[currentFrame]; - + logExecution m_frameScenarios[currentFrame] = frameScenario; - + logExecution if (!m_supportThreads) { processCaches(10); DoCulling(); } - + logExecution m_apiContainer->hDevice->beginFrame(); if (thisFrameScenario != nullptr && !m_apiContainer->hDevice->getIsVulkanAxisSystem()) { m_apiContainer->hDevice->drawStageAndDeps(thisFrameScenario->getDrawStage()); } + logExecution m_apiContainer->hDevice->commitFrame(); + logExecution m_apiContainer->hDevice->reset(); + logExecution if (!m_apiContainer->hDevice->getIsAsynBuffUploadSupported()) { + logExecution DoUpdate(); + logExecution } + logExecution + if (m_supportThreads) { cullingFuture.wait(); + logExecution cullingFinished = std::promise(); - + logExecution if (m_apiContainer->hDevice->getIsAsynBuffUploadSupported()) { updateFuture.wait(); + logExecution updateFinished = std::promise(); + logExecution } } - + logExecution m_apiContainer->hDevice->increaseFrameNumber(); + logExecution } diff --git a/wowViewerLib/src/engine/SceneComposer.h b/wowViewerLib/src/engine/SceneComposer.h index 2a3fa613c..c2e6d2766 100644 --- a/wowViewerLib/src/engine/SceneComposer.h +++ b/wowViewerLib/src/engine/SceneComposer.h @@ -11,34 +11,77 @@ #include "../include/iostuff.h" #include "../gapi/interface/IDevice.h" #include "SceneScenario.h" - +#include "algorithms/FrameCounter.h" class SceneComposer { private: - ApiContainer *m_apiContainer; + HApiContainer m_apiContainer = nullptr; + private: + + std::thread cullingThread; std::thread updateThread; std::thread loadingResourcesThread; - bool m_supportThreads = false; + bool m_supportThreads = true; bool m_isTerminating = false; + FrameCounter singleUpdateCNT; + FrameCounter meshesCollectCNT; + FrameCounter updateBuffersCNT; + FrameCounter updateBuffersDeviceCNT; + FrameCounter postLoadCNT; + FrameCounter textureUploadCNT; + FrameCounter drawStageAndDepsCNT; + FrameCounter endUpdateCNT; + + void DoCulling(); void DoUpdate(); void processCaches(int limit); - std::promise nextDeltaTime; - std::promise nextDeltaTimeForUpdate; + //Flip-flop delta promises + std::array,2> nextDeltaTime; + std::array,2> nextDeltaTimeForUpdate; std::promise cullingFinished; std::promise updateFinished; std::array m_frameScenarios; + + int getPromiseInd() { + auto frameNum = m_apiContainer->hDevice->getFrameNumber(); + auto promiseInd = frameNum & 1; + return promiseInd; + } + int getNextPromiseInd() { + auto frameNum = m_apiContainer->hDevice->getFrameNumber(); + auto promiseInd = (frameNum + 1) & 1; + return promiseInd; + } public: - SceneComposer(ApiContainer *apiContainer); + SceneComposer(HApiContainer apiContainer); + ~SceneComposer() { + m_isTerminating = true; + auto promiseInd = getPromiseInd(); + try { + float delta = 1.0f; + nextDeltaTime[promiseInd].set_value(delta); + } catch (...) {} + + cullingThread.join(); + + if (m_apiContainer->hDevice->getIsAsynBuffUploadSupported()) { + try { + nextDeltaTimeForUpdate[promiseInd].set_value(1.0f); + } catch (...) {} + updateThread.join(); + } + loadingResourcesThread.join(); + } void draw(HFrameScenario frameScenario); }; diff --git a/wowViewerLib/src/engine/SceneScenario.cpp b/wowViewerLib/src/engine/SceneScenario.cpp index af367c38f..4eca64623 100644 --- a/wowViewerLib/src/engine/SceneScenario.cpp +++ b/wowViewerLib/src/engine/SceneScenario.cpp @@ -31,10 +31,12 @@ HDrawStage FrameScenario::getDrawStage() { return lastDrawStage; } +//FrameScenario::addDrawStage(std::shared_ptr, std::shared_ptr, std::shared_ptr, std::vector, std::allocator>> const&, bool, ViewPortDimensions const&, bool, mathfu::Vector const&, std::shared_ptr) +//FrameScenario::addDrawStage(std::shared_ptr, std::shared_ptr, std::shared_ptr, std::__debug::vector, std::allocator>> const&, bool, ViewPortDimensions const&, bool, mathfu::Vector const&, std::shared_ptr) HDrawStage FrameScenario::addDrawStage(HUpdateStage updateStage, HScene scene, HCameraMatrices matricesForDrawing, - const std::vector &drawStageDependencies, bool setViewPort, - const ViewPortDimensions &viewPortDimensions, bool clearScreen, - const mathfu::vec4 &clearColor, HFrameBuffer fbTarget) { + std::vector const &drawStageDependencies, bool setViewPort, + ViewPortDimensions const &viewPortDimensions, bool clearScreen, bool invertedZ, + mathfu::vec4 const &clearColor, HFrameBuffer fbTarget) { HDrawStage drawStage = std::make_shared(); drawStage->drawStageDependencies = drawStageDependencies; @@ -43,6 +45,7 @@ HDrawStage FrameScenario::addDrawStage(HUpdateStage updateStage, HScene scene, H drawStage->viewPortDimensions = viewPortDimensions; drawStage->clearScreen = clearScreen; drawStage->clearColor = clearColor; + drawStage->invertedZ = invertedZ; drawStage->target = fbTarget; // drawStage->sceneWideBlockVSPSChunk; diff --git a/wowViewerLib/src/engine/SceneScenario.h b/wowViewerLib/src/engine/SceneScenario.h index e0eedbb7d..f4d853890 100644 --- a/wowViewerLib/src/engine/SceneScenario.h +++ b/wowViewerLib/src/engine/SceneScenario.h @@ -32,6 +32,9 @@ struct CullStage { //Output: int adtAreadId = -1; + int areaId = -1; + int parentAreaId = -1; + std::vector m_currentInteriorGroups = {}; bool currentWmoGroupIsExtLit = false; bool currentWmoGroupShowExtSkybox = false; @@ -41,6 +44,8 @@ struct CullStage { ExteriorView exteriorView = ExteriorView(); std::vector interiorViews = {}; + HFrameDepedantData frameDepedantData = std::make_shared(); + std::vector> adtArray = {}; std::vector> m2Array = {}; std::vector> wmoArray = {}; @@ -52,6 +57,14 @@ struct UpdateStage { HCullStage cullResult; animTime_t delta; HCameraMatrices cameraMatrices; + +//Output + HMeshesToRender opaqueMeshes; + HMeshesToRender transparentMeshes; + + std::vector uniformBufferChunks; + std::vector texturesForUpload; + // }; typedef std::shared_ptr HUpdateStage; @@ -81,11 +94,11 @@ class FrameScenario { HDrawStage addDrawStage(HUpdateStage updateStage, HScene scene, HCameraMatrices matricesForDrawing, - const std::vector &drawStageDependencies, + std::vector const &drawStageDependencies, bool setViewPort, - const ViewPortDimensions &viewPortDimensions, - bool clearScreen, - const mathfu::vec4 &clearColor, HFrameBuffer fbTarget); + ViewPortDimensions const &viewPortDimensions, + bool clearScreen, bool invertedZ, + mathfu::vec4 const &clearColor, HFrameBuffer fbTarget); HDrawStage getDrawStage(); }; diff --git a/wowViewerLib/src/engine/WowFilesCacheStorage.h b/wowViewerLib/src/engine/WowFilesCacheStorage.h index 1661da6fe..9aa3db6c9 100644 --- a/wowViewerLib/src/engine/WowFilesCacheStorage.h +++ b/wowViewerLib/src/engine/WowFilesCacheStorage.h @@ -39,7 +39,7 @@ class WoWFilesCacheStorage : public IFileRequester { Cache skelCache; public: WoWFilesCacheStorage(IFileRequest * requestProcessor); - void provideFile(CacheHolderType holderType, const char *fileName, const HFileContent &data) override ; + void provideFile(CacheHolderType holderType, const char *fileName, const HFileContent &data) ; void rejectFile(CacheHolderType holderType, const char* fileName) override ; void actuallDropCache(); diff --git a/wowViewerLib/src/engine/algorithms/C3Spline.cpp b/wowViewerLib/src/engine/algorithms/C3Spline.cpp new file mode 100644 index 000000000..be3d85f14 --- /dev/null +++ b/wowViewerLib/src/engine/algorithms/C3Spline.cpp @@ -0,0 +1,123 @@ +// +// Created by Deamon on 2/14/2021. +// + +#include "C3Spline.h" + + + +C3Spline::C3Spline(M2Array &splinePoints) { + if (splinePoints.size > 0) { + auto firstElem = splinePoints.getElement(0); + m_splinePoints = std::vector(firstElem, firstElem+splinePoints.size); + } +} + +float C3Spline::segLength(unsigned int segment, mathfu::mat4 &coeffs) { + + float t = 0.050000001; + float length = 0.0; + + mathfu::vec3 curPos = {0, 0, 0}; + mathfu::vec3 nextPos = {0, 0, 0}; + + this->evaluate(segment, 0.0, coeffs, curPos); + unsigned int segmenta = 20; + do + { + this->evaluate(segment, t, coeffs, nextPos); + float coeffsa = (nextPos.z - curPos.z) * (nextPos.z - curPos.z) + + (nextPos.y - curPos.y) * (nextPos.y - curPos.y) + + (nextPos.x - curPos.x) * (nextPos.x - curPos.x); + + assert(coeffsa >= 0.0); + + curPos = nextPos; + length = sqrt(coeffsa) + length; + t = t + 0.050000001; + } + while ( segmenta-- != 1 ); + + return length; +} + +double EvaluatePolynomial(unsigned int count, float *coefficients, float t) +{ + double result = *coefficients; + unsigned int i = 1; + if ( count ) + { + do + result = result * t + coefficients[i++]; + while ( i <= count ); + } + return result; +} + +void C3Spline::evaluate(unsigned int segment, float t, mathfu::mat4 &coeffs, mathfu::vec3 &pos) { + pos = {0, 0, 0}; + + for (int i = 0; i < 4; i++) { + assert(segment < m_splinePoints.size()); + + auto column = coeffs.GetColumn(i); + C3Vector &segmentVec = m_splinePoints[segment]; + float val = EvaluatePolynomial(3, column.data_, t); + + pos += mathfu::vec3(segmentVec) * val; + + ++segment; + } +} + + +void C3Spline::evaluateDer1(unsigned int segment, float t, mathfu::mat4 &coeffs, mathfu::vec3 &der) { + der = {0,0,0}; + + for (int i = 0; i < 4; i++) { + assert (segment < this->m_splinePoints.size()); + + auto column = coeffs.GetColumn(i); + + float val = EvaluatePolynomial(2, column.data_, t); + C3Vector &segmentVec = m_splinePoints[segment]; + der += mathfu::vec3(segmentVec) * val; + + segment++; + } +} + + +void C3Spline::arclengthSegT(float s, mathfu::mat4 *coeffs, unsigned int segCount, unsigned int *seg, float *t) { + if ( segCount == 1 ) + { + *seg = 0; + *t = s; + } + else + { + float tLength = this->splineLength * s; + float totLength = 0.0; + *seg = 0; + if ( segCount != 1 ) + { + do + { + assert(*seg < segmentLengths.size()); + + if ( tLength < segmentLengths[*seg] + totLength ) + break; + + totLength = segmentLengths[*seg] + totLength; + *seg = *seg + 1; + } + while ( *seg < segCount - 1 ); + } + + assert(*seg < segmentLengths.size()); + assert(segmentLengths[*seg] > 0.0); + + *t = (tLength - totLength) / segmentLengths[*seg]; + } +} + diff --git a/wowViewerLib/src/engine/algorithms/C3Spline.h b/wowViewerLib/src/engine/algorithms/C3Spline.h new file mode 100644 index 000000000..f4a8805af --- /dev/null +++ b/wowViewerLib/src/engine/algorithms/C3Spline.h @@ -0,0 +1,30 @@ +// +// Created by Deamon on 2/14/2021. +// + +#ifndef AWEBWOWVIEWERCPP_C3SPLINE_H +#define AWEBWOWVIEWERCPP_C3SPLINE_H + + +#include "mathHelper.h" + +class C3Spline { +public: + C3Spline(M2Array &splinePoints); + +protected: + std::vector m_splinePoints = {}; + std::vector segmentLengths = {}; + float splineLength = 0; + + float segLength(unsigned int segment, mathfu::mat4 &coeffs); + void evaluate(unsigned int segment, float t, mathfu::mat4 &coeffs, mathfu::vec3 &pos); + void evaluateDer1(unsigned int segment, float t, mathfu::mat4 &coeffs, mathfu::vec3 &der); + + void arclengthSegT(float s, mathfu::mat4 *coeffs, unsigned int segCount, unsigned int *seg, float *t); + + virtual void calcSegmentLength() = 0; +}; + + +#endif //AWEBWOWVIEWERCPP_C3SPLINE_H diff --git a/wowViewerLib/src/engine/algorithms/C3Spline_Bezier3.cpp b/wowViewerLib/src/engine/algorithms/C3Spline_Bezier3.cpp new file mode 100644 index 000000000..a35aec13f --- /dev/null +++ b/wowViewerLib/src/engine/algorithms/C3Spline_Bezier3.cpp @@ -0,0 +1,72 @@ +// +// Created by Deamon on 2/14/2021. +// + +#include "C3Spline_Bezier3.h" + +mathfu::mat4 s_bezierCoeffs = { + -1.0,3.0,-3.0,1.0, + 3.0,-6.0,3.0,0.0, + -3.0,3.0,0.0,0.0, + 1.0,0,0,0 +}; + +mathfu::mat4 s_bezierDer1Coeffs = { + -3.0, 6.0, -3.0, 0, + 9.0, -12.0, 3.0, 0, + -9.0, 6.0, 0.0, 0, + 3.0, 0.0, 0.0, 0 +}; + +C3Spline_Bezier3::C3Spline_Bezier3(M2Array &splinePoints) : C3Spline(splinePoints) { + + //Calculate segment lengths + calcSegmentLength(); +} + +void C3Spline_Bezier3::posArclength(float angle, mathfu::vec3 &pos) { + unsigned int segment; + + C3Spline_Bezier3::arclengthSegT(angle, &segment, &angle); + C3Spline_Bezier3::evaluate(segment, angle, pos); +} + +void C3Spline_Bezier3::velArclength(float t, mathfu::vec3 &vel) { + unsigned int segment; + + C3Spline_Bezier3::arclengthSegT(t, &segment, &t); + C3Spline_Bezier3::evaluateDer1(segment, t, vel); +} + +void C3Spline_Bezier3::arclengthSegT(float s, unsigned int *seg, float *t) { + C3Spline::arclengthSegT(s, &s_bezierCoeffs, this->m_splinePoints.size() / 3, seg, t); +} + +void C3Spline_Bezier3::evaluate(unsigned int segment, float t, mathfu::vec3 &pos) { + C3Spline::evaluate(3 * segment, t, s_bezierCoeffs, pos); +} + +void C3Spline_Bezier3::evaluateDer1(unsigned int segment, float t, mathfu::vec3 &der) { + C3Spline::evaluateDer1(3 * segment, t, s_bezierDer1Coeffs, der); +} + +float C3Spline_Bezier3::segLength(unsigned int segment) { + return C3Spline::segLength(3 *segment, s_bezierCoeffs); +} + +void C3Spline_Bezier3::calcSegmentLength() { + int segCount = m_splinePoints.size() / 3; + segmentLengths.reserve(segCount); + + for (int i = 0; i < segCount; i++) { + float segLength = C3Spline_Bezier3::segLength(i); + segmentLengths.push_back(segLength); + } + + for (float segmentLength : segmentLengths) { + splineLength += segmentLength; + } +} + + + diff --git a/wowViewerLib/src/engine/algorithms/C3Spline_Bezier3.h b/wowViewerLib/src/engine/algorithms/C3Spline_Bezier3.h new file mode 100644 index 000000000..1735e6fff --- /dev/null +++ b/wowViewerLib/src/engine/algorithms/C3Spline_Bezier3.h @@ -0,0 +1,35 @@ +// +// Created by Deamon on 2/14/2021. +// + +#ifndef AWEBWOWVIEWERCPP_C3SPLINE_BEZIER3_H +#define AWEBWOWVIEWERCPP_C3SPLINE_BEZIER3_H + +#include "C3Spline.h" + +class C3Spline_Bezier3 : public C3Spline { +public: + C3Spline_Bezier3(M2Array &splinePoints); + + void posArclength( float angle, mathfu::vec3 &pos); + void velArclength(float t, mathfu::vec3 &vel); + + mathfu::vec3 getFirstPoint() { + return mathfu::vec3(m_splinePoints[0]); + }; + mathfu::vec3 getLastPoint() { + return mathfu::vec3(m_splinePoints[m_splinePoints.size() - 1]); + }; +private: + float segLength(unsigned int segment); + + void arclengthSegT(float s, unsigned int *seg, float *t); + + void evaluate(unsigned int segment, float t, mathfu::vec3 &pos); + void evaluateDer1(unsigned int segment, float t, mathfu::vec3 &der); + + void calcSegmentLength() override; +}; + + +#endif //AWEBWOWVIEWERCPP_C3SPLINE_BEZIER3_H diff --git a/wowViewerLib/src/engine/algorithms/FrameCounter.cpp b/wowViewerLib/src/engine/algorithms/FrameCounter.cpp index d7fe9d150..555ba6607 100644 --- a/wowViewerLib/src/engine/algorithms/FrameCounter.cpp +++ b/wowViewerLib/src/engine/algorithms/FrameCounter.cpp @@ -5,31 +5,33 @@ #include "FrameCounter.h" #include #include +#include void FrameCounter::beginMeasurement() { -#ifndef SKIP_VULKAN - //m_startTime = std::chrono::high_resolution_clock::now(); -#endif +//#ifndef SKIP_VULKAN + m_startTime = std::chrono::high_resolution_clock::now(); +//#endif } void FrameCounter::endMeasurement(const std::string &source) { - #ifndef SKIP_VULKAN - //auto end = std::chrono::high_resolution_clock::now(); - //frameCounter++; +// #ifndef SKIP_VULKAN + hi_res_time_point end = std::chrono::high_resolution_clock::now(); + frameCounter++; - //double time_taken = - // std::chrono::duration_cast(end - m_startTime).count(); - - //m_accomulatedTimeInterval += time_taken; - - ////More than 1 second elapsed - //if (m_accomulatedTimeInterval > 1000) { - // auto timePerFrame = m_accomulatedTimeInterval / frameCounter; - - // std::cout << source << " perframe time = " << timePerFrame << "ms " << std::endl; + double time_taken = + std::chrono::duration_cast(end - m_startTime).count(); +// + m_accomulatedTimeInterval += time_taken; +// + //More than 1 second elapsed +// if (m_accomulatedTimeInterval > 1000) { + if (frameCounter > 10) { + timePerFrame = m_accomulatedTimeInterval / frameCounter; +// - // frameCounter = 0; - // m_accomulatedTimeInterval = 0; - //} -#endif +// + frameCounter = 0; + m_accomulatedTimeInterval = 0; + } +//#endif } diff --git a/wowViewerLib/src/engine/algorithms/FrameCounter.h b/wowViewerLib/src/engine/algorithms/FrameCounter.h index 699817353..a5e2c0d6e 100644 --- a/wowViewerLib/src/engine/algorithms/FrameCounter.h +++ b/wowViewerLib/src/engine/algorithms/FrameCounter.h @@ -15,12 +15,21 @@ class FrameCounter { void beginMeasurement(); void endMeasurement(const std::string &source); + double getTimePerFrame() { + return timePerFrame; + } private: -#ifndef SKIP_VULKAN - std::chrono::system_clock::time_point m_startTime; +//#if defined(_MSC_VER) +// std::chrono::time_point m_startTime; +//#else + using hi_res_time_point = std::chrono::time_point; + + hi_res_time_point m_startTime; +//#endif double m_accomulatedTimeInterval = 0; int frameCounter = 0; -#endif + + double timePerFrame; }; diff --git a/wowViewerLib/src/engine/algorithms/PortalCullingAlgo.cpp b/wowViewerLib/src/engine/algorithms/PortalCullingAlgo.cpp deleted file mode 100644 index 78508915a..000000000 --- a/wowViewerLib/src/engine/algorithms/PortalCullingAlgo.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by deamon on 11.09.17. -// - -#include "PortalCullingAlgo.h" diff --git a/wowViewerLib/src/engine/algorithms/PortalCullingAlgo.h b/wowViewerLib/src/engine/algorithms/PortalCullingAlgo.h deleted file mode 100644 index 193cd3c0f..000000000 --- a/wowViewerLib/src/engine/algorithms/PortalCullingAlgo.h +++ /dev/null @@ -1,12 +0,0 @@ -// -// Created by deamon on 11.09.17. -// - -#ifndef WEBWOWVIEWERCPP_PORTALCULLINGALGO_H -#define WEBWOWVIEWERCPP_PORTALCULLINGALGO_H - - -#include "../objects/wmo/wmoObject.h" - - -#endif //WEBWOWVIEWERCPP_PORTALCULLINGALGO_H diff --git a/wowViewerLib/src/engine/algorithms/animate.h b/wowViewerLib/src/engine/algorithms/animate.h index b387a62f1..9c7a52646 100644 --- a/wowViewerLib/src/engine/algorithms/animate.h +++ b/wowViewerLib/src/engine/algorithms/animate.h @@ -12,14 +12,24 @@ struct AnimationStruct { int animationIndex; + + int repeatTimes = 1; animTime_t animationTime; M2Sequence *animationRecord; + bool animationFoundInParent; + + int mainVariationIndex; + M2Sequence* mainVariationRecord; }; struct FullAnimationInfo { AnimationStruct currentAnimation; AnimationStruct nextSubAnimation; float blendFactor; + + //TODO: + // float currentAnimationBlendOut; + // float nextSubAnimationBlendIn; }; diff --git a/wowViewerLib/src/engine/algorithms/mathHelper.cpp b/wowViewerLib/src/engine/algorithms/mathHelper.cpp index 43a4e74d8..8d8657249 100644 --- a/wowViewerLib/src/engine/algorithms/mathHelper.cpp +++ b/wowViewerLib/src/engine/algorithms/mathHelper.cpp @@ -5,6 +5,7 @@ #include "mathHelper.h" #include "grahamScan.h" #include +#include float MathHelper::fp69ToFloat(uint16_t x) { @@ -93,7 +94,7 @@ std::vector MathHelper::getFrustumClipsFromMatrix(mathfu::mat4 &ma for (int i = 0; i < 6; i++) { //Hand made normalize - float invVecLength = 1 / (planes[i].xyz().Length()); + float invVecLength = 1.0f / (planes[i].xyz().Length()); planes[i] = planes[i] * invVecLength; } @@ -303,15 +304,15 @@ bool MathHelper::checkFrustum(const std::vector &planes, const CAa if (out == 8) return false; } - // check frustum outside/inside box -// if (points.size() > 0) { -// int out = 0; for (int i = 0; i < 8; i++) out += ((points[i].x > box.max.x) ? 1 : 0); if (out == 8) return false; -// out = 0; for (int i = 0; i < 8; i++) out += ((points[i].x < box.min.x) ? 1 : 0); if (out == 8) return false; -// out = 0; for (int i = 0; i < 8; i++) out += ((points[i].y > box.max.y) ? 1 : 0); if (out == 8) return false; -// out = 0; for (int i = 0; i < 8; i++) out += ((points[i].y < box.min.y) ? 1 : 0); if (out == 8) return false; -// out = 0; for (int i = 0; i < 8; i++) out += ((points[i].z > box.max.z) ? 1 : 0); if (out == 8) return false; -// out = 0; for (int i = 0; i < 8; i++) out += ((points[i].z < box.min.z) ? 1 : 0); if (out == 8) return false; -// } +// check frustum outside/inside box + if (points.size() > 0) { + int out = 0; for (int i = 0; i < 8; i++) out += ((points[i].x > box.max.x) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i < 8; i++) out += ((points[i].x < box.min.x) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i < 8; i++) out += ((points[i].y > box.max.y) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i < 8; i++) out += ((points[i].y < box.min.y) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i < 8; i++) out += ((points[i].z > box.max.z) ? 1 : 0); if (out == 8) return false; + out = 0; for (int i = 0; i < 8; i++) out += ((points[i].z < box.min.z) ? 1 : 0); if (out == 8) return false; + } return true; } @@ -634,3 +635,176 @@ bool MathHelper::isPointInsideNonConvex(mathfu::vec3 &p, const CAaBox &aabb, con return false; } + +bool MathHelper::isAabbIntersect2d(CAaBox a, CAaBox b) { + + bool result = (a.min.x <= b.max.x && a.max.x >= b.min.x) && + (a.min.y <= b.max.y && a.max.y >= b.min.y); + + return result; +} + +mathfu::vec3 MathHelper::calcExteriorColorDir(mathfu::mat4 lookAtMat, int time) { + // Phi Table + static const std::array, 4> phiTable = { + { + { 0.0f, 2.2165682f }, + { 0.25f, 1.9198623f }, + { 0.5f, 2.2165682f }, + { 0.75f, 1.9198623f } + } + }; + + // Theta Table + + + static const std::array, 4> thetaTable = { + { + {0.0f, 3.926991f}, + {0.25f, 3.926991f}, + { 0.5f, 3.926991f }, + { 0.75f, 3.926991f } + } + }; + +// float phi = DayNight::InterpTable(&DayNight::phiTable, 4u, DayNight::g_dnInfo.dayProgression); +// float theta = DayNight::InterpTable(&DayNight::thetaTable, 4u, DayNight::g_dnInfo.dayProgression); + + float phi = phiTable[0][1]; + float theta = thetaTable[0][1]; + + //Find Index + float timeF = time / 2880.0f; + int firstIndex = -1; + for (int i = 0; i < 4; i++) { + if (timeF < phiTable[i][0]) { + firstIndex = i; + break; + } + } + if (firstIndex == -1) { + firstIndex = 3; + } + { + float alpha = (phiTable[firstIndex][0] - timeF) / (thetaTable[firstIndex][0] - thetaTable[firstIndex-1][0]); + phi = phiTable[firstIndex][1]*(1.0 - alpha) + phiTable[firstIndex - 1][1]*alpha; + } + + + // Convert from spherical coordinates to XYZ + // x = rho * sin(phi) * cos(theta) + // y = rho * sin(phi) * sin(theta) + // z = rho * cos(phi) + + float sinPhi = (float) sin(phi); + float cosPhi = (float) cos(phi); + + float sinTheta = (float) sin(theta); + float cosTheta = (float) cos(theta); + + mathfu::mat3 lookAtRotation = mathfu::mat4::ToRotationMatrix(lookAtMat); + + mathfu::vec4 sunDirWorld = mathfu::vec4(sinPhi * cosTheta, sinPhi * sinTheta, cosPhi, 0); +// mathfu::vec4 sunDirWorld = mathfu::vec4(-0.30822, -0.30822, -0.89999998, 0); + return (lookAtRotation * sunDirWorld.xyz()); +} + +mathfu::vec3 MathHelper::hsv2rgb(MathHelper::hsv in) { + double hh, p, q, t, ff; + long i; + mathfu::vec3 out; + + if(in.s <= 0.0) { // < is bogus, just shuts up warnings + out.x = in.v; + out.y = in.v; + out.z = in.v; + return out; + } + hh = in.h; + if(hh >= 360.0) hh = 0.0; + hh /= 60.0; + i = (long)hh; + ff = hh - i; + p = in.v * (1.0 - in.s); + q = in.v * (1.0 - (in.s * ff)); + t = in.v * (1.0 - (in.s * (1.0 - ff))); + + switch(i) { + case 0: + out.x = in.v; + out.y = t; + out.z = p; + break; + case 1: + out.x = q; + out.y = in.v; + out.z = p; + break; + case 2: + out.x = p; + out.y = in.v; + out.z = t; + break; + + case 3: + out.x = p; + out.y = q; + out.z = in.v; + break; + case 4: + out.x = t; + out.y = p; + out.z = in.v; + break; + case 5: + default: + out.x = in.v; + out.y = p; + out.z = q; + break; + } + return out; +} + +MathHelper::hsv MathHelper::rgb2hsv(mathfu::vec3 in) { + hsv out; + double min, max, delta; + + min = in.x < in.y ? in.x : in.y; + min = min < in.z ? min : in.z; + + max = in.x > in.y ? in.x : in.y; + max = max > in.z ? max : in.z; + + out.v = max; // v + delta = max - min; + if (delta < 0.00001) + { + out.s = 0; + out.h = 0; // undefined, maybe nan? + return out; + } + if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash + out.s = (delta / max); // s + } else { + // if max is 0, then r = g = b = 0 + // s = 0, h is undefined + out.s = 0.0; + out.h = -1; //NAN; // its now undefined + return out; + } + if( in.x >= max ) // > is bogus, just keeps compilor happy + out.h = ( in.y - in.z ) / delta; // between yellow & magenta + else + if( in.y >= max ) + out.h = 2.0 + ( in.z - in.x ) / delta; // between cyan & yellow + else + out.h = 4.0 + ( in.x - in.y ) / delta; // between magenta & cyan + + out.h *= 60.0; // degrees + + if( out.h < 0.0 ) + out.h += 360.0; + + return out; +} diff --git a/wowViewerLib/src/engine/algorithms/mathHelper.h b/wowViewerLib/src/engine/algorithms/mathHelper.h index b4e4810ab..bf225774d 100644 --- a/wowViewerLib/src/engine/algorithms/mathHelper.h +++ b/wowViewerLib/src/engine/algorithms/mathHelper.h @@ -12,27 +12,21 @@ #include #define toRadian(x) (float) ((float) (x) * ((float)M_PI/ (float)180.0)) -const float ROUNDING_ERROR_f32 = 0.001f; -inline bool feq(const float a, const float b, const float tolerance = ROUNDING_ERROR_f32) -{ - return (a + tolerance >= b) && (a - tolerance <= b); -} - - -inline int worldCoordinateToAdtIndex(float x) { - return floor((32.0f - (x / 533.33333f))); -} - -inline int worldCoordinateToGlobalAdtChunk(float x) { - return floor(( (32.0f*16.0f) - (x / (533.33333f / 16.0f) ))); -} - -inline float AdtIndexToWorldCoordinate(int x) { - return (32.0f - x) * 533.33333f; -} class MathHelper { public: + static constexpr float TILESIZE = 1600.0f/3.0f ; + static constexpr float CHUNKSIZE = TILESIZE / 16.0f; + static constexpr float UNITSIZE = CHUNKSIZE / 8.0f; + + typedef struct { + double h; // angle in degrees + double s; // a fraction between 0 and 1 + double v; // a fraction between 0 and 1 + } hsv; + + static hsv rgb2hsv(mathfu::vec3 in); + static mathfu::vec3 hsv2rgb(hsv in); static float fp69ToFloat(uint16_t x); @@ -79,7 +73,6 @@ class MathHelper { }; static const mathfu::mat4 &getAdtToWorldMat4() { - const float TILESIZE = 533.333333333f; // mathfu::mat4 adtToWorldMat4 = mathfu::mat4::Identity(); // adtToWorldMat4 *= MathHelper::RotationX(toRadian(90)); @@ -107,6 +100,8 @@ class MathHelper { static float distanceFromAABBToPoint(const CAaBox &aabb, mathfu::vec3 &p); + static bool isAabbIntersect2d(CAaBox a, CAaBox b); + static bool isPointInsideAABB(const CAaBox &aabb, mathfu::vec3 &p); static bool isPointInsideAABB(const mathfu::vec2 aabb[2], mathfu::vec2 &p); @@ -120,7 +115,26 @@ class MathHelper { static float distance_aux(float p, float lower, float upper); static float distanceFromAABBToPoint2DSquared(const mathfu::vec2 aabb[2], mathfu::vec2 &p); + static mathfu::vec3 calcExteriorColorDir(mathfu::mat4 lookAtMat, int time); }; +const float ROUNDING_ERROR_f32 = 0.001f; +inline bool feq(const float a, const float b, const float tolerance = ROUNDING_ERROR_f32) +{ + return (a + tolerance >= b) && (a - tolerance <= b); +} + + +inline int worldCoordinateToAdtIndex(float x) { + return floor((32.0f - (x / MathHelper::TILESIZE))); +} + +inline int worldCoordinateToGlobalAdtChunk(float x) { + return floor(( (32.0f*16.0f) - (x / (MathHelper::CHUNKSIZE) ))); +} + +inline float AdtIndexToWorldCoordinate(float x) { + return (32.0f - x) * MathHelper::TILESIZE; +} #endif //WOWVIEWERLIB_MATHHELPER_H diff --git a/wowViewerLib/src/engine/algorithms/quick-sort-omp.h b/wowViewerLib/src/engine/algorithms/quick-sort-omp.h index d85f2b52f..75a1dcff5 100644 --- a/wowViewerLib/src/engine/algorithms/quick-sort-omp.h +++ b/wowViewerLib/src/engine/algorithms/quick-sort-omp.h @@ -5,6 +5,7 @@ #ifndef AWEBWOWVIEWERCPP_QUICK_SORT_OMP_H #define AWEBWOWVIEWERCPP_QUICK_SORT_OMP_H #include +#include "utility.h" void quickSort_parallel_internal(int* array, int left, int right, int cutoff, std::function &compare); @@ -72,8 +73,84 @@ void quickSort_parallel_internal(int* array, int left, int right, int cutoff, st } } } - } +namespace internal +{ + std::size_t g_depth = 0L; + const std::size_t cutoff = 1000000L; + + template + void qsort3w(RanIt _First, RanIt _Last, _Pred compare) + { + if (_First >= _Last) return; + + std::size_t _Size = 0L; g_depth++; + if ((_Size = std::distance(_First, _Last)) > 0) + { + RanIt _LeftIt = _First, _RightIt = _Last; + bool is_swapped_left = false, is_swapped_right = false; + typename std::iterator_traits::value_type _Pivot = *_First; + + RanIt _FwdIt = _First + 1; + while (_FwdIt <= _RightIt) + { + if (compare(*_FwdIt, _Pivot)) + { + is_swapped_left = true; + std::iter_swap(_LeftIt, _FwdIt); + _LeftIt++; _FwdIt++; + } + + else if (compare(_Pivot, *_FwdIt)) { + is_swapped_right = true; + std::iter_swap(_RightIt, _FwdIt); + _RightIt--; + } + + else _FwdIt++; + } + + if (_Size >= internal::cutoff) + { +#pragma omp taskgroup + { +#pragma omp task untied mergeable + if ((std::distance(_First, _LeftIt) > 0) && (is_swapped_left)) + qsort3w(_First, _LeftIt - 1, compare); + +#pragma omp task untied mergeable + if ((std::distance(_RightIt, _Last) > 0) && (is_swapped_right)) + qsort3w(_RightIt + 1, _Last, compare); + } + } + + else + { +#pragma omp task untied mergeable + { + if ((std::distance(_First, _LeftIt) > 0) && is_swapped_left) + qsort3w(_First, _LeftIt - 1, compare); + + if ((std::distance(_RightIt, _Last) > 0) && is_swapped_right) + qsort3w(_RightIt + 1, _Last, compare); + } + } + } + } + + template + void parallel_sort(BidirIt _First, BidirIt _Last, _Pred compare) + { + std::size_t pos = 0L; g_depth = 0L; + if (!misc::sorted(_First, _Last, pos, compare)) + { +#pragma omp parallel num_threads(12) +#pragma omp master + internal::qsort3w(_First, _Last - 1, compare); + } + } +} + #endif //AWEBWOWVIEWERCPP_QUICK_SORT_OMP_H diff --git a/wowViewerLib/src/engine/algorithms/utility.h b/wowViewerLib/src/engine/algorithms/utility.h new file mode 100644 index 000000000..0fe909b49 --- /dev/null +++ b/wowViewerLib/src/engine/algorithms/utility.h @@ -0,0 +1,106 @@ +#ifndef UTILITY_STL_H +#define UTILITY_STL_H + +namespace misc +{ + const std::size_t minval_radix = 7; + const std::size_t maxval_radix = 8; + + struct partitioner + { + typedef std::pair partition_range; + + public: + explicit partitioner(const partition_range& range, const std::size_t count) + : _left(std::size_t(0)), position(std::size_t(0)), _range(range), partitions(count) + { + length = static_cast( + std::ceil(this->distance() / (double)this->count_base())); + } + + partitioner(const partitioner& p) + : _left(p.left_base()), _range(p.range_base()), partitions(p.count_base()) { } + + public: + const partitioner& operator[](std::size_t index) { + position = index; return *this; + } + + public: + std::size_t size() const { + return static_cast( + std::ceil(this->distance() / (double)this->size_base())); + } + + std::size_t partition_size() const { + return this->size_base(); + } + + std::size_t first() const { + std::size_t pos = this->position_base() * this->size_base(); + return (pos <= _range.second) ? pos : _range.second; + } + std::size_t second() const { + std::size_t pos = (this->position_base() + 1) * this->size_base(); + return (pos <= _range.second) ? pos : _range.second; + } + + public: + std::size_t distance() const { + return this->range_base().second - this->range_base().first + 1; + } + + public: + std::size_t left_base() const { return _left; } + partition_range range_base() const { return _range; } + std::size_t count_base() const { return partitions; } + std::size_t size_base() const { return length; } + std::size_t position_base() const { return position; } + + protected: + + std::size_t _left; + partition_range _range; + std::size_t partitions; + std::size_t length; + std::size_t position; + + }; + + + + template + std::size_t sorted(BidirIt _First, BidirIt _Last, \ + std::size_t& position, _Pred compare) + { + bool is_sorted = true; + + BidirIt _MidIt = _First + (_Last - _First) / 2; + if (compare(*_MidIt, *_First) || compare(*(_Last - 1), *_MidIt) || + compare(*(_Last - 1), *_First)) return !is_sorted; + + for (auto _FwdIt = _First; _FwdIt != _Last - 1 && is_sorted; _FwdIt++) + { + if (compare(*(_FwdIt + 1), *_FwdIt)) + { + if (is_sorted == true) + position = std::distance(_First, _FwdIt); + + is_sorted = false; + } + } + + return is_sorted; + } + + template + void print_out(RandomIt _First, RandomIt _Second) + { + for (auto it = _First; it != _Second; it++) + std::cout << *it << " "; + + std::cout << "\n"; + } +} + +#endif // UTILITY_STL_H diff --git a/wowViewerLib/src/engine/cache/cache.h b/wowViewerLib/src/engine/cache/cache.h index 551c89346..2af3e4ce4 100644 --- a/wowViewerLib/src/engine/cache/cache.h +++ b/wowViewerLib/src/engine/cache/cache.h @@ -32,25 +32,34 @@ class Cache { private: IFileRequest *m_fileRequestProcessor; public: - std::mutex accessMutex; + std::mutex cacheMapMutex; std::mutex getFileMutex; - std::unique_lock processCacheLock; - std::unique_lock provideFileLock; + std::mutex objectsToBeProcessedMutex; + std::unordered_map> m_cache; + std::unordered_map> m_cacheFdid; std::forward_list m_objectsToBeProcessed; public: Cache(IFileRequest *fileRequestProcessor, CacheHolderType holderType) : m_fileRequestProcessor(fileRequestProcessor), holderType(holderType){ - processCacheLock = std::unique_lock(accessMutex,std::defer_lock); - provideFileLock = std::unique_lock(accessMutex,std::defer_lock); } void processCacheQueue(int limit) { int objectsProcessed = 0; - processCacheLock.lock(); + auto const &m_cache_const = m_cache; while (!m_objectsToBeProcessed.empty()) { + std::unique_lock lck (objectsToBeProcessedMutex,std::defer_lock); + std::unique_lock cacheLck (cacheMapMutex,std::defer_lock); + + lck.lock(); auto const it = m_objectsToBeProcessed.front(); - std::weak_ptr weakPtr = m_cache.at(it.fileName); + m_objectsToBeProcessed.pop_front(); + lck.unlock(); + + + cacheLck.lock(); + std::weak_ptr weakPtr = m_cache_const.at(it.fileName); + cacheLck.unlock(); // std::cout << "Processing file " << it.fileName << std::endl << std::flush; if (!weakPtr.expired()) { @@ -63,14 +72,12 @@ class Cache { } // std::cout << "Processed file " << it.fileName << std::endl << std::flush; - m_objectsToBeProcessed.pop_front(); - objectsProcessed++; if (objectsProcessed > limit) { break; } } - processCacheLock.unlock(); + } void provideFile(std::string &fileName, HFileContent fileContent) { trim(fileName); @@ -79,12 +86,17 @@ class Cache { // std::cout << "filename:" << fileName << " hex: " << string_to_hex(fileName) << std::endl; // std::cout << "first in storage:" << m_cache.begin()->first << " hex: " << string_to_hex(m_cache.begin()->first) << std::endl << std::flush; - provideFileLock.lock(); - auto it = m_cache.find(fileName); - if (it != m_cache.end()) { + auto const &m_cache_const = m_cache; + + std::unique_lock cacheLck(cacheMapMutex, std::defer_lock); + cacheLck.lock(); + auto it = m_cache_const.find(fileName); + bool found = it != m_cache_const.end(); + cacheLck.unlock(); + if (found) { + std::lock_guard guard(objectsToBeProcessedMutex); m_objectsToBeProcessed.push_front({fileName, fileContent}); } - provideFileLock.unlock(); } /* @@ -93,12 +105,17 @@ class Cache { std::shared_ptr get (std::string fileName) { std::lock_guard lock(getFileMutex); + std::unique_lock cacheLck(cacheMapMutex, std::defer_lock); + fileName = trimmed(fileName); std::transform(fileName.begin(), fileName.end(),fileName.begin(), ::toupper); std::replace(fileName.begin(), fileName.end(), '\\', '/'); + cacheLck.lock(); auto it = m_cache.find(fileName); - if(it != m_cache.end() ) { + bool found = it != m_cache.end(); + cacheLck.unlock(); + if(found) { if (std::shared_ptr shared = it->second.lock()) { //element found; return shared; @@ -107,10 +124,12 @@ class Cache { std::shared_ptr sharedPtr = std::make_shared(fileName); std::weak_ptr weakPtr(sharedPtr); + cacheLck.lock(); m_cache[fileName] = weakPtr; + cacheLck.unlock(); // std::cout << "m_cache.size() == " << m_cache.size() << " " << __PRETTY_FUNCTION__ << std::endl; - m_fileRequestProcessor->requestFile(fileName.c_str(), this->holderType); + m_fileRequestProcessor->requestFile(fileName, this->holderType, weakPtr); return sharedPtr; } @@ -121,28 +140,34 @@ class Cache { return nullptr; } - std::lock_guard lock(getFileMutex); + std::lock_guard guard(getFileMutex); + + std::unique_lock cacheLck(cacheMapMutex, std::defer_lock); std::stringstream ss; ss << "File" << std::setfill('0') << std::setw(8) << std::hex << id <<".unk"; std::string fileName = ss.str(); - auto it = m_cache.find(fileName); - if (it != m_cache.end()) { - if (!it->second.expired()) { - return it->second.lock(); - } else { + cacheLck.lock(); + { + auto it = m_cache.find(fileName); + bool found = it != m_cache.end(); + if (found) { + if (!it->second.expired()) { + return it->second.lock(); + } else { // std::cout << "getFileId: fileName = " << fileName << " is expired" << std::endl; - m_cache.erase(it); + } } } - std::shared_ptr sharedPtr = std::make_shared(id); std::weak_ptr weakPtr(sharedPtr); + m_cache[fileName] = weakPtr; + cacheLck.unlock(); - m_fileRequestProcessor->requestFile(fileName.c_str(), this->holderType); + m_fileRequestProcessor->requestFile(fileName, this->holderType, weakPtr); return sharedPtr; } @@ -150,7 +175,12 @@ class Cache { void reject(std::string fileName) { trim(fileName); + std::unique_lock cacheLck(cacheMapMutex, std::defer_lock); + + cacheLck.lock(); std::weak_ptr weakPtr = m_cache[fileName]; + cacheLck.unlock(); + if (!weakPtr.expired()) { if (std::shared_ptr sharedPtr = weakPtr.lock()) { @@ -160,8 +190,15 @@ class Cache { } void clear() { + std::unique_lock cacheLck(cacheMapMutex, std::defer_lock); + cacheLck.lock(); m_cache.clear(); + cacheLck.unlock(); + + std::unique_lock lck(objectsToBeProcessedMutex, std::defer_lock); + lck.lock(); m_objectsToBeProcessed.clear(); + lck.unlock(); } private: /* diff --git a/wowViewerLib/src/engine/camera/firstPersonCamera.h b/wowViewerLib/src/engine/camera/firstPersonCamera.h index 62635e6b6..84b0ea7a0 100644 --- a/wowViewerLib/src/engine/camera/firstPersonCamera.h +++ b/wowViewerLib/src/engine/camera/firstPersonCamera.h @@ -79,6 +79,7 @@ class FirstPersonCamera: public ICamera { public: void tick(animTime_t timeDelta) override; void setCameraPos(float x, float y, float z) override; + void setCameraLookAt(float x, float y, float z) override {}; }; diff --git a/wowViewerLib/src/engine/camera/firstPersonOrthoCamera.h b/wowViewerLib/src/engine/camera/firstPersonOrthoCamera.h index 0f5681fd5..bd7762130 100644 --- a/wowViewerLib/src/engine/camera/firstPersonOrthoCamera.h +++ b/wowViewerLib/src/engine/camera/firstPersonOrthoCamera.h @@ -5,6 +5,7 @@ #ifndef WOWMAPVIEWERREVIVED_FIRSTPERSONORTHOCAMERA_H #define WOWMAPVIEWERREVIVED_FIRSTPERSONORTHOCAMERA_H +#define _USE_MATH_DEFINES #include #include "mathfu/glsl_mappings.h" #include "CameraInterface.h" diff --git a/wowViewerLib/src/engine/camera/firstPersonOrthoStaticCamera.cpp b/wowViewerLib/src/engine/camera/firstPersonOrthoStaticCamera.cpp new file mode 100644 index 000000000..da1865d38 --- /dev/null +++ b/wowViewerLib/src/engine/camera/firstPersonOrthoStaticCamera.cpp @@ -0,0 +1,70 @@ +// +// Created by deamon on 18.05.17. +// + +#include +#include "firstPersonOrthoStaticCamera.h" + +void FirstPersonOrthoStaticCamera::addForwardDiff(float val) {} +void FirstPersonOrthoStaticCamera::addHorizontalViewDir(float val) {} +void FirstPersonOrthoStaticCamera::addVerticalViewDir(float val) {} +void FirstPersonOrthoStaticCamera::startMovingForward(){} +void FirstPersonOrthoStaticCamera::stopMovingForward(){} +void FirstPersonOrthoStaticCamera::startMovingBackwards(){} +void FirstPersonOrthoStaticCamera::stopMovingBackwards(){} +void FirstPersonOrthoStaticCamera::startStrafingLeft(){} +void FirstPersonOrthoStaticCamera::stopStrafingLeft(){} +void FirstPersonOrthoStaticCamera::startStrafingRight(){} +void FirstPersonOrthoStaticCamera::stopStrafingRight(){} +void FirstPersonOrthoStaticCamera::startMovingUp(){} +void FirstPersonOrthoStaticCamera::stopMovingUp(){} +void FirstPersonOrthoStaticCamera::startMovingDown(){} +void FirstPersonOrthoStaticCamera::stopMovingDown(){} + +void FirstPersonOrthoStaticCamera::tick (animTime_t timeDelta) { +// cameraRotationMat = cameraRotationMat * MathHelper::RotationX(90*M_PI/180); + lookAtMat = + mathfu::mat4::LookAt( + camera.xyz(), + lookAt, + mathfu::vec3(0,0,1)); + + + mathfu::vec4 interiorSunDir = mathfu::vec4(-0.30822f, -0.30822f, -0.89999998f, 0); + interiorSunDir = lookAtMat.Transpose().Inverse() * interiorSunDir; + interiorSunDir = mathfu::vec4(interiorSunDir.xyz() * (1.0f / interiorSunDir.xyz().Length()), 0.0f); + + this->interiorDirectLightDir = interiorSunDir; + + mathfu::vec4 upVector ( 0.0, 0.0 , 1.0 , 0.0); + mathfu::mat3 lookAtRotation = mathfu::mat4::ToRotationMatrix(lookAtMat); + this->upVector = (lookAtRotation * upVector.xyz()); +} +void FirstPersonOrthoStaticCamera::setCameraPos (float x, float y, float z) { + //Reset camera + this->camera = mathfu::vec4(x, y, z, 0); +} +void FirstPersonOrthoStaticCamera::setCameraLookAt(float x, float y, float z) { + this->lookAt = mathfu::vec3(x, y, z); +} + + +HCameraMatrices FirstPersonOrthoStaticCamera::getCameraMatrices(float fov, + float canvasAspect, + float nearPlane, + float farPlane) { + HCameraMatrices cameraMatrices = std::make_shared(); + cameraMatrices->perspectiveMat = mathfu::mat4::Perspective( + fov, + canvasAspect, + nearPlane, + farPlane); + cameraMatrices->lookAtMat = lookAtMat; + + cameraMatrices->cameraPos = camera; + cameraMatrices->viewUp = mathfu::vec4(upVector, 0); + cameraMatrices->interiorDirectLightDir = this->interiorDirectLightDir; + + return cameraMatrices; +} + diff --git a/wowViewerLib/src/engine/camera/firstPersonOrthoStaticCamera.h b/wowViewerLib/src/engine/camera/firstPersonOrthoStaticCamera.h new file mode 100644 index 000000000..61f110bce --- /dev/null +++ b/wowViewerLib/src/engine/camera/firstPersonOrthoStaticCamera.h @@ -0,0 +1,64 @@ +// +// Created by deamon on 18.05.17. +// + +#ifndef WOWMAPVIEWERREVIVED_FIRSTPERSONORTHOCAMERA_H +#define WOWMAPVIEWERREVIVED_FIRSTPERSONORTHOCAMERA_H + +#include "CameraInterface.h" + + +class FirstPersonOrthoStaticCamera: public ICamera { +public: + FirstPersonOrthoStaticCamera(){}; + +private: + mathfu::vec4 camera = {0, 0, 0, 0}; + mathfu::vec4 interiorDirectLightDir = {0, 0, 0, 0}; + mathfu::vec3 lookAt = {0, 0, 0}; + mathfu::vec3 upVector = {0, 0, 0}; + mathfu::mat4 lookAtMat = {}; +public: + //Implemented IControllable + void addHorizontalViewDir(float val) override; + void addVerticalViewDir(float val) override; + void addForwardDiff(float val) override; + void startMovingForward() override; + void stopMovingForward() override; + void startMovingBackwards() override; + void stopMovingBackwards() override; + void startStrafingLeft() override; + void stopStrafingLeft() override; + void startStrafingRight() override; + void stopStrafingRight() override; + void startMovingUp() override; + void stopMovingUp() override; + void startMovingDown() override; + void stopMovingDown() override; + void zoomInFromMouseScroll(float val) override {}; + void zoomInFromTouch(float val) override {}; + void addCameraViewOffset(float x, float y) override {}; + void setMovementSpeed(float value) override {}; + + void getCameraPosition(float *position) override { + position[0] = camera.x; + position[1] = camera.y; + position[2] = camera.z; + } + + +public: + //Implemented ICamera + HCameraMatrices getCameraMatrices(float fov, + float canvasAspect, + float nearPlane, + float farPlane) override; + +public: + void tick(animTime_t timeDelta) override; + void setCameraPos(float x, float y, float z) override; + void setCameraLookAt(float x, float y, float z) override; +}; + + +#endif //WOWMAPVIEWERREVIVED_FIRSTPERSONORTHOCAMERA_H diff --git a/wowViewerLib/src/engine/camera/firstPersonOrthoStaticTopDownCamera.cpp b/wowViewerLib/src/engine/camera/firstPersonOrthoStaticTopDownCamera.cpp new file mode 100644 index 000000000..c251e2db9 --- /dev/null +++ b/wowViewerLib/src/engine/camera/firstPersonOrthoStaticTopDownCamera.cpp @@ -0,0 +1,72 @@ +// +// Created by deamon on 18.05.17. +// + +#include +#include "firstPersonOrthoStaticTopDownCamera.h" +#include "m2TiedCamera.h" + +void FirstPersonOrthoStaticTopDownCamera::addForwardDiff(float val) {} +void FirstPersonOrthoStaticTopDownCamera::addHorizontalViewDir(float val) {} +void FirstPersonOrthoStaticTopDownCamera::addVerticalViewDir(float val) {} +void FirstPersonOrthoStaticTopDownCamera::startMovingForward(){} +void FirstPersonOrthoStaticTopDownCamera::stopMovingForward(){} +void FirstPersonOrthoStaticTopDownCamera::startMovingBackwards(){} +void FirstPersonOrthoStaticTopDownCamera::stopMovingBackwards(){} +void FirstPersonOrthoStaticTopDownCamera::startStrafingLeft(){} +void FirstPersonOrthoStaticTopDownCamera::stopStrafingLeft(){} +void FirstPersonOrthoStaticTopDownCamera::startStrafingRight(){} +void FirstPersonOrthoStaticTopDownCamera::stopStrafingRight(){} +void FirstPersonOrthoStaticTopDownCamera::startMovingUp(){} +void FirstPersonOrthoStaticTopDownCamera::stopMovingUp(){} +void FirstPersonOrthoStaticTopDownCamera::startMovingDown(){} +void FirstPersonOrthoStaticTopDownCamera::stopMovingDown(){} + +void FirstPersonOrthoStaticTopDownCamera::tick (animTime_t timeDelta) { +// cameraRotationMat = cameraRotationMat * MathHelper::RotationX(90*M_PI/180); + lookAtMat = + mathfu::mat4( + 0,1,0,0, + -1,0,0,0, + 0,0,1,0, + 0,0,0,1 + ) * mathfu::mat4::FromTranslationVector(-this->camera.xyz()); + + mathfu::vec4 interiorSunDir = mathfu::vec4(-0.30822f, -0.30822f, -0.89999998f, 0); + interiorSunDir = lookAtMat.Transpose().Inverse() * interiorSunDir; + interiorSunDir = mathfu::vec4(interiorSunDir.xyz() * (1.0f / interiorSunDir.xyz().Length()), 0.0f); + + this->interiorDirectLightDir = interiorSunDir; + + mathfu::vec4 upVector ( 0.0, 0.0 , 1.0 , 0.0); + mathfu::mat3 lookAtRotation = mathfu::mat4::ToRotationMatrix(lookAtMat); + this->upVector = (lookAtRotation * upVector.xyz()); +} +void FirstPersonOrthoStaticTopDownCamera::setCameraPos (float x, float y, float z) { + //Reset camera + this->camera = mathfu::vec4(x, y, z, 0); +} +void FirstPersonOrthoStaticTopDownCamera::setCameraLookAt(float x, float y, float z) { + this->lookAt = mathfu::vec3(x, y, z); +} + + +HCameraMatrices FirstPersonOrthoStaticTopDownCamera::getCameraMatrices(float fov, + float canvasAspect, + float nearPlane, + float farPlane) { + HCameraMatrices cameraMatrices = std::make_shared(); + cameraMatrices->perspectiveMat = mathfu::mat4::Perspective( + fov, + canvasAspect, + nearPlane, + farPlane); + cameraMatrices->lookAtMat = lookAtMat; + + cameraMatrices->cameraPos = camera; + cameraMatrices->viewUp = mathfu::vec4(upVector, 0); + cameraMatrices->interiorDirectLightDir = this->interiorDirectLightDir; + + return cameraMatrices; +} + diff --git a/wowViewerLib/src/engine/camera/firstPersonOrthoStaticTopDownCamera.h b/wowViewerLib/src/engine/camera/firstPersonOrthoStaticTopDownCamera.h new file mode 100644 index 000000000..3ba9e7ab7 --- /dev/null +++ b/wowViewerLib/src/engine/camera/firstPersonOrthoStaticTopDownCamera.h @@ -0,0 +1,67 @@ +// +// Created by deamon on 18.05.17. +// + +#ifndef WOWMAPVIEWERREVIVED_FIRSTPERSONORTHOTOPDOWNCAMERA_H +#define WOWMAPVIEWERREVIVED_FIRSTPERSONORTHOTOPDOWNCAMERA_H + +#define _USE_MATH_DEFINES +#include +#include "mathfu/glsl_mappings.h" +#include "CameraInterface.h" + + +class FirstPersonOrthoStaticTopDownCamera: public ICamera { +public: + FirstPersonOrthoStaticTopDownCamera(){}; + +private: + mathfu::vec4 camera = {0, 0, 0, 0}; + mathfu::vec4 interiorDirectLightDir = {0, 0, 0, 0}; + mathfu::vec3 lookAt = {0, 0, 0}; + mathfu::vec3 upVector = {0, 0, 0}; + mathfu::mat4 lookAtMat = {}; +public: + //Implemented IControllable + void addHorizontalViewDir(float val) override; + void addVerticalViewDir(float val) override; + void addForwardDiff(float val) override; + void startMovingForward() override; + void stopMovingForward() override; + void startMovingBackwards() override; + void stopMovingBackwards() override; + void startStrafingLeft() override; + void stopStrafingLeft() override; + void startStrafingRight() override; + void stopStrafingRight() override; + void startMovingUp() override; + void stopMovingUp() override; + void startMovingDown() override; + void stopMovingDown() override; + void zoomInFromMouseScroll(float val) override {}; + void zoomInFromTouch(float val) override {}; + void addCameraViewOffset(float x, float y) override {}; + void setMovementSpeed(float value) override {}; + + void getCameraPosition(float *position) override { + position[0] = camera.x; + position[1] = camera.y; + position[2] = camera.z; + } + + +public: + //Implemented ICamera + HCameraMatrices getCameraMatrices(float fov, + float canvasAspect, + float nearPlane, + float farPlane) override; + +public: + void tick(animTime_t timeDelta) override; + void setCameraPos(float x, float y, float z) override; + void setCameraLookAt(float x, float y, float z) override; +}; + + +#endif //WOWMAPVIEWERREVIVED_FIRSTPERSONORTHOTOPDOWNCAMERA_H diff --git a/wowViewerLib/src/engine/camera/m2TiedCamera.h b/wowViewerLib/src/engine/camera/m2TiedCamera.h index 7ab1783e2..604573e74 100644 --- a/wowViewerLib/src/engine/camera/m2TiedCamera.h +++ b/wowViewerLib/src/engine/camera/m2TiedCamera.h @@ -59,6 +59,9 @@ class m2TiedCamera : public ICamera { bool isCompatibleWithInfiniteZ() override { return false; } + + void setCameraLookAt(float x, float y, float z) override {}; + }; diff --git a/wowViewerLib/src/engine/camera/planarCamera.cpp b/wowViewerLib/src/engine/camera/planarCamera.cpp index 31aa62c69..d784434c7 100644 --- a/wowViewerLib/src/engine/camera/planarCamera.cpp +++ b/wowViewerLib/src/engine/camera/planarCamera.cpp @@ -1,6 +1,7 @@ // // Created by Deamon on 10/15/2018. // +#define _USE_MATH_DEFINES #include "planarCamera.h" #include "../../../3rdparty/mathfu/include/mathfu/matrix.h" diff --git a/wowViewerLib/src/engine/camera/planarCamera.h b/wowViewerLib/src/engine/camera/planarCamera.h index 67a6ff84a..a2ade0d35 100644 --- a/wowViewerLib/src/engine/camera/planarCamera.h +++ b/wowViewerLib/src/engine/camera/planarCamera.h @@ -87,6 +87,8 @@ class PlanarCamera: public ICamera { void tick(animTime_t timeDelta) override; void setCameraPos(float x, float y, float z) override; void setCameraOffset(float x, float y, float z) override; + + void setCameraLookAt(float x, float y, float z) override {} ; }; #endif //AWEBWOWVIEWERCPP_PLANARCAMERA_H diff --git a/wowViewerLib/src/engine/engineClassList.h b/wowViewerLib/src/engine/engineClassList.h index a0e0fcb94..8f7a12e4d 100644 --- a/wowViewerLib/src/engine/engineClassList.h +++ b/wowViewerLib/src/engine/engineClassList.h @@ -9,7 +9,7 @@ class AdtObject; class M2Object; class WmoObject; class AnimationManager; -class M2SkinProfile; +struct M2SkinProfile; class ParticleEmitter; diff --git a/wowViewerLib/src/engine/geometry/m2Geom.cpp b/wowViewerLib/src/engine/geometry/m2Geom.cpp index 4cf14fe0f..681f86848 100644 --- a/wowViewerLib/src/engine/geometry/m2Geom.cpp +++ b/wowViewerLib/src/engine/geometry/m2Geom.cpp @@ -56,7 +56,7 @@ chunkDef M2Geom::m2FileTable = { { [](M2Geom &file, ChunkData &chunkData) { debuglog("Entered 2PXE"); - chunkData.readValue(file.exp2Records); + chunkData.readValue(file.exp2); } } }, @@ -89,7 +89,27 @@ chunkDef M2Geom::m2FileTable = { { [](M2Geom &file, ChunkData &chunkData) { debuglog("Entered RPID"); + file.recursiveFileDataIDs = + std::vector(chunkData.chunkLen / 4); + for (int i = 0; i < file.recursiveFileDataIDs.size(); i++) { + chunkData.readValue(file.recursiveFileDataIDs[i]); + } + + } + } + }, + { + 'DIPG', + { + [](M2Geom &file, ChunkData &chunkData) { + debuglog("Entered GPID"); + file.particleModelFileDataIDs = + std::vector(chunkData.chunkLen / 4); + + for (int i = 0; i < file.particleModelFileDataIDs.size(); i++) { + chunkData.readValue(file.particleModelFileDataIDs[i]); + } } } @@ -118,25 +138,68 @@ chunkDef M2Geom::m2FileTable = { { [](M2Geom &file, ChunkData &chunkData) { debuglog("Entered 3VFW"); - file.m_wfv3 = 1; + chunkData.readValue(file.m_wfv3); } } - } + }, + { + '1VFW', + { + [](M2Geom &file, ChunkData &chunkData) { + debuglog("Entered 1VFW"); + float bumpScale; + chunkData.readValue(bumpScale); + WaterFallDataV3 *dataV3 = new WaterFallDataV3(); + dataV3->bumpScale = bumpScale; + dataV3->values0_x = 0; + dataV3->values0_y = 0.5; + dataV3->values0_z = 0.0; + dataV3->value1_w = 1.0f; + dataV3->values0_w = 0.5; + dataV3->value1_x = 0; + dataV3->value1_y = 0; + dataV3->value2_w = 0; + dataV3->value3_y = 0; + dataV3->value3_x = 0; + dataV3->basecolor = {0xff, 0xff, 0xff, 0xff}; + dataV3->flags = 0; + dataV3->unk0 = 0; + dataV3->values3_w = 0; + dataV3->values3_z = 0; + dataV3->values4_y = 0; + + file.m_wfv1 = dataV3; + } + } + }, + { + '1DGP', + { + [](M2Geom &file, ChunkData &chunkData) { + debuglog("Entered PGD1"); + chunkData.readValue(file.particleGeosetData); + } + } + }, + { + 'CBAP', + { + [](M2Geom &file, ChunkData &chunkData) { + debuglog("Entered PABC"); + int arrayLen = chunkData.chunkLen >> 1; + file.blackListAnimations = std::vector(arrayLen); + + for (int i = 0; i < arrayLen; i++) { + chunkData.readValue(file.blackListAnimations[i]); + } + } + } + }, + } }; - -// /* -// {name: "pos", type : "vector3f"}, 0+12 = 12 -// {name: "bonesWeight", type : "uint8Array", len: 4}, 12+4 = 16 -// {name: "bones", type : "uint8Array", len: 4}, 16+4 = 20 -// {name: "normal", type : "vector3f"}, 20+12 = 32 -// {name: "textureX", type : "float32"}, 32+4 = 36 -// {name: "textureY", type : "float32"}, 36+4 = 40 -// {name : "textureX2", type : "float32"}, 40+4 = 44 -// {name : "textureY2", type : "float32"} 44+4 = 48 -// */ static GBufferBinding staticM2Bindings[6] = { {+m2Shader::Attribute::aPosition, 3, GBindingType::GFLOAT, false, 48, 0 }, {+m2Shader::Attribute::boneWeights, 4, GBindingType::GUNSIGNED_BYTE, true, 48, 12}, // bonesWeight @@ -146,149 +209,7 @@ static GBufferBinding staticM2Bindings[6] = { {+m2Shader::Attribute::aTexCoord2, 2, GBindingType::GFLOAT, false, 48, 40} // texcoord }; -void initEXP2(M2Array *exp2Records) { - exp2Records->initM2Array(exp2Records); - for (int i = 0; i < exp2Records->size; i++) { - Exp2Record *exp2Record = exp2Records->getElement(i); - exp2Record->unk3.timestamps.initM2Array(exp2Records); - exp2Record->unk3.values.initM2Array(exp2Records); - } -} -void initM2Textures(M2Data *m2Header){ - int32_t texturesSize = m2Header->textures.size; - for (int i = 0; i < texturesSize; i++) { - M2Texture *texture = m2Header->textures.getElement(i); - texture->filename.initM2Array(m2Header); - } -} -void initCompBones(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad){ - int32_t bonesSize = m2Header->bones.size; - for (int i = 0; i < bonesSize; i++) { - M2CompBone *compBone = m2Header->bones.getElement(i); - compBone->translation.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - compBone->rotation.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - compBone->scaling.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} -void initM2Color(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - int32_t m2ColorSize = m2Header->colors.size; - for (int i = 0; i < m2ColorSize; i++) { - M2Color * m2Color = m2Header->colors.getElement(i); - m2Color->alpha.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - m2Color->color.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} -void initM2TextureWeight(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - int32_t textureWeightSize = m2Header->texture_weights.size; - for (int i = 0; i < textureWeightSize; i++) { - M2TextureWeight * textureWeight = m2Header->texture_weights.getElement(i); - textureWeight->weight.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} -void initM2TextureTransform(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - int32_t textureTransformSize = m2Header->texture_transforms.size; - for (int i = 0; i < textureTransformSize; i++) { - M2TextureTransform * textureTransform = m2Header->texture_transforms.getElement(i); - textureTransform->translation.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - textureTransform->rotation.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - textureTransform->scaling.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} -void initM2Attachment(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - int32_t attachmentCount = m2Header->attachments.size; - for (int i = 0; i < attachmentCount; i++) { - M2Attachment *attachment = m2Header->attachments.getElement(i); - attachment->animate_attached.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} -void initM2Event(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - int32_t eventCount = m2Header->events.size; - for (int i = 0; i < eventCount; i++) { - M2Event *event = m2Header->events.getElement(i); - event->enabled.initTrackBase(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} -void initM2Light(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - int32_t lightCount = m2Header->lights.size; - for (int i = 0; i < lightCount; i++) { - M2Light *light = m2Header->lights.getElement(i); - light->ambient_color.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - light->ambient_intensity.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - light->diffuse_color.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - light->diffuse_intensity.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - light->attenuation_start.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - light->attenuation_end.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - light->visibility.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} - -void initM2Particle(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - assert(sizeof(M2Particle) == 492); - int32_t particleEmitterCount = m2Header->particle_emitters.size; - for (int i = 0; i < particleEmitterCount; i++) { - M2Particle *particleEmitter = m2Header->particle_emitters.getElement(i); - - particleEmitter->old.emissionSpeed.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.speedVariation.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.verticalRange.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.horizontalRange.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.gravity.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.lifespan.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.emissionRate.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.emissionAreaLength.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.emissionAreaWidth.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - particleEmitter->old.zSource.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - - particleEmitter->old.enabledIn.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} -void initM2Ribbon(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - int32_t ribbonEmitterCount = m2Header->ribbon_emitters.size; - for (int i = 0; i < ribbonEmitterCount; i++) { - M2Ribbon *ribbonEmitter = m2Header->ribbon_emitters.getElement(i); - - ribbonEmitter->textureIndices.initM2Array(m2Header); - ribbonEmitter->materialIndices.initM2Array(m2Header); - - ribbonEmitter->colorTrack.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - ribbonEmitter->alphaTrack.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - ribbonEmitter->heightAboveTrack.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - ribbonEmitter->heightBelowTrack.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - ribbonEmitter->texSlotTrack.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - ribbonEmitter->visibilityTrack.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} -void initM2Camera(M2Data *m2Header, CM2SequenceLoad *cm2SequenceLoad) { - int32_t cameraCount = m2Header->cameras.size; - for (int i = 0; i < cameraCount; i++) { - M2Camera *camera = m2Header->cameras.getElement(i); - camera->positions.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - camera->target_position.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - camera->roll.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - camera->FoV.initTrack(m2Header, m2Header->sequences, cm2SequenceLoad); - } -} - - -void initM2ParticlePartTracks(M2Data *m2Header) { - assert(sizeof(M2Particle) == 492); - int32_t particleEmitterCount = m2Header->particle_emitters.size; - for (int i = 0; i < particleEmitterCount; i++) { - M2Particle *particleEmitter = m2Header->particle_emitters.getElement(i); - - particleEmitter->old.geometry_model_filename.initM2Array(m2Header); - particleEmitter->old.recursion_model_filename.initM2Array(m2Header); - - particleEmitter->old.alphaTrack.initPartTrack(m2Header); - particleEmitter->old.colorTrack.initPartTrack(m2Header); - particleEmitter->old.scaleTrack.initPartTrack(m2Header); - particleEmitter->old.headCellTrack.initPartTrack(m2Header); - particleEmitter->old.tailCellTrack.initPartTrack(m2Header); - - particleEmitter->old.splinePoints.initM2Array(m2Header); - } -} void M2Geom::process(HFileContent m2File, const std::string &fileName) { this->m2File = m2File; @@ -358,25 +279,29 @@ void M2Geom::process(HFileContent m2File, const std::string &fileName) { } } - std::cout << "Found " << directLights << " direct lights and " << pointLights << " point lights" << std::endl; + //std::cout << "Found " << directLights << " direct lights and " << pointLights << " point lights" << std::endl; } if (m2Header->global_flags.flag_has_blend_maps) { m2Header->blend_map_overrides.initM2Array(m2Header); } initM2ParticlePartTracks(m2Header); - initM2Textures(m2Header); + initM2Textures(m2Header, m2Header->textures); - if (exp2Records != nullptr) { - initEXP2(exp2Records); + if (exp2 != nullptr) { + initEXP2(exp2); } initTracks(nullptr); - if (m_wfv3 != 0) { + if (m_wfv3 != nullptr) { m2Header->textures.size = 4; } + if (particleGeosetData != nullptr) { + particleGeosetData->pgd.initM2Array(&particleGeosetData->pgd); + } + //Step 2: init tracks fsStatus = FileStatus::FSLoaded; } @@ -385,25 +310,25 @@ void M2Geom::initTracks(CM2SequenceLoad * cm2SequenceLoad) { M2Data *m2Header = this->m_m2Data; - initCompBones(m2Header, cm2SequenceLoad); - initM2Color(m2Header, cm2SequenceLoad); - initM2TextureWeight(m2Header, cm2SequenceLoad); - initM2TextureTransform(m2Header, cm2SequenceLoad); - initM2Attachment(m2Header, cm2SequenceLoad); - initM2Event(m2Header, cm2SequenceLoad); - initM2Light(m2Header, cm2SequenceLoad); - initM2Particle(m2Header, cm2SequenceLoad); - initM2Ribbon(m2Header, cm2SequenceLoad); - initM2Camera(m2Header, cm2SequenceLoad); + initCompBones(m2Header, &m2Header->bones, &m2Header->sequences, cm2SequenceLoad); + initM2Color(m2Header, &m2Header->sequences, cm2SequenceLoad); + initM2TextureWeight(m2Header, &m2Header->sequences, cm2SequenceLoad); + initM2TextureTransform(m2Header, &m2Header->sequences, cm2SequenceLoad); + initM2Attachment(m2Header, &m2Header->attachments, &m2Header->sequences, cm2SequenceLoad); + initM2Event(m2Header, &m2Header->sequences, cm2SequenceLoad); + initM2Light(m2Header, &m2Header->sequences, cm2SequenceLoad); + initM2Particle(m2Header, &m2Header->sequences, cm2SequenceLoad); + initM2Ribbon(m2Header, &m2Header->sequences, cm2SequenceLoad); + initM2Camera(m2Header, &m2Header->sequences, cm2SequenceLoad); } -HGVertexBuffer M2Geom::getVBO(IDevice &device) { +HGVertexBuffer M2Geom::getVBO(const HGDevice &device) { if (vertexVbo.get() == nullptr) { if (m_m2Data->vertices.size == 0) { return nullptr; } - vertexVbo = device.createVertexBuffer(); + vertexVbo = device->createVertexBuffer(); vertexVbo->uploadData( m_m2Data->vertices.getElement(0), m_m2Data->vertices.size*sizeof(M2Vertex)); @@ -468,7 +393,7 @@ std::array M2Geom::createDynamicVao( return result; } -HGVertexBufferBindings M2Geom::getVAO(IDevice &device, SkinGeom *skinGeom) { +HGVertexBufferBindings M2Geom::getVAO(const HGDevice& device, SkinGeom *skinGeom) { HGVertexBufferBindings bufferBindings = nullptr; if (vaoMap.find(skinGeom) != vaoMap.end()) { bufferBindings = vaoMap.at(skinGeom); @@ -482,7 +407,7 @@ HGVertexBufferBindings M2Geom::getVAO(IDevice &device, SkinGeom *skinGeom) { HGIndexBuffer iboBuffer = skinGeom->getIBO(device); //2. Create buffer binding and fill it - bufferBindings = device.createVertexBufferBindings(); + bufferBindings = device->createVertexBufferBindings(); bufferBindings->setIndexBuffer(iboBuffer); GVertexBufferBinding vertexBinding; @@ -498,30 +423,10 @@ HGVertexBufferBindings M2Geom::getVAO(IDevice &device, SkinGeom *skinGeom) { return bufferBindings; } -int M2Geom::findAnimationIndex(uint32_t anim_id) { - if (m_m2Data->sequence_lookups.size == 0) - return -1; - size_t i (anim_id % m_m2Data->sequence_lookups.size); - - for (size_t stride (1); true; ++stride) - { - if (*m_m2Data->sequence_lookups[i] == -1) - { - return -1; - } - if (m_m2Data->sequences[*m_m2Data->sequence_lookups[i]]->id == anim_id) - { - return *m_m2Data->sequence_lookups[i]; - } - - i = (i + stride * stride) % m_m2Data->sequence_lookups.size; - // so original_i + 1, original_i + 1 + 4, original_i + 1 + 4 + 9, … - } -} -void M2Geom::loadLowPriority(ApiContainer *m_api, uint32_t animationId, uint32_t variationId) { - int animationIndex = findAnimationIndex(animationId); +void M2Geom::loadLowPriority(const HApiContainer& m_api, uint32_t animationId, uint32_t variationId) { + int animationIndex = findAnimationIndex(animationId, &m_m2Data->sequence_lookups, &m_m2Data->sequences); if (animationIndex < 0) return; AnimCacheRecord animCacheRecord; diff --git a/wowViewerLib/src/engine/geometry/m2Geom.h b/wowViewerLib/src/engine/geometry/m2Geom.h index a5066fc49..db2f10b3b 100644 --- a/wowViewerLib/src/engine/geometry/m2Geom.h +++ b/wowViewerLib/src/engine/geometry/m2Geom.h @@ -30,27 +30,38 @@ class M2Geom : public PersistentFile { m_modelFileId = fileDataId; }; + std::string getName() { + return m_modelName; + } + void process(HFileContent m2File, const std::string &fileName) override; - HGVertexBuffer getVBO(IDevice &device); - HGVertexBufferBindings getVAO(IDevice &device, SkinGeom *skinGeom); + HGVertexBuffer getVBO(const HGDevice &device); + HGVertexBufferBindings getVAO(const HGDevice& device, SkinGeom *skinGeom); std::array createDynamicVao(IDevice &device, std::array &dynVBOs, SkinGeom *skinGeom, M2SkinSection *skinSection); - - int findAnimationIndex(uint32_t anim_id); - void loadLowPriority(ApiContainer *m_api, uint32_t animationId, uint32_t subAnimationId); + void loadLowPriority(const HApiContainer& m_api, uint32_t animationId, uint32_t subAnimationId); M2Data * getM2Data(){ if (fsStatus == FileStatus::FSLoaded) {return m_m2Data;} else {return nullptr;}}; M2Data *m_m2Data = nullptr; std::vector skinFileDataIDs; + std::vector recursiveFileDataIDs; + std::vector particleModelFileDataIDs; std::vector textureFileDataIDs; std::vector animationFileDataIDs; - M2Array *exp2Records = nullptr; + + std::vector blackListAnimations; + + PGD1_chunk * particleGeosetData = nullptr; + + EXP2 *exp2 = nullptr; std::vector txacMesh = {}; std::vector txacMParticle = {}; int m_skid = -1; - int m_wfv3 = 0; + WaterFallDataV3 *m_wfv3 = 0; + WaterFallDataV3 *m_wfv1 = 0; + private: HFileContent m2File; diff --git a/wowViewerLib/src/engine/geometry/skinGeom.cpp b/wowViewerLib/src/engine/geometry/skinGeom.cpp index d88c6b635..85e90bb24 100644 --- a/wowViewerLib/src/engine/geometry/skinGeom.cpp +++ b/wowViewerLib/src/engine/geometry/skinGeom.cpp @@ -21,22 +21,14 @@ void SkinGeom::process(HFileContent skinFile, const std::string &fileName) { fsStatus = FileStatus::FSLoaded; } -HGIndexBuffer SkinGeom::getIBO(IDevice &device) { +HGIndexBuffer SkinGeom::getIBO(const HGDevice &device) { if (indexVbo == nullptr) { - int indiciesLength = this->m_skinData->indices.size; + auto indicies = generateIndexBuffer(); - uint16_t *indicies = new uint16_t[indiciesLength]; - - for (int i = 0; i < indiciesLength; i++) { - indicies[i] = *this->m_skinData->vertices.getElement(*this->m_skinData->indices.getElement(i)); - } - - indexVbo = device.createIndexBuffer(); + indexVbo = device->createIndexBuffer(); indexVbo->uploadData( &indicies[0], - indiciesLength * sizeof(uint16_t)); - - delete[] indicies; + indicies.size() * sizeof(uint16_t)); } return indexVbo; @@ -107,6 +99,7 @@ void SkinGeom::fixData(M2Data *m2File) { } void SkinGeom::fixShaderIdBasedOnLayer(M2Data *m2File) { +/* M2SkinProfile* skinFileData = this->m_skinData; bool reducingIsNeeded = false; @@ -216,4 +209,17 @@ void SkinGeom::fixShaderIdBasedOnLayer(M2Data *m2File) { } } } -} \ No newline at end of file + */ +} + +std::vector SkinGeom::generateIndexBuffer() { + int indiciesLength = this->m_skinData->indices.size; + + std::vector indicies = std::vector(indiciesLength); + + for (int i = 0; i < indiciesLength; i++) { + indicies[i] = *this->m_skinData->vertices.getElement(*this->m_skinData->indices.getElement(i)); + } + + return indicies; +} diff --git a/wowViewerLib/src/engine/geometry/skinGeom.h b/wowViewerLib/src/engine/geometry/skinGeom.h index 554a84b87..b73677c20 100644 --- a/wowViewerLib/src/engine/geometry/skinGeom.h +++ b/wowViewerLib/src/engine/geometry/skinGeom.h @@ -16,7 +16,9 @@ class SkinGeom : public PersistentFile { SkinGeom(int fileDataId){}; void process(HFileContent skinFile, const std::string &fileName) override; - HGIndexBuffer getIBO(IDevice &device); + HGIndexBuffer getIBO(const HGDevice &device); + + std::vector generateIndexBuffer(); M2SkinProfile * getSkinData(){ if (fsStatus == FileStatus::FSLoaded) {return m_skinData;} else {return nullptr;}}; diff --git a/wowViewerLib/src/engine/geometry/wmoGroupGeom.cpp b/wowViewerLib/src/engine/geometry/wmoGroupGeom.cpp index 466911ad3..cb6fa37c6 100644 --- a/wowViewerLib/src/engine/geometry/wmoGroupGeom.cpp +++ b/wowViewerLib/src/engine/geometry/wmoGroupGeom.cpp @@ -6,6 +6,7 @@ #include "../persistance/header/wmoFileHeader.h" #include "../shader/ShaderDefinitions.h" #include "../../gapi/interface/IDevice.h" +#include "../algorithms/mathHelper.h" #include chunkDef WmoGroupGeom::wmoGroupTable = { @@ -287,9 +288,9 @@ void WmoGroupGeom::fixColorVertexAlpha(SMOHeader *mohd) { } -HGVertexBuffer WmoGroupGeom::getVBO(IDevice &device) { +HGVertexBuffer WmoGroupGeom::getVBO(const HGDevice &device) { if (combinedVBO == nullptr) { - combinedVBO = device.createVertexBuffer(); + combinedVBO = device->createVertexBuffer(); PACK( struct VboFormat { @@ -353,9 +354,9 @@ HGVertexBuffer WmoGroupGeom::getVBO(IDevice &device) { return combinedVBO; } -HGIndexBuffer WmoGroupGeom::getIBO(IDevice &device) { +HGIndexBuffer WmoGroupGeom::getIBO(const HGDevice &device) { if (indexVBO == nullptr) { - indexVBO = device.createIndexBuffer(); + indexVBO = device->createIndexBuffer(); indexVBO->uploadData( &indicies[0], indicesLen * sizeof(uint16_t)); @@ -374,16 +375,14 @@ static GBufferBinding staticWMOBindings[7] = { {+wmoShader::Attribute::aColor2, 4, GBindingType::GUNSIGNED_BYTE, true, 56, 52} }; -static GBufferBinding staticWMOWaterBindings[1] = { - {+waterShader::Attribute::aPosition, 3, GBindingType::GFLOAT, false, 12, 0}, -// {+waterShader::Attribute::aDepth, 1, GBindingType::GFLOAT, false, 24, 0 }, -// {+waterShader::Attribute::aTexCoord, 2, GBindingType::GFLOAT, false, 24, 4}, - +static GBufferBinding staticWMOWaterBindings[2] = { + {+waterShader::Attribute::aPositionTransp, 4, GBindingType::GFLOAT, false, 24, 0}, + {+waterShader::Attribute::aTexCoord, 2, GBindingType::GFLOAT, false, 24, 16} }; -HGVertexBufferBindings WmoGroupGeom::getVertexBindings(IDevice &device) { +HGVertexBufferBindings WmoGroupGeom::getVertexBindings(const HGDevice &device) { if (vertexBufferBindings == nullptr) { - vertexBufferBindings = device.createVertexBufferBindings(); + vertexBufferBindings = device->createVertexBufferBindings(); vertexBufferBindings->setIndexBuffer(getIBO(device)); GVertexBufferBinding vertexBinding; @@ -426,13 +425,16 @@ int WmoGroupGeom::getLegacyWaterType(int a) { return a; } -HGVertexBufferBindings WmoGroupGeom::getWaterVertexBindings(IDevice &device) { +HGVertexBufferBindings WmoGroupGeom::getWaterVertexBindings(const HGDevice &device) { if (vertexWaterBufferBindings == nullptr) { if (this->m_mliq == nullptr) return nullptr; - const float UNITSIZE = 533.3433333f / 16.0f / 8.0f; - - std::vector lVertices; + PACK( + struct LiquidVertexFormat { + mathfu::vec4_packed pos_transp; + mathfu::vec2_packed uv; + }); + std::vector lVertices; // lVertices.reserve((m_mliq->xverts)*(m_mliq->yverts)*3); mathfu::vec3 pos(m_mliq->basePos.x, m_mliq->basePos.y, m_mliq->basePos.z); @@ -442,11 +444,17 @@ HGVertexBufferBindings WmoGroupGeom::getWaterVertexBindings(IDevice &device) { for (int i = 0; i < m_mliq->xverts; i++) { int p = j*m_mliq->xverts + i; - lVertices.push_back(mathfu::vec3_packed(mathfu::vec3( - pos.x + (UNITSIZE * i), - pos.y + (UNITSIZE * j), - m_liquidVerticles[p].waterVert.height - ))); + + LiquidVertexFormat lvfVertex; + lvfVertex.pos_transp = mathfu::vec4_packed(mathfu::vec4( + pos.x + (MathHelper::UNITSIZE * i), + pos.y + (MathHelper::UNITSIZE * j), + m_liquidVerticles[p].waterVert.height, + 1.0 + )); + lvfVertex.uv = mathfu::vec2(0,0); + + lVertices.push_back(lvfVertex); } } @@ -467,6 +475,7 @@ HGVertexBufferBindings WmoGroupGeom::getWaterVertexBindings(IDevice &device) { } + int16_t vertindexes[4] = { (int16_t) (j * (m_mliq->xverts) + i), (int16_t) (j * (m_mliq->xverts) + i + 1), @@ -485,18 +494,18 @@ HGVertexBufferBindings WmoGroupGeom::getWaterVertexBindings(IDevice &device) { } } - waterIBO = device.createIndexBuffer(); + waterIBO = device->createIndexBuffer(); waterIBO->uploadData( &iboBuffer[0], iboBuffer.size() * sizeof(uint16_t)); - waterVBO = device.createVertexBuffer(); + waterVBO = device->createVertexBuffer(); waterVBO->uploadData( &lVertices[0], - lVertices.size() * sizeof(mathfu::vec3_packed) + lVertices.size() * sizeof(LiquidVertexFormat) ); - vertexWaterBufferBindings = device.createVertexBufferBindings(); + vertexWaterBufferBindings = device->createVertexBufferBindings(); vertexWaterBufferBindings->setIndexBuffer(waterIBO); GVertexBufferBinding vertexBinding; diff --git a/wowViewerLib/src/engine/geometry/wmoGroupGeom.h b/wowViewerLib/src/engine/geometry/wmoGroupGeom.h index 5b7d1d620..e667a4898 100644 --- a/wowViewerLib/src/engine/geometry/wmoGroupGeom.h +++ b/wowViewerLib/src/engine/geometry/wmoGroupGeom.h @@ -26,8 +26,8 @@ class WmoGroupGeom : public PersistentFile { bool hasWater() const {return m_mliq != nullptr; }; - HGVertexBufferBindings getVertexBindings(IDevice &device); - HGVertexBufferBindings getWaterVertexBindings(IDevice &device); + HGVertexBufferBindings getVertexBindings(const HGDevice &device); + HGVertexBufferBindings getWaterVertexBindings(const HGDevice &device); private: int normalOffset = 0; int textOffset = 0; @@ -39,8 +39,8 @@ class WmoGroupGeom : public PersistentFile { std::function m_attenuateFunc; int getLegacyWaterType(int a); - HGVertexBuffer getVBO(IDevice &device); - HGIndexBuffer getIBO(IDevice &device); + HGVertexBuffer getVBO(const HGDevice &device); + HGIndexBuffer getIBO(const HGDevice &device); public: HFileContent m_wmoGroupFile; diff --git a/wowViewerLib/src/engine/geometry/wmoMainGeom.cpp b/wowViewerLib/src/engine/geometry/wmoMainGeom.cpp index 02ef3f786..ab9d55202 100644 --- a/wowViewerLib/src/engine/geometry/wmoMainGeom.cpp +++ b/wowViewerLib/src/engine/geometry/wmoMainGeom.cpp @@ -178,8 +178,25 @@ chunkDef WmoMainGeom::wmoMainTable = { chunkData.readValue(object.skyboxM2FileId); } } - }, + { + 'MAVG', { + [](WmoMainGeom &object, ChunkData &chunkData) { + debuglog("Entered MAVG"); + object.mavgsLen = chunkData.chunkLen / sizeof(MAVG); + chunkData.readValues(object.mavgs, object.mavgsLen); + } + } + }, + { + 'MCVP', { + [](WmoMainGeom &object, ChunkData &chunkData) { + debuglog("Entered MCVP"); + object.convexVolumePlanesLen = chunkData.chunkLen / sizeof(mathfu::vec4_packed); + chunkData.readValues(object.convexVolumePlanes, object.convexVolumePlanesLen); + } + } + } } }; diff --git a/wowViewerLib/src/engine/geometry/wmoMainGeom.h b/wowViewerLib/src/engine/geometry/wmoMainGeom.h index f94a5ad14..7182cc872 100644 --- a/wowViewerLib/src/engine/geometry/wmoMainGeom.h +++ b/wowViewerLib/src/engine/geometry/wmoMainGeom.h @@ -27,46 +27,52 @@ class WmoMainGeom : public PersistentFile { SMOHeader *header; PointerChecker groups = (groupsLen); - int groupsLen; + int groupsLen = 0; std::vector> gfids; PointerChecker portal_vertices = (portal_verticesLen); - int portal_verticesLen; + int portal_verticesLen = 0; PointerChecker portals = (portalsLen); - int portalsLen; + int portalsLen = 0; PointerChecker portalReferences = (portalReferencesLen); - int portalReferencesLen; + int portalReferencesLen = 0; PointerChecker materials = (materialsLen); - int materialsLen; + int materialsLen = 0; PointerChecker textureNamesField = (textureNamesFieldLen); int textureNamesFieldLen = 0; PointerChecker doodadNamesField = (doodadNamesFieldLen); - int doodadNamesFieldLen; + int doodadNamesFieldLen = 0; PointerChecker doodadFileDataIds = (doodadFileDataIdsLen); int doodadFileDataIdsLen = 0; PointerChecker doodadSets = (doodadSetsLen); - int doodadSetsLen; + int doodadSetsLen = 0; PointerChecker doodadDefs = (doodadDefsLen); - int doodadDefsLen; + int doodadDefsLen = 0; PointerChecker lights = (lightsLen); int lightsLen = 0; PointerChecker fogs = (fogsLen); - int fogsLen; + int fogsLen = 0; PointerChecker skyBoxM2FileName = (skyBoxM2FileNameLen); int skyBoxM2FileNameLen = 0; + PointerChecker mavgs = (mavgsLen); + int mavgsLen = 0; + + PointerChecker convexVolumePlanes = (convexVolumePlanesLen); + int convexVolumePlanesLen = 0; + int skyboxM2FileId = 0; }; diff --git a/wowViewerLib/src/engine/managers/CRibbonEmitter.cpp b/wowViewerLib/src/engine/managers/CRibbonEmitter.cpp index ed7ee6a7a..9b47409d5 100644 --- a/wowViewerLib/src/engine/managers/CRibbonEmitter.cpp +++ b/wowViewerLib/src/engine/managers/CRibbonEmitter.cpp @@ -4,6 +4,7 @@ #include "../../gapi/interface/IVertexBufferBindings.h" #include "../shader/ShaderDefinitions.h" #include "../../../3rdparty/mathfu/include/mathfu/glsl_mappings.h" +#include "../../gapi/UniformBufferStructures.h" static GBufferBinding staticRibbonBindings[3] = { {+ribbonShader::Attribute::aPosition, 3, GBindingType::GFLOAT, false, 24, 0 }, // 0 @@ -13,8 +14,11 @@ static GBufferBinding staticRibbonBindings[3] = { }; //----- (00A19710) -------------------------------------------------------- -CRibbonEmitter::CRibbonEmitter(ApiContainer *api, M2Object *object, std::vector &materials, std::vector &textureIndicies) : m_api(api) +CRibbonEmitter::CRibbonEmitter(HApiContainer api, M2Object *object, + std::vector &materials, + std::vector &textureIndicies, int textureTransformLookup) : m_api(api) { + this->textureTransformLookup = textureTransformLookup; this->m_refCount = 1; this->m_prevPos.x = 0.0; this->m_prevPos.y = 0.0; @@ -82,13 +86,6 @@ CRibbonEmitter::CRibbonEmitter(ApiContainer *api, M2Object *object, std::vector< createMesh(object, materials, textureIndicies); } -PACK( - struct meshParticleWideBlockPS { - float uAlphaTest; - float padding[3]; // according to std140 - int uPixelShader; - float padding2[3]; - }); extern EGxBlendEnum M2BlendingModeToEGxBlendEnum [8]; void CRibbonEmitter::createMesh(M2Object *m2Object, std::vector &materials, std::vector &textureIndicies) { @@ -147,13 +144,31 @@ void CRibbonEmitter::createMesh(M2Object *m2Object, std::vector &mat meshTemplate.ubo[2] = nullptr; meshTemplate.ubo[3] = nullptr; - meshTemplate.ubo[4] = device->createUniformBufferChunk(sizeof(meshParticleWideBlockPS)); + meshTemplate.ubo[4] = device->createUniformBufferChunk(sizeof(Particle::meshParticleWideBlockPS)); - meshTemplate.ubo[4]->setUpdateHandler([](IUniformBufferChunk *self) { - meshParticleWideBlockPS& blockPS = self->getObject(); + auto blendMode = meshTemplate.blendMode; + auto textureTransformLookupIndex = (this->textureTransformLookup>=0) ? this->textureTransformLookup + i : -1; + meshTemplate.ubo[4]->setUpdateHandler([blendMode, m2Object, textureTransformLookupIndex](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) { + Particle::meshParticleWideBlockPS& blockPS = self->getObject(); blockPS.uAlphaTest = -1.0f; blockPS.uPixelShader = 0; + blockPS.uBlendMode = static_cast(blendMode); + + mathfu::mat4 textureTransformMat = mathfu::mat4::Identity(); + if (textureTransformLookupIndex >= 0) { + textureTransformMat = m2Object->getTextureTransformByLookup(textureTransformLookupIndex); + } + + auto textureTranslate = textureTransformMat.GetColumn(3); + blockPS.textureTranslate0 = textureTranslate.x; + blockPS.textureTranslate1 = textureTranslate.y; + blockPS.textureTranslate2 = textureTranslate.z; + + blockPS.textureScale0 = textureTransformMat.GetColumn(0).Length(); + blockPS.textureScale1 = textureTransformMat.GetColumn(1).Length(); + blockPS.textureScale2 = textureTransformMat.GetColumn(2).Length(); + }); @@ -191,7 +206,7 @@ CRibbonEmitter * CRibbonEmitter::SetGravity(float a2) //----- (00A19A80) -------------------------------------------------------- bool CRibbonEmitter::IsDead() const { - return this->m_readPos == this->m_writePos; + return (this->m_readPos == this->m_writePos) || (m_gxIndices.size() <= 0); } //----- (00A19AA0) -------------------------------------------------------- @@ -219,7 +234,7 @@ void CRibbonEmitter::SetAlpha(float a2) void CRibbonEmitter::InitInterpDeltas() { float diffLen = (this->m_prevPos - this->m_currPos).LengthSquared(); - assert(diffLen >= 0.0); +// assert(diffLen >= 0.0); float scale = sqrtf(diffLen); @@ -849,7 +864,7 @@ void CRibbonEmitter::Initialize(float edgesPerSec, float edgeLifeSpanInSec, CImV this->m_ribbonEmitterflags.m_initialized = 1; } -void CRibbonEmitter::collectMeshes(std::vector &meshes, int renderOrder) { +void CRibbonEmitter::collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder) { auto &currFrame = frame[m_api->hDevice->getUpdateFrameNumber()]; if (currFrame.isDead) return; @@ -858,7 +873,11 @@ void CRibbonEmitter::collectMeshes(std::vector &meshes, int renderOrder) auto mesh = currFrame.m_meshes[i]; mesh->setRenderOrder(renderOrder); - meshes.push_back(mesh); + if (mesh->getIsTransparent()) { + transparentMeshes.push_back(mesh); + } else { + opaqueMeshes.push_back(mesh); + } } } diff --git a/wowViewerLib/src/engine/managers/CRibbonEmitter.h b/wowViewerLib/src/engine/managers/CRibbonEmitter.h index 9b02af9b9..26fc6a79e 100644 --- a/wowViewerLib/src/engine/managers/CRibbonEmitter.h +++ b/wowViewerLib/src/engine/managers/CRibbonEmitter.h @@ -21,6 +21,7 @@ struct CRibbonVertex class CRibbonEmitter { + int textureTransformLookup = -1; int m_refCount; std::vector m_edges; int m_writePos; @@ -72,7 +73,7 @@ class CRibbonEmitter { int m_priority; private: - ApiContainer *m_api; + HApiContainer m_api; struct RibbonFrame { HGIndexBuffer m_indexVBO; @@ -80,7 +81,7 @@ class CRibbonEmitter { HGVertexBufferBindings m_bindings; std::vector m_meshes = {}; - bool isDead = false; + bool isDead = true; }; std::array frame; @@ -88,7 +89,7 @@ class CRibbonEmitter { void createMesh(M2Object *object, std::vector &materials, std::vector &textureIndicies); public: - CRibbonEmitter(ApiContainer *m_api, M2Object *object, std::vector &materials, std::vector &textureIndicies); + CRibbonEmitter(HApiContainer m_api, M2Object *object, std::vector &materials, std::vector &textureIndicies, int textureTransformLookup); void SetDataEnabled(char a2); void SetUserEnabled(char a2); CRibbonEmitter *SetGravity(float a2); @@ -114,7 +115,7 @@ class CRibbonEmitter { //CTexture **SetTexture(unsigned int a2, CTexture *a3); //int ReplaceTexture(unsigned int a2, CTexture *a3); - void collectMeshes(std::vector &meshes, int renderOrder); + void collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder); void updateBuffers(); }; diff --git a/wowViewerLib/src/engine/managers/animationManager.cpp b/wowViewerLib/src/engine/managers/animationManager.cpp index fe00e5fad..f15f46c6d 100644 --- a/wowViewerLib/src/engine/managers/animationManager.cpp +++ b/wowViewerLib/src/engine/managers/animationManager.cpp @@ -9,22 +9,26 @@ #include "../persistance/header/M2FileHeader.h" #include "mathfu/glsl_mappings.h" -AnimationManager::AnimationManager(ApiContainer *api, HM2Geom m2Geom) { +AnimationManager::AnimationManager(HApiContainer api, std::shared_ptr boneMasterData, bool hasExp2) { this->m_api = api; - this->m_m2Geom = m2Geom; - this->m_m2File = m2Geom->getM2Data(); + this->boneMasterData = boneMasterData; - this->mainAnimationId = 0; - this->mainAnimationIndex = 0; + this->m_hasExp2 = hasExp2; this->animationInfo.currentAnimation.animationIndex = 0; this->animationInfo.currentAnimation.animationTime = 0; - this->animationInfo.currentAnimation.animationRecord = m_m2File->sequences.getElement(0); + this->animationInfo.currentAnimation.animationRecord = boneMasterData->getSkelData()->m_sequences->getElement(0); + this->animationInfo.currentAnimation.animationFoundInParent = false; + this->animationInfo.currentAnimation.mainVariationIndex = 0; + this->animationInfo.currentAnimation.mainVariationRecord = this->animationInfo.currentAnimation.animationRecord; + calcAnimRepetition(this->animationInfo.currentAnimation); this->animationInfo.nextSubAnimation.animationIndex = -1; this->animationInfo.nextSubAnimation.animationTime = 0; this->animationInfo.nextSubAnimation.animationRecord = nullptr; + this->animationInfo.blendFactor = 0; + this->firstCalc = true; this->initBonesIsCalc(); @@ -37,55 +41,50 @@ AnimationManager::AnimationManager(ApiContainer *api, HM2Geom m2Geom) { } } -const int AnimationManager::findAnimationIndex(uint32_t anim_id) const { - if (m_m2File->sequence_lookups.size == 0) - return -1; - - size_t i (anim_id % m_m2File->sequence_lookups.size); - - for (size_t stride (1); true; ++stride) - { - if (*m_m2File->sequence_lookups[i] == -1) - { - return -1; - } - if (m_m2File->sequences[*m_m2File->sequence_lookups[i]]->id == anim_id) - { - return *m_m2File->sequence_lookups[i]; - } - - i = (i + stride * stride) % m_m2File->sequence_lookups.size; - // so original_i + 1, original_i + 1 + 4, original_i + 1 + 4 + 9, … - } -} - void AnimationManager::initBonesIsCalc() { - bonesIsCalculated = std::vector((unsigned long) m_m2File->bones.size); + auto bones = boneMasterData->getSkelData()->m_m2CompBones; + bonesIsCalculated = std::vector((unsigned long) bones->size); - for (int i = 0; i < m_m2File->bones.size; i++) { + for (int i = 0; i < bones->size; i++) { bonesIsCalculated[i] = false; } } void AnimationManager::initBlendMatrices() { - unsigned long matCount = (unsigned long) std::max(m_m2File->bones.size, m_m2File->texture_transforms.size); + auto &bones = *boneMasterData->getSkelData()->m_m2CompBones; + + unsigned long matCount = (unsigned long) std::max(bones.size, boneMasterData->getM2Geom()->getM2Data()->texture_transforms.size); blendMatrixArray = std::vector(matCount, mathfu::mat4::Identity()); } void AnimationManager::initGlobalSequenceTimes() { globalSequenceTimes = std::vector( - (unsigned long) (m_m2File->global_loops.size > 0 ? m_m2File->global_loops.size : 0)); + (unsigned long) (boneMasterData->getSkelData()->m_globalSequences->size > 0 ? + boneMasterData->getSkelData()->m_globalSequences->size : + 0)); for (int i = 0; i < globalSequenceTimes.size(); i++) { globalSequenceTimes[i] = 0; } + + if (boneMasterData->getParentSkelData() != nullptr) { + parentGlobalSequenceTimes = std::vector( + (unsigned long) (boneMasterData->getParentSkelData()->m_globalSequences->size > 0 ? + boneMasterData->getParentSkelData()->m_globalSequences->size : + 0)); + + for (int i = 0; i < parentGlobalSequenceTimes.size(); i++) { + globalSequenceTimes[i] = 0; + } + } } void AnimationManager::calculateBoneTree() { - this->childBonesLookup = std::vector>(m_m2File->bones.size); - for (int i = 0; i < m_m2File->bones.size; i++) { - for (int j = 0; j < m_m2File->bones.size; j++) { - if (m_m2File->bones[j]->parent_bone == i) { + auto &bones = *boneMasterData->getSkelData()->m_m2CompBones; + this->childBonesLookup = std::vector>(bones.size); + for (int i = 0; i < bones.size; i++) { + for (int j = 0; j < bones.size; j++) { + if (bones[j]->parent_bone == i) { childBonesLookup[i].push_back(j); } } @@ -98,44 +97,71 @@ void AnimationManager::resetCurrentAnimation() { for (auto &a : globalSequenceTimes) { a = 0; } + for (auto &a : parentGlobalSequenceTimes) { + a = 0; + } } bool AnimationManager::setAnimationId(int animationId, bool reset) { int animationIndex = -1; + bool animationFoundInParent = false; + + animationIndex = findAnimationIndex(animationId, + boneMasterData->getSkelData()->m_sequence_lookups, + boneMasterData->getSkelData()->m_sequences); - animationIndex = findAnimationIndex(animationId); + auto sequences = boneMasterData->getSkelData()->m_sequences; - if ((animationIndex == -1) && (m_m2File->sequence_lookups.size == 0) && (m_m2File->sequences.size > 0)) { - for (int i = 0; i < m_m2File->sequences.size; i++) { - const M2Sequence* animationRecord = m_m2File->sequences[i]; - if (animationRecord->id == animationId) { - animationIndex = i; + if (animationIndex <= -1 && boneMasterData->getParentSkelData() != nullptr) { + bool animationIsBanned = false; + //Test against PABC + auto &bannedAnims = boneMasterData->getM2Geom()->blackListAnimations; + for (auto const a : bannedAnims) { + if (a == animationId) { + animationIsBanned = true; break; } } + + if (!animationIsBanned) { + animationIndex = findAnimationIndex(animationId, + boneMasterData->getParentSkelData()->m_sequence_lookups, + boneMasterData->getParentSkelData()->m_sequences); + if (animationIndex > -1) { + sequences = boneMasterData->getParentSkelData()->m_sequences; + animationFoundInParent = true; + } + } } + + if (animationIndex > -1) { while ( - ((m_m2File->sequences[animationIndex]->flags & 0x20) == 0) && - ((m_m2File->sequences[animationIndex]->flags & 0x40) > 0) + (((*sequences)[animationIndex]->flags & 0x20) == 0) && + (((*sequences)[animationIndex]->flags & 0x40) > 0) ) { - animationIndex = m_m2File->sequences[animationIndex]->aliasNext; + animationIndex = (*sequences)[animationIndex]->aliasNext; if (animationIndex < 0) break; } } - if ((animationIndex > - 1)&& (reset || (animationIndex != this->mainAnimationIndex) )) { + if ((animationIndex > - 1) && (reset || (animationIndex != this->animationInfo.currentAnimation.animationIndex) )) { //Reset animation - this->mainAnimationId = animationId; - this->mainAnimationIndex = animationIndex; - this->animationInfo.currentAnimation.animationIndex = animationIndex; - this->animationInfo.currentAnimation.animationRecord = m_m2File->sequences[animationIndex]; + this->animationInfo.currentAnimation.animationRecord = (*sequences)[animationIndex]; this->animationInfo.currentAnimation.animationTime = 0; + this->animationInfo.currentAnimation.animationFoundInParent = animationFoundInParent; + this->animationInfo.currentAnimation.mainVariationIndex = animationIndex; + this->animationInfo.currentAnimation.mainVariationRecord = (*sequences)[animationIndex]; + + calcAnimRepetition(this->animationInfo.currentAnimation); this->animationInfo.nextSubAnimation.animationIndex = -1; this->animationInfo.nextSubAnimation.animationRecord = nullptr; this->animationInfo.nextSubAnimation.animationTime = 0; + this->animationInfo.nextSubAnimation.animationFoundInParent = false; + this->animationInfo.nextSubAnimation.mainVariationIndex = -1; + this->animationInfo.nextSubAnimation.mainVariationRecord = nullptr; this->firstCalc = true; @@ -159,12 +185,13 @@ inline void calcAnimationTransform( mathfu::mat4 *billboardMatrix, mathfu::vec4 &pivotPoint, mathfu::vec4 &negatePivotPoint, + M2Array &global_loops, std::vector &globalSequenceTimes, + bool &isAnimated, M2Track &translationTrack, M2Track &rotationTrack, M2Track &scaleTrack, - M2Data * m2Data, const FullAnimationInfo &animationInfo ) { tranformMat = tranformMat * mathfu::mat4::FromTranslationVector(pivotPoint.xyz()); @@ -174,7 +201,7 @@ inline void calcAnimationTransform( mathfu::vec4 transVec = animateTrackWithBlend( animationInfo, translationTrack, - m2Data->global_loops, + global_loops, globalSequenceTimes, defaultValue ); @@ -190,7 +217,7 @@ inline void calcAnimationTransform( mathfu::quat quaternionResult = animateTrackWithBlend( animationInfo, rotationTrack, - m2Data->global_loops, + global_loops, globalSequenceTimes, defaultValue); @@ -203,7 +230,7 @@ inline void calcAnimationTransform( mathfu::vec4 scaleResult = animateTrackWithBlend( animationInfo, scaleTrack, - m2Data->global_loops, + global_loops, globalSequenceTimes, defaultValue); @@ -217,46 +244,50 @@ inline void calcTextureAnimationTransform( mathfu::mat4 &tranformMat, mathfu::vec4 &pivotPoint, mathfu::vec4 &negatePivotPoint, + M2Array &global_loops, std::vector &globalSequenceTimes, bool &isAnimated, M2Track &translationTrack, M2Track &rotationTrack, M2Track &scaleTrack, - M2Data * m2Data, const FullAnimationInfo &animationInfo ) { - tranformMat = tranformMat * mathfu::mat4::FromTranslationVector(pivotPoint.xyz()); -// - if (scaleTrack.values.size > 0) { - mathfu::vec4 defaultValue = mathfu::vec4(1,1,1,1); - mathfu::vec4 scaleResult = animateTrackWithBlend( - animationInfo, - scaleTrack, - m2Data->global_loops, - globalSequenceTimes, - defaultValue); - tranformMat = tranformMat * mathfu::mat4::FromScaleVector(scaleResult.xyz()); - isAnimated = true; - } if (rotationTrack.values.size > 0) { mathfu::quat defaultValue = mathfu::quat(1,0,0,0); mathfu::quat quaternionResult = animateTrackWithBlend( animationInfo, rotationTrack, - m2Data->global_loops, + global_loops, globalSequenceTimes, defaultValue); + tranformMat = tranformMat * mathfu::mat4::FromTranslationVector(pivotPoint.xyz()); tranformMat = tranformMat * quaternionResult.ToMatrix4(); + tranformMat = tranformMat * mathfu::mat4::FromTranslationVector(negatePivotPoint.xyz()); + isAnimated = true; + } + if (scaleTrack.values.size > 0) { + mathfu::vec4 defaultValue = mathfu::vec4(1,1,1,1); + mathfu::vec4 scaleResult = animateTrackWithBlend( + animationInfo, + scaleTrack, + global_loops, + globalSequenceTimes, + defaultValue); + + tranformMat = tranformMat * mathfu::mat4::FromTranslationVector(pivotPoint.xyz()); + tranformMat = tranformMat * mathfu::mat4::FromScaleVector(scaleResult.xyz()); + tranformMat = tranformMat * mathfu::mat4::FromTranslationVector(negatePivotPoint.xyz()); isAnimated = true; } + if (translationTrack.values.size > 0) { mathfu::vec4 defaultValue = mathfu::vec4(0,0,0,0); mathfu::vec4 transVec = animateTrackWithBlend( animationInfo, translationTrack, - m2Data->global_loops, + global_loops, globalSequenceTimes, defaultValue ); @@ -264,9 +295,6 @@ inline void calcTextureAnimationTransform( tranformMat = tranformMat * mathfu::mat4::FromTranslationVector(transVec.xyz()); isAnimated = true; } - - - tranformMat = tranformMat * mathfu::mat4::FromTranslationVector(negatePivotPoint.xyz()); } @@ -275,22 +303,24 @@ void AnimationManager::calcAnimMatrixes (std::vector &textAnimMatr mathfu::vec4 pivotPoint(0.5, 0.5, 0, 0); mathfu::vec4 negatePivotPoint = -pivotPoint; - for (int i = 0; i < m_m2File->texture_transforms.size; i++) { - M2TextureTransform *textAnimData = m_m2File->texture_transforms[i]; + auto &global_loops = *boneMasterData->getSkelData()->m_globalSequences; + + auto &texture_transforms = boneMasterData->getM2Geom()->getM2Data()->texture_transforms; + for (int i = 0; i < texture_transforms.size; i++) { + M2TextureTransform *textAnimData = texture_transforms[i]; textAnimMatrices[i] = mathfu::mat4::Identity(); bool isAnimated; calcTextureAnimationTransform(textAnimMatrices[i], - pivotPoint, negatePivotPoint, - this->globalSequenceTimes, - isAnimated, - textAnimData->translation, - textAnimData->rotation, - textAnimData->scaling, - m_m2File, - animationInfo + pivotPoint, negatePivotPoint, + global_loops, + this->globalSequenceTimes, + isAnimated, + textAnimData->translation, + textAnimData->rotation, + textAnimData->scaling, + animationInfo ); - this->isAnimated = isAnimated ? isAnimated : this->isAnimated; } } @@ -372,7 +402,17 @@ AnimationManager::calcBoneMatrix( ) { if (this->bonesIsCalculated[boneIndex]) return; - M2CompBone *boneDefinition = m_m2File->bones[boneIndex]; + auto skelData = boneMasterData->getSkelData(); + auto globalSequenceTimes = &this->globalSequenceTimes; + if (this->animationInfo.currentAnimation.animationFoundInParent) { + skelData = boneMasterData->getParentSkelData(); + globalSequenceTimes = &this->parentGlobalSequenceTimes; + } + + + + M2CompBone *boneDefinition = skelData->m_m2CompBones->getElement(boneIndex); + auto &globalSequences = skelData->m_globalSequences; int parentBone = boneDefinition->parent_bone; @@ -455,12 +495,12 @@ AnimationManager::calcBoneMatrix( calcAnimationTransform(animatedMatrix, billboardMatrix, pivotPoint, negatePivotPoint, - this->globalSequenceTimes, + *globalSequences, + *globalSequenceTimes, isAnimated, boneDefinition->translation, boneDefinition->rotation, boneDefinition->scaling, - m_m2File, animationInfo); boneMatrices[boneIndex] = parentBoneMat * animatedMatrix; } else { @@ -490,7 +530,7 @@ AnimationManager::calcBoneMatrix( ).Normalized(); mathfu::vec4 &yAxis = animatedMatrix.GetColumn(1); - if (m_m2Geom->m_m2Data->global_flags.flag_unk_0x2000 == 1) { + if (isMirrored) { currentBoneMat.GetColumn(1) = mathfu::vec4( -yAxis.y, -yAxis.z, @@ -516,7 +556,7 @@ AnimationManager::calcBoneMatrix( } else { currentBoneMat.GetColumn(0) = mathfu::vec4(0, 0, -1, 0); - if (m_m2Geom->m_m2Data->global_flags.flag_unk_0x2000 == 1) { + if (isMirrored) { currentBoneMat.GetColumn(1) = mathfu::vec4(-1.0, 0, 0, 0); } else { currentBoneMat.GetColumn(1) = mathfu::vec4(1.0, 0, 0, 0); @@ -646,7 +686,8 @@ void AnimationManager::calcBones ( if (true) { //Animate everything with standard animation - for (int i = 0; i < m_m2File->bones.size; i++) { + auto &bones = *boneMasterData->getSkelData()->m_m2CompBones; + for (int i = 0; i < bones.size; i++) { this->calcBoneMatrix(boneMatrices, i, modelViewMatrix); // std::cout << "boneMatrices[" << std::to_string(i) << "] = " << dumpMatrix(boneMatrices[i]) << std::endl; } @@ -663,8 +704,8 @@ void AnimationManager::calcBones ( } */ - int closedHandAnimation = -1; - closedHandAnimation = findAnimationIndex(15); //ANIMATION_HANDSCLOSED = 15 +// int closedHandAnimation = -1; +// closedHandAnimation = findAnimationIndex(15); //ANIMATION_HANDSCLOSED = 15 // if (closedHandAnimation >= 0) { // if (this->leftHandClosed && m_m2File->key_bone_lookup.size > (13+5)) { @@ -708,56 +749,99 @@ void AnimationManager::update( std::vector &transparencies, std::vector &lights, std::vector &particleEmitters, - std::vector &ribbonEmitters + std::vector &ribbonEmitters) { + - /*cameraDetails, , particleEmitters*/) { - if (m_m2File->sequences.size <= 0) return; + auto &global_loops = *boneMasterData->getSkelData()->m_globalSequences; + auto &bones = *boneMasterData->getSkelData()->m_m2CompBones; - const M2Sequence* mainAnimationRecord = m_m2File->sequences[this->mainAnimationIndex]; - const M2Sequence* currentAnimationRecord = m_m2File->sequences[this->animationInfo.currentAnimation.animationIndex]; + { + auto sequences = *boneMasterData->getSkelData()->m_sequences; + if (sequences.size <= 0) return; + } + + const M2Sequence* currentAnimationRecord = this->animationInfo.currentAnimation.animationRecord; this->animationInfo.currentAnimation.animationTime += deltaTime; //Update global sequences - int minGlobal = 0; //12; + int minGlobal = 0; int maxGlobal = (int) this->globalSequenceTimes.size(); -// int minGlobal = 0; -// int maxGlobal = 1; for (int i = minGlobal; i < maxGlobal; i++) { this->globalSequenceTimes[i] += deltaTimeForGS; - if (m_m2File->global_loops[i]->timestamp > 0) { // Global sequence values can be 0's - this->globalSequenceTimes[i] = fmodf(this->globalSequenceTimes[i], (float)m_m2File->global_loops[i]->timestamp); + if (global_loops[i]->timestamp > 0) { // Global sequence values can be 0's + this->globalSequenceTimes[i] = fmodf(this->globalSequenceTimes[i], (float)global_loops[i]->timestamp); + } + } + + if (boneMasterData->getParentSkelData() != nullptr) { + minGlobal = 0; + maxGlobal = (int) this->parentGlobalSequenceTimes.size(); + + for (int i = minGlobal; i < maxGlobal; i++) { + this->parentGlobalSequenceTimes[i] += deltaTimeForGS; + if (global_loops[i]->timestamp > 0) { // Global sequence values can be 0's + this->parentGlobalSequenceTimes[i] = fmodf(this->parentGlobalSequenceTimes[i], (float)global_loops[i]->timestamp); + } } } /* Pick next animation if there is one and no next animation was picked before */ const M2Sequence* subAnimRecord = nullptr; - if (this->animationInfo.nextSubAnimation.animationIndex < 0 && mainAnimationRecord->variationNext > -1) { + if (this->animationInfo.nextSubAnimation.animationIndex < 0 && + this->animationInfo.currentAnimation.mainVariationRecord->variationNext > -1 && + this->animationInfo.currentAnimation.repeatTimes <= 0 + ) { + M2Array * sequences = boneMasterData->getSkelData()->m_sequences; + if (this->animationInfo.currentAnimation.animationFoundInParent) { + sequences = boneMasterData->getParentSkelData()->m_sequences;; + } + //if (currentAnimationPlayedTimes) int probability = ((float)rand() / (float)RAND_MAX) * 0x7FFF; int calcProb = 0; /* First iteration is out of loop */ - auto currentSubAnimIndex = this->mainAnimationIndex; - subAnimRecord = m_m2File->sequences[currentSubAnimIndex]; + auto currentSubAnimIndex = this->animationInfo.currentAnimation.mainVariationIndex; + subAnimRecord = (*sequences)[currentSubAnimIndex]; calcProb += subAnimRecord->frequency; while ((calcProb < probability) && (subAnimRecord->variationNext > -1)) { currentSubAnimIndex = subAnimRecord->variationNext; - subAnimRecord = m_m2File->sequences[currentSubAnimIndex]; + subAnimRecord = (*sequences)[currentSubAnimIndex]; - calcProb += subAnimRecord->frequency; + if (this->animationInfo.currentAnimation.animationIndex != currentSubAnimIndex) { + calcProb += subAnimRecord->frequency; + } } this->animationInfo.nextSubAnimation.animationIndex = currentSubAnimIndex; - this->animationInfo.nextSubAnimation.animationRecord = m_m2File->sequences[currentSubAnimIndex]; + this->animationInfo.nextSubAnimation.animationRecord = (*sequences)[currentSubAnimIndex]; this->animationInfo.nextSubAnimation.animationTime = 0; + this->animationInfo.nextSubAnimation.animationFoundInParent = this->animationInfo.currentAnimation.animationFoundInParent; + this->animationInfo.nextSubAnimation.mainVariationIndex = this->animationInfo.currentAnimation.mainVariationIndex; + this->animationInfo.nextSubAnimation.mainVariationRecord = this->animationInfo.currentAnimation.mainVariationRecord; + calcAnimRepetition(this->animationInfo.nextSubAnimation); + } else { + //This is done to trigger blending in transition start and end of same variation when it's repeated for whatever reason + if (this->animationInfo.currentAnimation.repeatTimes > 0) { + this->animationInfo.nextSubAnimation = this->animationInfo.currentAnimation; + this->animationInfo.nextSubAnimation.repeatTimes--; + } } animTime_t currAnimLeft = currentAnimationRecord->duration - this->animationInfo.currentAnimation.animationTime; animTime_t subAnimBlendTime = 0; - if (this->animationInfo.nextSubAnimation.animationIndex > -1) { - subAnimRecord = m_m2File->sequences[this->animationInfo.nextSubAnimation.animationIndex]; + + if (this->animationInfo.nextSubAnimation.animationIndex > -1 + //&& ((this->animationInfo.nextSubAnimation.animationRecord->flags & 0x80) > 0) + ) { + M2Array * sequences = boneMasterData->getSkelData()->m_sequences; + if (this->animationInfo.nextSubAnimation.animationFoundInParent) { + sequences = boneMasterData->getParentSkelData()->m_sequences;; + } + + subAnimRecord = (*sequences)[this->animationInfo.nextSubAnimation.animationIndex]; subAnimBlendTime = subAnimRecord->blendtime; } @@ -771,16 +855,21 @@ void AnimationManager::update( } if (this->animationInfo.currentAnimation.animationTime >= currentAnimationRecord->duration) { + this->animationInfo.currentAnimation.repeatTimes--; + if (this->animationInfo.nextSubAnimation.animationIndex > -1) { - if (this->animationInfo.nextSubAnimation.animationIndex > -1) { - while ( - ((m_m2File->sequences[this->animationInfo.nextSubAnimation.animationIndex]->flags & 0x20) == 0) && - ((m_m2File->sequences[this->animationInfo.nextSubAnimation.animationIndex]->flags & 0x40) > 0) - ) { - this->animationInfo.nextSubAnimation.animationIndex = m_m2File->sequences[this->animationInfo.nextSubAnimation.animationIndex]->aliasNext; - this->animationInfo.nextSubAnimation.animationRecord = m_m2File->sequences[this->animationInfo.nextSubAnimation.animationIndex]; - if (this->animationInfo.nextSubAnimation.animationIndex < 0) break; - } + M2Array * sequences = boneMasterData->getSkelData()->m_sequences; + if (this->animationInfo.nextSubAnimation.animationFoundInParent) { + sequences = boneMasterData->getParentSkelData()->m_sequences;; + } + + while ( + (((*sequences)[this->animationInfo.nextSubAnimation.animationIndex]->flags & 0x20) == 0) && + (((*sequences)[this->animationInfo.nextSubAnimation.animationIndex]->flags & 0x40) > 0) + ) { + this->animationInfo.nextSubAnimation.animationIndex = (*sequences)[this->animationInfo.nextSubAnimation.animationIndex]->aliasNext; + this->animationInfo.nextSubAnimation.animationRecord = (*sequences)[this->animationInfo.nextSubAnimation.animationIndex]; + if (this->animationInfo.nextSubAnimation.animationIndex < 0) break; } this->animationInfo.currentAnimation = this->animationInfo.nextSubAnimation; @@ -792,6 +881,7 @@ void AnimationManager::update( this->animationInfo.blendFactor = 1.0; deferredLoadingStarted = false; } else if (currentAnimationRecord->duration > 0) { + //Ideally this code would never be called again this->animationInfo.currentAnimation.animationTime = fmod(this->animationInfo.currentAnimation.animationTime, currentAnimationRecord->duration); } } @@ -800,7 +890,10 @@ void AnimationManager::update( /* Update animated values */ if ((currentAnimationRecord->flags & 0x20) == 0) { if (!deferredLoadingStarted) { - m_m2Geom->loadLowPriority(m_api, currentAnimationRecord->id, currentAnimationRecord->variationIndex); + boneMasterData->loadLowPriority(m_api, + currentAnimationRecord->id, + currentAnimationRecord->variationIndex, + animationInfo.currentAnimation.animationFoundInParent); deferredLoadingStarted = true; } return; @@ -809,7 +902,10 @@ void AnimationManager::update( }; if ((animationInfo.nextSubAnimation.animationRecord != nullptr) && ((animationInfo.nextSubAnimation.animationRecord->flags & 0x20) == 0)) { if (!deferredLoadingStarted) { - m_m2Geom->loadLowPriority(m_api, animationInfo.nextSubAnimation.animationRecord->id, animationInfo.nextSubAnimation.animationRecord->variationIndex); + boneMasterData->loadLowPriority(m_api, + animationInfo.nextSubAnimation.animationRecord->id, + animationInfo.nextSubAnimation.animationRecord->variationIndex, + animationInfo.nextSubAnimation.animationFoundInParent); deferredLoadingStarted = true; } return; @@ -819,13 +915,13 @@ void AnimationManager::update( this->calcAnimMatrixes(textAnimMatrices); - for (int i = 0; i < m_m2File->bones.size; i++) { + for (int i = 0; i < bones.size; i++) { this->bonesIsCalculated[i] = false; } this->calcBones(bonesMatrices, modelViewMatrix); mathfu::mat4 invModelViewMatrix = modelViewMatrix.Inverse(); - for (int i = 0; i < m_m2File->bones.size; i++) { + for (int i = 0; i < bones.size; i++) { bonesMatrices[i] = invModelViewMatrix * bonesMatrices[i]; } @@ -839,7 +935,8 @@ void AnimationManager::update( } void AnimationManager::calcSubMeshColors(std::vector &subMeshColors) { - M2Array &colors = this->m_m2File->colors; + M2Array &colors = boneMasterData->getM2Geom()->getM2Data()->colors; + auto &global_loops = *boneMasterData->getSkelData()->m_globalSequences; static mathfu::vec3 defaultVector(1.0, 1.0, 1.0); static float defaultAlpha = 1.0; @@ -848,7 +945,7 @@ void AnimationManager::calcSubMeshColors(std::vector &subMeshColor mathfu::vec3 colorResult1 = animateTrackWithBlend( animationInfo, colors[i]->color, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultVector ); @@ -860,7 +957,7 @@ void AnimationManager::calcSubMeshColors(std::vector &subMeshColor float resultAlpha1 = animateTrackWithBlend( animationInfo, colors[i]->alpha, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultAlpha ); @@ -871,14 +968,15 @@ void AnimationManager::calcSubMeshColors(std::vector &subMeshColor void AnimationManager::calcTransparencies(std::vector &transparencies) { - M2Array &transparencyRecords = this->m_m2File->texture_weights; + M2Array &transparencyRecords = boneMasterData->getM2Geom()->getM2Data()->texture_weights; + auto &global_loops = *boneMasterData->getSkelData()->m_globalSequences; static float defaultAlpha = 1.0; for (int i = 0; i < transparencyRecords.size; i++) { float result1 = animateTrackWithBlend( animationInfo, transparencyRecords[i]->weight, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultAlpha ); @@ -889,8 +987,11 @@ void AnimationManager::calcTransparencies(std::vector &transparencies) { void AnimationManager::calcLights(std::vector &lights, std::vector &bonesMatrices) { - auto &lightRecords = m_m2File->lights; + auto &lightRecords = boneMasterData->getM2Geom()->getM2Data()->lights; + auto &global_loops = *boneMasterData->getSkelData()->m_globalSequences; + if (lightRecords.size <= 0) return; + static mathfu::vec3 defaultVector(1.0, 1.0, 1.0); static float defaultFloat = 1.0; @@ -901,7 +1002,7 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto mathfu::vec4(animateTrackWithBlend( animationInfo, lightRecord->ambient_color, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultVector ), 1.0); @@ -910,7 +1011,7 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto animateTrackWithBlend( animationInfo, lightRecord->ambient_intensity, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -918,7 +1019,7 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto mathfu::vec4(animateTrackWithBlend( animationInfo, lightRecord->diffuse_color, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultVector ), 1.0); @@ -957,7 +1058,7 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto animateTrackWithBlend( animationInfo, lightRecord->diffuse_intensity, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -966,7 +1067,7 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto animateTrackWithBlend( animationInfo, lightRecord->attenuation_start, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -974,7 +1075,7 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto animateTrackWithBlend( animationInfo, lightRecord->attenuation_end, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -984,7 +1085,7 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto animateTrackWithBlend( animationInfo, lightRecord->visibility, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultChar ); @@ -999,6 +1100,11 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto // diffuse_intensity = 1.0; // } + if (boneMasterData->getM2Geom()->getM2Data()->global_flags.flag_unk_0x8000 == 0) { + attenuation_start = 1.6666f; + attenuation_end = 5.2666602f; + } + lights[i].ambient_color = ambient_color; lights[i].ambient_intensity = ambient_intensity; lights[i].ambient_color = ambient_color * ambient_intensity; @@ -1013,15 +1119,17 @@ void AnimationManager::calcLights(std::vector &lights, std::vecto } void AnimationManager::calcCamera(M2CameraResult &camera, int cameraId, mathfu::mat4 &placementMatrix) { - int animationIndex = this->mainAnimationIndex; + int animationIndex = this->animationInfo.currentAnimation.animationIndex; animTime_t animationTime = this->animationInfo.currentAnimation.animationTime; - auto &cameraRecords = m_m2File->cameras; + auto &global_loops = *boneMasterData->getSkelData()->m_globalSequences; + + auto &cameraRecords = boneMasterData->getM2Geom()->getM2Data()->cameras; if (cameraRecords.size <= 0) return; static mathfu::vec3 defaultVector(0.0, 0.0, 0.0); static float defaultFloat = 1.0; - auto animationRecord = m_m2File->sequences[animationIndex]; + auto animationRecord = animationInfo.currentAnimation.animationRecord; M2Camera * cameraRecord = cameraRecords.getElement(cameraId); mathfu::vec3 currentPosition = @@ -1030,7 +1138,7 @@ void AnimationManager::calcCamera(M2CameraResult &camera, int cameraId, mathfu:: animationRecord->duration, animationIndex, cameraRecord->positions, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultVector ); @@ -1042,7 +1150,7 @@ void AnimationManager::calcCamera(M2CameraResult &camera, int cameraId, mathfu:: animationRecord->duration, animationIndex, cameraRecord->target_position, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultVector ); @@ -1055,7 +1163,7 @@ void AnimationManager::calcCamera(M2CameraResult &camera, int cameraId, mathfu:: animationRecord->duration, animationIndex, cameraRecord->FoV, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1066,7 +1174,7 @@ void AnimationManager::calcCamera(M2CameraResult &camera, int cameraId, mathfu:: animationRecord->duration, animationIndex, cameraRecord->roll, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1082,8 +1190,11 @@ void AnimationManager::calcCamera(M2CameraResult &camera, int cameraId, mathfu:: void AnimationManager::calcParticleEmitters(std::vector &particleEmitters, std::vector &bonesMatrices) { - auto &peRecords = m_m2File->particle_emitters; + auto &peRecords = boneMasterData->getM2Geom()->getM2Data()->particle_emitters; if (peRecords.size <= 0) return; + + auto &global_loops = *boneMasterData->getSkelData()->m_globalSequences; + static mathfu::vec3 defaultVector(1.0, 1.0, 1.0); static float defaultFloat = 0.0; static unsigned char defaultChar = 1; @@ -1105,7 +1216,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part enabledIn = animateTrackWithBlend( animationInfo, peRecord.old.enabledIn, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultChar ); @@ -1118,7 +1229,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part animateTrackWithBlend( animationInfo, peRecord.old.emissionSpeed, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1126,7 +1237,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part animateTrackWithBlend( animationInfo, peRecord.old.speedVariation, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1134,7 +1245,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part animateTrackWithBlend( animationInfo, peRecord.old.verticalRange, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1142,7 +1253,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part animateTrackWithBlend( animationInfo, peRecord.old.horizontalRange, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1150,7 +1261,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part aniProp->gravity = animateTrackWithBlend( animationInfo, peRecord.old.gravityCompr, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultVector ); @@ -1161,7 +1272,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part -animateTrackWithBlend( animationInfo, peRecord.old.gravity, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat )); @@ -1172,7 +1283,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part animateTrackWithBlend( animationInfo, peRecord.old.lifespan, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1180,7 +1291,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part animateTrackWithBlend( animationInfo, peRecord.old.emissionRate, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1188,7 +1299,7 @@ void AnimationManager::calcParticleEmitters(std::vector &part animateTrackWithBlend( animationInfo, peRecord.old.emissionAreaWidth, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1196,16 +1307,16 @@ void AnimationManager::calcParticleEmitters(std::vector &part animateTrackWithBlend( animationInfo, peRecord.old.emissionAreaLength, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); - if (m_m2Geom.get()->exp2Records == nullptr) { + if (!m_hasExp2) { aniProp->zSource = animateTrackWithBlend( animationInfo, peRecord.old.zSource, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1216,8 +1327,11 @@ void AnimationManager::calcParticleEmitters(std::vector &part } void AnimationManager::calcRibbonEmitters(std::vector &ribbonEmitters){ - auto &ribbonRecords = m_m2File->ribbon_emitters; + auto &ribbonRecords = boneMasterData->getM2Geom()->getM2Data()->ribbon_emitters; if (ribbonRecords.size <= 0) return; + + auto &global_loops = *boneMasterData->getSkelData()->m_globalSequences; + static mathfu::vec3 defaultVector(1.0, 1.0, 1.0); static mathfu::vec4 defaultVector4(1.0, 1.0, 1.0, 1.0); static float defaultFloat = 1.0; @@ -1232,7 +1346,7 @@ void AnimationManager::calcRibbonEmitters(std::vector &ribbonE animateTrackWithBlend( animationInfo, ribbonRecord->colorTrack, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultVector4 ); @@ -1242,7 +1356,7 @@ void AnimationManager::calcRibbonEmitters(std::vector &ribbonE animateTrackWithBlend( animationInfo, ribbonRecord->alphaTrack, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1253,7 +1367,7 @@ void AnimationManager::calcRibbonEmitters(std::vector &ribbonE animateTrackWithBlend( animationInfo, ribbonRecord->heightAboveTrack, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1263,7 +1377,7 @@ void AnimationManager::calcRibbonEmitters(std::vector &ribbonE animateTrackWithBlend( animationInfo, ribbonRecord->heightBelowTrack, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultFloat ); @@ -1273,7 +1387,7 @@ void AnimationManager::calcRibbonEmitters(std::vector &ribbonE animateTrackWithBlend( animationInfo, ribbonRecord->texSlotTrack, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultInt ); @@ -1284,7 +1398,7 @@ void AnimationManager::calcRibbonEmitters(std::vector &ribbonE animateTrackWithBlend( animationInfo, ribbonRecord->visibilityTrack, - this->m_m2File->global_loops, + global_loops, this->globalSequenceTimes, defaultChar ); @@ -1299,3 +1413,15 @@ void AnimationManager::setAnimationPercent(float percent) { animationInfo.currentAnimation.animationRecord->duration * percent; } } + +void AnimationManager::calcAnimRepetition(AnimationStruct &animationStruct) { + auto &seqRec = animationStruct.animationRecord; + animationStruct.repeatTimes = + seqRec->replay.min + ( + (seqRec->replay.max - seqRec->replay.min) * ((float)std::rand() / (float)RAND_MAX) + ) - 1; +} + +int AnimationManager::getCurrentAnimationIndex() { + return this->animationInfo.currentAnimation.animationIndex; +} diff --git a/wowViewerLib/src/engine/managers/animationManager.h b/wowViewerLib/src/engine/managers/animationManager.h index 8985e06d8..f962c9a5c 100644 --- a/wowViewerLib/src/engine/managers/animationManager.h +++ b/wowViewerLib/src/engine/managers/animationManager.h @@ -12,25 +12,26 @@ class AnimationManager; #include "particles/particleEmitter.h" #include "CRibbonEmitter.h" #include "../algorithms/animate.h" +#include "../objects/m2/m2Helpers/CBoneMasterData.h" class AnimationManager { private: - ApiContainer *m_api; - HM2Geom m_m2Geom; - M2Data *m_m2File; + HApiContainer m_api; + std::shared_ptr boneMasterData; - int mainAnimationId; - int mainAnimationIndex = -1; FullAnimationInfo animationInfo; - bool firstCalc; - bool isAnimated; + bool firstCalc = true; bool deferredLoadingStarted = false; + bool isMirrored = false; + bool m_hasExp2 = false; + int leftHandClosed = 0; int rightHandClosed = 0; std::vector globalSequenceTimes; + std::vector parentGlobalSequenceTimes; std::vector bonesIsCalculated; std::vector blendMatrixArray; std::vector> childBonesLookup; @@ -39,16 +40,16 @@ class AnimationManager { void initBlendMatrices(); void initGlobalSequenceTimes(); - const int findAnimationIndex(uint32_t anim_id) const; - void calculateBoneTree(); void calcAnimMatrixes (std::vector &textAnimMatrices); + void calcAnimRepetition(AnimationStruct &animationStruct); public: - AnimationManager(ApiContainer *api, HM2Geom m2File); + AnimationManager(HApiContainer api, std::shared_ptr boneMasterData, bool hasExp2); void resetCurrentAnimation(); bool setAnimationId(int animationId, bool reset); + int getCurrentAnimationIndex(); void setAnimationPercent(float percent); void update ( animTime_t deltaTime, @@ -80,9 +81,6 @@ class AnimationManager { bool getIsFirstCalc() { return firstCalc; } - bool getIsAnimated() { - return isAnimated; - } void calcLights(std::vector &lights, std::vector &bonesMatrices); diff --git a/wowViewerLib/src/engine/managers/particles/CRndSeed.cpp b/wowViewerLib/src/engine/managers/particles/CRndSeed.cpp index 8e0e3083e..5f9992fbc 100644 --- a/wowViewerLib/src/engine/managers/particles/CRndSeed.cpp +++ b/wowViewerLib/src/engine/managers/particles/CRndSeed.cpp @@ -29,9 +29,8 @@ float CRndSeed::Uniform() { } else { result = fi.f - 2.0f; } -// result = (float) (drand48() * 2.0f - 1.0f); -// result = 0.0; - assert(result < 1.0 || result > -1.0); + + assert(result <= 1.0 && result >= -1.0); return result; } @@ -43,17 +42,9 @@ float CRndSeed::UniformPos() { uint32_t u = this->uint32t(); // [1, 2) fi.i = 0x3f800000 | (0x7fffff & u); - float result; - if (u & 0x80000000) { - result = 1.0f - fi.f; - } else { - result = fi.f - 1.0f; - } - -// result = (float) drand48(); -// result = 0.5; + float result = fi.f - 1.0; - assert(result > 0.0 || result < 1.0); + assert(result >= 0.0 && result <= 1.0); return result; } diff --git a/wowViewerLib/src/engine/managers/particles/generators/CParticleGenerator.cpp b/wowViewerLib/src/engine/managers/particles/generators/CParticleGenerator.cpp index b2e0d116e..105b9d554 100644 --- a/wowViewerLib/src/engine/managers/particles/generators/CParticleGenerator.cpp +++ b/wowViewerLib/src/engine/managers/particles/generators/CParticleGenerator.cpp @@ -23,7 +23,7 @@ float CParticleGenerator::GetLifeSpan(int16_t state) { float CParticleGenerator::CalcVelocity() { float velocity = aniProp.emissionSpeed; - velocity *= 1.0 + aniProp.speedVariation * seed.Uniform(); + velocity *= 1.0f + aniProp.speedVariation * seed.Uniform(); return velocity; } diff --git a/wowViewerLib/src/engine/managers/particles/generators/CParticleGenerator.h b/wowViewerLib/src/engine/managers/particles/generators/CParticleGenerator.h index 0d5114b76..db797c692 100644 --- a/wowViewerLib/src/engine/managers/particles/generators/CParticleGenerator.h +++ b/wowViewerLib/src/engine/managers/particles/generators/CParticleGenerator.h @@ -5,8 +5,6 @@ #ifndef WEBWOWVIEWERCPP_CPARTICLEGENERATOR_H #define WEBWOWVIEWERCPP_CPARTICLEGENERATOR_H - -#include "mathfu/glsl_mappings.h" #include "../particle.h" #include "../CGeneratorAniProp.h" #include "../CRndSeed.h" diff --git a/wowViewerLib/src/engine/managers/particles/generators/CSplineGenerator.cpp b/wowViewerLib/src/engine/managers/particles/generators/CSplineGenerator.cpp index 046f0868a..5d927b989 100644 --- a/wowViewerLib/src/engine/managers/particles/generators/CSplineGenerator.cpp +++ b/wowViewerLib/src/engine/managers/particles/generators/CSplineGenerator.cpp @@ -3,6 +3,13 @@ // #include "CSplineGenerator.h" +#include "../../../algorithms/mathHelper.h" + +CSplineGenerator::CSplineGenerator(CRndSeed &seed, M2Particle *particle, bool particlesGoUp) : + CParticleGenerator(seed, particle), particlesGoUp(particlesGoUp), splineBezier3(particle->old.splinePoints) { + +} + void CSplineGenerator::CreateParticle(CParticle2 &p, animTime_t delta) { float dvary = delta * this->seed.UniformPos(); @@ -38,14 +45,59 @@ void CSplineGenerator::CreateParticle(CParticle2 &p, animTime_t delta) { areaX = 0.0; } - auto areaY = this->aniProp.emissionAreaX; + auto areaY = this->aniProp.emissionAreaY; if (areaY >= 0.0) { areaY = std::min(areaY, 1.0f); } else { areaY = 0.0; } - float emissionArea = areaY - areaX; - float radius = areaX + emissionArea* this->seed.UniformPos(); + if (fabs(this->aniProp.emissionAreaY - this->m_areaY) >= 0.00000023841858f) { + this->m_areaY = areaY; + } else { + areaY = (areaY - areaX) * this->seed.UniformPos() + areaX; + } + + //Get particle position from spline using areaY as angle and C3Spline::Pos + if (areaY > 0.0) { + if (areaY < 1.0f ) { + splineBezier3.posArclength(areaY, p.position); + } else { + p.position = splineBezier3.getLastPoint(); + } + } else { + p.position = splineBezier3.getFirstPoint(); + } + + float velocity = this->CalcVelocity(); + float zSource = this->aniProp.zSource; + mathfu::vec3 resVelocityVector = mathfu::vec3(0,0,velocity); + if (zSource > 0.001) { + auto pos = p.position; + float zdiff = pos.z - zSource; + velocity = velocity / sqrt(pos.y*pos.y + pos.x * pos.x + zdiff*zdiff); + resVelocityVector.x = pos.x * velocity; + resVelocityVector.y = pos.y * velocity; + resVelocityVector.z = zdiff * velocity; + } else if (!feq(this->aniProp.verticalRange,0.0)){ + //Get vector from spline using areaY and C3Spline::Vel + float t = areaY; + t = std::max(t, 0.0f); + t = std::min(t, 1.0f); + + mathfu::vec3 vec; + splineBezier3.velArclength(t, vec); + vec = vec.Normalized(); + + auto rotMat = mathfu::quat::FromAngleAxis(this->seed.Uniform() * aniProp.verticalRange, vec).ToMatrix(); + auto zVec = rotMat.GetColumn(2); + if ( !feq(this->aniProp.horizontalRange,0.0) ) { + p.position += (this->aniProp.horizontalRange * this->seed.UniformPos()); + } + + resVelocityVector = zVec * velocity; + } + p.velocity = resVelocityVector; } + diff --git a/wowViewerLib/src/engine/managers/particles/generators/CSplineGenerator.h b/wowViewerLib/src/engine/managers/particles/generators/CSplineGenerator.h index 04e39b2c3..d48e8a56e 100644 --- a/wowViewerLib/src/engine/managers/particles/generators/CSplineGenerator.h +++ b/wowViewerLib/src/engine/managers/particles/generators/CSplineGenerator.h @@ -7,18 +7,20 @@ #include "CParticleGenerator.h" +#include "../../../algorithms/C3Spline_Bezier3.h" class CSplineGenerator : public CParticleGenerator { public: - CSplineGenerator(CRndSeed &seed, M2Particle *particle, bool particlesGoUp) : CParticleGenerator(seed, particle), particlesGoUp(particlesGoUp) { - - } + CSplineGenerator(CRndSeed &seed, M2Particle *particle, bool particlesGoUp); ~CSplineGenerator() override {} ; void CreateParticle(CParticle2 &p, animTime_t delta) override; private: + C3Spline_Bezier3 splineBezier3; + bool particlesGoUp = false; + float m_areaY = 0; }; #endif //WEBWOWVIEWERCPP_CSPLINEGENERATOR_H diff --git a/wowViewerLib/src/engine/managers/particles/particleEmitter.cpp b/wowViewerLib/src/engine/managers/particles/particleEmitter.cpp index d1796c3f6..44c4441fc 100644 --- a/wowViewerLib/src/engine/managers/particles/particleEmitter.cpp +++ b/wowViewerLib/src/engine/managers/particles/particleEmitter.cpp @@ -12,6 +12,8 @@ #include "generators/CPlaneGenerator.h" #include "../../../gapi/interface/IDevice.h" #include "../../persistance/header/M2FileHeader.h" +#include "../../../gapi/UniformBufferStructures.h" +#include "generators/CSplineGenerator.h" HGIndexBuffer ParticleEmitter::m_indexVBO = nullptr; @@ -87,7 +89,7 @@ static const struct { }; -ParticleEmitter::ParticleEmitter(ApiContainer *api, M2Particle *particle, M2Object *m2Object, HM2Geom geom, int txac_val_raw) : m_seed(rand()), m_api(api), m2Object(m2Object) { +ParticleEmitter::ParticleEmitter(HApiContainer api, M2Particle *particle, M2Object *m2Object, HM2Geom geom, int txac_val_raw) : m_seed(rand()), m_api(api), m2Object(m2Object) { if (!randTableInited) { for (int i = 0; i < 128; i++) { @@ -136,6 +138,9 @@ ParticleEmitter::ParticleEmitter(ApiContainer *api, M2Particle *particle, M2Obje case 2: this->generator = new CSphereGenerator(this->m_seed, particle, 0 != (m_data->old.flags & 0x100)); break; + case 3: + this->generator = new CSplineGenerator(this->m_seed, particle, 0 != (m_data->old.flags & 0x100)); + break; default: // this->generator = new CPlaneGenerator(this->m_seed, particle); this->generator = nullptr; @@ -248,14 +253,6 @@ void ParticleEmitter::selectShaderId() { } } -PACK( -struct meshParticleWideBlockPS { - float uAlphaTest; - float padding[3]; // according to std140 - int uPixelShader; - float padding2[3]; -}); - std::array PaticleBlendingModeToEGxBlendEnum1 = { EGxBlendEnum::GxBlend_Opaque, @@ -269,12 +266,12 @@ std::array PaticleBlendingModeToEGxBlendEnum1 = }; void ParticleEmitter::createMesh() { - std::shared_ptr device = m_api->hDevice; + HGDevice device = m_api->hDevice; if (m_indexVBO == nullptr) { m_indexVBO = device->createIndexBuffer(); int vo = 0; - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < MAX_PARTICLES_PER_EMITTER; i++) { szIndexBuff.push_back(vo + 0); szIndexBuff.push_back(vo + 1); szIndexBuff.push_back(vo + 2); @@ -347,10 +344,11 @@ void ParticleEmitter::createMesh() { meshTemplate.ubo[2] = nullptr; meshTemplate.ubo[3] = nullptr; - meshTemplate.ubo[4] = device->createUniformBufferChunk(sizeof(meshParticleWideBlockPS)); + meshTemplate.ubo[4] = device->createUniformBufferChunk(sizeof(Particle::meshParticleWideBlockPS)); - meshTemplate.ubo[4]->setUpdateHandler([this](IUniformBufferChunk *self) { - meshParticleWideBlockPS &blockPS = self->getObject(); + auto l_blendMode = meshTemplate.blendMode; + meshTemplate.ubo[4]->setUpdateHandler([this, l_blendMode](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) { + Particle::meshParticleWideBlockPS &blockPS = self->getObject(); uint8_t blendMode = m_data->old.blendingType; if (blendMode == 0) { blockPS.uAlphaTest = -1.0f; @@ -363,6 +361,7 @@ void ParticleEmitter::createMesh() { int uPixelShader = particleMaterialShader[this->shaderId].pixelShader; blockPS.uPixelShader = uPixelShader; + blockPS.uBlendMode = static_cast(l_blendMode); }); frame[i].m_mesh = device->createParticleMesh(meshTemplate); @@ -508,9 +507,9 @@ void ParticleEmitter::StepUpdate(animTime_t delta) { std::list listForDeletion; - int numThreads = m_api->getConfig()->getThreadCount(); + int numThreads = m_api->getConfig()->threadCount; - #pragma omp parallel for schedule(dynamic, 4) num_threads(numThreads) + #pragma omp parallel for schedule(dynamic, 200) default(none) shared(delta, forces) num_threads(numThreads) for (int i = 0; i < this->particles.size(); i++) { auto &p = this->particles[i]; bool killParticle = false; @@ -544,11 +543,11 @@ void ParticleEmitter::EmitNewParticles(animTime_t delta) { float rate = this->generator->GetEmissionRate(); this->emission += delta * rate; - while (this->emission > 1) { + while (this->emission > 1.0f) { if (particles.size() < MAX_PARTICLES_PER_EMITTER) { this->CreateParticle(delta); } - this->emission -= 1; + this->emission -= 1.0f; } } void ParticleEmitter::CreateParticle(animTime_t delta) { @@ -1093,10 +1092,13 @@ ParticleEmitter::BuildQuadT3( static const float vys[4] = {1, -1, 1, -1}; static const float txs[4] = {0, 0, 1, 1}; static const float tys[4] = {0, 1, 0, 1}; + + if (szVertexCnt >= MAX_PARTICLES_PER_EMITTER) return; + ParticleBuffStructQuad &record = szVertexBuf[szVertexCnt++]; // mathfu::mat4 inverseLookAt = mathfu::mat4::Identity(); - mathfu::mat4 inverseLookAt = this->inverseViewMatrix; + mathfu::mat4 &inverseLookAt = this->inverseViewMatrix; for (int i = 0; i < 4; i++) { @@ -1123,7 +1125,7 @@ ParticleEmitter::BuildQuadT3( } -void ParticleEmitter::collectMeshes(std::vector &meshes, int renderOrder) { +void ParticleEmitter::collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder) { if (getGenerator() == nullptr) return; auto ¤tFrame = frame[m_api->hDevice->getUpdateFrameNumber()]; @@ -1132,7 +1134,11 @@ void ParticleEmitter::collectMeshes(std::vector &meshes, int renderOrder HGParticleMesh mesh = frame[m_api->hDevice->getUpdateFrameNumber()].m_mesh; mesh->setRenderOrder(renderOrder); - meshes.push_back(mesh); + if (mesh->getIsTransparent()) { + transparentMeshes.push_back(mesh); + } else { + opaqueMeshes.push_back(mesh); + } } void ParticleEmitter::updateBuffers() { diff --git a/wowViewerLib/src/engine/managers/particles/particleEmitter.h b/wowViewerLib/src/engine/managers/particles/particleEmitter.h index e22cf3d1c..e7dd12d88 100644 --- a/wowViewerLib/src/engine/managers/particles/particleEmitter.h +++ b/wowViewerLib/src/engine/managers/particles/particleEmitter.h @@ -56,7 +56,7 @@ struct CParticleMaterialFlags { class ParticleEmitter { public: - ParticleEmitter(ApiContainer *api, M2Particle *particle, M2Object *m2Object, HM2Geom geom, int txac_val_raw); + ParticleEmitter(HApiContainer api, M2Particle *particle, M2Object *m2Object, HM2Geom geom, int txac_val_raw); ~ParticleEmitter() { delete generator; } @@ -67,7 +67,7 @@ class ParticleEmitter { CParticleGenerator * getGenerator(){ return generator; } - void collectMeshes(std::vector &meshes, int renderOrder); + void collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder); void updateBuffers(); int flags = 6; @@ -78,7 +78,7 @@ class ParticleEmitter { static float RandTable[128]; static bool randTableInited; private: - ApiContainer *m_api; + HApiContainer m_api; M2Particle *m_data; M2Object *m2Object; diff --git a/wowViewerLib/src/engine/objects/ViewsObjects.cpp b/wowViewerLib/src/engine/objects/ViewsObjects.cpp index d3dd715b4..8c1b78803 100644 --- a/wowViewerLib/src/engine/objects/ViewsObjects.cpp +++ b/wowViewerLib/src/engine/objects/ViewsObjects.cpp @@ -3,19 +3,30 @@ // #include "ViewsObjects.h" +#include "../../gapi/UniformBufferStructures.h" +#include "../shader/ShaderDefinitions.h" +#include -void ExteriorView::collectMeshes(std::vector &renderedThisFrame) { - auto inserter = std::back_inserter(renderedThisFrame); - std::copy(drawnChunks.begin(), drawnChunks.end(), inserter); +void ExteriorView::collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes) { + { + auto inserter = std::back_inserter(opaqueMeshes); + std::copy(this->m_opaqueMeshes.begin(), this->m_opaqueMeshes.end(), inserter); + } + + { + auto inserter = std::back_inserter(transparentMeshes); + std::copy(this->m_transparentMeshes.begin(), this->m_transparentMeshes.end(), inserter); + } - GeneralView::collectMeshes(renderedThisFrame); + GeneralView::collectMeshes(opaqueMeshes, transparentMeshes); } -void GeneralView::collectMeshes(std::vector &renderedThisFrame) { +void GeneralView::collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes) { for (auto& wmoGroup : drawnWmos) { - wmoGroup->collectMeshes(renderedThisFrame, renderOrder); + wmoGroup->collectMeshes(opaqueMeshes, transparentMeshes, renderOrder); } + // for (auto& m2 : drawnM2s) { // m2->collectMeshes(renderedThisFrame, renderOrder); // m2->drawParticles(renderedThisFrame , renderOrder); @@ -24,13 +35,17 @@ void GeneralView::collectMeshes(std::vector &renderedThisFrame) { void GeneralView::addM2FromGroups(mathfu::mat4 &frustumMat, mathfu::mat4 &lookAtMat4, mathfu::vec4 &cameraPos) { std::vector> candidates; - for (auto &wmoGroup : drawnWmos) { + for (auto &wmoGroup : wmosForM2) { auto doodads = wmoGroup->getDoodads(); std::copy(doodads->begin(), doodads->end(), std::back_inserter(candidates)); } //Delete duplicates - std::sort( candidates.begin(), candidates.end() ); +#if (_LIBCPP_HAS_PARALLEL_ALGORITHMS) + std::sort(std::execution::par_unseq, candidates.begin(), candidates.end() ); +#else + std::sort(candidates.begin(), candidates.end() ); +#endif candidates.erase( unique( candidates.begin(), candidates.end() ), candidates.end() ); // std::vector candidateResults = std::vector(candidates.size(), false); @@ -54,7 +69,7 @@ void GeneralView::addM2FromGroups(mathfu::mat4 &frustumMat, mathfu::mat4 &lookAt for (auto &m2Candidate : candidates) { if (m2Candidate == nullptr) continue; if (m2Candidate->m_cullResult) { - drawnM2s.push_back(m2Candidate); + drawnM2s.insert(m2Candidate); } } } @@ -63,12 +78,115 @@ void GeneralView::setM2Lights(std::shared_ptr m2Object) { m2Object->setUseLocalLighting(false); } +static std::array DrawPortalBindings = { + {+drawPortalShader::Attribute::aPosition, 3, GBindingType::GFLOAT, false, 12, 0 }, // 0 + //24 +}; + +void GeneralView::produceTransformedPortalMeshes(HApiContainer apiContainer, std::vector &opaqueMeshes, std::vector &transparentMeshes) { + + std::vector indiciesArray; + std::vector verticles; + int k = 0; + //int l = 0; + std::vector stripOffsets; + stripOffsets.push_back(0); + int verticleOffset = 0; + int stripOffset = 0; + for (int i = 0; i < this->worldPortalVertices.size(); i++) { + //if (portalInfo.index_count != 4) throw new Error("portalInfo.index_count != 4"); + + int verticlesCount = this->worldPortalVertices[i].size(); + if ((verticlesCount - 2) <= 0) { + stripOffsets.push_back(stripOffsets[i]); + continue; + }; + + for (int j =0; j < (((int)verticlesCount)-2); j++) { + indiciesArray.push_back(verticleOffset+0); + indiciesArray.push_back(verticleOffset+j+1); + indiciesArray.push_back(verticleOffset+j+2); + } + stripOffset += ((verticlesCount-2) * 3); + + for (int j =0; j < verticlesCount; j++) { + verticles.push_back(this->worldPortalVertices[i][j].x); + verticles.push_back(this->worldPortalVertices[i][j].y); + verticles.push_back(this->worldPortalVertices[i][j].z); + } + + verticleOffset += verticlesCount; + stripOffsets.push_back(stripOffset); + } + + if (verticles.empty()) return; + + auto hDevice = apiContainer->hDevice; + + portalPointsFrame.m_indexVBO = hDevice->createIndexBuffer(); + portalPointsFrame.m_bufferVBO = hDevice->createVertexBuffer(); + + portalPointsFrame.m_bindings = hDevice->createVertexBufferBindings(); + portalPointsFrame.m_bindings->setIndexBuffer(portalPointsFrame.m_indexVBO); + + GVertexBufferBinding vertexBinding; + vertexBinding.vertexBuffer = portalPointsFrame.m_bufferVBO; + vertexBinding.bindings = std::vector(DrawPortalBindings.begin(), DrawPortalBindings.end()); + + portalPointsFrame.m_bindings->addVertexBufferBinding(vertexBinding); + portalPointsFrame.m_bindings->save(); + + + portalPointsFrame.m_indexVBO->uploadData((void *) indiciesArray.data(), (int) (indiciesArray.size() * sizeof(uint16_t))); + portalPointsFrame.m_bufferVBO->uploadData((void *) verticles.data(), (int) (verticles.size() * sizeof(float))); + + { + HGShaderPermutation shaderPermutation = hDevice->getShader("drawPortalShader", nullptr); + + //Create mesh + gMeshTemplate meshTemplate(portalPointsFrame.m_bindings, shaderPermutation); + + meshTemplate.depthWrite = false; + meshTemplate.depthCulling = !apiContainer->getConfig()->renderPortalsIgnoreDepth; + meshTemplate.backFaceCulling = false; + + meshTemplate.blendMode = EGxBlendEnum::GxBlend_Alpha; + + //Let's assume ribbons are always at least transparent + if (meshTemplate.blendMode == EGxBlendEnum::GxBlend_Opaque) { + meshTemplate.blendMode = EGxBlendEnum::GxBlend_Alpha; + } + + meshTemplate.start = 0; + meshTemplate.end = indiciesArray.size(); + meshTemplate.element = DrawElementMode::TRIANGLES; + + meshTemplate.textureCount = 0; + + meshTemplate.ubo[0] = nullptr; //m_api->getSceneWideUniformBuffer(); + meshTemplate.ubo[1] = nullptr; + meshTemplate.ubo[2] = nullptr; + + meshTemplate.ubo[3] = nullptr; + meshTemplate.ubo[4] = hDevice->createUniformBufferChunk(sizeof(DrawPortalShader::meshWideBlockPS)); + + meshTemplate.ubo[4]->setUpdateHandler([](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) { + auto& blockPS = self->getObject(); + + blockPS.uColor = {0.058, 0.058, 0.819607843, 0.3}; + }); + + + transparentMeshes.push_back(hDevice->createParticleMesh(meshTemplate)); + } +} + void InteriorView::setM2Lights(std::shared_ptr m2Object) { - if (!drawnWmos[0]->getIsLoaded()) return; + if (ownerGroupWMO == nullptr || !ownerGroupWMO->getIsLoaded()) return; - if (drawnWmos[0]->getDontUseLocalLightingForM2()) { + if (ownerGroupWMO->getDontUseLocalLightingForM2()) { m2Object->setUseLocalLighting(false); } else { - drawnWmos[0]->assignInteriorParams(m2Object); + ownerGroupWMO->assignInteriorParams(m2Object); } } diff --git a/wowViewerLib/src/engine/objects/ViewsObjects.h b/wowViewerLib/src/engine/objects/ViewsObjects.h index 72c42f26c..473c1db57 100644 --- a/wowViewerLib/src/engine/objects/ViewsObjects.h +++ b/wowViewerLib/src/engine/objects/ViewsObjects.h @@ -25,32 +25,46 @@ class ADTObjRenderRes { class GeneralView { public: bool viewCreated = false; - std::vector> drawnWmos = {}; - std::vector> drawnM2s = {}; + std::unordered_set> drawnWmos = {}; //Wmos which draw their meshes + std::unordered_set> wmosForM2 = {}; //Wmo which contribute M2s to the scene + std::unordered_set> drawnM2s = {}; + //Support several frustum planes because of how portal culling works - std::vector> portalVertices = {}; + std::vector> worldPortalVertices = {}; std::vector> frustumPlanes = {}; + int level = -1; int renderOrder = -1; + struct PortalPointsFrame { + HGIndexBuffer m_indexVBO; + HGVertexBuffer m_bufferVBO; + + HGVertexBufferBindings m_bindings; + std::vector m_meshes = {}; + } portalPointsFrame; + + virtual void collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes); + virtual void setM2Lights(std::shared_ptr m2Object); - virtual void collectMeshes(std::vector &renderedThisFrame); - virtual void setM2Lights(std::shared_ptr m2Object);; + void produceTransformedPortalMeshes(HApiContainer apiContainer,std::vector &opaqueMeshes, std::vector &transparentMeshes); void addM2FromGroups(mathfu::mat4 &frustumMat, mathfu::mat4 &lookAtMat4, mathfu::vec4 &cameraPos); }; class InteriorView : public GeneralView { public: int portalIndex; + std::shared_ptr ownerGroupWMO = {}; //Wmos which portals belong to void setM2Lights(std::shared_ptr m2Object) override; }; class ExteriorView : public GeneralView { public: std::vector> drawnADTs = {}; - std::vector drawnChunks = {}; + std::vector m_opaqueMeshes = {}; + std::vector m_transparentMeshes = {}; public: - void collectMeshes(std::vector &renderedThisFrame) override; + void collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes) override; }; diff --git a/wowViewerLib/src/engine/objects/adt/adtObject.cpp b/wowViewerLib/src/engine/objects/adt/adtObject.cpp index a47e95e3c..11686ed04 100644 --- a/wowViewerLib/src/engine/objects/adt/adtObject.cpp +++ b/wowViewerLib/src/engine/objects/adt/adtObject.cpp @@ -21,8 +21,8 @@ static GBufferBinding bufferBinding[5] = { }; static GBufferBinding staticWaterBindings[2] = { - {+adtWater::Attribute::aPositionTransp, 4, GBindingType::GFLOAT, false, 24, 0}, - {+adtWater::Attribute::aTexCoord, 2, GBindingType::GFLOAT, false, 24, 16}, + {+waterShader::Attribute::aPositionTransp, 4, GBindingType::GFLOAT, false, 24, 0}, + {+waterShader::Attribute::aTexCoord, 2, GBindingType::GFLOAT, false, 24, 16}, // {+waterShader::Attribute::aDepth, 1, GBindingType::GFLOAT, false, 24, 0 }, // {+waterShader::Attribute::aTexCoord, 2, GBindingType::GFLOAT, false, 24, 4}, @@ -45,9 +45,10 @@ void AdtObject::loadingFinished() { loadM2s(); loadWmos(); - loadWater(); m_loaded = true; + + loadWater(); } void AdtObject::loadM2s() { @@ -137,169 +138,193 @@ void AdtObject::loadWmos() { } } } -void AdtObject::loadWater() { - if (m_adtFile->mH2OHeader == nullptr) return; - //Parse the blob - PACK( - struct LiquidVertexFormat { - mathfu::vec4_packed pos_transp; - mathfu::vec2_packed uv; - }); - std::vector vertexBuffer; - std::vector indexBuffer; +HGMesh AdtObject::createWaterMeshFromInstance(int x_chunk, int y_chunk, SMLiquidInstance &liquidInstance, mathfu::vec3 liquidBasePos) { - const float TILESIZE = 533.3433333f ; - const float CHUNKSIZE = TILESIZE / 16.0f; - const float UNITSIZE = CHUNKSIZE / 8.0f; + uint64_t infoMask = 0xFFFFFFFFFFFFFFFF; // default = all water + if (liquidInstance.offset_exists_bitmap > 0 && liquidInstance.height > 0) + { + size_t bitmask_size = static_cast(std::ceil(liquidInstance.height * liquidInstance.width / 8.0f)); + std::memcpy(&infoMask, &m_adtFile->mH2OBlob[liquidInstance.offset_exists_bitmap - m_adtFile->mH2OblobOffset], bitmask_size); + } - mathfu::vec3 adtBasePos = mathfu::vec3(AdtIndexToWorldCoordinate(adt_y), AdtIndexToWorldCoordinate(adt_x), 0); + float *heightPtr = nullptr; + if (liquidInstance.offset_vertex_data != 0) { + heightPtr = ((float *) (&m_adtFile->mH2OBlob[liquidInstance.offset_vertex_data - m_adtFile->mH2OblobOffset])); + } int basetextureFDID = 0; - mathfu::vec3 color = mathfu::vec3(1,1,1); - for (int y_chunk = 0; y_chunk < 16; y_chunk++) { - for (int x_chunk = 0; x_chunk < 16; x_chunk++) { - auto &liquidChunk = m_adtFile->mH2OHeader->chunks[y_chunk*16 + x_chunk]; - + mathfu::vec3 color = mathfu::vec3(0,0,0); + mathfu::vec3 minimapStaticCol = {0,0,0}; + //SmallHack + int liquidFlags = 0; + int l_liquidType = liquidInstance.liquid_type; + int l_liquidObjectType = liquidInstance.liquid_object_or_lvf; + + if (basetextureFDID == 0 && (m_api->databaseHandler != nullptr)) { + if (liquidInstance.liquid_object_or_lvf > 42) { + std::vector liqMats; + m_api->databaseHandler->getLiquidObjectData(liquidInstance.liquid_object_or_lvf, liqMats); + for (auto &liqMat : liqMats) { + if (liqMat.FileDataId != 0) { + basetextureFDID = liqMat.FileDataId; + if (liqMat.color1[0] > 0 || liqMat.color1[1] > 0 || liqMat.color1[2] > 0) { + color = mathfu::vec3(liqMat.color1[2], liqMat.color1[1], liqMat.color1[0]); + } + minimapStaticCol = mathfu::vec3(liqMat.minimapStaticCol[2], liqMat.minimapStaticCol[1], liqMat.minimapStaticCol[0]); - if (liquidChunk.layer_count == 0) continue; + liquidFlags = liqMat.flags; + break; + } + } + } else { - auto *liquidInstPtr = - ((SMLiquidInstance *)(&m_adtFile->mH2OBlob[liquidChunk.offset_instances - m_adtFile->mH2OblobOffset])); + std::vector liquidTypeData; + m_api->databaseHandler->getLiquidTypeData(liquidInstance.liquid_type, liquidTypeData); + for (auto ltd: liquidTypeData) { + if (ltd.FileDataId != 0) { + basetextureFDID = ltd.FileDataId; - mathfu::vec3 liquidBasePos = - adtBasePos - - mathfu::vec3( - CHUNKSIZE*y_chunk, - CHUNKSIZE*x_chunk, - 0); + if (ltd.color1[0] > 0 || ltd.color1[1] > 0 || ltd.color1[2] > 0) { + color = mathfu::vec3(ltd.color1[0], ltd.color1[1], ltd.color1[2]); + } + minimapStaticCol = mathfu::vec3(ltd.minimapStaticCol[2], ltd.minimapStaticCol[1], ltd.minimapStaticCol[0]); + liquidFlags = ltd.flags; + break; + } + } + } + } - for (int layerInd = 0; layerInd < liquidChunk.layer_count; layerInd++) { - SMLiquidInstance &liquidInstance = liquidInstPtr[layerInd]; +// int baseVertexIndForInst = vertexBuffer.size(); + int baseVertexIndForInst = 0; - uint64_t infoMask = 0xFFFFFFFFFFFFFFFF; // default = all water - if (liquidInstance.offset_exists_bitmap > 0 && liquidInstance.height > 0) - { - size_t bitmask_size = static_cast(std::ceil(liquidInstance.height * liquidInstance.width / 8.0f)); - std::memcpy(&infoMask, &m_adtFile->mH2OBlob[liquidInstance.offset_exists_bitmap - m_adtFile->mH2OblobOffset], bitmask_size); - } + int bitOffset = 0; + int i = this->m_adtFile->mcnkMap[x_chunk][y_chunk]; + auto &waterAaBB = waterTileAabb[i]; + SMChunk *mcnkChunk = &m_adtFile->mapTile[i]; +// - float *heightPtr = nullptr; - if (liquidInstance.offset_vertex_data != 0) { - heightPtr = ((float *) (&m_adtFile->mH2OBlob[liquidInstance.offset_vertex_data - m_adtFile->mH2OblobOffset])); - } +// +// float minX = mcnkChunk->position.x - (MathHelper::CHUNKSIZE); +// float maxX = mcnkChunk->position.x; +// float minY = mcnkChunk->position.y - (MathHelper::CHUNKSIZE); +// float maxY = mcnkChunk->position.y; - //SmallHack - if (basetextureFDID == 0 && (m_api->databaseHandler != nullptr)) { - if (liquidInstance.liquid_object_or_lvf > 42) { - std::vector liqMats; - m_api->databaseHandler->getLiquidObjectData(liquidInstance.liquid_object_or_lvf, liqMats); - for (auto &liqMat : liqMats) { - if (liqMat.FileDataId != 0) { - basetextureFDID = liqMat.FileDataId; - if (liqMat.color1[0] > 0 || liqMat.color1[1] > 0 || liqMat.color1[2] > 0) { - color = mathfu::vec3(liqMat.color1[0], liqMat.color1[1], liqMat.color1[2]); - } - break; - } - } - } else { - std::vector fileDataIds; - m_api->databaseHandler->getLiquidTypeData(liquidInstance.liquid_type, fileDataIds); - for (auto fdid: fileDataIds) { - if (fdid != 0) { - basetextureFDID = fdid; - break; - } - } - } - } + float minX = 999999; float maxX = -999999; + float minY = 999999; float maxY = -999999; + float minZ = 999999; float maxZ = -999999; - int baseVertexIndForInst = vertexBuffer.size(); - - int bitOffset = 0; - int i = this->m_adtFile->mcnkMap[y_chunk][x_chunk]; - auto &waterAaBB = waterTileAabb[i]; - SMChunk *mcnkChunk = &m_adtFile->mapTile[i]; - - float minZ = 999999; - float maxZ = -999999; - - float minX = mcnkChunk->position.x - (533.3433333 / 16.0); - float maxX = mcnkChunk->position.x; - float minY = mcnkChunk->position.y - (533.3433333 / 16.0); - float maxY = mcnkChunk->position.y; - minZ += mcnkChunk->position.z; - maxZ += mcnkChunk->position.z; - - for (int y = 0; y < liquidInstance.height + 1; y++) { - for (int x = 0; x < liquidInstance.width + 1; x++) { - mathfu::vec3 pos = - liquidBasePos - - mathfu::vec3( - UNITSIZE*(y+liquidInstance.y_offset), - UNITSIZE*(x+liquidInstance.x_offset), - -liquidInstance.min_height_level - ); - if (minZ > pos.z) minZ = pos.z; - if (maxZ < pos.z) maxZ = pos.z; - - bool hackBool = !((liquidInstance.liquid_object_or_lvf == 42) && (liquidInstance.liquid_type == 2)); - if (liquidInstance.liquid_object_or_lvf != 2 && heightPtr!= nullptr && hackBool) { - pos.z = heightPtr[y * (liquidInstance.width + 1) + x]; - } + minX = std::min(minX, waterAaBB.min.x); maxX = std::max(maxX, waterAaBB.max.x); + minY = std::min(minY, waterAaBB.min.y); maxY = std::max(maxY, waterAaBB.max.y); + minZ = std::min(minZ, waterAaBB.min.z); maxZ = std::max(maxZ, waterAaBB.max.z); - LiquidVertexFormat vertex; - vertex.pos_transp = mathfu::vec4(pos, 1.0); - vertex.uv = mathfu::vec2(0,0); + //Parse the blob + PACK( + struct LiquidVertexFormat { + mathfu::vec4_packed pos_transp; + mathfu::vec2_packed uv; + }); + std::vector vertexBuffer; + std::vector indexBuffer; - vertexBuffer.push_back(vertex); - } - } - waterAaBB = CAaBox( - C3Vector(mathfu::vec3(minX, minY, minZ)), - C3Vector(mathfu::vec3(maxX, maxY, maxZ)) + for (int y = 0; y < liquidInstance.height + 1; y++) { + for (int x = 0; x < liquidInstance.width + 1; x++) { + mathfu::vec3 pos = + liquidBasePos - + mathfu::vec3( + MathHelper::UNITSIZE*(y+liquidInstance.y_offset), + MathHelper::UNITSIZE*(x+liquidInstance.x_offset), + -liquidInstance.min_height_level ); + bool hackBool = !((liquidInstance.liquid_object_or_lvf == 42) && (liquidInstance.liquid_type == 2)); + if (liquidInstance.liquid_object_or_lvf != 2 && heightPtr!= nullptr && hackBool) { + pos.z = heightPtr[y * (liquidInstance.width + 1) + x]; + } + minX = std::min(minX, pos.x); maxX = std::max(maxX, pos.x); + minY = std::min(minY, pos.y); maxY = std::max(maxY, pos.y); + minZ = std::min(minZ, pos.z); maxZ = std::max(maxZ, pos.z); - for (int y = 0; y < liquidInstance.height; y++) { - for (int x = 0; x < liquidInstance.width; x++) { - if (((infoMask >> (bitOffset++)) & 1) == 0) continue; - int16_t vertindexes[4] = { - (int16_t) (baseVertexIndForInst + y * (liquidInstance.width +1 ) + x), - (int16_t) (baseVertexIndForInst + y * (liquidInstance.width + 1) + x + 1), - (int16_t) (baseVertexIndForInst + (y + 1) * (liquidInstance.width + 1) + x), - (int16_t) (baseVertexIndForInst + (y + 1) * (liquidInstance.width + 1) + x + 1), - }; + LiquidVertexFormat vertex; + vertex.pos_transp = mathfu::vec4(pos, 1.0); + vertex.uv = mathfu::vec2(0,0); - indexBuffer.push_back (vertindexes[0]); - indexBuffer.push_back (vertindexes[1]); - indexBuffer.push_back (vertindexes[2]); + vertexBuffer.push_back(vertex); + } + } + waterAaBB = CAaBox( + C3Vector(mathfu::vec3(minX, minY, minZ)), + C3Vector(mathfu::vec3(maxX, maxY, maxZ)) + ); - indexBuffer.push_back (vertindexes[1]); - indexBuffer.push_back (vertindexes[3]); - indexBuffer.push_back (vertindexes[2]); - } + for (int y = 0; y < liquidInstance.height; y++) { + for (int x = 0; x < liquidInstance.width; x++) { + if (((infoMask >> (bitOffset++)) & 1) == 0) continue; + int16_t vertindexes[4] = { + (int16_t) (baseVertexIndForInst + y * (liquidInstance.width +1 ) + x), + (int16_t) (baseVertexIndForInst + y * (liquidInstance.width + 1) + x + 1), + (int16_t) (baseVertexIndForInst + (y + 1) * (liquidInstance.width + 1) + x), + (int16_t) (baseVertexIndForInst + (y + 1) * (liquidInstance.width + 1) + x + 1), + }; + + indexBuffer.push_back (vertindexes[0]); + indexBuffer.push_back (vertindexes[1]); + indexBuffer.push_back (vertindexes[2]); + + indexBuffer.push_back (vertindexes[1]); + indexBuffer.push_back (vertindexes[3]); + indexBuffer.push_back (vertindexes[2]); + } + } + + //Query river color + mathfu::vec3 closeRiverColor = {0, 0, 0}; + if (m_api->getConfig()->useCloseRiverColorForDB) { + + mathfu::vec3 waterPos = (mathfu::vec3(waterAaBB.max) + mathfu::vec3(waterAaBB.min)) / 2.0f; + bool waterColorFound = true; + if (m_api->getConfig()->colorOverrideHolder != nullptr) { + waterColorFound = false; + int adt_global_x = worldCoordinateToGlobalAdtChunk(waterPos.y) % 16; + int adt_global_y = worldCoordinateToGlobalAdtChunk(waterPos.x) % 16; + + auto areaId = getAreaId(adt_global_x, adt_global_y); + + for (auto &riverOverride : *m_api->getConfig()->colorOverrideHolder) { + if (riverOverride.areaId == areaId) { + closeRiverColor = riverOverride.color.xyz(); + waterColorFound = true; + break; } } } + if (!waterColorFound) { + std::vector lightResults = {}; + this->m_mapApi->getLightResultsFromDB(waterPos, m_api->getConfig(), lightResults, nullptr); + for (auto &_light : lightResults) { + closeRiverColor += mathfu::vec3(_light.closeRiverColor) * _light.blendCoef; + } + closeRiverColor = mathfu::vec3(closeRiverColor[2], closeRiverColor[1], closeRiverColor[0]); + } } - std::shared_ptr device = m_api->hDevice; - waterIBO = device->createIndexBuffer(); + HGDevice device = m_api->hDevice; + + auto waterIBO = device->createIndexBuffer(); waterIBO->uploadData( indexBuffer.data(), indexBuffer.size() * sizeof(uint16_t)); - waterVBO = device->createVertexBuffer(); + auto waterVBO = device->createVertexBuffer(); waterVBO->uploadData( vertexBuffer.data(), vertexBuffer.size() * sizeof(LiquidVertexFormat) ); - vertexWaterBufferBindings = device->createVertexBufferBindings(); + auto vertexWaterBufferBindings = device->createVertexBufferBindings(); vertexWaterBufferBindings->setIndexBuffer(waterIBO); GVertexBufferBinding vertexBinding; @@ -312,7 +337,7 @@ void AdtObject::loadWater() { //Create mesh(es) - HGShaderPermutation shaderPermutation = m_api->hDevice->getShader("adtWater", nullptr); + HGShaderPermutation shaderPermutation = m_api->hDevice->getShader("waterShader", nullptr); gMeshTemplate meshTemplate(vertexWaterBufferBindings, shaderPermutation); @@ -325,7 +350,6 @@ void AdtObject::loadWater() { meshTemplate.textureCount = 1; if (basetextureFDID != 0) { - auto htext = m_api->cacheStorage->getTextureCache()->getFileId(basetextureFDID); meshTemplate.texture[0] = m_api->hDevice->createBlpTexture(htext, true, true); } else { @@ -333,7 +357,7 @@ void AdtObject::loadWater() { } meshTemplate.ubo[0] = nullptr; //m_api->getSceneWideUniformBuffer(); - meshTemplate.ubo[1] = nullptr; + meshTemplate.ubo[1] = device->createUniformBufferChunk(sizeof(mathfu::mat4)); meshTemplate.ubo[2] = nullptr; meshTemplate.ubo[3] = nullptr; @@ -343,18 +367,66 @@ void AdtObject::loadWater() { meshTemplate.end = indexBuffer.size(); meshTemplate.element = DrawElementMode::TRIANGLES; - auto l_liquidType = 0; - meshTemplate.ubo[4]->setUpdateHandler([this, l_liquidType, color](IUniformBufferChunk* self) -> void { - mathfu::vec4 closeRiverColor = this->m_api->getConfig()->getCloseRiverColor(); - mathfu::vec4_packed &color_ = self->getObject(); - color_ = mathfu::vec4(closeRiverColor.xyz(), 0.7); + meshTemplate.ubo[1]->setUpdateHandler([](IUniformBufferChunk* self, const HFrameDepedantData &frameDepedantData ) -> void { + mathfu::mat4 &placementMat = self->getObject(); + placementMat = mathfu::mat4::Identity(); + }); + meshTemplate.ubo[4]->setUpdateHandler([this, l_liquidType, l_liquidObjectType, color, liquidFlags, minimapStaticCol, closeRiverColor](IUniformBufferChunk* self, const HFrameDepedantData &frameDepedantData) -> void { + mathfu::vec4_packed &color_ = self->getObject(); + if (!frameDepedantData->useMinimapWaterColor) { + if ((liquidFlags & 1024) > 0) {// Ocean + color_ = frameDepedantData->closeOceanColor; + } else if (liquidFlags == 15) { //River/Lake + if (frameDepedantData->useCloseRiverColorForDB) { + color_ = mathfu::vec4(closeRiverColor,0.7); + } else { + color_ = frameDepedantData->closeRiverColor; + } + } else { + color_ = mathfu::vec4(color, 0.7); + } + } else { + color_ = mathfu::vec4(minimapStaticCol, 0.7); + }; }); + auto mesh = m_api->hDevice->createMesh(meshTemplate); + mesh->setSortDistance(0); + return mesh; +} +void AdtObject::loadWater() { + if (m_adtFile->mH2OHeader == nullptr) return; + + mathfu::vec3 adtBasePos = mathfu::vec3(AdtIndexToWorldCoordinate(adt_y), AdtIndexToWorldCoordinate(adt_x), 0); + + for (int y_chunk = 0; y_chunk < 16; y_chunk++) { + for (int x_chunk = 0; x_chunk < 16; x_chunk++) { + auto &liquidChunk = m_adtFile->mH2OHeader->chunks[y_chunk*16 + x_chunk]; + if (liquidChunk.layer_count == 0) continue; + + auto *liquidInstPtr = + ((SMLiquidInstance *)(&m_adtFile->mH2OBlob[liquidChunk.offset_instances - m_adtFile->mH2OblobOffset])); - waterMesh = m_api->hDevice->createMesh(meshTemplate); + mathfu::vec3 liquidBasePos = + adtBasePos - + mathfu::vec3( + MathHelper::CHUNKSIZE*y_chunk, + MathHelper::CHUNKSIZE*x_chunk, + 0); + + int i = this->m_adtFile->mcnkMap[x_chunk][y_chunk]; + auto &waterAaBB = waterTileAabb[i]; + waterAaBB.min = mathfu::vec3(99999,99999,99999); + waterAaBB.max = mathfu::vec3(-99999,-99999,-99999); + for (int layerInd = 0; layerInd < liquidChunk.layer_count; layerInd++) { + SMLiquidInstance &liquidInstance = liquidInstPtr[layerInd]; + waterMeshes[i].push_back(createWaterMeshFromInstance(x_chunk,y_chunk,liquidInstance,liquidBasePos)); + } + } + } } @@ -454,7 +526,7 @@ void AdtObject::createVBO() { } /* 1.3 Make combinedVbo */ - std::shared_ptr device = m_api->hDevice; + HGDevice device = m_api->hDevice; combinedVbo = device->createVertexBuffer(); combinedVbo->uploadData(vboArray.data(), vboArray.size()*sizeof(float)); @@ -534,16 +606,16 @@ void AdtObject::calcBoundingBoxes() { } } - float minX = mcnkChunk->position.x - (533.3433333 / 16.0); + float minX = mcnkChunk->position.x - (MathHelper::CHUNKSIZE); float maxX = mcnkChunk->position.x; - float minY = mcnkChunk->position.y - (533.3433333 / 16.0); + float minY = mcnkChunk->position.y - (MathHelper::CHUNKSIZE); float maxY = mcnkChunk->position.y; minZ += mcnkChunk->position.z; maxZ += mcnkChunk->position.z; this->tileAabb[i] = CAaBox( - C3Vector(mathfu::vec3(minX, minY, minZ)), - C3Vector(mathfu::vec3(maxX, maxY, maxZ)) + C3Vector(mathfu::vec3(minX, minY, minZ-10)), + C3Vector(mathfu::vec3(maxX, maxY, maxZ+10)) ); this->globIndexY[i] = worldCoordinateToGlobalAdtChunk((minX + maxX) / 2.0f); @@ -552,17 +624,19 @@ void AdtObject::calcBoundingBoxes() { } void AdtObject::createMeshes() { - std::shared_ptr device = m_api->hDevice; + HGDevice device = m_api->hDevice; auto adtFileTex = m_adtFileTex; auto adtFile = m_adtFile; adtWideBlockPS = m_api->hDevice->createUniformBufferChunk(sizeof(ADT::modelWideBlockPS)); + int useHeightMixFormula = m_wdtFile->mphd->flags.adt_has_height_texturing > 0; +// int useHeightMixFormula = 1; auto api = m_api; - adtWideBlockPS->setUpdateHandler([api](IUniformBufferChunk *self){ + adtWideBlockPS->setUpdateHandler([api, useHeightMixFormula](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ auto *adtWideblockPS = &self->getObject(); - + adtWideblockPS->useHeightMixFormula[0] = useHeightMixFormula; }); if (adtVertexBindings != nullptr) { @@ -595,7 +669,8 @@ void AdtObject::createMeshes() { aTemplate.texture = std::vector(aTemplate.textureCount, nullptr); - aTemplate.ubo[4]->setUpdateHandler([&api, adtFileTex, noLayers, i, this](IUniformBufferChunk *self) { + int chunkIndex = i; + aTemplate.ubo[4]->setUpdateHandler([&api, adtFileTex, noLayers, chunkIndex, this](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) { auto &blockPS = self->getObject(); for (int j = 0; j < 4; j++) { @@ -604,17 +679,17 @@ void AdtObject::createMeshes() { blockPS.animationMat[j] = mathfu::mat4::Identity(); } - for (int j = 0; j < adtFileTex->mcnkStructs[i].mclyCnt; j++) { + for (int j = 0; j < adtFileTex->mcnkStructs[chunkIndex].mclyCnt; j++) { if ((adtFileTex->mtxp_len > 0) && !noLayers) { - auto const &textureParams = adtFileTex->mtxp[adtFileTex->mcnkStructs[i].mcly[j].textureId]; + auto const &textureParams = adtFileTex->mtxp[adtFileTex->mcnkStructs[chunkIndex].mcly[j].textureId]; blockPS.uHeightOffset[j] = textureParams.heightOffset; blockPS.uHeightScale[j] = textureParams.heightScale; } - blockPS.animationMat[j] = this->texturesPerMCNK[i].animTexture[j]; + blockPS.animationMat[j] = this->texturesPerMCNK[chunkIndex].animTexture[j]; } }); - aTemplate.ubo[2]->setUpdateHandler([this, i](IUniformBufferChunk *self) { + aTemplate.ubo[2]->setUpdateHandler([this, i](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) { auto &blockVS = self->getObject(); blockVS.uPos = mathfu::vec4( this->m_adtFile->mapTile[i].position.x, @@ -629,9 +704,9 @@ void AdtObject::createMeshes() { for (int j = 0; j < m_adtFileTex->mcnkStructs[i].mclyCnt; j++) { auto const &textureParams = m_adtFileTex->mtxp[m_adtFileTex->mcnkStructs[i].mcly[j].textureId]; - HGTexture layer_height = nullptr; + HGTexture layer_height = device->getWhiteTexturePixel(); if (textureParams.flags.do_not_load_specular_or_height_texture_but_use_cubemap == 0) { - if (!feq(textureParams.heightOffset, 1.0f) && !feq(textureParams.heightScale, 0.0)) { + if (!feq(textureParams.heightScale, 0.0)) { layer_height = getAdtHeightTexture(m_adtFileTex->mcnkStructs[i].mcly[j].textureId); } } @@ -665,7 +740,7 @@ void AdtObject::createMeshes() { } HGMesh hgMesh = device->createMesh(aTemplate); - adtMeshes.push_back(hgMesh); + adtMeshes[i] = hgMesh; } } } @@ -681,7 +756,7 @@ void AdtObject::loadAlphaTextures() { int createdThisRun = 0; for (int i = 0; i < chunkCount; i++) { - HGTexture alphaTexture = m_api->hDevice->createTexture(); + HGTexture alphaTexture = m_api->hDevice->createTexture(false, false); std::vector alphaTextureData; m_adtFileTex->processTexture(m_wdtFile->mphd->flags, i, alphaTextureData); @@ -694,8 +769,8 @@ void AdtObject::loadAlphaTextures() { -void AdtObject::collectMeshes(ADTObjRenderRes &adtRes, std::vector &renderedThisFrame, int renderOrder) { - m_lastTimeOfUpdateOrRefCheck = m_mapApi->getCurrentSceneTime(); +void AdtObject::collectMeshes(ADTObjRenderRes &adtRes, std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder) { + if (m_freeStrategy != nullptr) m_freeStrategy(false, true, m_mapApi->getCurrentSceneTime()); if (!m_loaded) return; @@ -703,12 +778,15 @@ void AdtObject::collectMeshes(ADTObjRenderRes &adtRes, std::vector &rend size_t meshCount = adtMeshes.size(); for (int i = 0; i < meshCount; i++) { - if (!adtRes.drawChunk[i]) continue; - adtMeshes[i]->setRenderOrder(renderOrder); - renderedThisFrame.push_back(adtMeshes[i]); - } - if (waterMesh!= nullptr) { - renderedThisFrame.push_back(waterMesh); + if (adtRes.drawChunk[i] && (adtMeshes[i] != nullptr)) { + adtMeshes[i]->setRenderOrder(renderOrder); + opaqueMeshes.push_back(adtMeshes[i]); + } + if (adtRes.drawWaterChunk[i]) { + for (auto const &waterMesh : waterMeshes[i]) { + transparentMeshes.push_back(waterMesh); + } + } } } void AdtObject::collectMeshesLod(std::vector &renderedThisFrame) { @@ -772,13 +850,13 @@ void AdtObject::doPostLoad() { } } void AdtObject::update(animTime_t deltaTime ) { - m_lastTimeOfUpdateOrRefCheck = m_mapApi->getCurrentSceneTime(); - m_lastDeltaTime = deltaTime; m_lastTimeOfUpdate = m_mapApi->getCurrentSceneTime(); // std::cout << "AdtObject::update finished called" << std::endl; - if (!m_loaded) return; + if (!m_loaded) { + return; + } if (adtWideBlockPS == nullptr) return; for (int i = 0; i < 256; i++) { @@ -788,10 +866,11 @@ void AdtObject::update(animTime_t deltaTime ) { if (m_adtFileTex->mtxp_len > 0) { auto const &textureParams = m_adtFileTex->mtxp[m_adtFileTex->mcnkStructs[i].mcly[j].textureId]; - if (m_adtFileTex->mcnkStructs[i].mcly[j].flags.unknown_0x800 != 0 || m_adtFileTex->mcnkStructs[i].mcly[j].flags.unknown_0x1000 != 0) { - float scaleFactor = -(1.0 / (float)(1 << textureParams.flags.texture_scale)) * (1.0f / -4.1666665f); +// if (m_adtFileTex->mcnkStructs[i].mcly[j].flags.unknown_0x800 != 0 || m_adtFileTex->mcnkStructs[i].mcly[j].flags.unknown_0x1000 != 0) { + float scaleFactor = (1.0f / (float)(1u << (textureParams.flags.texture_scale ))); +// float scaleFactor = ((float)(1u << (textureParams.flags.texture_scale )) / 4.0f); texturesPerMCNK[i].animTexture[j]*=mathfu::mat4::FromScaleVector({scaleFactor, scaleFactor, scaleFactor}); - } +// } } if (m_adtFileTex->mcnkStructs[i].mcly[j].flags.animation_enabled != 0) { @@ -943,8 +1022,8 @@ bool AdtObject::iterateQuadTree(ADTObjRenderRes &adtFrustRes, mathfu::vec4 &came if (quadTree == nullptr || quadTreeInd == -1 || curentLod == 4) { mathfu::vec2 aabb2D[2]; - aabb2D[0] = pos.xy() - mathfu::vec2(533.3333f * (x_offset + cell_len), 533.3333f*(y_offset + cell_len)); - aabb2D[1] = pos.xy() - mathfu::vec2(533.3333f *x_offset, 533.3333f*y_offset); + aabb2D[0] = pos.xy() - mathfu::vec2(MathHelper::TILESIZE * (x_offset + cell_len), MathHelper::TILESIZE*(y_offset + cell_len)); + aabb2D[1] = pos.xy() - mathfu::vec2(MathHelper::TILESIZE *x_offset, MathHelper::TILESIZE*y_offset); mathfu::vec2 point = camera.xy(); //General frustum cull! @@ -1101,10 +1180,11 @@ bool AdtObject::checkReferences( std::vector> &m2ObjectsCandidates, std::vector> &wmoCandidates, int x, int y, int x_len, int y_len) { - m_lastTimeOfUpdateOrRefCheck = m_mapApi->getCurrentSceneTime(); - if (!m_loaded) return false; + if (m_freeStrategy != nullptr) + m_freeStrategy(false, true, m_mapApi->getCurrentSceneTime()); + for (int k = x; k < x+x_len; k++) { for (int l = y; l < y + y_len; l++) { int i = this->m_adtFile->mcnkMap[k][l]; @@ -1181,8 +1261,11 @@ bool AdtObject::checkFrustumCulling(ADTObjRenderRes &adtFrustRes, mathfu::mat4 &lookAtMat4, std::vector> &m2ObjectsCandidates, std::vector> &wmoCandidates) { - - if (!this->m_loaded) return true; + if (!this->m_loaded) { + if (m_freeStrategy != nullptr) + m_freeStrategy(false, true, m_mapApi->getCurrentSceneTime()); + return true; + } bool atLeastOneIsDrawn = false; mostDetailedLod = 5; @@ -1217,11 +1300,15 @@ bool AdtObject::checkFrustumCulling(ADTObjRenderRes &adtFrustRes, 16, 16); } +// if (atLeastOneIsDrawn) { + if (m_freeStrategy != nullptr) + m_freeStrategy(false, true, m_mapApi->getCurrentSceneTime()); +// } return atLeastOneIsDrawn; } -AdtObject::AdtObject(ApiContainer *api, std::string &adtFileTemplate, std::string mapname, int adt_x, int adt_y, HWdtFile wdtFile) : alphaTextures(), adt_x(adt_x), adt_y(adt_y){ +AdtObject::AdtObject(HApiContainer api, std::string &adtFileTemplate, std::string mapname, int adt_x, int adt_y, HWdtFile wdtFile) : alphaTextures(), adt_x(adt_x), adt_y(adt_y){ m_api = api; tileAabb = std::vector(256); waterTileAabb = std::vector(256); @@ -1247,7 +1334,7 @@ AdtObject::AdtObject(ApiContainer *api, std::string &adtFileTemplate, std::strin } -AdtObject::AdtObject(ApiContainer *api, int adt_x, int adt_y, WdtFile::MapFileDataIDs &fileDataIDs, HWdtFile wdtFile): adt_x(adt_x), adt_y(adt_y) { +AdtObject::AdtObject(HApiContainer api, int adt_x, int adt_y, WdtFile::MapFileDataIDs &fileDataIDs, HWdtFile wdtFile): adt_x(adt_x), adt_y(adt_y) { m_api = api; tileAabb = std::vector(256); waterTileAabb = std::vector(256); @@ -1271,7 +1358,73 @@ AdtObject::AdtObject(ApiContainer *api, int adt_x, int adt_y, WdtFile::MapFileDa lodNormalTexture = m_api->cacheStorage->getTextureCache()->getFileId(fileDataIDs.mapTextureN); } +bool AdtObject::getWaterColorFromDB(mathfu::vec4 cameraPos, mathfu::vec3 &closeRiverColor) { + auto adt_x = worldCoordinateToAdtIndex(cameraPos.y); + auto adt_y = worldCoordinateToAdtIndex(cameraPos.x); + + if (adt_x != getAdtX() || adt_y != getAdtY()) + return false; + + int x_chunk = worldCoordinateToGlobalAdtChunk(cameraPos.y) % 16; + int y_chunk = worldCoordinateToGlobalAdtChunk(cameraPos.x) % 16; + + int i = this->m_adtFile->mcnkMap[x_chunk][y_chunk]; + auto &waterAaBB = waterTileAabb[i]; + + if ( + waterAaBB.min.x > 32*MathHelper::TILESIZE || waterAaBB.max.x < -32*MathHelper::TILESIZE || + waterAaBB.min.y > 32*MathHelper::TILESIZE || waterAaBB.max.x < -32*MathHelper::TILESIZE || + waterAaBB.min.z > 32*MathHelper::TILESIZE || waterAaBB.max.x < -32*MathHelper::TILESIZE + ) return false; + + mathfu::vec3 waterPos = (mathfu::vec3(waterAaBB.max) + mathfu::vec3(waterAaBB.min)) / 2.0f; + std::vector lightResults = {}; + closeRiverColor = {0,0,0}; + this->m_mapApi->getLightResultsFromDB(waterPos, m_api->getConfig(), lightResults, nullptr); + for (auto &_light : lightResults) { + closeRiverColor += mathfu::vec3(_light.closeRiverColor) * _light.blendCoef; + } + closeRiverColor = mathfu::vec3(closeRiverColor[2], closeRiverColor[1], closeRiverColor[0]); + + return true; +} + +CAaBox AdtObject::calcAABB() { + mathfu::vec3 minCoord = mathfu::vec3(20000, 20000, 20000); + mathfu::vec3 maxCoord = mathfu::vec3(-20000, -20000, -20000); + + for (auto &aaBox : tileAabb) { + minCoord = mathfu::vec3( + std::min(minCoord.x, aaBox.min.x), + std::min(minCoord.y, aaBox.min.y), + std::min(minCoord.z, aaBox.min.z) + ); + maxCoord = mathfu::vec3( + std::max(maxCoord.x, aaBox.max.x), + std::max(maxCoord.y, aaBox.max.y), + std::max(maxCoord.z, aaBox.max.z) + ); + } + for (auto &aaBox : waterTileAabb) { + minCoord = mathfu::vec3( + std::min(minCoord.x, aaBox.min.x), + std::min(minCoord.y, aaBox.min.y), + std::min(minCoord.z, aaBox.min.z) + ); + maxCoord = mathfu::vec3( + std::max(maxCoord.x, aaBox.max.x), + std::max(maxCoord.y, aaBox.max.y), + std::max(maxCoord.z, aaBox.max.z) + ); + } + + return CAaBox(mathfu::vec3_packed(minCoord), mathfu::vec3_packed(maxCoord)); +} + int AdtObject::getAreaId(int mcnk_x, int mcnk_y) { + if (!m_loaded) { + return 0; + } auto index = m_adtFile->mcnkMap[mcnk_x][mcnk_y]; if (index > -1) { return m_adtFile->mapTile[index].areaid; diff --git a/wowViewerLib/src/engine/objects/adt/adtObject.h b/wowViewerLib/src/engine/objects/adt/adtObject.h index 4fa7522f2..826de0a2e 100644 --- a/wowViewerLib/src/engine/objects/adt/adtObject.h +++ b/wowViewerLib/src/engine/objects/adt/adtObject.h @@ -21,19 +21,26 @@ class M2Object; #include "../iMapApi.h" #include "../ViewsObjects.h" +typedef std::function FreeStrategy; class AdtObject { public: - AdtObject(ApiContainer *api, std::string &adtFileTemplate, std::string mapname, int adt_x, int adt_y, HWdtFile wdtfile); - AdtObject(ApiContainer *api, int adt_x, int adt_y, WdtFile::MapFileDataIDs &fileDataIDs, HWdtFile wdtfile); - ~AdtObject() = default; + AdtObject(HApiContainer api, std::string &adtFileTemplate, std::string mapname, int adt_x, int adt_y, HWdtFile wdtfile); + AdtObject(HApiContainer api, int adt_x, int adt_y, WdtFile::MapFileDataIDs &fileDataIDs, HWdtFile wdtfile); + ~AdtObject() { +// std::cout << "~AdtObject called" << std::endl; + }; + + void setFreeStrategy(FreeStrategy &freeStrat) { + m_freeStrategy = freeStrat; + m_freeStrategy(false, true, m_mapApi->getCurrentSceneTime()); + } void setMapApi(IMapApi *api) { m_mapApi = api; - m_lastTimeOfUpdateOrRefCheck = m_mapApi->getCurrentSceneTime(); } - void collectMeshes(ADTObjRenderRes &adtRes, std::vector &renderedThisFrame, int renderOrder); + void collectMeshes(ADTObjRenderRes &adtRes, std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder); void collectMeshesLod(std::vector &renderedThisFrame); void update(animTime_t deltaTime); @@ -42,6 +49,12 @@ class AdtObject { int getAreaId(int mcnk_x, int mcnk_y); + int getAdtX() {return adt_x;} + int getAdtY() {return adt_y;} + + CAaBox calcAABB(); + bool getWaterColorFromDB(mathfu::vec4 cameraPos, mathfu::vec3 &closeRiverColor); + bool checkFrustumCulling( ADTObjRenderRes &adtFrustRes, mathfu::vec4 &cameraPos, @@ -61,11 +74,12 @@ class AdtObject { std::vector> &m2ObjectsCandidates, std::vector> &wmoCandidates, int x, int y, int x_len, int y_len); - animTime_t getLastTimeOfUpdate() { - return m_lastTimeOfUpdateOrRefCheck; + FreeStrategy &getFreeStrategy() { + return m_freeStrategy; } private: - animTime_t m_lastTimeOfUpdateOrRefCheck = 0; + FreeStrategy m_freeStrategy = [](bool doCheck, bool doUpdate, animTime_t currentTime) -> bool {return false;}; + animTime_t m_lastTimeOfUpdate = 0; animTime_t m_lastDeltaTime = 0; @@ -79,7 +93,7 @@ class AdtObject { void createMeshes(); void loadAlphaTextures(); - ApiContainer *m_api; + HApiContainer m_api; IMapApi *m_mapApi; HWdtFile m_wdtFile= nullptr; @@ -119,7 +133,10 @@ class AdtObject { HBlpTexture lodDiffuseTexture = nullptr; HBlpTexture lodNormalTexture = nullptr; - std::vector adtMeshes = {}; + + std::array adtMeshes = {}; + //16x16, then layer + std::array, 16*16> waterMeshes = {}; std::vector adtLodMeshes; std::vector tileAabb; @@ -133,10 +150,6 @@ class AdtObject { std::string m_adtFileTemplate; - HGVertexBuffer waterVBO; - HGIndexBuffer waterIBO; - HGVertexBufferBindings vertexWaterBufferBindings; - HGMesh waterMesh = nullptr; struct lodLevels { std::vector> m2Objects; @@ -162,6 +175,8 @@ class AdtObject { void loadM2s(); void loadWmos(); void loadWater(); + HGMesh createWaterMeshFromInstance(int x_chunk, int y_chunk, SMLiquidInstance &liquidInstance, mathfu::vec3 liquidBasePos); + bool checkNonLodChunkCulling(ADTObjRenderRes &adtFrustRes, mathfu::vec4 &cameraPos, std::vector &frustumPlanes, std::vector &frustumPoints, std::vector &hullLines, int x, diff --git a/wowViewerLib/src/engine/objects/iMapApi.h b/wowViewerLib/src/engine/objects/iMapApi.h index 7ce8798aa..de8d26022 100644 --- a/wowViewerLib/src/engine/objects/iMapApi.h +++ b/wowViewerLib/src/engine/objects/iMapApi.h @@ -13,6 +13,8 @@ struct StateForConditions { int currentAreaId = 0; int currentParentAreaId = 0; std::vector currentSkyboxIds = {}; + std::vector currentLightParams = {}; + std::vector currentZoneLights = {}; }; class IMapApi { @@ -23,6 +25,8 @@ class IMapApi { virtual std::shared_ptr getWmoObject(int fileDataId, SMMapObjDef &mapObjDef) = 0; virtual std::shared_ptr getWmoObject(std::string fileName, SMMapObjDefObj1 &mapObjDef) = 0; virtual std::shared_ptr getWmoObject(int fileDataId, SMMapObjDefObj1 &mapObjDef) = 0; + virtual void getLightResultsFromDB(mathfu::vec3 &cameraVec3, const Config *config, std::vector &lightResults,StateForConditions *stateForConditions) = 0; + virtual animTime_t getCurrentSceneTime() = 0; }; diff --git a/wowViewerLib/src/engine/objects/iScene.cpp b/wowViewerLib/src/engine/objects/iScene.cpp new file mode 100644 index 000000000..419882c8d --- /dev/null +++ b/wowViewerLib/src/engine/objects/iScene.cpp @@ -0,0 +1,6 @@ +#include "../DrawStage.h" +#include "iScene.h" + +IScene::~IScene() { +// std::cout << "~IScene called" << std::endl; +} \ No newline at end of file diff --git a/wowViewerLib/src/engine/objects/iScene.h b/wowViewerLib/src/engine/objects/iScene.h index 6eab62b99..c0d7dd24e 100644 --- a/wowViewerLib/src/engine/objects/iScene.h +++ b/wowViewerLib/src/engine/objects/iScene.h @@ -10,26 +10,35 @@ #include "../DrawStage.h" #include "wowFrameData.h" #include "../SceneScenario.h" +#include "../../exporters/IExporter.h" class IScene { public: - virtual ~IScene() = default; + virtual ~IScene() = 0; virtual void setReplaceTextureArray(std::vector &replaceTextureArray) = 0; + virtual void setMeshIdArray(std::vector &meshIds) = 0; virtual void setReplaceParticleColors(std::array, 3> &particleColorReplacement) {}; virtual void resetReplaceParticleColor() {}; virtual void setAnimationId(int animationId) = 0; + virtual void setMeshIds(std::vector &meshIds) = 0; virtual void resetAnimation() = 0; virtual void produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, std::vector &additionalChunks) = 0; + virtual void produceUpdateStage(HUpdateStage updateStage) = 0; virtual void checkCulling(HCullStage cullStage) = 0; virtual void doPostLoad(HCullStage cullStage) = 0; - virtual void update(HUpdateStage updateStage) = 0; - virtual void updateBuffers(HCullStage cullStage) = 0; + virtual void updateBuffers(HUpdateStage updateStage) = 0; virtual int getCameraNum() = 0; virtual std::shared_ptr createCamera(int cameraNum) = 0; + + virtual void exportScene(IExporter * exporter) {}; + + virtual void setAdtBoundingBoxHolder(HADTBoundingBoxHolder bbHolder) {}; + virtual void setMandatoryADTs(std::vector> mandatoryADTs) {}; + virtual void getAdtAreaId(const mathfu::vec4 &cameraPos, int &areaId, int &parentAreaId) {}; }; #endif //WEBWOWVIEWERCPP_IINNERSCENEAPI_H diff --git a/wowViewerLib/src/engine/objects/iWmoApi.h b/wowViewerLib/src/engine/objects/iWmoApi.h index 383029dd5..644603681 100644 --- a/wowViewerLib/src/engine/objects/iWmoApi.h +++ b/wowViewerLib/src/engine/objects/iWmoApi.h @@ -21,6 +21,7 @@ class IWmoApi { public: virtual std::shared_ptr getDoodad(int index) = 0; virtual SMOHeader *getWmoHeader() = 0; + virtual mathfu::vec3 getAmbientColor() = 0; virtual PointerChecker &getMaterials() = 0; virtual bool isLoaded() = 0; virtual std::function getAttenFunction() = 0; diff --git a/wowViewerLib/src/engine/objects/m2/m2Helpers/CBoneMasterData.cpp b/wowViewerLib/src/engine/objects/m2/m2Helpers/CBoneMasterData.cpp new file mode 100644 index 000000000..79d808912 --- /dev/null +++ b/wowViewerLib/src/engine/objects/m2/m2Helpers/CBoneMasterData.cpp @@ -0,0 +1,46 @@ +// +// Created by Deamon on 9/16/2020. +// + +#include "CBoneMasterData.h" +#include "../../../geometry/m2Geom.h" + +CBoneMasterData::CBoneMasterData(HM2Geom m2Geom, HSkelGeom skelGeom, HSkelGeom parentSkelGeom) { + m_m2Geom = m2Geom; + m_skelGeom = skelGeom; + m_parentSkelGeom = parentSkelGeom; + + if (skelGeom == nullptr) { + skelData.m_m2CompBones = &m_m2Geom->m_m2Data->bones; + skelData.m_key_bone_lookup = &m_m2Geom->m_m2Data->key_bone_lookup; + + skelData.m_globalSequences = &m_m2Geom->m_m2Data->global_loops; + skelData.m_sequences = &m_m2Geom->m_m2Data->sequences; + skelData.m_sequence_lookups = &m_m2Geom->m_m2Data->sequence_lookups; + + skelData.m_attachments = &m_m2Geom->m_m2Data->attachments; + skelData.m_attachment_lookup_table = &m_m2Geom->m_m2Data->attachment_lookup_table; + } else { + skelData.m_m2CompBones = &skelGeom->m_skb1->bones; + skelData.m_key_bone_lookup = &skelGeom->m_skb1->key_bone_lookup; + + skelData.m_globalSequences = &skelGeom->m_sks1->global_loops; + skelData.m_sequences = &skelGeom->m_sks1->sequences; + skelData.m_sequence_lookups = &skelGeom->m_sks1->sequence_lookups; + + skelData.m_attachments = &skelGeom->m_ska1->attachments; + skelData.m_attachment_lookup_table = &skelGeom->m_ska1->attachment_lookup_table; + + if (parentSkelGeom != nullptr) { + parentSkelData.m_m2CompBones = &parentSkelGeom->m_skb1->bones; + parentSkelData.m_key_bone_lookup = &parentSkelGeom->m_skb1->key_bone_lookup; + + parentSkelData.m_globalSequences = &parentSkelGeom->m_sks1->global_loops; + parentSkelData.m_sequences = &parentSkelGeom->m_sks1->sequences; + parentSkelData.m_sequence_lookups = &parentSkelGeom->m_sks1->sequence_lookups; + + parentSkelData.m_attachments = &parentSkelGeom->m_ska1->attachments; + parentSkelData.m_attachment_lookup_table = &parentSkelGeom->m_ska1->attachment_lookup_table; + } + } +} diff --git a/wowViewerLib/src/engine/objects/m2/m2Helpers/CBoneMasterData.h b/wowViewerLib/src/engine/objects/m2/m2Helpers/CBoneMasterData.h new file mode 100644 index 000000000..91fed577c --- /dev/null +++ b/wowViewerLib/src/engine/objects/m2/m2Helpers/CBoneMasterData.h @@ -0,0 +1,64 @@ +// +// Created by Deamon on 9/16/2020. +// + +#ifndef AWEBWOWVIEWERCPP_CBONEMASTERDATA_H +#define AWEBWOWVIEWERCPP_CBONEMASTERDATA_H + + +#include "../../../persistance/header/M2FileHeader.h" +#include "../../../wowCommonClasses.h" +#include "../../../ApiContainer.h" + +struct SkelData { + M2Array *m_m2CompBones = nullptr; + M2Array *m_key_bone_lookup = nullptr; + + M2Array *m_globalSequences = nullptr; + M2Array *m_sequences = nullptr; + M2Array *m_sequence_lookups = nullptr; + + M2Array *m_attachments = nullptr; + M2Array *m_attachment_lookup_table = nullptr; +}; + +class CBoneMasterData { +private: + HM2Geom m_m2Geom = nullptr; + HSkelGeom m_skelGeom = nullptr; + HSkelGeom m_parentSkelGeom = nullptr; + + SkelData skelData; + SkelData parentSkelData; +public: + CBoneMasterData(HM2Geom m2Geom, HSkelGeom skelGeom, HSkelGeom parentSkelGeom); + + SkelData *getSkelData() { + return &skelData; + } + SkelData *getParentSkelData() { + if (m_parentSkelGeom != nullptr) { + return &parentSkelData; + } else { + return nullptr; + } + } + + HM2Geom getM2Geom() { + return m_m2Geom; + } + + void loadLowPriority(HApiContainer m_api, uint32_t animationId, uint32_t variationId, bool foundInParentAnim) { + if (foundInParentAnim && m_parentSkelGeom != nullptr) { + m_parentSkelGeom->loadLowPriority(m_api, animationId, variationId); + } else if (m_skelGeom != nullptr) { + m_skelGeom->loadLowPriority(m_api, animationId, variationId); + } else { + m_m2Geom->loadLowPriority(m_api, animationId, variationId); + } + } +}; + + + +#endif //AWEBWOWVIEWERCPP_CBONEMASTERDATA_H diff --git a/wowViewerLib/src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.cpp b/wowViewerLib/src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.cpp index 2964020f6..ee9319bb4 100644 --- a/wowViewerLib/src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.cpp +++ b/wowViewerLib/src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.cpp @@ -11,12 +11,14 @@ float M2MeshBufferUpdater::calcFinalTransparency(const M2Object &m2Object, int b int renderFlagIndex = textMaterial->materialIndex; mathfu::vec4 meshColor = M2Object::getCombinedColor(m2SkinProfile, batchIndex, m2Object.subMeshColors); - float transparency = M2Object::getTransparency(m2SkinProfile, batchIndex, m2Object.transparencies); + float transparency = M2Object::getTextureWeight(m2SkinProfile, m2Object.m_m2Geom->getM2Data(), batchIndex, 0, m2Object.transparencies); float finalTransparency = meshColor.w; if ( textMaterial->textureCount && !(textMaterial->flags & 0x40)) { finalTransparency *= transparency; } + finalTransparency *= m2Object.m_alpha; + return finalTransparency; } @@ -25,19 +27,16 @@ void M2MeshBufferUpdater::assignUpdateEvents(HGM2Mesh &hmesh, M2Object *m2Object int batchIndex = materialData.batchIndex; auto vertexShader = materialData.vertexShader; - hmesh->getUniformBuffer(2)->setUpdateHandler([m2Object, m2SkinProfile, blendMode, batchIndex, vertexShader](IUniformBufferChunk *self){ + hmesh->getUniformBuffer(2)->setUpdateHandler([m2Object, m2SkinProfile, blendMode, batchIndex, vertexShader](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ auto m2Data = m2Object->m_m2Geom->getM2Data(); - auto textMaterial = m2SkinProfile->batches[batchIndex]; - int renderFlagIndex = textMaterial->materialIndex; + auto batch = m2SkinProfile->batches[batchIndex]; + int renderFlagIndex = batch->materialIndex; auto renderFlag = m2Data->materials[renderFlagIndex]; mathfu::vec4 meshColor = M2Object::getCombinedColor(m2SkinProfile, batchIndex, m2Object->subMeshColors); - float transparency = M2Object::getTransparency(m2SkinProfile, batchIndex, m2Object->transparencies); - float finalTransparency = meshColor.w; - if ( textMaterial->textureCount && !(textMaterial->flags & 0x40)) { - finalTransparency *= transparency; - } + + float finalTransparency = M2MeshBufferUpdater::calcFinalTransparency(*m2Object, batchIndex, m2SkinProfile); auto &meshblockVS = self->getObject(); meshblockVS.Color_Transparency = mathfu::vec4_packed(mathfu::vec4(meshColor.x, meshColor.y, meshColor.z, finalTransparency)); @@ -51,15 +50,22 @@ void M2MeshBufferUpdater::assignUpdateEvents(HGM2Mesh &hmesh, M2Object *m2Object //3. Update individual PS buffer auto pixelShader = materialData.pixelShader; - hmesh->getUniformBuffer(4)->setUpdateHandler([m2Object, m2SkinProfile, blendMode, batchIndex, pixelShader](IUniformBufferChunk *self) { + hmesh->getUniformBuffer(4)->setUpdateHandler([m2Object, m2SkinProfile, blendMode, batchIndex, pixelShader](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) { auto m2Data = m2Object->m_m2Geom->getM2Data(); - auto textMaterial = m2SkinProfile->batches[batchIndex]; - int renderFlagIndex = textMaterial->materialIndex; + auto batch = m2SkinProfile->batches[batchIndex]; + int renderFlagIndex = batch->materialIndex; auto renderFlag = m2Data->materials[renderFlagIndex]; float finalTransparency = M2MeshBufferUpdater::calcFinalTransparency(*m2Object, batchIndex, m2SkinProfile); + mathfu::vec4 uTexSampleAlpha = mathfu::vec4(1.0, 1.0, 1.0, 1.0); + for (int i = 0; i < std::max(batch->textureCount, 4); i++) { + uTexSampleAlpha[i] = M2Object::getTextureWeight(m2SkinProfile, m2Data, batchIndex, i, m2Object->transparencies); + } + + + float uAlphaTest; if (blendMode == EGxBlendEnum::GxBlend_AlphaKey) { uAlphaTest = 128.0f/255.0f * finalTransparency; //Maybe move this to shader logic? @@ -74,7 +80,10 @@ void M2MeshBufferUpdater::assignUpdateEvents(HGM2Mesh &hmesh, M2Object *m2Object meshblockPS.PixelShader = pixelShader; meshblockPS.IsAffectedByLight = ((renderFlag->flags & 0x1) > 0) ? 0 : 1; meshblockPS.UnFogged = ((renderFlag->flags & 0x2) > 0) ? 1 : 0; + meshblockPS.BlendMode = static_cast(blendMode); meshblockPS.uFogColorAndAlphaTest = mathfu::vec4(mathfu::vec3(0,0,0), uAlphaTest); + + meshblockPS.uTexSampleAlpha = uTexSampleAlpha; }); } @@ -84,32 +93,32 @@ void M2MeshBufferUpdater::updateSortData(HGM2Mesh &hmesh, const M2Object &m2Obje M2Batch *textMaterial = m2SkinProfile->batches.getElement(materialData.batchIndex); M2SkinSection *submesh = m2SkinProfile->skinSections.getElement(textMaterial->skinSectionIndex); - mathfu::vec4 centerBB = mathfu::vec4(mathfu::vec3(submesh->sortCenterPosition), 1.0); + mathfu::vec4 sortCenterPosition = mathfu::vec4(mathfu::vec3(submesh->sortCenterPosition), 1.0); const mathfu::mat4 &boneMat = m2Object.bonesMatrices[submesh->centerBoneIndex]; - centerBB = modelViewMat * (boneMat * centerBB); + sortCenterPosition = modelViewMat * (boneMat * sortCenterPosition); - float value = centerBB.xyz().Length(); + float value = sortCenterPosition.xyz().Length(); if (textMaterial->flags & 3) { mathfu::vec4 resultPoint; if ( value > 0.00000023841858 ) { - resultPoint = centerBB * (1.0f / value); + resultPoint = sortCenterPosition * (1.0f / value); } else { - resultPoint = centerBB; + resultPoint = sortCenterPosition; } mathfu::mat4 mat4 = modelViewMat * boneMat; - float dist = mat4.GetColumn(3).xyz().Length(); - float sortDist = dist * submesh->sortRadius; + float scale = mat4.GetColumn(0).xyz().Length(); + float sortDist = scale * submesh->sortRadius; resultPoint *= sortDist; if (textMaterial->flags & 1) { - value = (centerBB - resultPoint).xyz().Length(); + value = (sortCenterPosition - resultPoint).xyz().Length(); } else { - value = (centerBB + resultPoint).xyz().Length(); + value = (sortCenterPosition + resultPoint).xyz().Length(); } } @@ -117,7 +126,7 @@ void M2MeshBufferUpdater::updateSortData(HGM2Mesh &hmesh, const M2Object &m2Obje } void M2MeshBufferUpdater::fillLights(const M2Object &m2Object, M2::modelWideBlockPS &modelBlockPS) { - bool BCLoginScreenHack = m2Object.m_api->getConfig()->getBCLightHack(); + bool BCLoginScreenHack = m2Object.m_api->getConfig()->BCLightHack; int lightCount = (int) std::min(m2Object.lights.size(), (size_t) 4); for (int j = 0; j < lightCount; j++) { std::string uniformName; @@ -141,27 +150,37 @@ void M2MeshBufferUpdater::fillLights(const M2Object &m2Object, M2::modelWideBloc modelBlockPS.bcHack = BCLoginScreenHack ? 1 : 0; } +mathfu::mat4 M2MeshBufferUpdater::getTextureMatrix(const M2Object &m2Object, int textureMatIndex, M2Data *m2Data) { + if (textureMatIndex < 0) + return mathfu::mat4::Identity(); + + if (textureMatIndex >= m2Object.textAnimMatrices.size()) + return mathfu::mat4::Identity(); + + return m2Object.textAnimMatrices[textureMatIndex]; +} + void M2MeshBufferUpdater::fillTextureMatrices(const M2Object &m2Object, int batchIndex, M2Data *m2Data, M2SkinProfile *m2SkinProfile, mathfu::mat4 *uTextMat) { - const auto textureAnim = m2SkinProfile->batches[batchIndex]->textureTransformComboIndex; - int16_t textureMatIndex = *m2Data->texture_transforms_lookup_table[textureAnim]; - if (textureMatIndex >= 0 && textureMatIndex < m2Object.textAnimMatrices.size()) { - uTextMat[0] = m2Object.textAnimMatrices[textureMatIndex]; - } else { - uTextMat[0] = mathfu::mat4::Identity(); - } - if (textureAnim+1 < m2Data->texture_transforms_lookup_table.size) { - int textureMatIndex = *m2Data->texture_transforms_lookup_table[textureAnim+1]; - if (textureMatIndex >= 0 && textureMatIndex < m2Object.textAnimMatrices.size()) { - uTextMat[1] =m2Object.textAnimMatrices[textureMatIndex]; - } else { - uTextMat[1] = mathfu::mat4::Identity(); - } - } else { - uTextMat[1] = mathfu::mat4::Identity(); + auto textureAnim = m2SkinProfile->batches[batchIndex]->textureTransformComboIndex; + + if (m2Object.m_m2Geom->m_wfv1 != nullptr) { + textureAnim = 1; // hack for fdid 2445860 } -}; + + int16_t textureMatIndex = -1; + if (textureAnim < m2Data->texture_transforms_lookup_table.size) + textureMatIndex = *m2Data->texture_transforms_lookup_table[textureAnim]; + + uTextMat[0] = M2MeshBufferUpdater::getTextureMatrix(m2Object, textureMatIndex, m2Data); + + textureMatIndex = -1; + if (textureAnim+1 < m2Data->texture_transforms_lookup_table.size) + textureMatIndex = *m2Data->texture_transforms_lookup_table[textureAnim+1]; + + uTextMat[1] = M2MeshBufferUpdater::getTextureMatrix(m2Object, textureMatIndex, m2Data); +} mathfu::vec3 &M2MeshBufferUpdater::getFogColor(EGxBlendEnum blendMode, mathfu::vec3 &originalFogColor) { diff --git a/wowViewerLib/src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.h b/wowViewerLib/src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.h index 456931988..2385ffcba 100644 --- a/wowViewerLib/src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.h +++ b/wowViewerLib/src/engine/objects/m2/m2Helpers/M2MeshBufferUpdater.h @@ -4,7 +4,7 @@ #ifndef AWEBWOWVIEWERCPP_M2MESHBUFFERUPDATER_H #define AWEBWOWVIEWERCPP_M2MESHBUFFERUPDATER_H - +#define _USE_MATH_DEFINES #include #include "./../../../../gapi/UniformBufferStructures.h" @@ -20,6 +20,7 @@ class M2MeshBufferUpdater { static void fillLights(const M2Object &m2Object, M2::modelWideBlockPS &modelBlockPS); + static mathfu::mat4 getTextureMatrix(const M2Object &m2Object, int textureMatIndex, M2Data *m2Data); static void fillTextureMatrices(const M2Object &m2Object, int batchIndex, M2Data *m2Data, M2SkinProfile *m2SkinProfile, mathfu::mat4 *uTextMat); diff --git a/wowViewerLib/src/engine/objects/m2/m2Object.cpp b/wowViewerLib/src/engine/objects/m2/m2Object.cpp index 20ef429c3..f6ac9a93c 100644 --- a/wowViewerLib/src/engine/objects/m2/m2Object.cpp +++ b/wowViewerLib/src/engine/objects/m2/m2Object.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "m2Object.h" #include "../../algorithms/mathHelper.h" #include "../../managers/animationManager.h" @@ -16,8 +17,7 @@ #include "../../../gapi/interface/IDevice.h" #include "../../../gapi/interface/meshes/IM2Mesh.h" -//Legion shader stuff - +//Shader stuff enum class M2PixelShader : int { //Wotlk deprecated shaders Combiners_Decal = -1, @@ -187,7 +187,7 @@ int getVertexShaderId(int textureCount, int16_t shaderId) { return result; } -int getPixelShaderId(int textureCount, int16_t shaderId) { +int getPixelShaderId(int textureCount, uint16_t shaderId) { static const std::array array1 = { +M2PixelShader::Combiners_Mod_Mod2x, +M2PixelShader::Combiners_Mod_Mod, @@ -210,7 +210,7 @@ int getPixelShaderId(int textureCount, int16_t shaderId) { }; int result; - if ( shaderId < 0 ) + if ( (shaderId & 0x8000) > 0 ) { int pixelShaderId = shaderId & 0x7FFF; if ( (unsigned int)pixelShaderId >= M2ShaderTable.size()) { @@ -470,6 +470,29 @@ M2Object::~M2Object() { void M2Object::createAABB() { M2Data *m2Data = m_m2Geom->getM2Data(); + //Debug: calc bounding box from verticies + { + auto min = mathfu::vec3(9999, 9999, 9999); + auto max = mathfu::vec3(-9999, -9999, -9999); + + for (int i = 0; i < m2Data->vertices.size; i++) { + auto *vertex = m2Data->vertices.getElement(i); + + min = mathfu::vec3(mathfu::vec3(std::min(min.x, vertex->pos.x), + std::min(min.y, vertex->pos.y), + std::min(min.z, vertex->pos.z))); + + max = mathfu::vec3(mathfu::vec3(std::max(max.x, vertex->pos.x), + std::max(max.y, vertex->pos.y), + std::max(max.z, vertex->pos.z))); + } + +// std::cout << "calculated min = (" << min.x << ", " << min.y << ", " << min.z << ")" << std::endl; +// std::cout << "calculated max = (" << max.x << ", " << max.y << ", " << max.z << ")" << std::endl; + } + + + { C3Vector min = m2Data->bounding_box.min; C3Vector max = m2Data->bounding_box.max; @@ -543,8 +566,6 @@ void M2Object:: createPlacementMatrix(SMODoodadDef &def, mathfu::mat4 &wmoPlacem } void M2Object::createPlacementMatrix(SMDoodadDef &def) { - const float TILESIZE = 533.333333333; - float posx = def.position.x; float posy = def.position.y; float posz = def.position.z; @@ -649,127 +670,27 @@ mathfu::vec4 M2Object::getCombinedColor( return submeshColor; } -float M2Object::getTransparency( +float M2Object::getTextureWeight( M2SkinProfile *skinData, + M2Data * m2Data, int batchIndex, + int textureIndex, const std::vector &transparencies) { float transparency = 1.0; - int transpIndex = skinData->batches[batchIndex]->textureWeightComboIndex; + int transpLookupIndex = skinData->batches[batchIndex]->textureWeightComboIndex + textureIndex; + int transpIndex = -1; + if ((transpLookupIndex >= 0) && (transpLookupIndex < m2Data->transparency_lookup_table.size)) { + transpIndex = *m2Data->transparency_lookup_table[transpLookupIndex]; + } + if ((transpIndex >= 0) && (transparencies.size() > transpIndex)) { transparency = transparencies[transpIndex]; } return transparency; } -typedef struct { - double h; // angle in degrees - double s; // a fraction between 0 and 1 - double v; // a fraction between 0 and 1 -} hsv; - -hsv rgb2hsv(mathfu::vec3 in) -{ - hsv out; - double min, max, delta; - min = in.x < in.y ? in.x : in.y; - min = min < in.z ? min : in.z; - - max = in.x > in.y ? in.x : in.y; - max = max > in.z ? max : in.z; - - out.v = max; // v - delta = max - min; - if (delta < 0.00001) - { - out.s = 0; - out.h = 0; // undefined, maybe nan? - return out; - } - if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash - out.s = (delta / max); // s - } else { - // if max is 0, then r = g = b = 0 - // s = 0, h is undefined - out.s = 0.0; - out.h = NAN; // its now undefined - return out; - } - if( in.x >= max ) // > is bogus, just keeps compilor happy - out.h = ( in.y - in.z ) / delta; // between yellow & magenta - else - if( in.y >= max ) - out.h = 2.0 + ( in.z - in.x ) / delta; // between cyan & yellow - else - out.h = 4.0 + ( in.x - in.y ) / delta; // between magenta & cyan - - out.h *= 60.0; // degrees - - if( out.h < 0.0 ) - out.h += 360.0; - - return out; -} - - -mathfu::vec3 hsv2rgb(hsv in) -{ - double hh, p, q, t, ff; - long i; - mathfu::vec3 out; - - if(in.s <= 0.0) { // < is bogus, just shuts up warnings - out.x = in.v; - out.y = in.v; - out.z = in.v; - return out; - } - hh = in.h; - if(hh >= 360.0) hh = 0.0; - hh /= 60.0; - i = (long)hh; - ff = hh - i; - p = in.v * (1.0 - in.s); - q = in.v * (1.0 - (in.s * ff)); - t = in.v * (1.0 - (in.s * (1.0 - ff))); - - switch(i) { - case 0: - out.x = in.v; - out.y = t; - out.z = p; - break; - case 1: - out.x = q; - out.y = in.v; - out.z = p; - break; - case 2: - out.x = p; - out.y = in.v; - out.z = t; - break; - - case 3: - out.x = p; - out.y = q; - out.z = in.v; - break; - case 4: - out.x = t; - out.y = p; - out.z = in.v; - break; - case 5: - default: - out.x = in.v; - out.y = p; - out.z = q; - break; - } - return out; -} uint8_t miniLogic(const CImVector *a2) { @@ -801,6 +722,10 @@ void M2Object::setDiffuseColor(CImVector& value) { value.b / 255.0f, value.a / 255.0f); + if (value.a != 255) { +// std::cout << "Found index into MOLT = " << (int)value.a << std::endl; + } + /* uint8_t result = miniLogic(&value); if (result < 0xA8) { @@ -851,9 +776,19 @@ void M2Object::sortMaterials(mathfu::mat4 &modelViewMat) { M2Data * m2File = this->m_m2Geom->getM2Data(); M2SkinProfile * skinData = this->m_skinGeom->getSkinData(); - for (int i = 0; i < this->m_meshArray.size(); i++) { - //Update info for sorting - M2MeshBufferUpdater::updateSortData(this->m_meshArray[i], *this, m_materialArray[i], m2File, skinData, modelViewMat); + if (m_m2Geom->m_wfv3 == nullptr && m_m2Geom->m_wfv1 == nullptr) { + for (int i = 0; i < this->m_meshNaturalArray.size(); i++) { + //Update info for sorting + M2MeshBufferUpdater::updateSortData(this->m_meshNaturalArray[i], *this, m_materialArray[i], m2File, + skinData, modelViewMat); + } + for (int i = 0; i < this->m_meshForcedTranspArray.size(); i++) { + //Update info for sorting + if (this->m_meshForcedTranspArray[i] != nullptr) { + M2MeshBufferUpdater::updateSortData(this->m_meshForcedTranspArray[i], *this, m_materialArray[i], m2File, + skinData, modelViewMat); + } + } } } @@ -909,7 +844,13 @@ bool M2Object::doPostLoad(){ this->createAABB(); if (m_skinGeom == nullptr || m_skinGeom->getStatus() != FileStatus::FSLoaded) return false; - if (m_m2Geom->m_skid > 0 && m_skelGeom->getStatus() != FileStatus::FSLoaded) { + if (m_m2Geom->m_skid > 0 && (m_skelGeom == nullptr || m_skelGeom->getStatus() != FileStatus::FSLoaded)) { + return false; + } + if (m_m2Geom->m_skid > 0 && m_skelGeom->getStatus() == FileStatus::FSLoaded && + m_skelGeom->m_skpd!= nullptr && m_skelGeom->m_skpd->parent_skel_file_id != 0 && ( + m_parentSkelGeom == nullptr || m_parentSkelGeom->getStatus() != FileStatus::FSLoaded + )) { return false; } @@ -917,9 +858,10 @@ bool M2Object::doPostLoad(){ m_skinGeom->fixData(m_m2Geom->getM2Data()); this->createVertexBindings(); + this->createMeshes(); + m_boneMasterData = std::make_shared(m_m2Geom, m_skelGeom, m_parentSkelGeom); - this->createMeshes(); this->initAnimationManager(); this->initBoneAnimMatrices(); this->initTextAnimMatrices(); @@ -928,7 +870,6 @@ bool M2Object::doPostLoad(){ this->initLights(); this->initParticleEmitters(); this->initRibbonEmitters(); - m_hasBillboards = checkIfHasBillboarded(); this->m_loaded = true; @@ -986,10 +927,8 @@ void M2Object::update(double deltaTime, mathfu::vec3 &cameraPos, mathfu::mat4 &v this->ribbonEmitters ); - - - int minParticle = m_api->getConfig()->getMinParticle(); - int maxParticle = std::min(m_api->getConfig()->getMaxParticle(), (const int &) particleEmitters.size()); + int minParticle = m_api->getConfig()->minParticle; + int maxParticle = std::min(m_api->getConfig()->maxParticle, (const int &) particleEmitters.size()); mathfu::mat4 viewMatInv = viewMat.Inverse(); @@ -1040,8 +979,8 @@ void M2Object::uploadGeneratorBuffers(mathfu::mat4 &viewMat) { //Manually update vertices for dynamics updateDynamicMeshes(); - int minParticle = m_api->getConfig()->getMinParticle(); - int maxParticle = std::min(m_api->getConfig()->getMaxParticle(), (const int &) particleEmitters.size()); + int minParticle = m_api->getConfig()->minParticle; + int maxParticle = std::min(m_api->getConfig()->maxParticle, (const int &) particleEmitters.size()); for (int i = minParticle; i < maxParticle; i++) { particleEmitters[i]->updateBuffers(); @@ -1052,23 +991,64 @@ void M2Object::uploadGeneratorBuffers(mathfu::mat4 &viewMat) { } } -bool M2Object::getIsInstancable() { - if (!m_loaded || this->m_animationManager == nullptr) return false; +bool M2Object::isGeomReqFilesLoaded() { + if (!this->isMainDataLoaded()) return false; - return !(this->m_animationManager->getIsFirstCalc()|| this->m_animationManager->getIsAnimated()); + if (m_skinGeom == nullptr) { + Cache *skinGeomCache = m_api->cacheStorage->getSkinGeomCache(); + if (m_m2Geom->skinFileDataIDs.size() > 0) { + assert(m_m2Geom->skinFileDataIDs.size() > 0); + m_skinGeom = skinGeomCache->getFileId(m_m2Geom->skinFileDataIDs[0]); + } else if (!useFileId){ + assert(m_nameTemplate.size() > 0); + std::string skinFileName = m_nameTemplate + "00.skin"; + m_skinGeom = skinGeomCache->get(skinFileName); + } + return false; + } + if (m_skinGeom->getStatus() != FileStatus::FSLoaded) return false; + + if (m_m2Geom->m_skid > 0) { + auto skelCache = m_api->cacheStorage->getSkelCache(); + if (m_skelGeom == nullptr) { + auto skelCache = m_api->cacheStorage->getSkelCache(); + m_skelGeom = skelCache->getFileId(m_m2Geom->m_skid); + return false; + } + if (m_skelGeom->getStatus() == FileStatus::FSLoaded && m_parentSkelGeom == nullptr) { + if (m_skelGeom->m_skpd != nullptr && m_skelGeom->m_skpd->parent_skel_file_id != 0) { + m_parentSkelGeom = skelCache->getFileId(m_skelGeom->m_skpd->parent_skel_file_id); + return false; + } + } + if (m_parentSkelGeom != nullptr && m_parentSkelGeom->getStatus() != FileStatus::FSLoaded) + return false; + } + + return true; +} + +bool M2Object::isMainDataLoaded() { + if (!this->m_loaded && !this->m_loading) { + this->startLoading(); + } + if (m_m2Geom == nullptr) return false; + if (m_m2Geom->getStatus() != FileStatus::FSLoaded) return false; + + return true; } + const bool M2Object::checkFrustumCulling (const mathfu::vec4 &cameraPos, const std::vector &frustumPlanes, const std::vector &frustumPoints) { m_cullResult = false; if (!this->m_hasAABB) { - if (!this->m_loaded && !this->m_loading) { - this->startLoading(); - } - if (m_m2Geom->getStatus() != FileStatus::FSLoaded) return false; + if (!this->isMainDataLoaded()) return false; + if (m_m2Geom != nullptr) { this->createAABB(); + } else { + return false; } - return false; } if (m_alwaysDraw) { @@ -1179,7 +1159,7 @@ void M2Object::drawBB(mathfu::vec3 &color) { } -bool M2Object::prepearMatrial(M2MaterialInst &materialData, int batchIndex) { +bool M2Object::prepearMaterial(M2MaterialInst &materialData, int batchIndex) { auto &skinSections = m_skinGeom->getSkinData()->skinSections; M2Array* batches = &m_skinGeom->getSkinData()->batches; @@ -1188,9 +1168,13 @@ bool M2Object::prepearMatrial(M2MaterialInst &materialData, int batchIndex) { M2Batch* m2Batch = batches->getElement(batchIndex); auto skinSection = skinSections[m2Batch->skinSectionIndex]; - if ((this->m_meshIds.size() > 0) && (skinSection->skinSectionId > 0) && - (m_meshIds[(skinSection->skinSectionId / 100)] != (skinSection->skinSectionId % 100))) { - return false; + + { + auto meshGroup = (skinSection->skinSectionId / 100); + if ((meshGroup < this->m_meshIds.size()) && (skinSection->skinSectionId > 0) && + (m_meshIds[meshGroup] != (skinSection->skinSectionId % 100))) { + return false; + } } // materialArray.push(materialData); @@ -1208,7 +1192,7 @@ bool M2Object::prepearMatrial(M2MaterialInst &materialData, int batchIndex) { materialData.flags = m2Batch->flags; materialData.priorityPlane = m2Batch->priorityPlane; - if (m_api->getConfig()->getUseWotlkLogic()) { + if (m_api->getConfig()->useWotlkLogic) { std::string vertexShader; std::string pixelShader; getShaderNames(m2Batch, vertexShader, pixelShader); @@ -1234,7 +1218,7 @@ bool M2Object::prepearMatrial(M2MaterialInst &materialData, int batchIndex) { } void M2Object::createBoundingBoxMesh() { - return; + //Create bounding box mesh HGShaderPermutation boundingBoxshaderPermutation = m_api->hDevice->getShader("drawBBShader", nullptr); @@ -1243,11 +1227,11 @@ void M2Object::createBoundingBoxMesh() { meshTemplate.depthWrite = false; meshTemplate.depthCulling = true; meshTemplate.backFaceCulling = false; - meshTemplate.colorMask = 0; +// meshTemplate.colorMask = 0; meshTemplate.start = 0; meshTemplate.end = 36; - meshTemplate.blendMode = EGxBlendEnum ::GxBlend_Opaque; + meshTemplate.blendMode = EGxBlendEnum ::GxBlend_Alpha; meshTemplate.element = DrawElementMode::TRIANGLES; meshTemplate.textureCount = 0; @@ -1261,32 +1245,32 @@ void M2Object::createBoundingBoxMesh() { meshTemplate.ubo[3] = nullptr; meshTemplate.ubo[4] = nullptr; - M2Data *m2Data = m_m2Geom->getM2Data(); - CAaBox &aaBox = m2Data->bounding_box; - - - mathfu::vec3 center = mathfu::vec3( - (aaBox.min.x + aaBox.max.x) / 2, - (aaBox.min.y + aaBox.max.y) / 2, - (aaBox.min.z + aaBox.max.z) / 2 - ); + auto l_m2Geom = m_m2Geom; + bbBlockVS->setUpdateHandler([this, l_m2Geom](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ + M2Data *m2Data = l_m2Geom->getM2Data(); + CAaBox &aaBox = m2Data->bounding_box; - mathfu::vec3 scale = mathfu::vec3( - aaBox.max.x - center[0], - aaBox.max.y - center[1], - aaBox.max.z - center[2] - ); + mathfu::vec3 center = mathfu::vec3( + (aaBox.min.x + aaBox.max.x) / 2, + (aaBox.min.y + aaBox.max.y) / 2, + (aaBox.min.z + aaBox.max.z) / 2 + ); - bbModelWideBlockVS &blockVS = bbBlockVS->getObject(); - blockVS.uPlacementMat = m_placementMatrix; - blockVS.uBBScale = mathfu::vec4_packed(mathfu::vec4(scale, 0.0)); - blockVS.uBBCenter = mathfu::vec4_packed(mathfu::vec4(center, 0.0)); - blockVS.uColor = mathfu::vec4_packed(mathfu::vec4(0.1f, 0.7f, 0.1f, 0.1f)); + mathfu::vec3 scale = mathfu::vec3( + aaBox.max.x - center[0], + aaBox.max.y - center[1], + aaBox.max.z - center[2] + ); -// bbBlockVS->save(true); + bbModelWideBlockVS &blockVS = self->getObject(); + blockVS.uPlacementMat = m_placementMatrix; + blockVS.uBBScale = mathfu::vec4_packed(mathfu::vec4(scale, 0.0)); + blockVS.uBBCenter = mathfu::vec4_packed(mathfu::vec4(center, 0.0)); + blockVS.uColor = mathfu::vec4_packed(mathfu::vec4(0.1f, 0.7f, 0.1f, 0.1f)); + }); boundingBoxMesh = m_api->hDevice->createMesh(meshTemplate); - + boundingBoxMesh->setRenderOrder(1000); occlusionQuery = m_api->hDevice->createQuery(boundingBoxMesh); } @@ -1311,9 +1295,119 @@ bool M2Object::checkifBonesAreInRange(M2SkinProfile *skinProfile, M2SkinSection return true; } +float wfv_convert(float value, int16_t random) { + if ( value == 0.0 ) + return 0.0; + + int invertedVal = (int)(float)(1000.0 / fabs(value)); + + if ( !invertedVal ) + return 0.0; + + float multiplier = 1.0; + + if ( value <= 0.0 ) + multiplier = -1.0; + else + multiplier = 1.0; + return (float)((float)(int)(random % invertedVal) / (float)invertedVal) * multiplier; +} + +HGM2Mesh M2Object::createWaterfallMesh() { + HGShaderPermutation shaderPermutation = m_api->hDevice->getShader("waterfallShader", nullptr); + + gMeshTemplate meshTemplate(bufferBindings, shaderPermutation); + + auto skinData = m_skinGeom->getSkinData(); + auto m2Data = m_m2Geom->getM2Data(); + auto wfv3Data = m_m2Geom->m_wfv3 != nullptr ? m_m2Geom->m_wfv3 : m_m2Geom->m_wfv1; + + auto m2Batch = skinData->batches.getElement(0); + auto skinSection = skinData->skinSections[m2Batch->skinSectionIndex]; + + int renderFlagIndex = m2Batch->materialIndex; + + meshTemplate.depthWrite = false; + meshTemplate.depthCulling = true; + meshTemplate.backFaceCulling = false; + meshTemplate.triCCW = 1; + + meshTemplate.blendMode = EGxBlendEnum::GxBlend_Alpha; + + meshTemplate.start = (skinSection->indexStart + (skinSection->Level << 16)) * 2; + meshTemplate.end = skinSection->indexCount; + meshTemplate.element = DrawElementMode::TRIANGLES; + meshTemplate.skybox = m_boolSkybox; + + HGTexture texture[4] = {nullptr,nullptr,nullptr,nullptr}; + meshTemplate.texture.resize(5); + meshTemplate.textureCount = 5; + meshTemplate.texture[0] = getTexture(0); //mask + meshTemplate.texture[1] = getTexture(1); //whiteWater + meshTemplate.texture[2] = getTexture(2); //noise + meshTemplate.texture[3] = getTexture(3); //bumpTexture + meshTemplate.texture[4] = getTexture(4); //normalTex + + + meshTemplate.ubo[0] = nullptr; + meshTemplate.ubo[1] = vertexModelWideUniformBuffer; + meshTemplate.ubo[2] = m_api->hDevice->createUniformBufferChunk(sizeof(M2::WaterfallData::meshWideBlockVS)); + + meshTemplate.ubo[2]->setUpdateHandler([this, skinData, m2Data, wfv3Data](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ + auto &meshblockVS = self->getObject(); + meshblockVS.bumpScale = mathfu::vec4(wfv3Data->bumpScale, 0, 0, 0); + + M2MeshBufferUpdater::fillTextureMatrices(*this, 0, m2Data, skinData, meshblockVS.uTextMat); + }); + + meshTemplate.ubo[3] = nullptr; + meshTemplate.ubo[4] = m_api->hDevice->createUniformBufferChunk(sizeof(M2::WaterfallData::meshWideBlockPS)); + meshTemplate.ubo[4]->setUpdateHandler([this, skinData, m2Data, wfv3Data](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ + auto &meshblockPS = self->getObject(); + meshblockPS.baseColor = mathfu::vec4( + wfv3Data->basecolor.a / 255.0f, + wfv3Data->basecolor.r / 255.0f, + wfv3Data->basecolor.g / 255.0f, + wfv3Data->basecolor.b / 255.0f); + + meshblockPS.values0.x = wfv3Data->values0_x; + meshblockPS.values0.y = wfv3Data->values0_y; + meshblockPS.values0.z = wfv3Data->values0_z; + meshblockPS.values0.w = wfv3Data->values0_w; + + meshblockPS.values1.x = wfv3Data->value1_x; + meshblockPS.values1.y = wfv3Data->value1_y; + meshblockPS.values1.w = wfv3Data->value1_w; + + meshblockPS.m_values3.x = wfv3Data->value3_x; + meshblockPS.m_values3.y = wfv3Data->value3_y; + + meshblockPS.m_values2.x = wfv3Data->flags & 2; + meshblockPS.m_values2.y = wfv3Data->flags & 1; + + meshblockPS.m_values2.w = wfv3Data->value2_w; + meshblockPS.m_values3.w = wfv3Data->values3_w; + meshblockPS.m_values4.y = wfv3Data->values4_y; + + meshblockPS.values1.z = wfv_convert(meshblockPS.values1.y, (int16_t)((uint64_t)this)); + meshblockPS.m_values2.z = wfv_convert(meshblockPS.m_values2.w, (int16_t)((uint64_t)this)); + meshblockPS.m_values3.z = wfv_convert(wfv3Data->values3_z, (int16_t)((uint64_t)this)); + }); + + //Make mesh + auto hmesh = m_api->hDevice->createM2Mesh(meshTemplate); + hmesh->setM2Object(this); + hmesh->setLayer(0); + hmesh->setPriorityPlane(0); + hmesh->setQuery(nullptr); + + return hmesh; +} + void M2Object::createMeshes() { /* 1. Free previous subMeshArray */ - this->m_meshArray.clear(); + this->m_meshNaturalArray.clear(); + this->m_meshForcedTranspArray.clear(); this->m_materialArray.clear(); createBoundingBoxMesh(); @@ -1327,58 +1421,91 @@ void M2Object::createMeshes() { /* 2. Fill the materialArray */ std::vector batchesRequiringDynamicVao = {}; M2Array* batches = &m_skinGeom->getSkinData()->batches; - for (int i = 0; i < batches->size; i++) { - auto m2Batch = skinProfile->batches[i]; - auto skinSection = skinProfile->skinSections[m2Batch->skinSectionIndex]; - if (!checkifBonesAreInRange(skinProfile, skinSection)) { - batchesRequiringDynamicVao.push_back(i); - continue; - } - M2MaterialInst material; - HGM2Mesh hmesh = createSingleMesh(m_m2Data, i, 0, bufferBindings, m2Batch, skinSection, material); - - //hmesh->m_query = occlusionQuery; - - this->m_meshArray.push_back(hmesh); - this->m_materialArray.push_back(material); - - M2MeshBufferUpdater::assignUpdateEvents(hmesh, this, m_materialArray[m_materialArray.size()-1], m_m2Data, skinProfile); - } - - // Create meshes requiring dynamic - for (int j = 0; j < batchesRequiringDynamicVao.size(); j++) { - int i = batchesRequiringDynamicVao[j]; - auto m2Batch = skinProfile->batches[i]; - auto skinSection = skinProfile->skinSections[m2Batch->skinSectionIndex]; - - std::array dynVBOs; - auto dynVaos = m_m2Geom->createDynamicVao(*m_api->hDevice, dynVBOs, m_skinGeom.get(), skinSection); - - std::array dynamicMeshData; - - for (int k = 0; k < 4; k++) { - dynamicMeshData[k].batchIndex = i; - dynamicMeshData[k].m_bindings = dynVaos[k]; - dynamicMeshData[k].m_bufferVBO = dynVBOs[k]; + if (m_m2Geom->m_wfv3 == nullptr && m_m2Geom->m_wfv1 == nullptr) { + for (int i = 0; i < batches->size; i++) { + auto m2Batch = skinProfile->batches[i]; + auto skinSection = skinProfile->skinSections[m2Batch->skinSectionIndex]; + if (!checkifBonesAreInRange(skinProfile, skinSection)) { + batchesRequiringDynamicVao.push_back(i); + continue; + } M2MaterialInst material; - int correction = skinSection->indexStart + (skinSection->Level << 16); - dynamicMeshData[k].m_mesh = createSingleMesh(m_m2Data, i, correction, dynVaos[k], m2Batch, skinSection, material); + EGxBlendEnum mainBlendMode; + HGM2Mesh hmesh = createSingleMesh(m_m2Data, i, 0, bufferBindings, m2Batch, skinSection, material, + mainBlendMode, false); + + if (hmesh == nullptr) + continue; this->m_materialArray.push_back(material); - M2MeshBufferUpdater::assignUpdateEvents(dynamicMeshData[k].m_mesh, this, m_materialArray[m_materialArray.size()-1], m_m2Data, skinProfile); + this->m_meshNaturalArray.push_back(hmesh); + M2MeshBufferUpdater::assignUpdateEvents(hmesh, this, m_materialArray[m_materialArray.size() - 1], m_m2Data, + skinProfile); + + if (mainBlendMode == EGxBlendEnum::GxBlend_Opaque) { + EGxBlendEnum blendMode = EGxBlendEnum::GxBlend_Alpha; + M2MaterialInst materialTransp; + HGM2Mesh hmeshTrans = createSingleMesh(m_m2Data, i, 0, bufferBindings, m2Batch, skinSection, + materialTransp, blendMode, true); + + this->m_materialArray.push_back(material); + this->m_meshForcedTranspArray.push_back(hmeshTrans); + M2MeshBufferUpdater::assignUpdateEvents(hmeshTrans, this, m_materialArray[m_materialArray.size() - 1], + m_m2Data, skinProfile); + + } else { + m_meshForcedTranspArray.push_back(nullptr); + } } - dynamicMeshes.push_back(dynamicMeshData); + // Create meshes requiring dynamic + for (int j = 0; j < batchesRequiringDynamicVao.size(); j++) { + int i = batchesRequiringDynamicVao[j]; + auto m2Batch = skinProfile->batches[i]; + auto skinSection = skinProfile->skinSections[m2Batch->skinSectionIndex]; + + std::array dynVBOs; + auto dynVaos = m_m2Geom->createDynamicVao(*m_api->hDevice, dynVBOs, m_skinGeom.get(), skinSection); + + std::array dynamicMeshData; + + //Try to create mesh + M2MaterialInst testMaterial; + EGxBlendEnum blendMode; + auto testMesh = createSingleMesh(m_m2Data, i, 0, dynVaos[0], m2Batch, skinSection, testMaterial, blendMode, + false); + if (testMesh == nullptr) + continue; + + for (int k = 0; k < 4; k++) { + dynamicMeshData[k].batchIndex = i; + dynamicMeshData[k].m_bindings = dynVaos[k]; + dynamicMeshData[k].m_bufferVBO = dynVBOs[k]; + + M2MaterialInst material; + int correction = skinSection->indexStart + (skinSection->Level << 16); + dynamicMeshData[k].m_mesh = createSingleMesh(m_m2Data, i, correction, dynVaos[k], m2Batch, skinSection, + material, blendMode, false); + + this->m_materialArray.push_back(material); + M2MeshBufferUpdater::assignUpdateEvents(dynamicMeshData[k].m_mesh, this, + m_materialArray[m_materialArray.size() - 1], m_m2Data, + skinProfile); + } + + dynamicMeshes.push_back(dynamicMeshData); + } + } else { + m_meshNaturalArray.push_back(createWaterfallMesh()); } } HGM2Mesh M2Object::createSingleMesh(const M2Data *m_m2Data, int i, int indexStartCorrection, HGVertexBufferBindings finalBufferBindings, const M2Batch *m2Batch, - const M2SkinSection *skinSection, M2MaterialInst &material) { - - prepearMatrial(material, i); + const M2SkinSection *skinSection, M2MaterialInst &material, EGxBlendEnum &blendMode, bool overrideBlend) { + if (!prepearMaterial(material, i)) return nullptr; M2ShaderCacheRecord cacheRecord{}; cacheRecord.vertexShader = material.vertexShader; @@ -1404,7 +1531,12 @@ M2Object::createSingleMesh(const M2Data *m_m2Data, int i, int indexStartCorrecti meshTemplate.backFaceCulling = !(renderFlag->flags & 0x4); meshTemplate.triCCW = 1; - meshTemplate.blendMode = M2BlendingModeToEGxBlendEnum[renderFlag->blending_mode]; + if (overrideBlend) { + meshTemplate.blendMode = blendMode; + } else { + meshTemplate.blendMode = M2BlendingModeToEGxBlendEnum[renderFlag->blending_mode]; + blendMode = meshTemplate.blendMode; + } meshTemplate.start = (skinSection->indexStart + (skinSection->Level << 16) - indexStartCorrection) * 2; meshTemplate.end = skinSection->indexCount; @@ -1434,52 +1566,47 @@ M2Object::createSingleMesh(const M2Data *m_m2Data, int i, int indexStartCorrecti return hmesh; } -void M2Object::collectMeshes(std::vector &renderedThisFrame, int renderOrder) { - //2. Check if .skin file is loaded - if (m_m2Geom == nullptr) { - return; - } - - if (m_skinGeom == nullptr) { - Cache *skinGeomCache = m_api->cacheStorage->getSkinGeomCache(); - if (m_m2Geom->skinFileDataIDs.size() > 0) { - assert(m_m2Geom->skinFileDataIDs.size() > 0); - m_skinGeom = skinGeomCache->getFileId(m_m2Geom->skinFileDataIDs[0]); - } else if (!useFileId){ - assert(m_nameTemplate.size() > 0); - std::string skinFileName = m_nameTemplate + "00.skin"; - m_skinGeom = skinGeomCache->get(skinFileName); - } - return; - } +void M2Object::collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder) { + if (!this->isGeomReqFilesLoaded()) return; - if (m_m2Geom->m_skid > 0) { - if (m_skelGeom == nullptr) { - auto skelCache = m_api->cacheStorage->getSkelCache(); - m_skelGeom = skelCache->getFileId(m_m2Geom->m_skid); - return; - } - } + M2SkinProfile* skinData = this->m_skinGeom->getSkinData(); - if (!m_loaded) return; + int minBatch = m_api->getConfig()->m2MinBatch; + int maxBatch = std::min(m_api->getConfig()->m2MaxBatch, (const int &) this->m_meshNaturalArray.size()); - M2SkinProfile* skinData = this->m_skinGeom->getSkinData(); + if (m_api->getConfig()->renderM2) { + for (int i = minBatch; i < maxBatch; i++) { + float finalTransparency = M2MeshBufferUpdater::calcFinalTransparency(*this, i, skinData); + if ((finalTransparency < 0.0001)) + continue; - int minBatch = m_api->getConfig()->getM2MinBatch(); - int maxBatch = std::min(m_api->getConfig()->getM2MaxBatch(), (const int &) this->m_meshArray.size()); + HGM2Mesh mesh = this->m_meshNaturalArray[i]; + if (finalTransparency < 0.999 && i < this->m_meshForcedTranspArray.size() && + this->m_meshForcedTranspArray[i] != nullptr) { + mesh = this->m_meshForcedTranspArray[i]; + } - for (int i = minBatch; i < maxBatch; i++) { - float finalTransparency = M2MeshBufferUpdater::calcFinalTransparency(*this, i, skinData); - if ((finalTransparency < 0.0001) ) continue; + mesh->setRenderOrder(renderOrder); + if (mesh->getIsTransparent()) { + transparentMeshes.push_back(mesh); + } else { + opaqueMeshes.push_back(mesh); + } + } - this->m_meshArray[i]->setRenderOrder(renderOrder); - renderedThisFrame.push_back(this->m_meshArray[i]); + for (auto &dynMesh: dynamicMeshes) { + HGParticleMesh mesh = dynMesh[m_api->hDevice->getUpdateFrameNumber()].m_mesh; + mesh->setRenderOrder(renderOrder); + if (mesh->getIsTransparent()) { + transparentMeshes.push_back(mesh); + } else { + opaqueMeshes.push_back(mesh); + } + } } - for (auto &dynMesh : dynamicMeshes) { - HGParticleMesh mesh = dynMesh[m_api->hDevice->getUpdateFrameNumber()].m_mesh; - mesh->setRenderOrder(renderOrder); - renderedThisFrame.push_back(mesh); + if (m_api->getConfig()->drawM2BB) { + transparentMeshes.push_back(boundingBoxMesh); } // std::cout << "Collected meshes at update frame =" << m_api->hDevice->getUpdateFrameNumber() << std::endl; @@ -1487,27 +1614,13 @@ void M2Object::collectMeshes(std::vector &renderedThisFrame, int renderO } void M2Object::initAnimationManager() { - this->m_animationManager = new AnimationManager(m_api, m_m2Geom); -} - -bool M2Object::checkIfHasBillboarded() { - M2Data * m2File = this->m_m2Geom->getM2Data(); - for (int i = 0; i < m2File->bones.size; i++) { - M2CompBone * boneDefinition = m2File->bones.getElement(i); - if (( - boneDefinition->flags.cylindrical_billboard_lock_x & - boneDefinition->flags.cylindrical_billboard_lock_y & - boneDefinition->flags.cylindrical_billboard_lock_z & - boneDefinition->flags.spherical_billboard) > 0) { - return true; - } - } - return false; + this->m_animationManager = new AnimationManager(m_api, m_boneMasterData, m_m2Geom->exp2 != nullptr); } void M2Object::initBoneAnimMatrices() { - this->bonesMatrices = std::vector(m_m2Geom->getM2Data()->bones.size, mathfu::mat4::Identity());; + auto &bones = *m_boneMasterData->getSkelData()->m_m2CompBones; + this->bonesMatrices = std::vector(bones.size, mathfu::mat4::Identity());; } void M2Object::initTextAnimMatrices() { textAnimMatrices = std::vector(m_m2Geom->getM2Data()->texture_transforms.size, mathfu::mat4::Identity());; @@ -1518,7 +1631,7 @@ void M2Object::initSubmeshColors() { } void M2Object::initTransparencies() { - transparencies = std::vector(m_m2Geom->getM2Data()->transparency_lookup_table.size); + transparencies = std::vector(m_m2Geom->getM2Data()->texture_weights.size); } void M2Object::initLights() { @@ -1536,8 +1649,8 @@ void M2Object::initParticleEmitters() { ParticleEmitter *emitter = new ParticleEmitter(m_api, m_m2Geom->getM2Data()->particle_emitters.getElement(i), this, m_m2Geom, txacVal); particleEmitters.push_back(emitter); - if (m_m2Geom.get()->exp2Records != nullptr && emitter->getGenerator() != nullptr) { - emitter->getGenerator()->getAniProp()->zSource = m_m2Geom.get()->exp2Records->getElement(i)->zSource; + if (m_m2Geom->exp2 != nullptr && emitter->getGenerator() != nullptr) { + emitter->getGenerator()->getAniProp()->zSource = m_m2Geom->exp2->content.getElement(i)->zSource; } } } @@ -1546,20 +1659,23 @@ void M2Object::initRibbonEmitters() { // return; ribbonEmitters = std::vector(); // ribbonEmitters.reserve(m_m2Geom->getM2Data()->ribbon_emitters.size); - for (int i = 0; i < m_m2Geom->getM2Data()->ribbon_emitters.size; i++) { - M2Ribbon *m2Ribbon = m_m2Geom->getM2Data()->ribbon_emitters.getElement(i); + auto m2Data = m_m2Geom->getM2Data(); + for (int i = 0; i < m2Data->ribbon_emitters.size; i++) { + M2Ribbon *m2Ribbon = m2Data->ribbon_emitters.getElement(i); std::vector materials(m2Ribbon->materialIndices.size); std::vector textureIndicies(m2Ribbon->textureIndices.size); for (size_t j = 0; j < materials.size(); j++) { - materials[j] = *m_m2Geom->getM2Data()->materials[*m2Ribbon->materialIndices[j]]; + materials[j] = *m2Data->materials[*m2Ribbon->materialIndices[j]]; } for (size_t j = 0; j < textureIndicies.size(); j++) { textureIndicies[j] = *m2Ribbon->textureIndices[j]; } - auto emitter = new CRibbonEmitter(m_api, this, materials, textureIndicies); + int textureTransformLookup = (m2Data->global_flags.flag_unk_0x20000 != 0) ? m2Ribbon->textureTransformLookupIndex : -1; + + auto emitter = new CRibbonEmitter(m_api, this, materials, textureIndicies, textureTransformLookup); ribbonEmitters.push_back(emitter); CImVector color; @@ -1627,38 +1743,88 @@ mathfu::vec4 M2Object::getM2SceneAmbientLight() { return mathfu::vec4(ambientColor.x, ambientColor.y, ambientColor.z, 1.0) ; }; -mathfu::vec3 M2Object::getSunDir() { - if (m_setSunDir && getUseLocalLighting()) { - return mathfu::vec3(m_sunDirOverride.x, m_sunDirOverride.y, m_sunDirOverride.z); +void M2Object::getAvailableAnimation(std::vector &allAnimationList) { + auto &sequences = *m_boneMasterData->getSkelData()->m_sequences; + + allAnimationList.resize(0); + for (int i = 0; i < sequences.size; i++) { + allAnimationList.push_back(sequences[i]->id); } + if (m_parentSkelGeom != nullptr) { + auto &bannedAnims = m_m2Geom->blackListAnimations; + auto &sequences = *m_boneMasterData->getParentSkelData()->m_sequences; - return m_api->getConfig()->getExteriorDirectColorDir(); -} -void M2Object::getAvailableAnimation(std::vector &allAnimationList) { - allAnimationList.reserve(m_m2Geom->m_m2Data->sequences.size); - for (int i = 0; i < m_m2Geom->m_m2Data->sequences.size; i++) { - allAnimationList.push_back(m_m2Geom->m_m2Data->sequences[i]->id); + for (int i = 0; i < sequences.size; i++) { + bool animationIsBanned = false; + for (auto const a : bannedAnims) { + if (a != 0 && a == sequences[i]->id) { + animationIsBanned = true; + break; + } + } + if (!animationIsBanned) { + allAnimationList.push_back(sequences[i]->id); + } + } } + std::sort( allAnimationList.begin(), allAnimationList.end()); allAnimationList.erase( unique( allAnimationList.begin(), allAnimationList.end() ), allAnimationList.end()); } +void M2Object::getMeshIds(std::vector &meshIdList) { +// this->m_skin + std::unordered_set meshIdSet; + + auto skinData = m_skinGeom->getSkinData(); + M2Array& batches = skinData->batches; + + + for (int i = 0; i < batches.size; i++) { + auto m2Batch = batches[i]; + auto skinSection = skinData->skinSections[m2Batch->skinSectionIndex]; + + meshIdSet.insert(skinSection->skinSectionId); + } + + meshIdList = std::vector(meshIdSet.begin(), meshIdSet.end()); +} +mathfu::mat4 M2Object::getTextureTransformByLookup(int textureTrasformlookup) { + if (textureTrasformlookup < this->m_m2Geom->getM2Data()->texture_transforms_lookup_table.size) { + auto textureTransformIndex = *this->m_m2Geom->getM2Data()->texture_transforms_lookup_table.getElement(textureTrasformlookup); + if (textureTransformIndex >= 0 && textureTransformIndex < this->textAnimMatrices.size()) { + return this->textAnimMatrices[textureTransformIndex]; + } + } + + return mathfu::mat4::Identity(); +} -void M2Object::drawParticles(std::vector &meshes, int renderOrder) { +void M2Object::drawParticles(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder) { // return; // for (int i = 0; i< std::min((int)particleEmitters.size(), 10); i++) { - int minParticle = m_api->getConfig()->getMinParticle(); - int maxParticle = std::min(m_api->getConfig()->getMaxParticle(), (const int &) particleEmitters.size()); + int minParticle = m_api->getConfig()->minParticle; + int maxParticle = std::min(m_api->getConfig()->maxParticle, (const int &) particleEmitters.size()); // int maxBatch = particleEmitters.size(); for (int i = minParticle; i < maxParticle; i++) { -// for (int i = 0; i< particleEmitters.size(); i++) { - particleEmitters[i]->collectMeshes(meshes, renderOrder); + + //Respect PGD1 chunk + if (m_m2Geom->particleGeosetData != nullptr) { + auto geoset = *m_m2Geom->particleGeosetData->pgd.getElement(i); + auto meshGroup = (geoset / 100); + if ((meshGroup < this->m_meshIds.size()) && (geoset > 0) && + (m_meshIds[meshGroup] != (geoset % 100))) { + continue; + } + } + + particleEmitters[i]->collectMeshes(opaqueMeshes, transparentMeshes, renderOrder); } for (int i = 0; i < ribbonEmitters.size(); i++) { - ribbonEmitters[i]->collectMeshes(meshes, renderOrder); + ribbonEmitters[i]->collectMeshes(opaqueMeshes, transparentMeshes, renderOrder); } } @@ -1666,12 +1832,10 @@ HBlpTexture M2Object::getBlpTextureData(int textureInd) { M2Texture* textureDefinition = m_m2Geom->getM2Data()->textures.getElement(textureInd); //TODO:! Example of exception: "WORLD\\AZEROTH\\KARAZAHN\\PASSIVEDOODADS\\BURNINGBOOKS\\BOOKSONFIRE.m2" HBlpTexture blpData = nullptr; - if (textureDefinition == nullptr) { - return nullptr; - } - if (textureDefinition->type == 0) { + + if ((textureDefinition== nullptr) || textureDefinition->type == 0) { blpData = getHardCodedTexture(textureInd); - } else if ( (textureDefinition->type < this->m_replaceTextures.size()) ){ + } else if ((textureDefinition != nullptr) && (textureDefinition->type < this->m_replaceTextures.size()) ){ blpData = this->m_replaceTextures[textureDefinition->type]; } @@ -1689,8 +1853,8 @@ HGTexture M2Object::getTexture(int textureInd) { HGTexture hgTexture = m_api->hDevice->createBlpTexture( blpData, - (textureDefinition->flags & 1) > 0, - (textureDefinition->flags & 2) > 0 + textureDefinition!= nullptr ? ( (textureDefinition->flags & 1) > 0 ) : false, + textureDefinition!= nullptr ? ( (textureDefinition->flags & 2) > 0 ) : false ); return hgTexture; @@ -1700,7 +1864,7 @@ HBlpTexture M2Object::getHardCodedTexture(int textureInd) { M2Texture* textureDefinition = m_m2Geom->getM2Data()->textures.getElement(textureInd); auto textureCache = m_api->cacheStorage->getTextureCache(); HBlpTexture texture; - if (textureDefinition->filename.size > 0) { + if (textureDefinition != nullptr && textureDefinition->filename.size > 0) { std::string fileName = textureDefinition->filename.toString(); texture = textureCache->get(fileName); } else if (textureInd < m_m2Geom->textureFileDataIDs.size()) { @@ -1716,17 +1880,17 @@ HBlpTexture M2Object::getHardCodedTexture(int textureInd) { } void M2Object::createVertexBindings() { - std::shared_ptr device = m_api->hDevice; + HGDevice device = m_api->hDevice; //2. Create buffer binding and fill it - bufferBindings = m_m2Geom->getVAO(*device, m_skinGeom.get()); + bufferBindings = m_m2Geom->getVAO(device, m_skinGeom.get()); //3. Create model wide uniform buffer // vertexModelWideUniformBuffer = device->createUniformBuffer(sizeof(mathfu::mat4) * (m_m2Geom->m_m2Data->bones.size + 1)); vertexModelWideUniformBuffer = device->createUniformBufferChunk(sizeof(M2::modelWideBlockVS)); fragmentModelWideUniformBuffer = device->createUniformBufferChunk(sizeof(M2::modelWideBlockPS)); - vertexModelWideUniformBuffer->setUpdateHandler([this](IUniformBufferChunk *self){ + vertexModelWideUniformBuffer->setUpdateHandler([this](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ auto &blockVS = self->getObject(); blockVS.uPlacementMat = m_placementMatrix; @@ -1734,11 +1898,10 @@ void M2Object::createVertexBindings() { std::copy(bonesMatrices.data(), bonesMatrices.data() + interCount, blockVS.uBoneMatrixes); }); - fragmentModelWideUniformBuffer->setUpdateHandler([this](IUniformBufferChunk *self){ + fragmentModelWideUniformBuffer->setUpdateHandler([this](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ static mathfu::vec4 diffuseNon(0.0, 0.0, 0.0, 0.0); mathfu::vec4 localDiffuse = diffuseNon; - M2::modelWideBlockPS &blockPS = self->getObject(); blockPS.intLight.uInteriorAmbientColorAndApplyInteriorLight = @@ -1768,9 +1931,9 @@ void M2Object::createVertexBindings() { void M2Object::updateDynamicMeshes() { auto rootMatInverse = bonesMatrices[0].Inverse(); + auto frameNum = m_api->hDevice->getUpdateFrameNumber(); for (auto &dynamicMesh: dynamicMeshes) { - auto frameNum = m_api->hDevice->getUpdateFrameNumber(); auto &dynMeshData = dynamicMesh[frameNum]; M2SkinProfile* skinProfile = this->m_skinGeom->getSkinData(); @@ -1822,6 +1985,13 @@ void M2Object::setReplaceTextures(std::vector &replaceTextures) { createMeshes(); // recreate meshes } } +void M2Object::setMeshIds(std::vector &meshIds) { + m_meshIds = meshIds; + + if (m_loaded) { + createMeshes(); // recreate meshes + } +} void M2Object::setReplaceParticleColors(std::array, 3> &particleColorReplacement) { m_particleColorReplacement = particleColorReplacement; @@ -1841,3 +2011,7 @@ bool M2Object::getReplaceParticleColors(std::array, void M2Object::resetReplaceParticleColor() { particleColorReplacementIsSet = false; } + +int M2Object::getCurrentAnimationIndex() { + return m_animationManager->getCurrentAnimationIndex(); +} diff --git a/wowViewerLib/src/engine/objects/m2/m2Object.h b/wowViewerLib/src/engine/objects/m2/m2Object.h index 5a7d6889a..6b5640d4d 100644 --- a/wowViewerLib/src/engine/objects/m2/m2Object.h +++ b/wowViewerLib/src/engine/objects/m2/m2Object.h @@ -6,6 +6,7 @@ #define WOWVIEWERLIB_M2OBJECT_H class M2Object; +#define _USE_MATH_DEFINES #include #include "mathfu/glsl_mappings.h" @@ -22,10 +23,13 @@ class M2Object; #include "mathfu/internal/vector_4.h" #include "../../managers/CRibbonEmitter.h" #include "../../ApiContainer.h" +#include "m2Helpers/CBoneMasterData.h" class M2Object { public: - M2Object(ApiContainer *api, bool isSkybox = false, bool overrideSkyModelMat = true) : m_api(api), m_m2Geom(nullptr), + friend class IExporter; + + M2Object(HApiContainer api, bool isSkybox = false, bool overrideSkyModelMat = true) : m_api(api), m_m2Geom(nullptr), m_skinGeom(nullptr), m_animationManager(nullptr), m_boolSkybox(isSkybox), m_overrideSkyModelMat(overrideSkyModelMat) { } @@ -73,11 +77,13 @@ class M2Object { CAaBox aabb; CAaBox colissionAabb; - ApiContainer *m_api = nullptr; + HApiContainer m_api = nullptr; HM2Geom m_m2Geom = nullptr; HSkinGeom m_skinGeom = nullptr; HSkelGeom m_skelGeom = nullptr; + HSkelGeom m_parentSkelGeom = nullptr; + std::shared_ptr m_boneMasterData = nullptr; HGVertexBufferBindings bufferBindings = nullptr; HGUniformBufferChunk vertexModelWideUniformBuffer = nullptr; @@ -94,11 +100,6 @@ class M2Object { bool animationOverrideActive = false; float animationOverridePercent = 0; - mathfu::vec4 m_sunDirOverride; - bool m_setSunDir = false; - - - bool m_hasBillboards = false; std::string m_modelName; std::string m_nameTemplate = ""; @@ -131,7 +132,10 @@ class M2Object { std::unordered_map loadedTextures; - std::vector m_meshArray; + std::vector m_meshNaturalArray; + std::vector m_meshForcedTranspArray; + + //TODO: think about if it's viable to do forced transp for dyn meshes std::vector> dynamicMeshes; std::vector m_materialArray; AnimationManager *m_animationManager; @@ -155,7 +159,6 @@ class M2Object { void initRibbonEmitters(); void sortMaterials(mathfu::Matrix &modelViewMat); - bool checkIfHasBillboarded(); bool checkifBonesAreInRange(M2SkinProfile *skinProfile, M2SkinSection *mesh); @@ -163,9 +166,11 @@ class M2Object { void createBoundingBoxMesh(); static mathfu::vec4 getCombinedColor(M2SkinProfile *skinData, int batchIndex, const std::vector &subMeshColors) ; - static float getTransparency(M2SkinProfile *skinData, int batchIndex, const std::vector &transparencies) ; + static float getTextureWeight(M2SkinProfile *skinData, M2Data *m2data, int batchIndex, int textureIndex, const std::vector &transparencies) ; public: + void testExport(); + void setAlwaysDraw(bool value) { m_alwaysDraw = value; } @@ -188,6 +193,7 @@ class M2Object { std::vector replaceTextures); void setReplaceTextures(std::vector &replaceTextures); + void setMeshIds(std::vector &meshIds); void setReplaceParticleColors(std::array, 3> &particleColorReplacement); void resetReplaceParticleColor(); bool getReplaceParticleColors(std::array, 3> &particleColorReplacement); @@ -204,6 +210,7 @@ class M2Object { } void setAnimationId(int animationId); + int getCurrentAnimationIndex(); void resetCurrentAnimation(); void createPlacementMatrix(SMODoodadDef &def, mathfu::mat4 &wmoPlacementMat); void createPlacementMatrix(SMDoodadDef &def); @@ -223,21 +230,18 @@ class M2Object { float getHeight(); void getAvailableAnimation(std::vector &allAnimationList); + void getMeshIds(std::vector &meshIdList); + mathfu::mat4 getTextureTransformByLookup(int textureTrasformlookup); bool getGetIsLoaded() { return m_loaded; }; mathfu::mat4 getModelMatrix() { return m_placementMatrix; }; - bool getHasBillboarded() { - return m_hasBillboards; - } - bool getIsInstancable(); - - bool prepearMatrial(M2MaterialInst &materialData, int materialIndex); - void collectMeshes(std::vector &renderedThisFrame, int renderOrder); + bool prepearMaterial(M2MaterialInst &materialData, int materialIndex); + void collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder); bool setUseLocalLighting(bool value) { - if (hasModf0x2Flag) { - m_useLocalDiffuseColor = 0; - } +// if (hasModf0x2Flag) { +// m_useLocalDiffuseColor = 0; +// } if (m_useLocalDiffuseColor == -1) { m_useLocalDiffuseColor = value ? 1 : 0; } @@ -250,6 +254,9 @@ class M2Object { const std::vector &frustumPlanes, const std::vector &frustumPoints); + bool isMainDataLoaded(); + bool isGeomReqFilesLoaded(); + bool doPostLoad(); void update(double deltaTime, mathfu::vec3 &cameraPos, mathfu::mat4 &viewMat); void uploadGeneratorBuffers(mathfu::mat4 &viewMat); @@ -273,16 +280,10 @@ class M2Object { m_ambientColorOverride = ambientColor; } - void setSunDirOverride(mathfu::vec4 &sunDir, bool override) { - m_setSunDir = override; - m_sunDirOverride = sunDir; - } - - void drawParticles(std::vector &meshes, int renderOrder); + void drawParticles(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder); void createVertexBindings(); - mathfu::vec3 getSunDir(); int getCameraNum() { if (!getGetIsLoaded()) return 0; @@ -290,8 +291,9 @@ class M2Object { } HGM2Mesh createSingleMesh(const M2Data *m_m2Data, int i, int indexStartCorrection, HGVertexBufferBindings finalBufferBindings, const M2Batch *m2Batch, - const M2SkinSection *skinSection, M2MaterialInst &material); + const M2SkinSection *skinSection, M2MaterialInst &material, EGxBlendEnum &blendMode, bool overrideBlend); + HGM2Mesh createWaterfallMesh(); void updateDynamicMeshes(); }; diff --git a/wowViewerLib/src/engine/objects/scenes/NullScene.h b/wowViewerLib/src/engine/objects/scenes/NullScene.h index ab232e925..06c7f7479 100644 --- a/wowViewerLib/src/engine/objects/scenes/NullScene.h +++ b/wowViewerLib/src/engine/objects/scenes/NullScene.h @@ -9,19 +9,22 @@ class NullScene : public IScene { public: + ~NullScene() override {} virtual void setReplaceTextureArray(std::vector &replaceTextureArray) override {}; - + virtual void setMeshIdArray(std::vector &meshIds) override {}; virtual void setAnimationId(int animationId) override {}; + virtual void setMeshIds(std::vector &meshIds) override {}; + virtual void produceUpdateStage(HUpdateStage updateStage) override {}; virtual void produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, std::vector &additionalChunks) override { - resultDrawStage->meshesToRender = std::make_shared(); + resultDrawStage->transparentMeshes = std::make_shared(); + resultDrawStage->opaqueMeshes = std::make_shared(); }; virtual void checkCulling(HCullStage cullStage) override {}; virtual void doPostLoad(HCullStage cullStage) override {}; - virtual void update(HUpdateStage updateStage) override {}; - virtual void updateBuffers(HCullStage cullStage) override {}; + virtual void updateBuffers(HUpdateStage updateStage) override {}; virtual void resetAnimation() override {}; virtual int getCameraNum() override {return 0;}; virtual std::shared_ptr createCamera(int cameraNum) override { return nullptr;}; diff --git a/wowViewerLib/src/engine/objects/scenes/m2Scene.cpp b/wowViewerLib/src/engine/objects/scenes/m2Scene.cpp index 6591d722f..fca3e1828 100644 --- a/wowViewerLib/src/engine/objects/scenes/m2Scene.cpp +++ b/wowViewerLib/src/engine/objects/scenes/m2Scene.cpp @@ -26,24 +26,29 @@ void M2Scene::getCandidatesEntities(std::vector &hullLines, mathfu void M2Scene::updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &cameraVec3, StateForConditions &stateForConditions, const AreaRecord &areaRecord) { Config* config = this->m_api->getConfig(); - if (config->getUseTimedGloabalLight()) { - Map::updateLightAndSkyboxData(cullStage, cameraVec3, stateForConditions, areaRecord); - } else if (config->getUseM2AmbientLight()) { + Map::updateLightAndSkyboxData(cullStage, cameraVec3, stateForConditions, areaRecord); + if (config->globalLighting == EParameterSource::eM2) { auto ambient = m_m2Object->getM2SceneAmbientLight(); if (ambient.Length() < 0.0001) ambient = mathfu::vec4(1.0,1.0,1.0,1.0); - m_api->getConfig()->setExteriorAmbientColor(ambient.x, ambient.y, ambient.z, 1.0); - m_api->getConfig()->setExteriorHorizontAmbientColor(ambient.x, ambient.y, ambient.z, 1.0); - m_api->getConfig()->setExteriorGroundAmbientColor(ambient.x, ambient.y, ambient.z, 1.0); - m_api->getConfig()->setExteriorDirectColor(0.0,0.0,0.0,0.0); - m_api->getConfig()->setExteriorDirectColorDir(0.0,0.0,0.0); + auto frameDepedantData = cullStage->frameDepedantData; + + frameDepedantData->exteriorAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + frameDepedantData->exteriorHorizontAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + frameDepedantData->exteriorGroundAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + frameDepedantData->exteriorDirectColor = mathfu::vec4(0.0,0.0,0.0,0.0); + frameDepedantData->exteriorDirectColorDir = mathfu::vec3(0.0,0.0,0.0); } + auto frameDepedantData = cullStage->frameDepedantData; + frameDepedantData->FogDataFound = false; } extern "C" { - extern void supplyPointer(int *availablePointer, int length); + extern void supplyAnimationList(int *availablePointer, int length); + extern void supplyMeshIds(int *availablePointer, int length); + extern void offerFileAsDownload(std::string filename, std::string mime); } void M2Scene::doPostLoad(HCullStage cullStage) { @@ -76,40 +81,22 @@ void M2Scene::doPostLoad(HCullStage cullStage) { m_api->camera->setCameraPos(1.0,0,0); m_api->camera->setCameraOffset(0,0,0); } +#ifdef __EMSCRIPTEN__ std::vector availableAnimations; m_m2Object->getAvailableAnimation(availableAnimations); -#ifdef __EMSCRIPTEN__ - supplyPointer(&availableAnimations[0], availableAnimations.size()); + + supplyAnimationList(&availableAnimations[0], availableAnimations.size()); + + std::vector meshIds; + m_m2Object->getMeshIds(meshIds); + + supplyMeshIds(&meshIds[0], meshIds.size()); + #endif } Map::doPostLoad(cullStage); } - -//mathfu::vec4 M2Scene::getAmbientColor() { -// if (doOverride) { -// return m_ambientColorOverride; -// } else { -// return m_m2Object->getAmbientLight(); -// } -//} -// -//bool M2Scene::getCameraSettings(M2CameraResult &result) { -// if (m_cameraView > -1 && m_m2Object->getGetIsLoaded()) { -// result = m_m2Object->updateCamera(0, m_cameraView); -// return true; -// } -// return false; -//} -// -//void M2Scene::setAmbientColorOverride(mathfu::vec4 &ambientColor, bool override) { -// doOverride = override; -// m_ambientColorOverride = ambientColor; -// -// m_m2Object->setAmbientColorOverride(ambientColor, override); -//} - - void M2Scene::setReplaceTextureArray(std::vector &replaceTextureArray) { //std::cout << "replaceTextureArray.size == " << replaceTextureArray.size() << std::endl; //std::cout << "m_m2Object == " << m_m2Object << std::endl; @@ -132,6 +119,10 @@ void M2Scene::setReplaceTextureArray(std::vector &replaceTextureArray) { m_m2Object->setReplaceTextures(replaceTextures); } +void M2Scene::setMeshIdArray(std::vector &meshIds) { + m_m2Object->setMeshIds(meshIds); +} + int M2Scene::getCameraNum() { return m_m2Object->getCameraNum(); } @@ -144,7 +135,7 @@ std::shared_ptr M2Scene::createCamera(int cameraNum) { return std::make_shared(m_m2Object, cameraNum); } -M2Scene::M2Scene(ApiContainer *api, std::string m2Model, int cameraView) { +M2Scene::M2Scene(HApiContainer api, std::string m2Model, int cameraView) { m_api = api; m_m2Model = m2Model; m_cameraView = cameraView; m_sceneMode = SceneMode::smM2; m_suppressDrawingSky = true; @@ -159,9 +150,11 @@ M2Scene::M2Scene(ApiContainer *api, std::string m2Model, int cameraView) { m2Object->calcWorldPosition(); m_m2Object = m2Object; + + api->getConfig()->globalFog = EParameterSource::eConfig; } -M2Scene::M2Scene(ApiContainer *api, int fileDataId, int cameraView) { +M2Scene::M2Scene(HApiContainer api, int fileDataId, int cameraView) { m_api = api; m_cameraView = cameraView; m_sceneMode = SceneMode::smM2; m_suppressDrawingSky = true; @@ -175,6 +168,8 @@ M2Scene::M2Scene(ApiContainer *api, int fileDataId, int cameraView) { m2Object->calcWorldPosition(); m_m2Object = m2Object; + + api->getConfig()->globalFog = EParameterSource::eConfig; } void M2Scene::setReplaceParticleColors(std::array, 3> &particleColorReplacement) { @@ -185,48 +180,7 @@ void M2Scene::resetReplaceParticleColor() { m_m2Object->resetReplaceParticleColor(); } +void M2Scene::exportScene(IExporter* exporter) { + exporter->addM2Object(m_m2Object); -/* -void M2Scene::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, std::vector &additionalChunks) { - if (updateStage == nullptr) return; - if (resultDrawStage == nullptr) return; - - auto cullStage = updateStage->cullResult; - - resultDrawStage->meshesToRender = std::make_shared(); - - for (auto m2Object : updateStage->cullResult->m2Array) { - m2Object->collectMeshes(resultDrawStage->meshesToRender->meshes, 0); - m2Object->drawParticles(resultDrawStage->meshesToRender->meshes, 0); - } - - - auto renderMats = resultDrawStage->matricesForRendering; - auto config = m_api->getConfig(); - - resultDrawStage->sceneWideBlockVSPSChunk = m_api->hDevice->createUniformBufferChunk(sizeof(sceneWideBlockVSPS)); - resultDrawStage->sceneWideBlockVSPSChunk->setUpdateHandler([renderMats, config](IUniformBufferChunk *chunk) -> void { - auto *blockPSVS = &chunk->getObject(); - blockPSVS->uLookAtMat = renderMats->lookAtMat; - blockPSVS->uPMatrix = renderMats->perspectiveMat; - blockPSVS->uInteriorSunDir = renderMats->interiorDirectLightDir; - blockPSVS->uViewUp = renderMats->viewUp; - - -// auto ambient = mathfu::vec4(0.3929412066936493f, 0.26823532581329346f, 0.3082353174686432f, 0); - auto ambient = mathfu::vec4(1.0f, 1.0f, 1.0f, 0); - blockPSVS->extLight.uExteriorAmbientColor = ambient; - blockPSVS->extLight.uExteriorHorizontAmbientColor = ambient; - blockPSVS->extLight.uExteriorGroundAmbientColor = ambient; - blockPSVS->extLight.uExteriorDirectColor = mathfu::vec4(0.0,0.0,0.0,1.0); - blockPSVS->extLight.uExteriorDirectColorDir = mathfu::vec4(0.0,0.0,0.0,1.0); - }); - - additionalChunks.push_back(resultDrawStage->sceneWideBlockVSPSChunk); - - std::sort(resultDrawStage->meshesToRender->meshes.begin(), - resultDrawStage->meshesToRender->meshes.end(), - IDevice::sortMeshes - ); } -*/ \ No newline at end of file diff --git a/wowViewerLib/src/engine/objects/scenes/m2Scene.h b/wowViewerLib/src/engine/objects/scenes/m2Scene.h index 8e501b08e..b00b9c321 100644 --- a/wowViewerLib/src/engine/objects/scenes/m2Scene.h +++ b/wowViewerLib/src/engine/objects/scenes/m2Scene.h @@ -28,8 +28,8 @@ class M2Scene : public Map { StateForConditions &stateForConditions, const AreaRecord &areaRecord) override; public: - explicit M2Scene(ApiContainer *api, std::string m2Model, int cameraView = - 1); - explicit M2Scene(ApiContainer *api, int fileDataId, int cameraView = - 1); + explicit M2Scene(HApiContainer api, std::string m2Model, int cameraView = - 1); + explicit M2Scene(HApiContainer api, int fileDataId, int cameraView = - 1); ~M2Scene() override { @@ -39,17 +39,23 @@ class M2Scene : public Map { void setAnimationId(int animationId) override { m_m2Object->setAnimationId(animationId); }; + void setMeshIds(std::vector &meshIds) override { + m_m2Object->setMeshIds(meshIds); + }; void resetAnimation() override { m_m2Object->resetCurrentAnimation(); } void setReplaceTextureArray(std::vector &replaceTextureArray) override; + void setMeshIdArray(std::vector &meshIds) override ; void setReplaceParticleColors(std::array, 3> &particleColorReplacement) override; void resetReplaceParticleColor() override; void doPostLoad(HCullStage cullStage) override; int getCameraNum() override ; std::shared_ptr createCamera(int cameraNum) override; + + void exportScene(IExporter* exporter) override; }; diff --git a/wowViewerLib/src/engine/objects/scenes/map.cpp b/wowViewerLib/src/engine/objects/scenes/map.cpp index a5a150bed..ef08baea1 100644 --- a/wowViewerLib/src/engine/objects/scenes/map.cpp +++ b/wowViewerLib/src/engine/objects/scenes/map.cpp @@ -5,16 +5,18 @@ #include #include #include +#include #include "map.h" #include "../../algorithms/mathHelper.h" #include "../../algorithms/grahamScan.h" #include "../../persistance/wdtFile.h" #include "../../../gapi/interface/meshes/IM2Mesh.h" #include "../../../gapi/interface/IDevice.h" -#include "../wowFrameData.h" #include "../../algorithms/quick-sort-omp.h" #include "../../../gapi/UniformBufferStructures.h" #include "../../shader/ShaderDefinitions.h" +#include "tbb/tbb.h" +#include "../../algorithms/FrameCounter.h" //#include "../../algorithms/quicksort-dualpivot.h" @@ -256,7 +258,7 @@ std::array skyConusIBO = { 121 , 97 , 121 , }; -HGMesh createSkyMesh(IDevice *device, Config *config) { +HGVertexBufferBindings createSkyBindings(IDevice *device) { auto skyIBO = device->createIndexBuffer(); skyIBO->uploadData( skyConusIBO.data(), @@ -279,15 +281,31 @@ HGMesh createSkyMesh(IDevice *device, Config *config) { skyBindings->addVertexBufferBinding(vertexBinding); skyBindings->save(); + return skyBindings; +} + +HGMesh createSkyMesh(IDevice *device, HGVertexBufferBindings skyBindings, Config *config, bool conusFor0x4Sky) { auto skyVs = device->createUniformBufferChunk(sizeof(DnSky::meshWideBlockVS)); - skyVs->setUpdateHandler([config](IUniformBufferChunk *self) -> void { + skyVs->setUpdateHandler([config, conusFor0x4Sky](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) -> void { auto &meshblockVS = self->getObject(); - meshblockVS.skyColor[0] = config->getSkyTopColor(); - meshblockVS.skyColor[1] = config->getSkyMiddleColor(); - meshblockVS.skyColor[2] = config->getSkyBand1Color(); - meshblockVS.skyColor[3] = config->getSkyBand2Color(); - meshblockVS.skyColor[4] = config->getSkySmogColor(); - meshblockVS.skyColor[5] = config->getSkyFogColor(); + + if (!conusFor0x4Sky) { + meshblockVS.skyColor[0] = frameDepedantData->SkyTopColor; + meshblockVS.skyColor[1] = frameDepedantData->SkyMiddleColor; + meshblockVS.skyColor[2] = frameDepedantData->SkyBand1Color; + meshblockVS.skyColor[3] = frameDepedantData->SkyBand2Color; + meshblockVS.skyColor[4] = frameDepedantData->SkySmogColor; + meshblockVS.skyColor[5] = frameDepedantData->SkyFogColor; + } else { + auto EndFogColorV4_1 = mathfu::vec4(frameDepedantData->EndFogColor, 0.0); + auto EndFogColorV4_2 = mathfu::vec4(frameDepedantData->EndFogColor, 1.0); + meshblockVS.skyColor[0] = EndFogColorV4_1; + meshblockVS.skyColor[1] = EndFogColorV4_1; + meshblockVS.skyColor[2] = EndFogColorV4_1; + meshblockVS.skyColor[3] = EndFogColorV4_1; + meshblockVS.skyColor[4] = EndFogColorV4_1; + meshblockVS.skyColor[5] = EndFogColorV4_2; + } }); //TODO: Pass m_skyConeAlpha to fragment shader @@ -300,7 +318,7 @@ HGMesh createSkyMesh(IDevice *device, Config *config) { meshTemplate.depthCulling = true; meshTemplate.backFaceCulling = false; meshTemplate.skybox = true; - meshTemplate.blendMode = EGxBlendEnum::GxBlend_Opaque; + meshTemplate.blendMode = conusFor0x4Sky ? EGxBlendEnum::GxBlend_Alpha : EGxBlendEnum::GxBlend_Opaque; meshTemplate.texture.resize(0); @@ -314,8 +332,13 @@ HGMesh createSkyMesh(IDevice *device, Config *config) { meshTemplate.ubo[4] = nullptr; meshTemplate.element = DrawElementMode::TRIANGLE_STRIP; - meshTemplate.start = 0; - meshTemplate.end = 300; + if (conusFor0x4Sky) { + meshTemplate.start = 198 * 2; + meshTemplate.end = 102; + } else { + meshTemplate.start = 0; + meshTemplate.end = 300; + } //Make mesh HGMesh hmesh = device->createMesh(meshTemplate); @@ -411,10 +434,15 @@ void Map::checkCulling(HCullStage cullStage) { areaRecord = m_api->databaseHandler->getArea(cullStage->adtAreadId); } } - m_api->getConfig()->setAreaName(areaRecord.areaName); + + + m_api->getConfig()->areaName = areaRecord.areaName; stateForConditions.currentAreaId = areaRecord.areaId; stateForConditions.currentParentAreaId = areaRecord.parentAreaId; + cullStage->areaId = areaRecord.areaId; + cullStage->parentAreaId = areaRecord.parentAreaId; + updateLightAndSkyboxData(cullStage, cameraVec3, stateForConditions, areaRecord); ///----------------------------------- @@ -458,12 +486,14 @@ void Map::checkCulling(HCullStage cullStage) { frustumPoints); } - for (auto model : m_exteriorSkyBoxes) { - if (model != nullptr) { - model->checkFrustumCulling(cameraPos, - frustumPlanes, - frustumPoints); - cullStage->exteriorView.drawnM2s.push_back(model); + if (config->renderSkyDom) { + for (auto model : m_exteriorSkyBoxes) { + if (model != nullptr) { + model->checkFrustumCulling(cameraPos, + frustumPlanes, + frustumPoints); + cullStage->exteriorView.drawnM2s.insert(model); + } } } } @@ -474,7 +504,7 @@ void Map::checkCulling(HCullStage cullStage) { } cullStage->exteriorView.addM2FromGroups(frustumMat, lookAtMat4, cameraPos); for (auto &adtRes : cullStage->exteriorView.drawnADTs) { - adtRes->adtObject->collectMeshes(*adtRes.get(), cullStage->exteriorView.drawnChunks, cullStage->exteriorView.renderOrder); + adtRes->adtObject->collectMeshes(*adtRes, cullStage->exteriorView.m_opaqueMeshes, cullStage->exteriorView.m_transparentMeshes, cullStage->exteriorView.renderOrder); } //Collect M2s for update @@ -486,13 +516,20 @@ void Map::checkCulling(HCullStage cullStage) { std::copy(cullStage->exteriorView.drawnM2s.begin(), cullStage->exteriorView.drawnM2s.end(), inserter); //Sort and delete duplicates - std::sort( cullStage->m2Array.begin(), cullStage->m2Array.end() ); - cullStage->m2Array.erase( unique( cullStage->m2Array.begin(), cullStage->m2Array.end() ), cullStage->m2Array.end() ); - cullStage->m2Array = std::vector>(cullStage->m2Array.begin(), cullStage->m2Array.end()); + if (cullStage->m2Array.size() > 2) { +// internal::parallel_sort(cullStage->m2Array.begin(), cullStage->m2Array.end(), +// [](auto &first, auto &end) { return first < end; }); +#if (_LIBCPP_HAS_PARALLEL_ALGORITHMS) + std::sort(std::execution::par_unseq, cullStage->m2Array.begin(), cullStage->m2Array.end() ); +#else + std::sort(cullStage->m2Array.begin(), cullStage->m2Array.end() ); +#endif + cullStage->m2Array.erase(unique(cullStage->m2Array.begin(), cullStage->m2Array.end()), + cullStage->m2Array.end()); + } std::sort( cullStage->wmoArray.begin(), cullStage->wmoArray.end() ); cullStage->wmoArray.erase( unique( cullStage->wmoArray.begin(), cullStage->wmoArray.end() ), cullStage->wmoArray.end() ); - cullStage->wmoArray = std::vector>(cullStage->wmoArray.begin(), cullStage->wmoArray.end()); cullStage->adtArray = std::vector>(cullStage->adtArray.begin(), cullStage->adtArray.end()); @@ -507,75 +544,26 @@ void Map::checkCulling(HCullStage cullStage) { // } } +mathfu::vec3 blendV3(mathfu::vec3 a, mathfu::vec3 b, float alpha) { + return (a - b) * alpha + a; +} + void Map::updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &cameraVec3, StateForConditions &stateForConditions, const AreaRecord &areaRecord) {///----------------------------------- Config* config = this->m_api->getConfig(); - if (!config->getUseTimedGloabalLight()) return; - bool fogRecordWasFound = false; mathfu::vec3 endFogColor = mathfu::vec3(0.0, 0.0, 0.0); + std::vector wmoFogs = {}; if (cullStage->m_currentWMO != nullptr) { - CImVector sunFogColor; - fogRecordWasFound = cullStage->m_currentWMO->checkFog(cameraVec3, sunFogColor); - if (fogRecordWasFound) { - endFogColor = - mathfu::vec3((sunFogColor.r & 0xFF) / 255.0f, - ((sunFogColor.g) & 0xFF) / 255.0f, - ((sunFogColor.b) & 0xFF) / 255.0f); - } + cullStage->m_currentWMO->checkFog(cameraVec3, wmoFogs); } + std::vector lightResults; if ((m_api->databaseHandler != nullptr)) { //Check zoneLight - LightResult zoneLightResult; - std::vector lightResults; - bool zoneLightFound = false; - int LightId; - for (const auto &zoneLight : m_zoneLights) { - if (MathHelper::isPointInsideNonConvex(cameraVec3, zoneLight.aabb, zoneLight.points)) { - zoneLightFound = true; - LightId = zoneLight.LightID; - break; - } - } - - if (zoneLightFound) { - m_api->databaseHandler->getLightById(LightId, config->getCurrentTime(), zoneLightResult); - } - - m_api->databaseHandler->getEnvInfo(m_mapId, - cameraVec3.x, - cameraVec3.y, - cameraVec3.z, - config->getCurrentTime(), - lightResults - ); - - //Calc final blendcoef for zoneLight; - if (zoneLightFound) { - float blendCoef = 1.0; - - for (auto &_light : lightResults) { - if (!_light.isDefault) { - blendCoef -= _light.blendCoef; - } - } - if (blendCoef > 0) { - zoneLightResult.blendCoef = blendCoef; - lightResults.push_back(zoneLightResult); - //Delete default from results; - auto it = lightResults.begin(); - while (it != lightResults.end()) { - if (it->isDefault) { - lightResults.erase(it); - break; - } else - it++; - } - } - } + getLightResultsFromDB(cameraVec3, config, lightResults, &stateForConditions); //Delete skyboxes that are not in light array std::unordered_map> perFdidMap; @@ -617,22 +605,31 @@ void Map::updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &ca } skyBox->setAlpha(_light.blendCoef); + if ((_light.skyBoxFlags & 4) > 0 ) { + //In this case conus is still rendered been, but all values are final fog values. + auto fdd = cullStage->frameDepedantData; + fdd->overrideValuesWithFinalFog = true; + } + if ((_light.skyBoxFlags & 2) == 0) { +// m_skyConeAlpha -= _light.blendCoef; m_skyConeAlpha -= _light.blendCoef; } if (_light.skyBoxFlags & 1) { - skyBox->setOverrideAnimationPerc(config->getCurrentTime() / 2880.0, true); + skyBox->setOverrideAnimationPerc(config->currentTime / 2880.0, true); } } - //Blend glow and ambient mathfu::vec3 ambientColor = {0, 0, 0}; mathfu::vec3 horizontAmbientColor = {0, 0, 0}; mathfu::vec3 groundAmbientColor = {0, 0, 0}; mathfu::vec3 directColor = {0, 0, 0}; mathfu::vec3 closeRiverColor = {0, 0, 0}; + mathfu::vec3 farRiverColor = {0, 0, 0}; + mathfu::vec3 closeOceanColor = {0, 0, 0}; + mathfu::vec3 farOceanColor = {0, 0, 0}; mathfu::vec3 SkyTopColor = {0, 0, 0}; mathfu::vec3 SkyMiddleColor = {0, 0, 0}; @@ -640,7 +637,104 @@ void Map::updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &ca mathfu::vec3 SkyBand2Color = {0, 0, 0}; mathfu::vec3 SkySmogColor = {0, 0, 0}; mathfu::vec3 SkyFogColor = {0, 0, 0}; + float currentGlow = 0; + + for (auto &_light : lightResults) { + currentGlow += _light.glow * _light.blendCoef; + ambientColor += mathfu::vec3(_light.ambientColor) * _light.blendCoef; + horizontAmbientColor += mathfu::vec3(_light.horizontAmbientColor) * _light.blendCoef; + groundAmbientColor += mathfu::vec3(_light.groundAmbientColor) * _light.blendCoef; + directColor += mathfu::vec3(_light.directColor) * _light.blendCoef; + + closeRiverColor += mathfu::vec3(_light.closeRiverColor) * _light.blendCoef; + farRiverColor += mathfu::vec3(_light.farRiverColor) * _light.blendCoef; + closeOceanColor += mathfu::vec3(_light.closeOceanColor) * _light.blendCoef; + farOceanColor += mathfu::vec3(_light.farOceanColor) * _light.blendCoef; + + SkyTopColor += mathfu::vec3(_light.SkyTopColor.data()) * _light.blendCoef; + SkyMiddleColor += mathfu::vec3(_light.SkyMiddleColor) * _light.blendCoef; + SkyBand1Color += mathfu::vec3(_light.SkyBand1Color) * _light.blendCoef; + SkyBand2Color += mathfu::vec3(_light.SkyBand2Color) * _light.blendCoef; + SkySmogColor += mathfu::vec3(_light.SkySmogColor) * _light.blendCoef; + SkyFogColor += mathfu::vec3(_light.SkyFogColor.data()) * _light.blendCoef; + } + + //Database is in BGRA + float ambientMult = areaRecord.ambientMultiplier * 2.0f + 1; +// ambientColor *= ambientMult; +// groundAmbientColor *= ambientMult; +// horizontAmbientColor *= ambientMult; + + if (config->glowSource == EParameterSource::eDatabase) { + auto fdd = cullStage->frameDepedantData; + fdd->currentGlow = currentGlow; + } else if (config->glowSource == EParameterSource::eConfig) { + auto fdd = cullStage->frameDepedantData; + fdd->currentGlow = config->currentGlow; + } + + + if (config->globalLighting == EParameterSource::eDatabase) { + auto fdd = cullStage->frameDepedantData; + + fdd->exteriorAmbientColor = mathfu::vec4(ambientColor[2], ambientColor[1], ambientColor[0], 0); + fdd->exteriorGroundAmbientColor = mathfu::vec4(groundAmbientColor[2], groundAmbientColor[1], groundAmbientColor[0], + 0); + fdd->exteriorHorizontAmbientColor = mathfu::vec4(horizontAmbientColor[2], horizontAmbientColor[1], + horizontAmbientColor[0], 0); + fdd->exteriorDirectColor = mathfu::vec4(directColor[2], directColor[1], directColor[0], 0); + auto extDir = MathHelper::calcExteriorColorDir( + cullStage->matricesForCulling->lookAtMat, + m_api->getConfig()->currentTime + ); + fdd->exteriorDirectColorDir = { extDir.x, extDir.y, extDir.z }; + } else if (config->globalLighting == EParameterSource::eConfig) { + auto fdd = cullStage->frameDepedantData; + + fdd->exteriorAmbientColor = config->exteriorAmbientColor; + fdd->exteriorGroundAmbientColor = config->exteriorGroundAmbientColor; + fdd->exteriorHorizontAmbientColor = config->exteriorHorizontAmbientColor; + fdd->exteriorDirectColor = config->exteriorDirectColor; + auto extDir = MathHelper::calcExteriorColorDir( + cullStage->matricesForCulling->lookAtMat, + m_api->getConfig()->currentTime + ); + fdd->exteriorDirectColorDir = { extDir.x, extDir.y, extDir.z }; + } + + { + auto fdd = cullStage->frameDepedantData; + fdd->useMinimapWaterColor = config->useMinimapWaterColor; + fdd->useCloseRiverColorForDB = config->useCloseRiverColorForDB; + } + if (config->waterColorParams == EParameterSource::eDatabase) + { + auto fdd = cullStage->frameDepedantData; + fdd->closeRiverColor = mathfu::vec4(closeRiverColor[2], closeRiverColor[1], closeRiverColor[0], 0); + fdd->farRiverColor = mathfu::vec4(farRiverColor[2], farRiverColor[1], farRiverColor[0], 0); + fdd->closeOceanColor = mathfu::vec4(closeOceanColor[2], closeOceanColor[1], closeOceanColor[0], 0); + fdd->farOceanColor = mathfu::vec4(farOceanColor[2], farOceanColor[1], farOceanColor[0], 0); + } else if (config->waterColorParams == EParameterSource::eConfig) { + auto fdd = cullStage->frameDepedantData; + fdd->closeRiverColor = config->closeRiverColor; + fdd->farRiverColor = config->farRiverColor; + fdd->closeOceanColor = config->closeOceanColor; + fdd->farOceanColor = config->farOceanColor; + } + if (config->skyParams == EParameterSource::eDatabase) { + auto fdd = cullStage->frameDepedantData; + fdd->SkyTopColor = mathfu::vec4(SkyTopColor[2], SkyTopColor[1], SkyTopColor[0], 1.0); + fdd->SkyMiddleColor = mathfu::vec4(SkyMiddleColor[2], SkyMiddleColor[1], SkyMiddleColor[0], 1.0); + fdd->SkyBand1Color = mathfu::vec4(SkyBand1Color[2], SkyBand1Color[1], SkyBand1Color[0], 1.0); + fdd->SkyBand2Color = mathfu::vec4(SkyBand2Color[2], SkyBand2Color[1], SkyBand2Color[0], 1.0); + fdd->SkySmogColor = mathfu::vec4(SkySmogColor[2], SkySmogColor[1], SkySmogColor[0], 1.0); + fdd->SkyFogColor = mathfu::vec4(SkyFogColor[2], SkyFogColor[1], SkyFogColor[0], 1.0); + } + } + + //Handle fog + { float FogEnd = 0; float FogScaler = 0; float FogDensity = 0; @@ -648,6 +742,7 @@ void Map::updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &ca float FogHeightScaler = 0; float FogHeightDensity = 0; float SunFogAngle = 0; + mathfu::vec3 EndFogColor = {0, 0, 0}; float EndFogColorDistance = 0; mathfu::vec3 SunFogColor = {0, 0, 0}; @@ -655,77 +750,214 @@ void Map::updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &ca mathfu::vec3 FogHeightColor = {0, 0, 0}; mathfu::vec4 FogHeightCoefficients = {0, 0, 0, 0}; - config->currentGlow = 0; - for (auto &_light : lightResults) { - config->currentGlow += _light.glow * _light.blendCoef; - ambientColor += mathfu::vec3(_light.ambientColor) * _light.blendCoef; - horizontAmbientColor += mathfu::vec3(_light.horizontAmbientColor) * _light.blendCoef; - groundAmbientColor += mathfu::vec3(_light.groundAmbientColor) * _light.blendCoef; + std::vector combinedResults = {}; + float totalSummator = 0.0; - directColor += mathfu::vec3(_light.directColor) * _light.blendCoef; - closeRiverColor += mathfu::vec3(_light.closeRiverColor) * _light.blendCoef; + //Apply fog from WMO + { + bool fogDefaultExist = false; + int fogDefaultIndex = -1; + for (int i = 0; i < wmoFogs.size() && totalSummator < 1.0f; i++) { + auto &fogRec = wmoFogs[i]; + if (fogRec.isDefault) { + fogDefaultExist = true; + fogDefaultIndex = i; + continue; + } + if (totalSummator + fogRec.blendCoef > 1.0f) { + fogRec.blendCoef = 1.0f - totalSummator; + totalSummator = 1.0f; + } else { + totalSummator += fogRec.blendCoef; + } + combinedResults.push_back(fogRec); + } - SkyTopColor += mathfu::vec3(_light.SkyTopColor) * _light.blendCoef; - SkyMiddleColor += mathfu::vec3(_light.SkyMiddleColor) * _light.blendCoef; - SkyBand1Color += mathfu::vec3(_light.SkyBand1Color) * _light.blendCoef; - SkyBand2Color += mathfu::vec3(_light.SkyBand2Color) * _light.blendCoef; - SkySmogColor += mathfu::vec3(_light.SkySmogColor) * _light.blendCoef; - SkyFogColor += mathfu::vec3(_light.SkyFogColor) * _light.blendCoef; + if (fogDefaultExist && totalSummator < 1.0f) { + wmoFogs[fogDefaultIndex].blendCoef = 1.0f - totalSummator; + totalSummator = 1.0f; + combinedResults.push_back(wmoFogs[fogDefaultIndex]); + } + } + + //Apply fogs from lights + if (totalSummator < 1.0) { + if (config->globalFog == EParameterSource::eDatabase) { + bool fogDefaultExist = false; + int fogDefaultIndex = -1; + + for (int i = 0; i < lightResults.size() && totalSummator < 1.0f; i++) { + auto &fogRec = lightResults[i]; + if (fogRec.isDefault) { + fogDefaultExist = true; + fogDefaultIndex = i; + continue; + } + if (totalSummator + fogRec.blendCoef > 1.0f) { + fogRec.blendCoef = 1.0f - totalSummator; + totalSummator = 1.0f; + } else { + totalSummator += fogRec.blendCoef; + } + combinedResults.push_back(fogRec); + } + if (fogDefaultExist && totalSummator < 1.0f) { + lightResults[fogDefaultIndex].blendCoef = 1.0f - totalSummator; + totalSummator = 1.0f; + combinedResults.push_back(lightResults[fogDefaultIndex]); + } + } else if (config->globalFog == EParameterSource::eConfig) { + LightResult globalFog; + globalFog.FogScaler = config->FogScaler; + globalFog.FogEnd = config->FogEnd; + globalFog.FogDensity = config->FogDensity; + + globalFog.FogHeightScaler = config->FogHeightScaler; + globalFog.FogHeightDensity = config->FogHeightDensity; + globalFog.SunFogAngle = config->SunFogAngle; + globalFog.EndFogColorDistance = config->EndFogColorDistance; + globalFog.SunFogStrength = config->SunFogStrength; + + globalFog.blendCoef = 1.0 - totalSummator; + globalFog.isDefault = true; + + globalFog.EndFogColor = {config->EndFogColor.z, config->EndFogColor.y, config->EndFogColor.x}; + globalFog.SunFogColor = {config->SunFogColor.z, config->SunFogColor.y, config->SunFogColor.x}; + globalFog.FogHeightColor = {config->FogHeightColor.z, config->FogHeightColor.y, config->FogHeightColor.x}; + + combinedResults.push_back(globalFog); + } + } + std::sort(combinedResults.begin(), combinedResults.end(), [](const LightResult &a, const LightResult &b) -> bool { + return a.blendCoef > b.blendCoef; + }); + + //Rebalance blendCoefs + if (totalSummator < 1.0f && totalSummator > 0.0f) { + for (auto &_light : combinedResults) { + _light.blendCoef = _light.blendCoef / totalSummator; + } + } + for (auto &_light : combinedResults) { FogEnd += _light.FogEnd * _light.blendCoef; FogScaler += _light.FogScaler * _light.blendCoef; FogDensity += _light.FogDensity * _light.blendCoef; FogHeight += _light.FogHeight * _light.blendCoef; FogHeightScaler += _light.FogHeightScaler * _light.blendCoef; - FogHeightDensity += _light.FogHeightScaler * _light.blendCoef; + FogHeightDensity += _light.FogHeightDensity * _light.blendCoef; SunFogAngle += _light.SunFogAngle * _light.blendCoef; - EndFogColor += mathfu::vec3(_light.EndFogColor) * _light.blendCoef; + + EndFogColor += mathfu::vec3(_light.EndFogColor.data()) * _light.blendCoef; EndFogColorDistance += _light.EndFogColorDistance * _light.blendCoef; - SunFogColor += mathfu::vec3(_light.SunFogColor) * _light.blendCoef; + SunFogColor += mathfu::vec3(_light.SunFogColor.data()) * _light.blendCoef; SunFogStrength += _light.SunFogStrength * _light.blendCoef; - FogHeightColor += mathfu::vec3(_light.FogHeightColor) * _light.blendCoef; + FogHeightColor += mathfu::vec3(_light.FogHeightColor.data()) * _light.blendCoef; FogHeightCoefficients += mathfu::vec4(_light.FogHeightCoefficients) * _light.blendCoef; } - //Database is in BGRA - float ambientMult = areaRecord.ambientMultiplier * 2.0f + 1; -// ambientColor *= ambientMult; -// groundAmbientColor *= ambientMult; -// horizontAmbientColor *= ambientMult; - - config->setExteriorAmbientColor(ambientColor[2], ambientColor[1], ambientColor[0], 0); - config->setExteriorGroundAmbientColor(groundAmbientColor[2], groundAmbientColor[1], groundAmbientColor[0], 0); - config->setExteriorHorizontAmbientColor(horizontAmbientColor[2], horizontAmbientColor[1], - horizontAmbientColor[0], 0); - config->setExteriorDirectColor(directColor[2], directColor[1], directColor[0], 0); - config->setCloseRiverColor(closeRiverColor[2], closeRiverColor[1], closeRiverColor[0], 0); - - - config->setSkyTopColor(SkyTopColor[2], SkyTopColor[1], SkyTopColor[0]); - config->setSkyMiddleColor(SkyMiddleColor[2], SkyMiddleColor[1], SkyMiddleColor[0]); - config->setSkyBand1Color(SkyBand1Color[2], SkyBand1Color[1], SkyBand1Color[0]); - config->setSkyBand2Color(SkyBand2Color[2], SkyBand2Color[1], SkyBand2Color[0]); - config->setSkySmogColor(SkySmogColor[2], SkySmogColor[1], SkySmogColor[0]); - config->setSkyFogColor(SkyFogColor[2], SkyFogColor[1], SkyFogColor[0]); - - config->setFogEnd(FogEnd); - config->setFogScaler(FogScaler); - config->setFogDensity(FogDensity); - config->setFogHeight(FogHeight); - config->setFogHeightScaler(FogHeightScaler); - config->setFogHeightDensity(FogHeightDensity); - config->setSunFogAngle(SunFogAngle); - config->setEndFogColor(EndFogColor[2], EndFogColor[1], EndFogColor[0]); - config->setEndFogColorDistance(EndFogColorDistance); - config->setSunFogColor(SunFogColor[2], SunFogColor[1], SunFogColor[0]); - config->setSunFogStrength(SunFogStrength); - config->setFogHeightColor(FogHeightColor[2], FogHeightColor[1], FogHeightColor[0]); - config->setFogHeightCoefficients(FogHeightCoefficients[0],FogHeightCoefficients[1],FogHeightCoefficients[2],FogHeightCoefficients[3]); + //In case of no data -> disable the fog + { + auto fdd = cullStage->frameDepedantData; + fdd->FogDataFound = !combinedResults.empty(); +// std::cout << "combinedResults.empty() = " << combinedResults.empty() << std::endl; +// std::cout << "combinedResults.size() = " << combinedResults.size() << std::endl; + fdd->FogEnd = FogEnd; + fdd->FogScaler = FogScaler; + fdd->FogDensity = FogDensity; + fdd->FogHeight = FogHeight; + fdd->FogHeightScaler = FogHeightScaler; + fdd->FogHeightDensity = FogHeightDensity; + fdd->SunFogAngle = SunFogAngle; + if (fdd->overrideValuesWithFinalFog) { + fdd->FogColor = mathfu::vec3(EndFogColor[2], EndFogColor[1], EndFogColor[0]); + } else { + fdd->FogColor = fdd->SkyFogColor.xyz(); + } + fdd->EndFogColor = mathfu::vec3(EndFogColor[2], EndFogColor[1], EndFogColor[0]); + fdd->EndFogColorDistance = EndFogColorDistance; + fdd->SunFogColor = mathfu::vec3(SunFogColor[2], SunFogColor[1], SunFogColor[0]); + fdd->SunFogStrength = SunFogStrength; + fdd->FogHeightColor = mathfu::vec3(FogHeightColor[2], FogHeightColor[1], FogHeightColor[0]); + fdd->FogHeightCoefficients = mathfu::vec4(FogHeightCoefficients[0], FogHeightCoefficients[1], + FogHeightCoefficients[2], FogHeightCoefficients[3]); + } } // this->m_api->getConfig()->setClearColor(0,0,0,0); } +void Map::getLightResultsFromDB(mathfu::vec3 &cameraVec3, const Config *config, std::vector &lightResults, StateForConditions *stateForConditions) { + if (m_api->databaseHandler == nullptr) + return ; + + LightResult zoneLightResult; + + bool zoneLightFound = false; + int LightId; + for (const auto &zoneLight : m_zoneLights) { + CAaBox laabb = zoneLight.aabb; + auto const vec50 = mathfu::vec3(50.0f,50.0f,0); + laabb.min = (mathfu::vec3(laabb.min) - vec50); + laabb.max = (mathfu::vec3(laabb.max) + vec50); + if (MathHelper::isPointInsideNonConvex(cameraVec3, zoneLight.aabb, zoneLight.points)) { + zoneLightFound = true; + + if (stateForConditions != nullptr) { + stateForConditions->currentZoneLights.push_back(zoneLight.ID); + } + LightId = zoneLight.LightID; + break; + } + } + + if (zoneLightFound) { + m_api->databaseHandler->getLightById(LightId, config->currentTime, zoneLightResult); + if (stateForConditions != nullptr) { + stateForConditions->currentZoneLights.push_back(zoneLightResult.lightParamId); + } + } + + m_api->databaseHandler->getEnvInfo(m_mapId, + cameraVec3.x, + cameraVec3.y, + cameraVec3.z, + config->currentTime, + lightResults + ); + + + if (stateForConditions != nullptr) { + for (auto &_light : lightResults) { + stateForConditions->currentZoneLights.push_back(_light.lightParamId); + } + } + + //Calc final blendcoef for zoneLight; + if (zoneLightFound) { + float blendCoef = 1.0; + + for (auto &_light : lightResults) { + if (!_light.isDefault) { + blendCoef -= _light.blendCoef; + } + } + if (blendCoef > 0) { + zoneLightResult.blendCoef = blendCoef; + lightResults.push_back(zoneLightResult); + //Delete default from results; + auto it = lightResults.begin(); + while (it != lightResults.end()) { + if (it->isDefault) { + lightResults.erase(it); + break; + } else + it++; + } + } + } +} + void Map::getPotentialEntities(const mathfu::vec4 &cameraPos, std::vector> &potentialM2, HCullStage &cullStage, mathfu::mat4 &lookAtMat4, mathfu::vec4 &camera4, std::vector &frustumPlanes, std::vector &frustumPoints, @@ -734,8 +966,12 @@ void Map::getPotentialEntities(const mathfu::vec4 &cameraPos, std::vectormphd->flags.wdt_uses_global_map_obj) { int adt_x = worldCoordinateToAdtIndex(camera4.y); int adt_y = worldCoordinateToAdtIndex(camera4.x); + if ((adt_x >= 64) || (adt_x < 0)) return; + if ((adt_y >= 64) || (adt_y < 0)) return; + cullStage->adtAreadId = -1; - std::shared_ptr adtObjectCameraAt = mapTiles[adt_x][adt_y]; + auto &adtObjectCameraAt = mapTiles[adt_x][adt_y]; + if (adtObjectCameraAt != nullptr) { ADTObjRenderRes tempRes; tempRes.adtObject = adtObjectCameraAt; @@ -770,7 +1006,32 @@ void Map::getPotentialEntities(const mathfu::vec4 &cameraPos, std::vectorgetStatus() == FileStatus::FSLoaded) { + if (!m_wdtfile->mphd->flags.wdt_uses_global_map_obj) { + int adt_x = worldCoordinateToAdtIndex(cameraPos.y); + int adt_y = worldCoordinateToAdtIndex(cameraPos.x); + if ((adt_x >= 64) || (adt_x < 0)) return; + if ((adt_y >= 64) || (adt_y < 0)) return; + auto &adtObjectCameraAt = mapTiles[adt_x][adt_y]; + + int adt_global_x = worldCoordinateToGlobalAdtChunk(cameraPos.y) % 16; + int adt_global_y = worldCoordinateToGlobalAdtChunk(cameraPos.x) % 16; + + areaId = adtObjectCameraAt->getAreaId(adt_global_x, adt_global_y); + + if (areaId > 0 && (m_api->databaseHandler != nullptr)) { + auto areaRecord = m_api->databaseHandler->getArea(areaId); + parentAreaId = areaRecord.parentAreaId; + } + } + } + +} void Map::checkExterior(mathfu::vec4 &cameraPos, std::vector &frustumPoints, std::vector &hullLines, @@ -788,7 +1049,6 @@ void Map::checkExterior(mathfu::vec4 &cameraPos, } } - std::vector> m2ObjectsCandidates; std::vector> wmoCandidates; @@ -830,48 +1090,13 @@ void Map::checkExterior(mathfu::vec4 &cameraPos, } } - //Sort and delete duplicates - if (m2ObjectsCandidates.size() > 0) - { - auto *sortedArrayPtr = &m2ObjectsCandidates[0]; - std::vector indexArray = std::vector(m2ObjectsCandidates.size()); - for (int i = 0; i < indexArray.size(); i++) { - indexArray[i] = i; - } - quickSort_parallel( - indexArray.data(), - indexArray.size(), - m_api->getConfig()->getThreadCount(), - m_api->getConfig()->getQuickSortCutoff(), - [sortedArrayPtr](int indexA, int indexB) { - auto *pA = sortedArrayPtr[indexA].get(); - auto *pB = sortedArrayPtr[indexB].get(); - - return pA < pB; - }); - - if (m2ObjectsCandidates.size() > 1) { - std::shared_ptr prevElem = nullptr; - std::vector> m2Unique; - m2Unique.reserve(indexArray.size()); - for (int i = 0; i < indexArray.size(); i++) { - if (prevElem != m2ObjectsCandidates[indexArray[i]]) { - m2Unique.push_back(m2ObjectsCandidates[indexArray[i]]); - prevElem = m2ObjectsCandidates[indexArray[i]]; - } - } - m2ObjectsCandidates = m2Unique; - } - } - -// std::sort( m2ObjectsCandidates.begin(), m2ObjectsCandidates.end() ); -// m2ObjectsCandidates.erase( unique( m2ObjectsCandidates.begin(), m2ObjectsCandidates.end() ), m2ObjectsCandidates.end() ); + //3.2 Iterate over all global WMOs and M2s (they have uniqueIds) { - int numThreads = m_api->getConfig()->getThreadCount(); + int numThreads = m_api->getConfig()->threadCount; - #pragma omp parallel for num_threads(numThreads) + #pragma omp parallel for schedule(static, 1000) default( none) shared(m2ObjectsCandidates,cameraPos, cullStage, frustumPoints) for (size_t i = 0; i < m2ObjectsCandidates.size(); i++) { auto m2ObjectCandidate = m2ObjectsCandidates[i]; bool frustumResult = m2ObjectCandidate->checkFrustumCulling( @@ -885,7 +1110,7 @@ void Map::checkExterior(mathfu::vec4 &cameraPos, for (size_t i = 0; i < m2ObjectsCandidates.size(); i++) { auto m2ObjectCandidate = m2ObjectsCandidates[i]; if (m2ObjectCandidate->m_cullResult) { - cullStage->exteriorView.drawnM2s.push_back(m2ObjectCandidate); + cullStage->exteriorView.drawnM2s.insert(m2ObjectCandidate); cullStage->m2Array.push_back(m2ObjectCandidate); } } @@ -919,81 +1144,143 @@ void Map::getCandidatesEntities(std::vector &hullLines, mathfu::ma // std::cout << AdtIndexToWorldCoordinate(adt_y_min) <<" "<< AdtIndexToWorldCoordinate(adt_x_min) << std::endl; - - for (int i = 0; i < frustumPoints.size(); i++) { mathfu::vec3 &frustumPoint = frustumPoints[i]; - minx = std::min(frustumPoint.x, minx); - maxx = std::max(frustumPoint.x, maxx); - miny = std::min(frustumPoint.y, miny); - maxy = std::max(frustumPoint.y, maxy); + minx = std::min(frustumPoint.x, minx); + maxx = std::max(frustumPoint.x, maxx); + miny = std::min(frustumPoint.y, miny); + maxy = std::max(frustumPoint.y, maxy); } - int adt_x_min = std::max(worldCoordinateToAdtIndex(maxy), 0); - int adt_x_max = std::min(worldCoordinateToAdtIndex(miny), 63); + int adt_x_min = std::max(worldCoordinateToAdtIndex(maxy), 0); + int adt_x_max = std::min(worldCoordinateToAdtIndex(miny), 63); + + int adt_y_min = std::max(worldCoordinateToAdtIndex(maxx), 0); + int adt_y_max = std::min(worldCoordinateToAdtIndex(minx), 63); - int adt_y_min = std::max(worldCoordinateToAdtIndex(maxx), 0); - int adt_y_max = std::min(worldCoordinateToAdtIndex(minx), 63); - int adt_global_x = worldCoordinateToGlobalAdtChunk(cameraPos.y); - int adt_global_y = worldCoordinateToGlobalAdtChunk(cameraPos.x); if (!m_wdtfile->mphd->flags.wdt_uses_global_map_obj) { for (int i = adt_x_min; i <= adt_x_max; i++) { for (int j = adt_y_min; j <= adt_y_max; j++) { - if ((i < 0) || (i > 64)) continue; - if ((j < 0) || (j > 64)) continue; - - auto adtObject = mapTiles[i][j]; - if (adtObject != nullptr) { - - std::shared_ptr adtFrustRes = std::make_shared(); - adtFrustRes->adtObject = adtObject; - - - bool result = adtObject->checkFrustumCulling( - *adtFrustRes.get(), - cameraPos, - adt_global_x, - adt_global_y, - cullStage->exteriorView.frustumPlanes[0], //TODO:! - frustumPoints, - hullLines, - lookAtMat4, m2ObjectsCandidates, wmoCandidates); - if (result) { - cullStage->exteriorView.drawnADTs.push_back(adtFrustRes); - cullStage->adtArray.push_back(adtFrustRes); - } - } else if (!m_lockedMap && true) { //(m_wdtfile->mapTileTable->mainInfo[j][i].Flag_HasADT > 0) { - if (m_wdtfile->mphd->flags.wdt_has_maid) { - auto &mapFileIds = m_wdtfile->mapFileDataIDs[j * 64 + i]; - if (mapFileIds.rootADT > 0) { - adtObject = std::make_shared(m_api, i, j, - m_wdtfile->mapFileDataIDs[j * 64 + i], - m_wdtfile); - } else { - continue; - } - } else { - std::string adtFileTemplate = - "world/maps/" + mapName + "/" + mapName + "_" + std::to_string(i) + "_" + - std::to_string(j); - adtObject = std::make_shared(m_api, adtFileTemplate, mapName, i, j, m_wdtfile); - } - - - adtObject->setMapApi(this); - mapTiles[i][j] = adtObject; - } + checkADTCulling(i, j, hullLines, lookAtMat4, cameraPos, frustumPoints, cullStage, m2ObjectsCandidates, wmoCandidates); } } + for (auto &mandatoryAdt : m_mandatoryADT) { + checkADTCulling(mandatoryAdt[0], mandatoryAdt[1], hullLines, lookAtMat4, cameraPos, frustumPoints, cullStage, m2ObjectsCandidates, wmoCandidates); + } + } else { wmoCandidates.push_back(wmoMap); } } } +void Map::checkADTCulling(int i, int j, std::vector &hullLines, mathfu::mat4 &lookAtMat4, + mathfu::vec4 &cameraPos, std::vector &frustumPoints, HCullStage &cullStage, + std::vector> &m2ObjectsCandidates, + std::vector> &wmoCandidates) { + if ((i < 0) || (i > 64)) return; + if ((j < 0) || (j > 64)) return; + + int adt_global_x = worldCoordinateToGlobalAdtChunk(cameraPos.y); + int adt_global_y = worldCoordinateToGlobalAdtChunk(cameraPos.x); + + if (this->m_adtBBHolder != nullptr) { + bool bbCheck = MathHelper::checkFrustum( + cullStage->exteriorView.frustumPlanes[0], //TODO:! + (*this->m_adtBBHolder)[i][j], + frustumPoints + ); + + if (!bbCheck) + return; + } + + auto adtObject = mapTiles[i][j]; + if (adtObject != nullptr) { + + std::shared_ptr adtFrustRes = std::make_shared(); + adtFrustRes->adtObject = adtObject; + + + bool result = adtObject->checkFrustumCulling( + *adtFrustRes.get(), + cameraPos, + adt_global_x, + adt_global_y, + cullStage->exteriorView.frustumPlanes[0], //TODO:! + frustumPoints, + hullLines, + lookAtMat4, m2ObjectsCandidates, wmoCandidates); + +// if (this->m_adtBBHolder != nullptr) { +// //When adt passes BBHolder test, consider it influences the picture even if checkFrustumCulling +// //is false. So do forceful update for freeStrategy +// adtObject->getFreeStrategy()(false, true, this->getCurrentSceneTime()); +// } + + if (result) { + cullStage->exteriorView.drawnADTs.push_back(adtFrustRes); + cullStage->adtArray.push_back(adtFrustRes); + } + } else if (!m_lockedMap && true) { //(m_wdtfile->mapTileTable->mainInfo[j][i].Flag_HasADT > 0) { + if (m_wdtfile->mphd->flags.wdt_has_maid) { + auto &mapFileIds = m_wdtfile->mapFileDataIDs[j * 64 + i]; + if (mapFileIds.rootADT > 0) { + adtObject = std::make_shared(m_api, i, j, + m_wdtfile->mapFileDataIDs[j * 64 + i], + m_wdtfile); + } else { + return; + } + } else { + std::string adtFileTemplate = + "world/maps/" + mapName + "/" + mapName + "_" + std::to_string(i) + "_" + + std::to_string(j); + adtObject = std::make_shared(m_api, adtFileTemplate, mapName, i, j, m_wdtfile); + } + + adtObject->setMapApi(this); + adtObject->setFreeStrategy(zeroStateLambda); + + mapTiles[i][j] = adtObject; + } +} + +void Map::createAdtFreeLamdas() { + FreeStrategy lamda; + auto *config = m_api->getConfig(); + if (m_api->getConfig()->adtFreeStrategy == EFreeStrategy::eTimeBased) { + int l_currentTime = 0; + lamda = [l_currentTime, config](bool doCheck, bool doUpdate, animTime_t currentTime) mutable -> bool { + if (doCheck) { + return (currentTime - l_currentTime) > config->adtTTLWithoutUpdate; + } + if (doUpdate) { + l_currentTime = currentTime; + } + return false; + }; + } else if (m_api->getConfig()->adtFreeStrategy == EFreeStrategy::eFrameBase) { + int l_currentFrame = 0; + auto l_hDevice = m_api->hDevice; + lamda = [l_currentFrame, config, l_hDevice](bool doCheck, bool doUpdate, animTime_t currentTime) mutable -> bool { + if (doCheck) { + return (l_hDevice->getFrameNumber() - l_currentFrame) > config->adtFTLWithoutUpdate; + } + if (doUpdate) { + l_currentFrame = l_hDevice->getFrameNumber(); + } + return false; + }; + } + adtFreeLambda = lamda; + zeroStateLambda = lamda; + +} + void Map::doPostLoad(HCullStage cullStage) { int processedThisFrame = 0; int groupsProcessedThisFrame = 0; @@ -1016,11 +1303,13 @@ void Map::doPostLoad(HCullStage cullStage) { if (quadBindings == nullptr) { + const float epsilon = 0.f; + std::array vertexBuffer = { - mathfu::vec2_packed(mathfu::vec2(-1.0f + 0.001, -1.0f+ 0.001)), - mathfu::vec2_packed(mathfu::vec2(-1.0f+ 0.001, 1.0f- 0.001)), - mathfu::vec2_packed(mathfu::vec2(1.0f- 0.001, -1.0f+ 0.001)), - mathfu::vec2_packed(mathfu::vec2(1.0f- 0.001, 1.f- 0.001)) + mathfu::vec2_packed(mathfu::vec2(-1.0f + epsilon, -1.0f + epsilon)), + mathfu::vec2_packed(mathfu::vec2(-1.0f + epsilon, 1.0f - epsilon)), + mathfu::vec2_packed(mathfu::vec2(1.0f - epsilon, -1.0f+ epsilon)), + mathfu::vec2_packed(mathfu::vec2(1.0f - epsilon, 1.f - epsilon)) }; std::vector indexBuffer = { 0, 1, 2, @@ -1052,74 +1341,13 @@ void Map::doPostLoad(HCullStage cullStage) { quadBindings->save(); } if (skyMesh == nullptr) { - skyMesh = createSkyMesh(m_api->hDevice.get(), m_api->getConfig()); + auto skyMeshBinding = createSkyBindings(m_api->hDevice.get()); + skyMesh = createSkyMesh(m_api->hDevice.get(), skyMeshBinding, m_api->getConfig(), false); + skyMesh0x4Sky = createSkyMesh(m_api->hDevice.get(), skyMeshBinding, m_api->getConfig(), true); } }; -mathfu::vec3 calcExteriorColorDir(HCameraMatrices cameraMatrices, int time) { - // Phi Table - static const std::array, 4> phiTable = { - { - { 0.0f, 2.2165682f }, - { 0.25f, 1.9198623f }, - { 0.5f, 2.2165682f }, - { 0.75f, 1.9198623f } - } - }; - - // Theta Table - - - static const std::array, 4> thetaTable = { - { - {0.0f, 3.926991f}, - {0.25f, 3.926991f}, - { 0.5f, 3.926991f }, - { 0.75f, 3.926991f } - } - }; - -// float phi = DayNight::InterpTable(&DayNight::phiTable, 4u, DayNight::g_dnInfo.dayProgression); -// float theta = DayNight::InterpTable(&DayNight::thetaTable, 4u, DayNight::g_dnInfo.dayProgression); - - float phi = phiTable[0][1]; - float theta = thetaTable[0][1]; - //Find Index - float timeF = time / 2880.0f; - int firstIndex = -1; - for (int i = 0; i < 4; i++) { - if (timeF < phiTable[i][0]) { - firstIndex = i; - break; - } - } - if (firstIndex == -1) { - firstIndex = 3; - } - { - float alpha = (phiTable[firstIndex][0] - timeF) / (thetaTable[firstIndex][0] - thetaTable[firstIndex-1][0]); - phi = phiTable[firstIndex][1]*(1.0 - alpha) + phiTable[firstIndex - 1][1]*alpha; - } - - - // Convert from spherical coordinates to XYZ - // x = rho * sin(phi) * cos(theta) - // y = rho * sin(phi) * sin(theta) - // z = rho * cos(phi) - - float sinPhi = (float) sin(phi); - float cosPhi = (float) cos(phi); - - float sinTheta = (float) sin(theta); - float cosTheta = (float) cos(theta); - - mathfu::mat3 lookAtRotation = mathfu::mat4::ToRotationMatrix(cameraMatrices->lookAtMat); - - mathfu::vec4 sunDirWorld = mathfu::vec4(sinPhi * cosTheta, sinPhi * sinTheta, cosPhi, 0); -// mathfu::vec4 sunDirWorld = mathfu::vec4(-0.30822, -0.30822, -0.89999998, 0); - return (lookAtRotation * sunDirWorld.xyz()); -} void Map::update(HUpdateStage updateStage) { mathfu::vec3 cameraVec3 = updateStage->cameraMatrices->cameraPos.xyz(); @@ -1132,74 +1360,93 @@ void Map::update(HUpdateStage updateStage) { // std::for_each(frameData->m2Array.begin(), frameData->m2Array.end(), [deltaTime, &cameraVec3, &lookAtMat](M2Object *m2Object) { // #pragma - int numThreads = m_api->getConfig()->getThreadCount(); + int numThreads = m_api->getConfig()->threadCount; { - #pragma omp parallel for num_threads(numThreads) schedule(dynamic, 4) - for (int i = 0; i < updateStage->cullResult->m2Array.size(); i++) { - auto m2Object = updateStage->cullResult->m2Array[i]; - if (m2Object != nullptr) { - m2Object->update(deltaTime, cameraVec3, lookAtMat); - } - } + + auto &m2Array = updateStage->cullResult->m2Array; + m2UpdateframeCounter.beginMeasurement(); + +// auto n = m2Array.size(); +// #pragma omp parallel for schedule(static, 1000) num_threads(numThreads) default(none) shared(m2Array,n, deltaTime, cameraVec3,lookAtMat) +// for (int i = 0; i < n; i++ ) { +// auto m2Object = m2Array[i]; +// if (m2Object != nullptr) { +// m2Object->update(deltaTime, cameraVec3, lookAtMat); +// } +// } + + + + //std::for_each(std::execution::par_unseq, + // m2Array.begin(), + // m2Array.end(), [&](const std::shared_ptr &m2Object) { + // m2Object->update(deltaTime, cameraVec3, lookAtMat); + //}); + + /* + tbb::parallel_for(size_t(0), m2Array.size(), [&](size_t i) { + auto& m2Object = m2Array[i]; + m2Object->update(deltaTime, cameraVec3, lookAtMat); + }); + */ + + + tbb::parallel_for(tbb::blocked_range(0, m2Array.size(), 200), + [&](tbb::blocked_range r) { + for (size_t i = r.begin(); i != r.end(); ++i) { + auto& m2Object = m2Array[i]; + m2Object->update(deltaTime, cameraVec3, lookAtMat); + } + }, tbb::simple_partitioner()); + + m2UpdateframeCounter.endMeasurement("M2 update"); + + m_api->getConfig()->m2UpdateTime = m2UpdateframeCounter.getTimePerFrame(); } // }); // } - for (auto &wmoObject : updateStage->cullResult->wmoArray) { + for (const auto &wmoObject : updateStage->cullResult->wmoArray) { if (wmoObject == nullptr) continue; wmoObject->update(); } - for (auto &adtObjectRes : updateStage->cullResult->adtArray) { + for (const auto &adtObjectRes : updateStage->cullResult->adtArray) { adtObjectRes->adtObject->update(deltaTime); } //2. Calc distance every 100 ms // #pragma omp parallel for - for (int i = 0; i < updateStage->cullResult->m2Array.size(); i++) { - auto m2Object = updateStage->cullResult->m2Array[i]; + for (const auto& m2Object : updateStage->cullResult->m2Array) { if (m2Object == nullptr) continue; m2Object->calcDistance(cameraVec3); }; - - - - //Calc exteriorDirectColorDir - auto extDir = calcExteriorColorDir( - updateStage->cameraMatrices, - m_api->getConfig()->getCurrentTime() - ); - m_api->getConfig()->setExteriorDirectColorDir( - extDir.x, extDir.y, extDir.z - ); - - //8. Check fog color every 2 seconds - if (this->m_currentTime + updateStage->delta - this->m_lastTimeLightCheck > 30) { - - - this->m_lastTimeLightCheck = this->m_currentTime; - } - //Cleanup ADT every 10 seconds - if (this->m_currentTime + updateStage->delta- this->m_lastTimeAdtCleanup > 10000) { + if (adtFreeLambda!= nullptr && adtFreeLambda(true, false, this->m_currentTime)) { for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { auto adtObj = mapTiles[i][j]; //Free obj, if it was unused for 10 secs - if (adtObj != nullptr && ((this->m_currentTime - adtObj->getLastTimeOfUpdate()) > 10000)) { + if (adtObj != nullptr && adtObj->getFreeStrategy()(true, false, this->m_currentTime)) { +// std::cout << "try to free adtObj" << std::endl; + mapTiles[i][j] = nullptr; } } } - this->m_lastTimeAdtCleanup = this->m_currentTime; + adtFreeLambda(false, true, this->m_currentTime + updateStage->delta); } this->m_currentTime += updateStage->delta; + + //Collect meshes } -void Map::updateBuffers(HCullStage cullStage) { +void Map::updateBuffers(HUpdateStage updateStage) { + auto cullStage = updateStage->cullResult; + for (int i = 0; i < cullStage->m2Array.size(); i++) { auto m2Object = cullStage->m2Array[i]; if (m2Object != nullptr) { @@ -1316,96 +1563,33 @@ std::shared_ptr Map::getWmoObject(int fileDataId, SMMapObjDefObj1 &ma animTime_t Map::getCurrentSceneTime() { return m_currentTime; } +void Map::produceUpdateStage(HUpdateStage updateStage) { + this->update(updateStage); -void Map::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, std::vector &additionalChunks) { - auto cullStage = updateStage->cullResult; - auto renderedThisFramePreSort = std::vector(); + //Create meshes + updateStage->opaqueMeshes = std::make_shared(); + updateStage->transparentMeshes = std::make_shared(); - bool frameBufferSupported = m_api->hDevice->getIsRenderbufferSupported(); + auto &opaqueMeshes = updateStage->opaqueMeshes->meshes; + auto transparentMeshes = std::vector(); - if (quadBindings == nullptr) - return; + auto cullStage = updateStage->cullResult; + auto fdd = cullStage->frameDepedantData; - if ( !m_suppressDrawingSky && (m_skyConeAlpha > 0) && (cullStage->exteriorView.viewCreated || cullStage->currentWmoGroupIsExtLit)) { - renderedThisFramePreSort.push_back(skyMesh); - } + if (m_api->getConfig()->renderSkyDom && !m_suppressDrawingSky && + (cullStage->exteriorView.viewCreated || cullStage->currentWmoGroupIsExtLit)) { + if (fdd->overrideValuesWithFinalFog) { + if (skyMesh0x4Sky != nullptr) { + transparentMeshes.push_back(skyMesh0x4Sky); + skyMesh0x4Sky->setSortDistance(0); - HDrawStage origResultDrawStage = resultDrawStage; - if (frameBufferSupported) { - //Create new drawstage and draw everything there - resultDrawStage = std::make_shared(); - - resultDrawStage->drawStageDependencies = origResultDrawStage->drawStageDependencies; - resultDrawStage->matricesForRendering = origResultDrawStage->matricesForRendering; - resultDrawStage->setViewPort = origResultDrawStage->setViewPort; - resultDrawStage->viewPortDimensions = origResultDrawStage->viewPortDimensions;; - resultDrawStage->clearScreen = origResultDrawStage->clearScreen;; - resultDrawStage->clearColor = origResultDrawStage->clearColor;; - - resultDrawStage->target = m_api->hDevice->createFrameBuffer( - resultDrawStage->viewPortDimensions.maxs[0], - resultDrawStage->viewPortDimensions.maxs[1], - {ITextureFormat::itRGBA}, - ITextureFormat::itDepth32, - 2 - ); + } + } + if ((m_skyConeAlpha > 0) ) { + if (skyMesh != nullptr) + opaqueMeshes.push_back(skyMesh); + } } - //Create scenewide uniform - auto renderMats = resultDrawStage->matricesForRendering; - - auto config = m_api->getConfig(); - resultDrawStage->sceneWideBlockVSPSChunk = m_api->hDevice->createUniformBufferChunk(sizeof(sceneWideBlockVSPS)); - resultDrawStage->sceneWideBlockVSPSChunk->setUpdateHandler([renderMats, config](IUniformBufferChunk *chunk) -> void { - auto *blockPSVS = &chunk->getObject(); - - blockPSVS->uLookAtMat = renderMats->lookAtMat; - blockPSVS->uPMatrix = renderMats->perspectiveMat; - blockPSVS->uInteriorSunDir = renderMats->interiorDirectLightDir; - blockPSVS->uViewUp = renderMats->viewUp; - - blockPSVS->extLight.uExteriorAmbientColor = config->getExteriorAmbientColor(); - blockPSVS->extLight.uExteriorHorizontAmbientColor = config->getExteriorHorizontAmbientColor(); - blockPSVS->extLight.uExteriorGroundAmbientColor = config->getExteriorGroundAmbientColor(); - blockPSVS->extLight.uExteriorDirectColor = config->getExteriorDirectColor(); - blockPSVS->extLight.uExteriorDirectColorDir = mathfu::vec4(config->getExteriorDirectColorDir(), 1.0); - - blockPSVS->fogData.densityParams = mathfu::vec4( - (config->getFarPlane() - 30) * config->getFogScaler(), - (config->getFarPlane() - 30), - config->getFogDensity() / 1000, - 0); - blockPSVS->fogData.heightPlane = mathfu::vec4(0,0,0,0); - blockPSVS->fogData.color_and_heightRate = mathfu::vec4(config->getSkyFogColor().xyz(),config->getFogHeightScaler()); - blockPSVS->fogData.heightDensity_and_endColor = mathfu::vec4( - config->getFogHeightDensity(), - config->getEndFogColor().x, - config->getEndFogColor().y, - config->getEndFogColor().z - ); - blockPSVS->fogData.sunAngle_and_sunColor = mathfu::vec4( - config->getSunFogAngle(), - config->getSunFogColor().x * config->getSunFogStrength(), - config->getSunFogColor().y * config->getSunFogStrength(), - config->getSunFogColor().z * config->getSunFogStrength() - ); - blockPSVS->fogData.heightColor_and_endFogDistance = mathfu::vec4( - config->getFogHeightColor(), - (config->getEndFogColorDistance() > 0) ? - config->getEndFogColorDistance() : - 1000.0f - ); - blockPSVS->fogData.sunPercentage = mathfu::vec4( - (config->getSunFogColor().Length() > 0) ? 0.5f : 0.0f - , 0, 0, 0); - - }); - - - additionalChunks.push_back(resultDrawStage->sceneWideBlockVSPSChunk); - - //Create meshes - resultDrawStage->meshesToRender = std::make_shared(); - // Put everything into one array and sort std::vector vector; @@ -1418,11 +1602,13 @@ void Map::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, vector.push_back(&updateStage->cullResult->exteriorView); } -// if (m_api->getConfig()->getRenderWMO()) { + bool renderPortals = m_api->getConfig()->renderPortals; for (auto &view : vector) { - view->collectMeshes(renderedThisFramePreSort); + view->collectMeshes(opaqueMeshes, transparentMeshes); + if (renderPortals) { + view->produceTransformedPortalMeshes(m_api, opaqueMeshes, transparentMeshes); + } } -// } std::vector> m2ObjectsRendered; for (auto &view : vector) { @@ -1434,73 +1620,198 @@ void Map::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, // s.insert(i); // m2ObjectsRendered.assign( s.begin(), s.end() ); - std::sort( m2ObjectsRendered.begin(), m2ObjectsRendered.end() ); - m2ObjectsRendered.erase( unique( m2ObjectsRendered.begin(), m2ObjectsRendered.end() ), m2ObjectsRendered.end() ); +// std::set> s; +// unsigned size = m2ObjectsRendered.size(); +// for( unsigned i = 0; i < size; ++i ) s.insert( m2ObjectsRendered[i] ); +// m2ObjectsRendered.assign( s.begin(), s.end() ); -// if (m_api->getConfig()->getRenderM2()) { - for (auto &m2Object : m2ObjectsRendered) { - if (m2Object == nullptr) continue; - m2Object->collectMeshes(renderedThisFramePreSort, m_viewRenderOrder); - m2Object->drawParticles(renderedThisFramePreSort, m_viewRenderOrder); + if (m2ObjectsRendered.size() > 2) { + tbb::parallel_sort(m2ObjectsRendered.begin(), m2ObjectsRendered.end(), + [](auto &first, auto &end) { return first < end; } + ); + m2ObjectsRendered.erase(unique(m2ObjectsRendered.begin(), m2ObjectsRendered.end()), m2ObjectsRendered.end()); } -// } - //No need to sort array which has only one element - if (renderedThisFramePreSort.size() > 1) { - auto *sortedArrayPtr = &renderedThisFramePreSort[0]; - std::vector indexArray = std::vector(renderedThisFramePreSort.size()); - for (int i = 0; i < indexArray.size(); i++) { - indexArray[i] = i; + if (m_api->getConfig()->renderM2) { + for (auto &m2Object : m2ObjectsRendered) { + if (m2Object == nullptr) continue; + m2Object->collectMeshes(opaqueMeshes, transparentMeshes, m_viewRenderOrder); + m2Object->drawParticles(opaqueMeshes, transparentMeshes, m_viewRenderOrder); } + } - auto *config = m_api->getConfig(); -// if (config->getMovementSpeed() > 2) { - quickSort_parallel( - indexArray.data(), - indexArray.size(), - config->getThreadCount(), - config->getQuickSortCutoff(), -#include "../../../gapi/interface/sortLambda.h" + + //No need to sort array which has only one element + if (transparentMeshes.size() > 1) { + tbb::parallel_sort(transparentMeshes.begin(), transparentMeshes.end(), + #include "../../../gapi/interface/sortLambda.h" ); -// } else { -// quickSort_dualPoint( -// indexArray.data(), -// indexArray.size(), -// config->getThreadCount(), -// config->getQuickSortCutoff(), -// #include "../../../gapi/interface/sortLambda.h" -// ); -// } +// std::sort(std::execution::par_unseq, transparentMeshes.begin(), transparentMeshes.end(), +// #include "../../../gapi/interface/sortLambda.h" +// ); + updateStage->transparentMeshes->meshes = transparentMeshes; - resultDrawStage->meshesToRender->meshes.reserve(indexArray.size()); - for (int i = 0; i < indexArray.size(); i++) { - resultDrawStage->meshesToRender->meshes.push_back(renderedThisFramePreSort[indexArray[i]]); - } } else { - for (int i = 0; i < renderedThisFramePreSort.size(); i++) { - resultDrawStage->meshesToRender->meshes.push_back(renderedThisFramePreSort[i]); + auto &targetTranspMeshes = updateStage->transparentMeshes->meshes; + for (int i = 0; i < transparentMeshes.size(); i++) { + targetTranspMeshes.push_back(transparentMeshes[i]); + } + } + + //1. Collect buffers + std::vector &bufferChunks = updateStage->uniformBufferChunks; + bufferChunks.reserve((opaqueMeshes.size() + updateStage->transparentMeshes->meshes.size()) * 3); + int renderIndex = 0; + for (const auto &mesh : opaqueMeshes) { + for (int i = 0; i < 5; i++ ) { + auto bufferChunk = mesh->getUniformBuffer(i); + + if (bufferChunk != nullptr) { + bufferChunks.push_back(bufferChunk); + } } } + for (const auto &mesh : updateStage->transparentMeshes->meshes) { + for (int i = 0; i < 5; i++ ) { + auto bufferChunk = mesh->getUniformBuffer(i); - if (frameBufferSupported) { - doGaussBlur(resultDrawStage, origResultDrawStage); + if (bufferChunk != nullptr) { + bufferChunks.push_back(bufferChunk); + } + } } + + tbb::parallel_sort(bufferChunks.begin(), bufferChunks.end(), + [](auto &first, auto &end) { return first < end; } + ); + bufferChunks.erase(unique(bufferChunks.begin(), bufferChunks.end()), bufferChunks.end()); + } +void Map::produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, std::vector &additionalChunks) { + auto cullStage = updateStage->cullResult; + + //Create scenewide uniform + resultDrawStage->frameDepedantData = updateStage->cullResult->frameDepedantData; + auto renderMats = resultDrawStage->matricesForRendering; -void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultDrawStage) const { + resultDrawStage->opaqueMeshes = updateStage->opaqueMeshes; + resultDrawStage->transparentMeshes = updateStage->transparentMeshes; + + HDrawStage origResultDrawStage = resultDrawStage; + bool frameBufferSupported = m_api->hDevice->getIsRenderbufferSupported(); + + auto config = m_api->getConfig(); + resultDrawStage->sceneWideBlockVSPSChunk = m_api->hDevice->createUniformBufferChunk(sizeof(sceneWideBlockVSPS)); + resultDrawStage->sceneWideBlockVSPSChunk->setUpdateHandler([renderMats, config](IUniformBufferChunk *chunk, const HFrameDepedantData &fdd) -> void { + auto *blockPSVS = &chunk->getObject(); + + blockPSVS->uLookAtMat = renderMats->lookAtMat; + blockPSVS->uPMatrix = renderMats->perspectiveMat; + blockPSVS->uInteriorSunDir = renderMats->interiorDirectLightDir; + blockPSVS->uViewUp = renderMats->viewUp; + + blockPSVS->extLight.uExteriorAmbientColor = fdd->exteriorAmbientColor; + blockPSVS->extLight.uExteriorHorizontAmbientColor = fdd->exteriorHorizontAmbientColor; + blockPSVS->extLight.uExteriorGroundAmbientColor = fdd->exteriorGroundAmbientColor; + blockPSVS->extLight.uExteriorDirectColor = fdd->exteriorDirectColor; + blockPSVS->extLight.uExteriorDirectColorDir = mathfu::vec4(fdd->exteriorDirectColorDir, 1.0); + blockPSVS->extLight.uAdtSpecMult = mathfu::vec4(config->adtSpecMult, 0,0,1.0); + +// float fogEnd = std::min(config->getFarPlane(), config->getFogEnd()); + float fogEnd = config->farPlane; + if (config->disableFog || !fdd->FogDataFound) { + fogEnd = 100000000.0f; + fdd->FogScaler = 0; + fdd->FogDensity = 0; + } + + float fogStart = std::max(config->farPlane - 250, 0); + fogStart = std::max(fogEnd - fdd->FogScaler * (fogEnd - fogStart), 0); + + + blockPSVS->fogData.densityParams = mathfu::vec4( + fogStart, + fogEnd , + fdd->FogDensity / 1000, + 0); + blockPSVS->fogData.heightPlane = mathfu::vec4(0,0,0,0); + blockPSVS->fogData.color_and_heightRate = mathfu::vec4(fdd->FogColor,fdd->FogHeightScaler); + blockPSVS->fogData.heightDensity_and_endColor = mathfu::vec4( + fdd->FogHeightDensity, + fdd->EndFogColor.x, + fdd->EndFogColor.y, + fdd->EndFogColor.z + ); + blockPSVS->fogData.sunAngle_and_sunColor = mathfu::vec4( + fdd->SunFogAngle, + fdd->SunFogColor.x * fdd->SunFogStrength, + fdd->SunFogColor.y * fdd->SunFogStrength, + fdd->SunFogColor.z * fdd->SunFogStrength + ); + blockPSVS->fogData.heightColor_and_endFogDistance = mathfu::vec4( + fdd->FogHeightColor, + (fdd->EndFogColorDistance > 0) ? + fdd->EndFogColorDistance : + 1000.0f + ); + blockPSVS->fogData.sunPercentage = mathfu::vec4( + (fdd->SunFogColor.Length() > 0) ? 0.5f : 0.0f + , 0, 0, 0); + + }); + + updateStage->uniformBufferChunks.push_back(resultDrawStage->sceneWideBlockVSPSChunk); + + if (frameBufferSupported ) { + //Create a copy of exiting resultDrawStage + auto resultDrawStageCpy = std::make_shared(); + *resultDrawStageCpy = *resultDrawStage; + //Assign a new frame buffer to copy + resultDrawStageCpy->target = m_api->hDevice->createFrameBuffer( + resultDrawStage->viewPortDimensions.maxs[0], + resultDrawStage->viewPortDimensions.maxs[1], + {ITextureFormat::itRGBA}, + ITextureFormat::itDepth32, + m_api->hDevice->getMaxSamplesCnt(), + 4 + ); + resultDrawStageCpy->viewPortDimensions.mins = {0,0}; + + HDrawStage lastDrawStage = nullptr; + HDrawStage prevDrawStage = resultDrawStageCpy; + + if (!config->disableGlow) { + lastDrawStage = doGaussBlur(prevDrawStage, updateStage); + if (lastDrawStage != nullptr) + prevDrawStage = lastDrawStage; + } + + + //End of effects stack + //Make last stage to draw to initial resultDrawStage target + prevDrawStage->target = resultDrawStage->target; + //Replace all data in target drawStage with new data + *resultDrawStage = *prevDrawStage; + } +} + +HDrawStage Map::doGaussBlur(const HDrawStage parentDrawStage, HUpdateStage &updateStage) const { + if (quadBindings == nullptr) + return nullptr; ///2 Rounds of ffxgauss4 (Horizontal and Vertical blur) ///With two frameBuffers ///Size for buffers : is 4 times less than current canvas - int targetWidth = resultDrawStage->viewPortDimensions.maxs[0] >> 2; - int targetHeight = resultDrawStage->viewPortDimensions.maxs[1] >> 2; + int targetWidth = parentDrawStage->viewPortDimensions.maxs[0] >> 2; + int targetHeight = parentDrawStage->viewPortDimensions.maxs[1] >> 2; auto frameB1 = m_api->hDevice->createFrameBuffer( targetWidth, targetHeight, {ITextureFormat::itRGBA}, ITextureFormat::itDepth32, + 1, 2 ); auto frameB2 = m_api->hDevice->createFrameBuffer( @@ -1508,11 +1819,13 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD targetHeight, {ITextureFormat::itRGBA}, ITextureFormat::itDepth32, + 1, 2 ); auto vertexChunk = m_api->hDevice->createUniformBufferChunk(sizeof(mathfu::vec4_packed)); - vertexChunk->setUpdateHandler([](IUniformBufferChunk *self) -> void { + updateStage->uniformBufferChunks.push_back(vertexChunk); + vertexChunk->setUpdateHandler([](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) -> void { auto &meshblockVS = self->getObject(); meshblockVS.x = 1; meshblockVS.y = 1; @@ -1522,7 +1835,8 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD auto ffxGaussFrag = m_api->hDevice->createUniformBufferChunk(sizeof(FXGauss::meshWideBlockPS)); - ffxGaussFrag->setUpdateHandler([](IUniformBufferChunk *self) -> void { + updateStage->uniformBufferChunks.push_back(ffxGaussFrag); + ffxGaussFrag->setUpdateHandler([](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) -> void { auto &meshblockVS = self->getObject(); static const float s_texOffsetX[4] = {-1, 0, 0, -1}; static const float s_texOffsetY[4] = {2, 2, -1, -1};; @@ -1535,7 +1849,8 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD auto ffxGaussFrag2 = m_api->hDevice->createUniformBufferChunk(sizeof(FXGauss::meshWideBlockPS)); - ffxGaussFrag2->setUpdateHandler([](IUniformBufferChunk *self) -> void { + updateStage->uniformBufferChunks.push_back(ffxGaussFrag2); + ffxGaussFrag2->setUpdateHandler([](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) -> void { auto &meshblockVS = self->getObject(); static const float s_texOffsetX[4] = {-6, -1, 1, 6}; static const float s_texOffsetY[4] = {0, 0, 0, 0};; @@ -1546,7 +1861,8 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD } }); auto ffxGaussFrag3 = m_api->hDevice->createUniformBufferChunk(sizeof(FXGauss::meshWideBlockPS)); - ffxGaussFrag3->setUpdateHandler([](IUniformBufferChunk *self) -> void { + updateStage->uniformBufferChunks.push_back(ffxGaussFrag3); + ffxGaussFrag3->setUpdateHandler([](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) -> void { auto &meshblockVS = self->getObject(); static const float s_texOffsetX[4] = {0, 0, 0, 0}; static const float s_texOffsetY[4] = {10, 2, -2, -10};; @@ -1558,7 +1874,7 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD }); HGUniformBufferChunk frags[3] = {ffxGaussFrag, ffxGaussFrag2, ffxGaussFrag3}; - HDrawStage prevStage = resultDrawStage; + HDrawStage prevStage = parentDrawStage; for (int i = 0; i < 3; i++) { ///1. Create draw stage HDrawStage drawStage = std::make_shared(); @@ -1567,8 +1883,8 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD drawStage->matricesForRendering = nullptr; drawStage->setViewPort = true; drawStage->viewPortDimensions = {{0, 0}, - {resultDrawStage->viewPortDimensions.maxs[0] >> 2, - resultDrawStage->viewPortDimensions.maxs[1] >> 2}}; + {parentDrawStage->viewPortDimensions.maxs[0] >> 2, + parentDrawStage->viewPortDimensions.maxs[1] >> 2}}; drawStage->clearScreen = false; drawStage->target = ((i & 1) > 0) ? frameB1 : frameB2; @@ -1599,8 +1915,8 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD //Make mesh HGMesh hmesh = m_api->hDevice->createMesh(meshTemplate); - drawStage->meshesToRender = std::make_shared(); - drawStage->meshesToRender->meshes.push_back(hmesh); + drawStage->opaqueMeshes = std::make_shared(); + drawStage->opaqueMeshes->meshes.push_back(hmesh); ///3. Reassign previous frame prevStage = drawStage; @@ -1608,18 +1924,20 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD //And the final is ffxglow to screen { - auto config = m_api->getConfig(); - auto glow = config->currentGlow; + auto config = m_api->getConfig();; + + auto glow = parentDrawStage->frameDepedantData->currentGlow; auto ffxGlowfragmentChunk = m_api->hDevice->createUniformBufferChunk(sizeof(mathfu::vec4_packed)); - ffxGlowfragmentChunk->setUpdateHandler([glow, config](IUniformBufferChunk *self) -> void { + updateStage->uniformBufferChunks.push_back(ffxGlowfragmentChunk); + ffxGlowfragmentChunk->setUpdateHandler([glow, config](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) -> void { auto &meshblockVS = self->getObject(); meshblockVS.x = 1; meshblockVS.y = 1; meshblockVS.z = 0; //mix_coeficient - meshblockVS.w = config->getUseGaussBlur() ? fminf(0.5f, glow) : 0; //glow multiplier + meshblockVS.w = glow; //glow multiplier }); - auto shader = m_api->hDevice->getShader("fullScreen_quad", nullptr); + auto shader = m_api->hDevice->getShader("ffxGlowQuad", nullptr); gMeshTemplate meshTemplate(quadBindings, shader); meshTemplate.meshType = MeshType::eGeneralMesh; meshTemplate.depthWrite = false; @@ -1628,7 +1946,7 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD meshTemplate.blendMode = EGxBlendEnum::GxBlend_Opaque; meshTemplate.texture.resize(2); - meshTemplate.texture[0] = resultDrawStage->target->getAttachment(0); + meshTemplate.texture[0] = parentDrawStage->target->getAttachment(0); meshTemplate.texture[1] = prevStage->target->getAttachment(0); meshTemplate.textureCount = 2; @@ -1647,9 +1965,17 @@ void Map::doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultD //Make mesh HGMesh hmesh = m_api->hDevice->createMesh(meshTemplate); - origResultDrawStage->drawStageDependencies = {prevStage}; - origResultDrawStage->meshesToRender = std::make_shared(); - origResultDrawStage->meshesToRender->meshes.push_back(hmesh); + + auto resultDrawStage = std::make_shared(); + *resultDrawStage = *parentDrawStage; + resultDrawStage->sceneWideBlockVSPSChunk = nullptr; //Since it's not used but this shader and it's important for vulkan + resultDrawStage->drawStageDependencies = {prevStage}; + resultDrawStage->transparentMeshes = nullptr; + resultDrawStage->opaqueMeshes = std::make_shared(); + resultDrawStage->opaqueMeshes->meshes.push_back(hmesh); + resultDrawStage->target = nullptr; + + return resultDrawStage; } } @@ -1671,8 +1997,8 @@ void Map::loadZoneLights() { auto &points = innerZoneLightRecord.points; for (auto &zonePoint : zoneLight.points) { - minX = std::min(zonePoint.x, minX); minY = std::min(zonePoint.y, minY); - maxX = std::max(zonePoint.x, maxX); maxY = std::max(zonePoint.y, maxY); + minX = std::min(zonePoint.x, minX); minY = std::min(zonePoint.y, minY); + maxX = std::max(zonePoint.x, maxX); maxY = std::max(zonePoint.y, maxY); points.push_back(mathfu::vec2(zonePoint.x, zonePoint.y)); } diff --git a/wowViewerLib/src/engine/objects/scenes/map.h b/wowViewerLib/src/engine/objects/scenes/map.h index 2a8a93fda..4b60e1794 100644 --- a/wowViewerLib/src/engine/objects/scenes/map.h +++ b/wowViewerLib/src/engine/objects/scenes/map.h @@ -16,6 +16,8 @@ #include "../wdl/wdlObject.h" #include "../wowFrameData.h" #include "../../SceneScenario.h" +#include "tbb/tbb.h" +#include "../../algorithms/FrameCounter.h" enum class SceneMode { smMap, @@ -23,17 +25,29 @@ enum class SceneMode { smWMO }; -class Map : public IMapApi, public IScene { +class Map : public IScene, public IMapApi { +private: + void initMapTiles() { + for (auto &x : mapTiles) { + for (auto &y : x) { + y = nullptr; + } + } + } protected: - ApiContainer *m_api = nullptr; + + tbb::task_scheduler_init taskScheduler; + FrameCounter m2UpdateframeCounter; + + + HApiContainer m_api = nullptr; std::array, 64>, 64> mapTiles={}; + std::vector> m_mandatoryADT; std::string mapName; SceneMode m_sceneMode = SceneMode::smMap; float m_currentTime = 0; - float m_lastTimeLightCheck = 0; - float m_lastTimeAdtCleanup = 0; bool m_lockedMap = false; @@ -51,6 +65,7 @@ class Map : public IMapApi, public IScene { HGVertexBufferBindings quadBindings; float m_skyConeAlpha = 0.0; HGMesh skyMesh = nullptr; + HGMesh skyMesh0x4Sky = nullptr; //Map mode std::unordered_map> m_m2MapObjects = {}; @@ -78,6 +93,7 @@ class Map : public IMapApi, public IScene { int getCameraNum() override {return 0;}; std::shared_ptr createCamera(int cameraNum) override { return nullptr;}; + animTime_t getCurrentSceneTime() override ; virtual void getPotentialEntities(const mathfu::vec4 &cameraPos, std::vector> &potentialM2, @@ -90,6 +106,11 @@ class Map : public IMapApi, public IScene { std::vector> &m2ObjectsCandidates, std::vector> &wmoCandidates); + void checkADTCulling(int i, int j, std::vector &hullLines, mathfu::mat4 &lookAtMat4, + mathfu::vec4 &cameraPos, std::vector &frustumPoints, HCullStage &cullStage, + std::vector> &m2ObjectsCandidates, + std::vector> &wmoCandidates); + virtual void updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &cameraVec3, StateForConditions &stateForConditions, const AreaRecord &areaRecord); @@ -103,14 +124,22 @@ class Map : public IMapApi, public IScene { }; std::vector m_zoneLights; void loadZoneLights(); -public: - explicit Map() { + FreeStrategy adtFreeLambda; + FreeStrategy zeroStateLambda; + + HADTBoundingBoxHolder m_adtBBHolder = nullptr; + +protected: + explicit Map() : taskScheduler(10){ } +public: + explicit Map(HApiContainer api, int mapId, std::string mapName) : taskScheduler(10) { + initMapTiles(); - explicit Map(ApiContainer *api, int mapId, std::string mapName) { - m_mapId = mapId; m_api = api; mapName = mapName; + m_mapId = mapId; m_api = api; this->mapName = mapName; m_sceneMode = SceneMode::smMap; + createAdtFreeLamdas(); std::string wdtFileName = "world/maps/"+mapName+"/"+mapName+".wdt"; std::string wdlFileName = "world/maps/"+mapName+"/"+mapName+".wdl"; @@ -120,21 +149,30 @@ class Map : public IMapApi, public IScene { m_wdlObject->setMapApi(this); loadZoneLights(); + }; - explicit Map(ApiContainer *api, int mapId, int wdtFileDataId) { + explicit Map(HApiContainer api, int mapId, int wdtFileDataId) { + initMapTiles(); + m_mapId = mapId; m_api = api; mapName = ""; m_sceneMode = SceneMode::smMap; + createAdtFreeLamdas(); + m_wdtfile = api->cacheStorage->getWdtFileCache()->getFileId(wdtFileDataId); loadZoneLights(); }; - explicit Map(ApiContainer *api, std::string adtFileName, int i, int j, std::string mapName) { + explicit Map(HApiContainer api, std::string adtFileName, int i, int j, std::string mapName) { + initMapTiles(); + m_mapId = 0; m_api = api; this->mapName = mapName; m_sceneMode = SceneMode::smMap; + createAdtFreeLamdas(); + std::string wdtFileName = "world/maps/"+mapName+"/"+mapName+".wdt"; std::string wdlFileName = "world/maps/"+mapName+"/"+mapName+".wdl"; @@ -151,25 +189,34 @@ class Map : public IMapApi, public IScene { }; ~Map() override { - - } ; +// std::cout << "Map destroyed " << std::endl; + }; void setReplaceTextureArray(std::vector &replaceTextureArray) override {}; + void setMeshIdArray(std::vector &meshIds) override {}; void checkCulling(HCullStage cullStage) override; + void setMandatoryADTs(std::vector> mandatoryADTs) override { + m_mandatoryADT = mandatoryADTs; + } + void getAdtAreaId(const mathfu::vec4 &cameraPos, int &areaId, int &parentAreaId) override; + void setAnimationId(int animationId) override {}; + void setMeshIds(std::vector &meshIds) override {}; - void setAnimationId(int animationId) override { - - }; void resetAnimation() override { } + void setAdtBoundingBoxHolder(HADTBoundingBoxHolder bbHolder) override { + m_adtBBHolder = bbHolder; + } + void doPostLoad(HCullStage cullStage) override; - void update(HUpdateStage updateStage) override; - void updateBuffers(HCullStage cullStage) override; + void update(HUpdateStage updateStage); + void updateBuffers(HUpdateStage updateStage) override; + void produceUpdateStage(HUpdateStage updateStage) override; void produceDrawStage(HDrawStage resultDrawStage, HUpdateStage updateStage, std::vector &additionalChunks) override; private: void checkExterior(mathfu::vec4 &cameraPos, @@ -180,10 +227,12 @@ class Map : public IMapApi, public IScene { int viewRenderOrder, HCullStage cullStage); - void doGaussBlur(const HDrawStage &resultDrawStage, HDrawStage &origResultDrawStage) const; + HDrawStage doGaussBlur(const HDrawStage parentDrawStage, HUpdateStage &updateStage) const; + void getLightResultsFromDB(mathfu::vec3 &cameraVec3, const Config *config, std::vector &lightResults, StateForConditions *stateForConditions) override; + void createAdtFreeLamdas(); }; #endif //WEBWOWVIEWERCPP_MAP_H diff --git a/wowViewerLib/src/engine/objects/scenes/wmoScene.cpp b/wowViewerLib/src/engine/objects/scenes/wmoScene.cpp index 59323057d..01e95da15 100644 --- a/wowViewerLib/src/engine/objects/scenes/wmoScene.cpp +++ b/wowViewerLib/src/engine/objects/scenes/wmoScene.cpp @@ -22,6 +22,28 @@ void WmoScene::getCandidatesEntities(std::vector &hullLines, mathf wmoCandidates.push_back(this->m_wmoObject); }; + +void WmoScene::updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &cameraVec3, + StateForConditions &stateForConditions, const AreaRecord &areaRecord) { + Config* config = this->m_api->getConfig(); + config->globalFog = EParameterSource::eNone; + + Map::updateLightAndSkyboxData(cullStage, cameraVec3, stateForConditions, areaRecord); + + mathfu::vec4 ambient = mathfu::vec4(1.0,1.0,1.0,1.0); + + auto frameDepedantData = cullStage->frameDepedantData; + + frameDepedantData->exteriorAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + frameDepedantData->exteriorHorizontAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + frameDepedantData->exteriorGroundAmbientColor = mathfu::vec4(ambient.x, ambient.y, ambient.z, 1.0); + frameDepedantData->exteriorDirectColor = mathfu::vec4(0.3,0.30,0.3,0.3); + frameDepedantData->exteriorDirectColorDir = MathHelper::calcExteriorColorDir( + cullStage->matricesForCulling->lookAtMat, + m_api->getConfig()->currentTime + ); +} + /* void WmoScene::doPostLoad(HCullStage cullStage) { int processedThisFrame = 0; diff --git a/wowViewerLib/src/engine/objects/scenes/wmoScene.h b/wowViewerLib/src/engine/objects/scenes/wmoScene.h index 13b4576b4..41ccaeab2 100644 --- a/wowViewerLib/src/engine/objects/scenes/wmoScene.h +++ b/wowViewerLib/src/engine/objects/scenes/wmoScene.h @@ -25,15 +25,8 @@ class WmoScene : public Map { std::vector> &m2ObjectsCandidates, std::vector> &wmoCandidates) override; public: - void setDefaultLightParams(ApiContainer *api) { - api->getConfig()->setExteriorAmbientColor(0.8,0.8,0.8,0.8); - api->getConfig()->setExteriorHorizontAmbientColor(1.0,1.0,1.0,1.0); - api->getConfig()->setExteriorGroundAmbientColor(1.0,1.0,1.0,1.0); - api->getConfig()->setExteriorDirectColor(0.3,0.3,0.3,1.3); - api->getConfig()->setExteriorDirectColorDir(0.0,0.0,0.0); - } - explicit WmoScene(ApiContainer *api, std::string wmoModel) { + explicit WmoScene(HApiContainer api, std::string wmoModel) { m_api = api; m_wmoModel = wmoModel; m_sceneMode = SceneMode::smWMO; m_suppressDrawingSky = true; @@ -51,11 +44,9 @@ class WmoScene : public Map { wmoObject->setModelFileName(m_wmoModel); m_wmoObject = wmoObject; - - this->setDefaultLightParams(api); }; - explicit WmoScene(ApiContainer *api, int fileDataId) { + explicit WmoScene(HApiContainer api, int fileDataId) { m_api = api; m_sceneMode = SceneMode::smWMO; m_suppressDrawingSky = true; @@ -73,30 +64,14 @@ class WmoScene : public Map { wmoObject->setModelFileId(fileDataId); m_wmoObject = wmoObject; - - this->setDefaultLightParams(api); }; ~WmoScene() override { } - -// mathfu::vec4 getAmbientColor() override { -//// if (m_wmoObject->isLoaded()) { -//// return mathfu::vec4(m_wmoObject->getAmbientLight(), 0.0); -//// } else -//// return mathfu::vec4(0.0, 0.0, 0.0, 0.0); -// return m_api->getGlobalAmbientColor(); -// }; -// -// bool getCameraSettings(M2CameraResult&) override { -// return false; -// } -// -// void setAmbientColorOverride(mathfu::vec4 &ambientColor, bool override) override { -// -// }; + void updateLightAndSkyboxData(const HCullStage &cullStage, mathfu::vec3 &cameraVec3, + StateForConditions &stateForConditions, const AreaRecord &areaRecord) override; }; diff --git a/wowViewerLib/src/engine/objects/wdl/wdlObject.cpp b/wowViewerLib/src/engine/objects/wdl/wdlObject.cpp index 3c3d5fa15..7d9e45440 100644 --- a/wowViewerLib/src/engine/objects/wdl/wdlObject.cpp +++ b/wowViewerLib/src/engine/objects/wdl/wdlObject.cpp @@ -123,18 +123,18 @@ void WdlObject::loadingFinished() { this->loadWmos(); } -WdlObject::WdlObject(ApiContainer *api, std::string &wdlFileName) { +WdlObject::WdlObject(HApiContainer api, std::string &wdlFileName) { m_api = api; m_wdlFile = m_api->cacheStorage->getWdlFileCache()->get(wdlFileName); } -WdlObject::WdlObject(ApiContainer *api, int wdlFileDataId) { +WdlObject::WdlObject(HApiContainer api, int wdlFileDataId) { m_api = api; m_wdlFile = m_api->cacheStorage->getWdlFileCache()->getFileId(wdlFileDataId); } void WdlObject::checkSkyScenes(const StateForConditions &state, - std::vector> &m2ObjectsCandidates, + std::unordered_set> &m2ObjectsCandidates, const mathfu::vec4 &cameraPos, const std::vector &frustumPlanes, const std::vector &frustumPoints @@ -150,6 +150,13 @@ void WdlObject::checkSkyScenes(const StateForConditions &state, conditionPassed = false; break; } + case 2 : { + auto it = std::find(state.currentLightParams.begin(), state.currentLightParams.end(), condition.conditionValue); + if (it == state.currentLightParams.end()) { + conditionPassed = false; + } + break; + } case 3 : { auto it = std::find(state.currentSkyboxIds.begin(), state.currentSkyboxIds.end(), condition.conditionValue); if (it == state.currentSkyboxIds.end()) { @@ -157,13 +164,21 @@ void WdlObject::checkSkyScenes(const StateForConditions &state, } break; } - } + case 5 : { + auto it = std::find(state.currentZoneLights.begin(), state.currentZoneLights.end(), condition.conditionValue); + if (it == state.currentZoneLights.end()) { + conditionPassed = false; + } + break; + } + default: + std::cout << "Unk condition " << (int) condition.conditionType << std::endl; } } if (conditionPassed) { for (auto &m2Object : skyScene.m2Objects) { if (m2Object->checkFrustumCulling(cameraPos, frustumPlanes, frustumPoints)) { - m2ObjectsCandidates.push_back(m2Object); + m2ObjectsCandidates.insert(m2Object); } } } diff --git a/wowViewerLib/src/engine/objects/wdl/wdlObject.h b/wowViewerLib/src/engine/objects/wdl/wdlObject.h index f52ce0251..f3e2851a1 100644 --- a/wowViewerLib/src/engine/objects/wdl/wdlObject.h +++ b/wowViewerLib/src/engine/objects/wdl/wdlObject.h @@ -16,8 +16,8 @@ class WdlObject { public: - explicit WdlObject(ApiContainer *api, std::string &wdlFileName); - explicit WdlObject(ApiContainer *api, int wdlFileDataId); + explicit WdlObject(HApiContainer api, std::string &wdlFileName); + explicit WdlObject(HApiContainer api, int wdlFileDataId); void setMapApi(IMapApi *api) { m_mapApi = api; @@ -27,7 +27,7 @@ class WdlObject { std::vector> wmoObjects; private: - ApiContainer *m_api; + HApiContainer m_api; IMapApi *m_mapApi; HWdlFile m_wdlFile; @@ -60,7 +60,8 @@ class WdlObject { std::vector> &m2ObjectsCandidates, std::vector> &wmoCandidates); - void checkSkyScenes(const StateForConditions &state, std::vector> &m2ObjectsCandidates, + void checkSkyScenes(const StateForConditions &state, + std::unordered_set> &m2ObjectsCandidates, const mathfu::vec4 &cameraPos, const std::vector &frustumPlanes, const std::vector &frustumPoints); diff --git a/wowViewerLib/src/engine/objects/wmo/wmoGroupObject.cpp b/wowViewerLib/src/engine/objects/wmo/wmoGroupObject.cpp index eeccf6682..be576c6b1 100644 --- a/wowViewerLib/src/engine/objects/wmo/wmoGroupObject.cpp +++ b/wowViewerLib/src/engine/objects/wmo/wmoGroupObject.cpp @@ -165,7 +165,7 @@ inline constexpr const int operator+(WmoPixelShader const val) { return static_c inline constexpr const int operator+(WmoVertexShader const val) { return static_cast(val); }; -const int MAX_WMO_SHADERS = 23; +const int MAX_WMO_SHADERS = 24; static const struct { int vertexShader; int pixelShader; @@ -284,7 +284,12 @@ static const struct { { +WmoVertexShader::MapObjParallax, +WmoPixelShader::MapObjParallax, - } + }, + //23 // TODO: stub for now + { + +WmoVertexShader::MapObjDiffuse_T1, + +WmoPixelShader::MapObjTwoLayerDiffuseMod2x, + }, }; @@ -393,8 +398,8 @@ void WmoGroupObject::postLoad() { void WmoGroupObject::createMeshes() { Config *config = m_api->getConfig(); - int minBatch = config->getWmoMinBatch(); - int maxBatch = std::min(config->getWmoMaxBatch(), m_geom->batchesLen); + int minBatch = config->wmoMinBatch; + int maxBatch = std::min(config->wmoMaxBatch, m_geom->batchesLen); //In Taanant Jungle in Draenor some WMO has no indicies and crash :D if (m_geom->indicesLen == 0) { @@ -403,18 +408,18 @@ void WmoGroupObject::createMeshes() { PointerChecker &materials = m_wmoApi->getMaterials(); - std::shared_ptr device = m_api->hDevice; - HGVertexBufferBindings binding = m_geom->getVertexBindings(*device); + HGDevice device = m_api->hDevice; + HGVertexBufferBindings binding = m_geom->getVertexBindings(device); vertexModelWideUniformBuffer = device->createUniformBufferChunk(sizeof(WMO::modelWideBlockVS)); - vertexModelWideUniformBuffer->setUpdateHandler([this](IUniformBufferChunk *self){ + vertexModelWideUniformBuffer->setUpdateHandler([this](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ WMO::modelWideBlockVS &blockVS = self->getObject(); blockVS.uPlacementMat = *m_modelMatrix; }); fragmentModelWideUniformBuffer = device->createUniformBufferChunk(sizeof(WMO::modelWideBlockPS)); - fragmentModelWideUniformBuffer->setUpdateHandler([this](IUniformBufferChunk *self){ + fragmentModelWideUniformBuffer->setUpdateHandler([this](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ WMO::modelWideBlockPS &blockPS = self->getObject(); blockPS.intLight.uInteriorAmbientColorAndApplyInteriorLight = mathfu::vec4_packed( @@ -449,6 +454,9 @@ void WmoGroupObject::createMeshes() { if (shaderId >= MAX_WMO_SHADERS) { shaderId = 0; } +// if (shaderId == 23) { +// std::cout << "Hello" << std::endl; +// } int pixelShader = wmoMaterialShader[shaderId].pixelShader; int vertexShader = wmoMaterialShader[shaderId].vertexShader; @@ -503,13 +511,13 @@ void WmoGroupObject::createMeshes() { HGMesh hmesh = device->createMesh(meshTemplate); this->m_meshArray.push_back(hmesh); - hmesh->getUniformBuffer(2)->setUpdateHandler([this, &material, vertexShader](IUniformBufferChunk *self){ + hmesh->getUniformBuffer(2)->setUpdateHandler([this, &material, vertexShader](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData){ WMO::meshWideBlockVS &blockVS = self->getObject(); blockVS.UseLitColor = (material.flags.F_UNLIT > 0) ? 0 : 1; blockVS.VertexShader = vertexShader; }); - hmesh->getUniformBuffer(4)->setUpdateHandler([this, isBatchA, isBatchC, &material, blendMode, pixelShader](IUniformBufferChunk *self) { + hmesh->getUniformBuffer(4)->setUpdateHandler([this, isBatchA, isBatchC, &material, blendMode, pixelShader](IUniformBufferChunk *self, const HFrameDepedantData &frameDepedantData) { // mathfu::vec4 globalAmbientColor = m_api->getGlobalAmbientColor(); mathfu::vec4 localambientColor = this->getAmbientColor(); mathfu::vec3 directLight = mathfu::vec3(0,0,0); @@ -533,6 +541,7 @@ void WmoGroupObject::createMeshes() { blockPS.UseLitColor = (material.flags.F_UNLIT > 0) ? 0 : 1; blockPS.EnableAlpha = (blendMode > 0) ? 1 : 0; blockPS.PixelShader = pixelShader; + blockPS.BlendMode = blendMode; blockPS.uFogColor_AlphaTest = mathfu::vec4_packed( mathfu::vec4(0,0,0, alphaTest)); @@ -596,8 +605,8 @@ void WmoGroupObject::setLiquidType() { void WmoGroupObject::createWaterMeshes() { - std::shared_ptr device = m_api->hDevice; - HGVertexBufferBindings binding = m_geom->getWaterVertexBindings(*device); + HGDevice device = m_api->hDevice; + HGVertexBufferBindings binding = m_geom->getWaterVertexBindings(device); if (binding == nullptr) return; @@ -625,15 +634,32 @@ void WmoGroupObject::createWaterMeshes() { meshTemplate.blendMode = EGxBlendEnum::GxBlend_Alpha; - HGTexture texture1 = m_wmoApi->getTexture(material.diffuseNameIndex, false); - HGTexture texture2 = m_wmoApi->getTexture(material.envNameIndex, false); - HGTexture texture3 = m_wmoApi->getTexture(material.texture_2, false); + std::vector liquidTypeData; + int basetextureFDID = 0; + if (m_api->databaseHandler != nullptr) { + m_api->databaseHandler->getLiquidTypeData(this->liquid_type, liquidTypeData); + } + mathfu::vec3 color = mathfu::vec3(0,0,0); + int liquidFlags = 0; - meshTemplate.textureCount = 0; + for (auto ltd: liquidTypeData) { + if (ltd.FileDataId != 0) { + basetextureFDID = ltd.FileDataId; - meshTemplate.texture[0] = texture1; - meshTemplate.texture[1] = texture2; - meshTemplate.texture[2] = texture3; + if (ltd.color1[0] > 0 || ltd.color1[1] > 0 || ltd.color1[2] > 0) { + color = mathfu::vec3(ltd.color1[0], ltd.color1[1], ltd.color1[2]); + } + liquidFlags = ltd.flags; + break; + } + } + meshTemplate.textureCount = 1; + if (basetextureFDID != 0) { + auto htext = m_api->cacheStorage->getTextureCache()->getFileId(basetextureFDID); + meshTemplate.texture[0] = m_api->hDevice->createBlpTexture(htext, true, true); + } else { + meshTemplate.texture[0] = m_api->hDevice->getBlackTexturePixel(); + } meshTemplate.ubo[0] = nullptr;//m_api->getSceneWideUniformBuffer(); meshTemplate.ubo[1] = vertexModelWideUniformBuffer; @@ -647,12 +673,18 @@ void WmoGroupObject::createWaterMeshes() { meshTemplate.element = DrawElementMode::TRIANGLES; auto l_liquidType = liquid_type; - meshTemplate.ubo[4]->setUpdateHandler([l_liquidType](IUniformBufferChunk* self) -> void { - int (&waterType)[4] = self->getObject(); - waterType[0] = l_liquidType; - }); + meshTemplate.ubo[4]->setUpdateHandler([this, l_liquidType, liquidFlags, color](IUniformBufferChunk* self, const HFrameDepedantData &frameDepedantData) -> void { + mathfu::vec4_packed &color_ = self->getObject(); + if ((liquidFlags & 1024) > 0) {// Ocean + color_ = frameDepedantData->closeOceanColor; + } else if (liquidFlags == 15) { //River/Lake + color_ = frameDepedantData->closeRiverColor; + } else { + color_ = mathfu::vec4(color, 0.7); + } + }); HGMesh hmesh = device->createMesh(meshTemplate); m_waterMeshArray.push_back(hmesh); @@ -666,8 +698,9 @@ void WmoGroupObject::loadDoodads() { //Load all doodad from MOBR for (int i = 0; i < this->m_geom->doodadRefsLen; i++) { - m_doodads[i] = m_wmoApi->getDoodad(this->m_geom->doodadRefs[i]); - std::function event = [&]() -> void { + auto newDoodad = m_wmoApi->getDoodad(this->m_geom->doodadRefs[i]); + m_doodads[i] = newDoodad; + std::function event = [&, newDoodad]() -> void { this->m_recalcBoundries = true; }; if (m_doodads[i] != nullptr) { @@ -727,10 +760,20 @@ void WmoGroupObject::updateWorldGroupBBWithM2() { m_wmoApi->updateBB(); } -bool WmoGroupObject::checkGroupFrustum(mathfu::vec4 &cameraPos, +void WmoGroupObject::checkGroupFrustum(bool &drawDoodads, bool &drawGroup, + mathfu::vec4 &cameraPos, std::vector &frustumPlanes, std::vector &points) { - if (!m_loaded) return true; + drawDoodads = false; + drawGroup = false; + if (!m_loaded) { + //Force load of group if it's exterior + if (m_main_groupInfo->flags.EXTERIOR > 0) { + drawGroup = true; + drawDoodads = true; + } + return; + } CAaBox bbArray = this->m_worldGroupBorder; bool isInsideM2Volume = ( @@ -739,7 +782,7 @@ bool WmoGroupObject::checkGroupFrustum(mathfu::vec4 &cameraPos, cameraPos[2] > bbArray.min.z && cameraPos[2] < bbArray.max.z ); - bool drawDoodads = isInsideM2Volume || MathHelper::checkFrustum(frustumPlanes, bbArray, points); + drawDoodads = isInsideM2Volume || MathHelper::checkFrustum(frustumPlanes, bbArray, points); bbArray = this->m_volumeWorldGroupBorder; bool isInsideGroup = ( @@ -748,8 +791,7 @@ bool WmoGroupObject::checkGroupFrustum(mathfu::vec4 &cameraPos, cameraPos[2] > bbArray.min.z && cameraPos[2] < bbArray.max.z ); - bool drawGroup = isInsideGroup || MathHelper::checkFrustum(frustumPlanes, bbArray, points); - return drawGroup; + drawGroup = isInsideGroup || MathHelper::checkFrustum(frustumPlanes, bbArray, points); } bool WmoGroupObject::checkIfInsidePortals(mathfu::vec3 point, @@ -787,7 +829,7 @@ void WmoGroupObject::queryBspTree(CAaBox &bbox, int nodeId, PointerChecker &renderedThisFrame, int renderOrder) { +void WmoGroupObject::collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder) { if (!m_loaded) return; for (auto &i : this->m_meshArray) { i->setRenderOrder(renderOrder); - renderedThisFrame.push_back(i); + if (i->getIsTransparent()) { + opaqueMeshes.push_back(i); + } else { + transparentMeshes.push_back(i); + } } for (auto &i : this->m_waterMeshArray) { i->setRenderOrder(renderOrder); - renderedThisFrame.push_back(i); + transparentMeshes.push_back(i); } } mathfu::vec4 WmoGroupObject::getAmbientColor() { if (!m_geom->mogp->flags.EXTERIOR && !m_geom->mogp->flags.EXTERIOR_LIT) { mathfu::vec4 ambColor; - ambColor = mathfu::vec4( - ((float) m_geom->mohd->ambColor.r / 255.0f), - ((float) m_geom->mohd->ambColor.g / 255.0f), - ((float) m_geom->mohd->ambColor.b / 255.0f), - ((float) m_geom->mohd->ambColor.a / 255.0f) - ); + ambColor = mathfu::vec4(m_wmoApi->getAmbientColor(), 1.0); if ((m_geom->use_replacement_for_header_color == 1) && (*(int *) &m_geom->replacement_for_header_color != -1)) { ambColor = mathfu::vec4( ((float) m_geom->replacement_for_header_color.r / 255.0f), @@ -1216,6 +1257,33 @@ mathfu::vec4 WmoGroupObject::getAmbientColor() { return mathfu::vec4(0,0,0,0); } +void AdjustLighting(const mathfu::vec3 color_in, mathfu::vec3 &color_out_0, uint8_t a4, mathfu::vec3 &color_out_1, uint8_t a6) +{ + float maxInputComponent = std::max(color_in.x, std::max(color_in.y, color_in.z)); + + uint8_t v10 = 1; + color_out_0 = color_in; + if ( maxInputComponent > 0 ) + { + v10 = std::floor(maxInputComponent * 255.0f); + } + if ( v10 < a4 ) + { + auto hsv = MathHelper::rgb2hsv(color_in); + hsv.v = (float)((float)(a4) / (float)(v10)) * hsv.v; + color_out_0 = MathHelper::hsv2rgb(hsv); + } + if ( v10 <= a6 ) + { + color_out_1 = color_in; + } + else + { + float v12 = (float)((float)a6) / (float)v10; + color_out_1 = v12 * color_in; + } +} + void WmoGroupObject::assignInteriorParams(std::shared_ptr m2Object) { mathfu::vec4 ambientColor = getAmbientColor(); @@ -1250,8 +1318,17 @@ void WmoGroupObject::assignInteriorParams(std::shared_ptr m2Object) { temp2, temp, bspLeafList, cameraLocal, topZ, bottomZ, mocvColor, false); if (bottomZ < 99999) { - mocvColor = mathfu::vec4(mocvColor.z, mocvColor.y, mocvColor.x, 0); - ambientColor += mocvColor; + mocvColor = mathfu::vec4(mocvColor.x, mocvColor.y, mocvColor.z, 0); + + mathfu::vec3 someColor; + mathfu::vec3 colorCombined = (2.0f * mocvColor.xyz()) + ambientColor.xyz(); + colorCombined = mathfu::vec3::Min(mathfu::vec3(1.0f, 1.0f, 1.0f), colorCombined); + mathfu::vec3 ambientOut = {0,0,0}; + AdjustLighting(colorCombined, someColor, 0xA8u, ambientOut, 0x60u); + + ambientColor = mathfu::vec4(ambientOut.x, ambientOut.y, ambientOut.z, ambientColor.w); + +// ambientColor += mocvColor; } } diff --git a/wowViewerLib/src/engine/objects/wmo/wmoGroupObject.h b/wowViewerLib/src/engine/objects/wmo/wmoGroupObject.h index e1a1f1d94..b74e3e532 100644 --- a/wowViewerLib/src/engine/objects/wmo/wmoGroupObject.h +++ b/wowViewerLib/src/engine/objects/wmo/wmoGroupObject.h @@ -15,7 +15,7 @@ class WmoGroupObject; class WmoGroupObject { public: - WmoGroupObject(mathfu::mat4 &modelMatrix, ApiContainer *api, SMOGroupInfo &groupInfo, int groupNumber) : m_api(api){ + WmoGroupObject(mathfu::mat4 &modelMatrix, HApiContainer api, SMOGroupInfo &groupInfo, int groupNumber) : m_api(api){ m_modelMatrix = &modelMatrix; m_groupNumber = groupNumber; m_main_groupInfo = &groupInfo; @@ -34,21 +34,24 @@ class WmoGroupObject { return m_localGroupBorder; } const HWmoGroupGeom getWmoGroupGeom() const { return m_geom; }; - const std::vector > *getDoodads() const { return &m_doodads; }; + const std::vector > *getDoodads() const { + return &m_doodads; + }; void setWmoApi(IWmoApi *api); IWmoApi *getWmoApi() { return m_wmoApi; }; void setModelFileName(std::string modelName); void setModelFileId(int fileId); - void collectMeshes(std::vector &renderedThisFrame, int renderOrder); + void collectMeshes(std::vector &opaqueMeshes, std::vector &transparentMeshes, int renderOrder); bool getDontUseLocalLightingForM2() { return !m_useLocalLightingForM2; }; bool doPostLoad(); void update(); void uploadGeneratorBuffers(); - bool checkGroupFrustum(mathfu::vec4 &cameraVec4, + void checkGroupFrustum(bool &drawDoodads, bool &drawGroup, + mathfu::vec4 &cameraVec4, std::vector &frustumPlanes, std::vector &points); @@ -62,7 +65,7 @@ class WmoGroupObject { PointerChecker &portalRels, std::vector &candidateGroups); private: - ApiContainer *m_api = nullptr; + HApiContainer m_api = nullptr; IWmoApi *m_wmoApi = nullptr; HWmoGroupGeom m_geom = nullptr; diff --git a/wowViewerLib/src/engine/objects/wmo/wmoObject.cpp b/wowViewerLib/src/engine/objects/wmo/wmoObject.cpp index 22d91b4e7..6e37034d9 100644 --- a/wowViewerLib/src/engine/objects/wmo/wmoObject.cpp +++ b/wowViewerLib/src/engine/objects/wmo/wmoObject.cpp @@ -79,14 +79,23 @@ std::shared_ptr WmoObject::getDoodad(int index) { return nullptr; } + SMODoodadSet *defaultDooodadSetDef = &this->mainGeom->doodadSets[0]; SMODoodadSet *doodadSetDef = &this->mainGeom->doodadSets[doodadsSet]; - if (index < doodadSetDef->firstinstanceindex - || index > doodadSetDef->firstinstanceindex + doodadSetDef->numDoodads) return nullptr; - int doodadIndex = index - doodadSetDef->firstinstanceindex; + bool isInDefaultDoodadSetDef = + (index >= defaultDooodadSetDef->firstinstanceindex) && + (index < defaultDooodadSetDef->firstinstanceindex + defaultDooodadSetDef->numDoodads); - auto doodadObject = this->m_doodadsArray[doodadIndex]; - if (doodadObject != nullptr) return doodadObject; + bool isInCurrentDoodadSetDef = + (index >= doodadSetDef->firstinstanceindex) && + (index < doodadSetDef->firstinstanceindex + doodadSetDef->numDoodads); + + if (!isInCurrentDoodadSetDef && !isInDefaultDoodadSetDef) + return nullptr; + + auto iterator = this->m_doodadsUnorderedMap.find(index); + if (iterator != this->m_doodadsUnorderedMap.end()) + return iterator->second; SMODoodadDef *doodadDef = &this->mainGeom->doodadDefs[index]; @@ -105,7 +114,7 @@ std::shared_ptr WmoObject::getDoodad(int index) { } auto m2Object = std::make_shared(m_api); - m2Object->setDiffuseColor(doodadDef->color); + m2Object->setLoadParams(0, {},{}); if (fileIdMode) { m2Object->setModelFileId(doodadfileDataId); @@ -115,7 +124,18 @@ std::shared_ptr WmoObject::getDoodad(int index) { m2Object->createPlacementMatrix(*doodadDef, m_placementMatrix); m2Object->calcWorldPosition(); - this->m_doodadsArray[doodadIndex] = m2Object; + if (doodadDef->color.a != 255 && doodadDef->color.a < this->mainGeom->lightsLen) { + + auto &light = this->mainGeom->lights[doodadDef->color.a]; + m2Object->setDiffuseColor(light.color); + + + std::cout << "Found index into MOLT = " << (int)doodadDef->color.a << std::endl; + } else { + m2Object->setDiffuseColor(doodadDef->color); + } + + this->m_doodadsUnorderedMap[index] = m2Object; return m2Object; } @@ -369,6 +389,7 @@ void WmoObject::update() { groupObjectsLod2[i]->update(); } } + } void WmoObject::uploadGeneratorBuffers() { if (!m_loaded) return; @@ -461,7 +482,7 @@ void WmoObject::drawDebugLights(){ void WmoObject::drawTransformedPortalPoints(){ - return; + #ifndef CULLED_NO_PORTAL_DRAWING /* if (!m_loaded) return; @@ -637,14 +658,12 @@ void WmoObject::updateTransformedAntiPortalPoints(){ } void WmoObject::setLoadingParam(SMMapObjDef &mapObjDef) { - //this->m_placementMatrix = mathfu::mat4::Identity(); createPlacementMatrix(mapObjDef); this->m_doodadSet = mapObjDef.doodadSet; this->m_nameSet = mapObjDef.nameSet; } void WmoObject::setLoadingParam(SMMapObjDefObj1 &mapObjDef) { - //this->m_placementMatrix = mathfu::mat4::Identity(); createPlacementMatrix(mapObjDef); this->m_doodadSet = mapObjDef.doodadSet; @@ -711,9 +730,7 @@ void WmoObject::createBB(CAaBox bbox) { void WmoObject::updateBB() { CAaBox &AABB = this->m_bbox; -// -// var dontUseLocalLighting = ((mogp.flags & 0x40) > 0) || ((mogp.flags & 0x8) > 0); -// + for (int j = 0; j < this->groupObjects.size(); j++) { std::shared_ptr wmoGroupObject= this->groupObjects[j]; @@ -731,8 +748,11 @@ void WmoObject::updateBB() { } } +CAaBox WmoObject::getAABB() { + return this->m_bbox; +} + void WmoObject::createM2Array() { - this->m_doodadsArray = std::vector>(this->mainGeom->doodadDefsLen, nullptr); } void WmoObject::postWmoGroupObjectLoad(int groupId, int lod) { @@ -816,21 +836,31 @@ bool WmoObject::startTraversingWMOGroup( exteriorView.frustumPlanes.push_back(frustumPlanesExt); } - exteriorView.drawnWmos.push_back(this->groupObjects[i]); + exteriorView.drawnWmos.insert(this->groupObjects[i]); } } - if (traversingFromInterior) { - InteriorView &interiorView = createdInteriorViews[groupId]; + if (traversingFromInterior && m_api->getConfig()->usePortalCulling) { std::shared_ptr nextGroupObject = groupObjects[groupId]; + if (nextGroupObject->getIsLoaded() && nextGroupObject->getWmoGroupGeom()->mogp->flags2.isSplitGroupChild) { + groupId = nextGroupObject->getWmoGroupGeom()->mogp->parentSplitOrFirstChildGroupIndex; + nextGroupObject = groupObjects[groupId]; + } + + InteriorView &interiorView = createdInteriorViews[groupId]; //5.1 The portal is into interior wmo group. So go on. if (!interiorView.viewCreated) { interiorView.viewCreated = true; - interiorView.drawnWmos.push_back(nextGroupObject); + interiorView.drawnWmos.insert(nextGroupObject); + interiorView.wmosForM2.insert(nextGroupObject); interiorView.portalIndex = -1; interiorView.frustumPlanes.push_back(frustumPlanesExt); } + if (nextGroupObject->getIsLoaded() && nextGroupObject->getWmoGroupGeom()->mogp->flags2.isSplitGroupParent) { + this->addSplitChildWMOsToView(interiorView, groupId); + } + if (globalLevel+1 >= interiorView.level) { interiorView.level = globalLevel + 1; } else { @@ -851,17 +881,6 @@ bool WmoObject::startTraversingWMOGroup( globalLevel, 0); } else { - if (exteriorView.viewCreated) { - frustumPlanesExt = exteriorView.frustumPlanes[0]; - frustumPlanes.clear(); - std::transform(frustumPlanesExt.begin(), frustumPlanesExt.end(), - std::back_inserter(frustumPlanes), - [&](const mathfu::vec4 &p) -> mathfu::vec4 { - return transposeModelMat * p; - } - ); - } - if (!exteriorView.viewCreated) { exteriorView.viewCreated = true; exteriorView.frustumPlanes.push_back(frustumPlanesExt); @@ -875,22 +894,29 @@ bool WmoObject::startTraversingWMOGroup( for (int i = 0; i< mainGeom->groupsLen; i++) { - if ((mainGeom->groups[i].flags.EXTERIOR) > 0) { //exterior - if (this->groupObjects[i] != nullptr && this->groupObjects[i]->checkGroupFrustum(cameraVec4, frustumPlanesExt, frustumPointsExt)) { - exteriorView.drawnWmos.push_back(this->groupObjects[i]); - - this->transverseGroupWMO( - i, - false, - createdInteriorViews, - exteriorView, - cameraVec4, - cameraLocal, - inverseTransposeModelMat, - transverseVisitedPortals, - frustumPlanes, - globalLevel, - 0); + if ((mainGeom->groups[i].flags.EXTERIOR) > 0 || !m_api->getConfig()->usePortalCulling) { //exterior + if (this->groupObjects[i] != nullptr) { + bool drawDoodads, drawGroup; + this->groupObjects[i]->checkGroupFrustum(drawDoodads, drawGroup, cameraVec4, frustumPlanesExt, frustumPointsExt); + if (drawDoodads) { + exteriorView.wmosForM2.insert(this->groupObjects[i]); + } + if (drawGroup) { + exteriorView.drawnWmos.insert(this->groupObjects[i]); + + this->transverseGroupWMO( + i, + false, + createdInteriorViews, + exteriorView, + cameraVec4, + cameraLocal, + inverseTransposeModelMat, + transverseVisitedPortals, + frustumPlanes, + globalLevel, + 0); + } } } } @@ -933,8 +959,34 @@ bool WmoObject::startTraversingWMOGroup( //M2s will be collected later from separate function call return true; } -static const float dotepsilon = pow(1.5f, 2.0f); +void WmoObject::addSplitChildWMOsToView(InteriorView &interiorView, int groupId) { + if (!groupObjects[groupId]->getIsLoaded()) + return; + + auto &parentMogp = groupObjects[groupId]->getWmoGroupGeom()->mogp; + if (!parentMogp->flags2.isSplitGroupParent) + return; + + int nextChildGroupIndex = parentMogp->parentSplitOrFirstChildGroupIndex; + while (nextChildGroupIndex != -1) { + auto &groupWmo = groupObjects[nextChildGroupIndex]; + if (!groupWmo->getIsLoaded()) + return; + + auto &mogp = groupWmo->getWmoGroupGeom()->mogp; + if (!mogp->flags2.isSplitGroupChild) + break; + + interiorView.drawnWmos.insert(groupWmo); + interiorView.wmosForM2.insert(groupWmo); + nextChildGroupIndex = mogp->nextSplitGroupChildIndex; + } + +} + + +static const float dotepsilon = pow(1.5f, 2.0f); void WmoObject::transverseGroupWMO( int groupId, bool traversingStartedFromInterior, @@ -961,10 +1013,12 @@ void WmoObject::transverseGroupWMO( int numItems = groupObjects[groupId]->getWmoGroupGeom()->mogp->moprCount; if (groupObjects[groupId]->getWmoGroupGeom()->mogp->flags.showSkyBox) { - skyBox->checkFrustumCulling(cameraVec4, - {}, - {}); - allInteriorViews[groupId].drawnM2s.push_back(skyBox); + if (skyBox != nullptr) { + skyBox->checkFrustumCulling(cameraVec4, + {}, + {}); + allInteriorViews[groupId].drawnM2s.insert(skyBox); + } } for (int j = moprIndex; j < moprIndex+numItems; j++) { @@ -987,6 +1041,37 @@ void WmoObject::transverseGroupWMO( //So I need to make this hack exactly for this case.z bool hackCondition = (fabs(dotResult) > dotepsilon); + + /* Test code + auto crossProd = mathfu::vec3::CrossProduct( + geometryPerPortal[relation->portal_index].sortedVericles[0] - geometryPerPortal[relation->portal_index].sortedVericles[1], + geometryPerPortal[relation->portal_index].sortedVericles[0] - geometryPerPortal[relation->portal_index].sortedVericles[2] + ); + crossProd = crossProd.Normalized(); + float distantce = mathfu::vec3::DotProduct(crossProd,geometryPerPortal[relation->portal_index].sortedVericles[0]); + mathfu::vec4 manualEquation = mathfu::vec4(crossProd.x, crossProd.y, crossProd.z, -distantce); + */ + //Hacks to test! + if (!isInsidePortalThis) + { + std::vector worldSpacePortalVertices; + std::vector portalVerticesVec; + std::transform( + geometryPerPortal[relation->portal_index].sortedVericles.begin(), + geometryPerPortal[relation->portal_index].sortedVericles.end(), + std::back_inserter(portalVerticesVec), + [](mathfu::vec3 &d) -> mathfu::vec3 { return d;} + ); + std::transform(portalVerticesVec.begin(), portalVerticesVec.end(), + std::back_inserter(worldSpacePortalVertices), + [&](mathfu::vec3 &p) -> mathfu::vec3 { + return (this->m_placementMatrix * mathfu::vec4(p, 1.0f)).xyz(); + } + ); + InteriorView &interiorView = allInteriorViews[groupId]; + interiorView.worldPortalVertices.push_back(worldSpacePortalVertices); + } + if (!isInsidePortalThis && hackCondition) continue; //2.1 If portal has less than 4 vertices - skip it(invalid?) @@ -998,7 +1083,7 @@ void WmoObject::transverseGroupWMO( geometryPerPortal[relation->portal_index].sortedVericles.begin(), geometryPerPortal[relation->portal_index].sortedVericles.end(), std::back_inserter(portalVerticesVec), - [](mathfu::vec3 d) -> mathfu::vec3 { return d;} + [](mathfu::vec3 &d) -> mathfu::vec3 { return d;} ); bool visible = MathHelper::planeCull(portalVerticesVec, localFrustumPlanes); @@ -1045,6 +1130,15 @@ void WmoObject::transverseGroupWMO( } ); + std::vector worldSpacePortalVertices; + + std::transform(portalVerticesVec.begin(), portalVerticesVec.end(), + std::back_inserter(worldSpacePortalVertices), + [&](mathfu::vec3 &p) -> mathfu::vec3 { + return (this->m_placementMatrix * mathfu::vec4(p, 1.0f)).xyz(); + } + ); + //5. Traverse next std::shared_ptr nextGroupObject = groupObjects[nextGroup]; SMOGroupInfo &nextGroupInfo = mainGeom->groups[nextGroup]; @@ -1053,11 +1147,13 @@ void WmoObject::transverseGroupWMO( //5.1 The portal is into interior wmo group. So go on. if (!interiorView.viewCreated) { interiorView.viewCreated = true; - interiorView.drawnWmos.push_back(nextGroupObject); + interiorView.ownerGroupWMO = nextGroupObject; + interiorView.drawnWmos.insert(nextGroupObject); + interiorView.wmosForM2.insert(nextGroupObject); interiorView.portalIndex = relation->portal_index; } - interiorView.portalVertices.push_back(portalVerticesVec); + interiorView.worldPortalVertices.push_back(worldSpacePortalVertices); interiorView.frustumPlanes.push_back(worldSpaceFrustumPlanes); if (globalLevel+1 >= interiorView.level) { interiorView.level = globalLevel + 1; @@ -1065,6 +1161,10 @@ void WmoObject::transverseGroupWMO( assert("BVH is not working. Something is wrong!"); } + if (nextGroupObject->getIsLoaded() && nextGroupObject->getWmoGroupGeom()->mogp->flags2.isSplitGroupParent) { + this->addSplitChildWMOsToView(interiorView, nextGroup); + } + transverseGroupWMO( nextGroup, traversingStartedFromInterior, @@ -1086,7 +1186,7 @@ void WmoObject::transverseGroupWMO( exteriorView.viewCreated = true; exteriorView.level = globalLevel + 1; - exteriorView.portalVertices.push_back(portalVerticesVec); + exteriorView.worldPortalVertices.push_back(worldSpacePortalVertices); exteriorView.frustumPlanes.push_back(worldSpaceFrustumPlanes); } } @@ -1292,26 +1392,42 @@ std::function WmoObject::getAttenFunction() { } ; } -bool WmoObject::checkFog(mathfu::vec3 &cameraPos, CImVector &fogColor) { +void WmoObject::checkFog(mathfu::vec3 &cameraPos, std::vector &fogResults) { mathfu::vec3 cameraLocal = (m_placementInvertMatrix * mathfu::vec4(cameraPos, 1.0)).xyz(); for (int i = mainGeom->fogsLen-1; i >= 0; i--) { SMOFog &fogRecord = mainGeom->fogs[i]; mathfu::vec3 fogPosVec = mathfu::vec3(fogRecord.pos); float distanceToFog = (fogPosVec - cameraLocal).Length(); - if ((distanceToFog < fogRecord.larger_radius) /*|| fogRecord.larger_radius == 0*/) { - - fogColor.r = fogRecord.fog.color.r; - fogColor.g = fogRecord.fog.color.g; - fogColor.b = fogRecord.fog.color.b; -// this.sceneApi.setFogColor(fogRecord.fog_colorF); - //this.sceneApi.setFogStart(wmoFile.mfog.fog_end); -// this.sceneApi.setFogEnd(fogRecord.fog_end); - return true; + if ((distanceToFog < fogRecord.larger_radius) || fogRecord.flag_infinite_radius) { + LightResult wmoFog; + wmoFog.id = -1; + wmoFog.FogScaler = fogRecord.fog.start_scalar; + wmoFog.FogEnd = fogRecord.fog.end; + wmoFog.FogDensity = 0.18f; + + wmoFog.FogHeightScaler = 0; + wmoFog.FogHeightDensity = 0; + wmoFog.SunFogAngle = 0; + wmoFog.EndFogColorDistance = 0; + wmoFog.SunFogStrength = 0; + if (fogRecord.flag_infinite_radius) { + wmoFog.blendCoef = 1.0; + wmoFog.isDefault = true; + } else { + wmoFog.blendCoef = std::min( + (fogRecord.larger_radius - distanceToFog) / (fogRecord.larger_radius - fogRecord.smaller_radius), + 1.0f); + wmoFog.isDefault = false; + } + std::array fogColor = {fogRecord.fog.color.b/255.0f, fogRecord.fog.color.g/255.0f, fogRecord.fog.color.r/255.0f}; + wmoFog.EndFogColor = fogColor; + wmoFog.SunFogColor = fogColor; + wmoFog.FogHeightColor = fogColor; + + fogResults.push_back(wmoFog); } } - - return false; } bool WmoObject::hasPortals() { @@ -1348,3 +1464,29 @@ int WmoObject::getWmoGroupId(int groupNum) { return groupObjects[groupNum]->getWmoGroupGeom()->mogp->wmoGroupID; } + +mathfu::vec3 WmoObject::getAmbientColor() { + CImVector ambientColor = mainGeom->header->ambColor; + if (mainGeom->mavgs != nullptr) { + int recordIndex = 0; + for (int i = 0; i < mainGeom->mavgsLen; i++) { + if (mainGeom->mavgs[i].doodadSetID == m_doodadSet) { + recordIndex = i; + break; + } + } + auto &record = mainGeom->mavgs[recordIndex]; + if (record.flags & 1) { + ambientColor = record.color3; + } else { + ambientColor = record.color1; + } + } + mathfu::vec3 ambColor = mathfu::vec3( + ((float) ambientColor.r / 255.0f), + ((float) ambientColor.g / 255.0f), + ((float) ambientColor.b / 255.0f) + ); + + return ambColor; +} diff --git a/wowViewerLib/src/engine/objects/wmo/wmoObject.h b/wowViewerLib/src/engine/objects/wmo/wmoObject.h index 3b2b42700..e6b49cb18 100644 --- a/wowViewerLib/src/engine/objects/wmo/wmoObject.h +++ b/wowViewerLib/src/engine/objects/wmo/wmoObject.h @@ -33,12 +33,12 @@ struct WmoGroupResult { class WmoObject : public IWmoApi { public: - WmoObject(ApiContainer *api) : m_api(api) { + WmoObject(HApiContainer api) : m_api(api) { } ~WmoObject(); private: - ApiContainer *m_api; + HApiContainer m_api; HWmoMainGeom mainGeom = nullptr; bool m_loading = false; @@ -46,7 +46,7 @@ class WmoObject : public IWmoApi { CAaBox m_bbox; int m_nameSet; - int m_doodadSet; + int m_doodadSet = -1; std::vector geometryPerPortal; @@ -64,7 +64,7 @@ class WmoObject : public IWmoApi { std::vector drawGroupWMO; std::vector lodGroupLevelWMO; - std::vector> m_doodadsArray; + std::unordered_map> m_doodadsUnorderedMap; std::shared_ptr skyBox = nullptr; @@ -108,6 +108,7 @@ class WmoObject : public IWmoApi { virtual std::function getAttenFunction() override; virtual SMOHeader *getWmoHeader() override; + mathfu::vec3 getAmbientColor() override; virtual PointerChecker &getMaterials() override; @@ -123,7 +124,7 @@ class WmoObject : public IWmoApi { void createGroupObjects(); - bool checkFog(mathfu::vec3 &cameraPos, CImVector &fogColor); + void checkFog(mathfu::vec3 &cameraPos, std::vector &fogResults); bool doPostLoad(int &processedThisFrame); void update(); @@ -132,6 +133,8 @@ class WmoObject : public IWmoApi { void createM2Array(); void updateBB() override ; + CAaBox getAABB(); + public: //Portal culling // bool startTraversingFromInteriorWMO ( @@ -163,6 +166,9 @@ class WmoObject : public IWmoApi { std::vector &frustumPlanes, std::vector> &m2Candidates); + void addSplitChildWMOsToView(InteriorView &interiorView, int groupId); + + void transverseGroupWMO ( int groupId, bool traversingStartedFromInterior, diff --git a/wowViewerLib/src/engine/objects/wowFrameData.h b/wowViewerLib/src/engine/objects/wowFrameData.h index dad7f67fc..3b23aa01a 100644 --- a/wowViewerLib/src/engine/objects/wowFrameData.h +++ b/wowViewerLib/src/engine/objects/wowFrameData.h @@ -28,9 +28,7 @@ class WoWFrameData { mathfu::vec4 m_globalAmbientColor; mathfu::vec4 m_globalSunColor; - float uFogStart = -1; - float uFogEnd = -1; - mathfu::vec4 m_fogColor = mathfu::vec4(1.0, 1.0, 1.0, 1.0); + //Frustum culling results std::vector m_currentInteriorGroups = {}; diff --git a/wowViewerLib/src/engine/opengl/header.h b/wowViewerLib/src/engine/opengl/header.h index 86d7a3c70..21a37bf3b 100644 --- a/wowViewerLib/src/engine/opengl/header.h +++ b/wowViewerLib/src/engine/opengl/header.h @@ -2,26 +2,42 @@ #define WOWMAPVIEWERREVIVED_HEADER_H #define GL_GLEXT_PROTOTYPES 1 -#if defined(WITH_GLESv2) || defined(EMSCRIPTEN) +#ifdef __ANDROID__ +#define WITH_GLESv2 1 +#endif + +#if defined(WITH_GLESv2) || defined(__EMSCRIPTEN__) #include #include -#ifndef EMSCRIPTEN -#include -#include -#else + #define GL_BGRA GL_BGRA_EXT #define glClearDepth glClearDepthf #define glDepthRange glDepthRangef -#endif #define glGenVertexArrays glGenVertexArraysOES #define glBindVertexArray glBindVertexArrayOES +#define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR +#define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR +#define glDebugMessageControl glDebugMessageControlKHR +#define glDebugMessageCallback glDebugMessageCallbackKHR + +#define glPushDebugGroup glPushDebugGroupKHR +#define glPopDebugGroup glPopDebugGroupKHR +#define glDebugMessageInsert glDebugMessageInsertKHR +#define GL_DEBUG_SOURCE_APPLICATION GL_DEBUG_SOURCE_APPLICATION_KHR +#define GL_DEBUG_TYPE_MARKER GL_DEBUG_TYPE_MARKER_KHR +#define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR +#define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR #else #ifdef _WIN32 #undef GLFW_INCLUDE_VULKAN #include +#elif defined(__APPLE__) +#include +#include +#include #else #include #include diff --git a/wowViewerLib/src/engine/persistance/adtFile.cpp b/wowViewerLib/src/engine/persistance/adtFile.cpp index 3c31cee4e..0570b4e32 100644 --- a/wowViewerLib/src/engine/persistance/adtFile.cpp +++ b/wowViewerLib/src/engine/persistance/adtFile.cpp @@ -81,6 +81,28 @@ chunkDef AdtFile::adtFileTable = { } } }, + { + 'MTXF', + { + [](AdtFile& file, ChunkData& chunkData){ + debuglog("Entered MTXF"); + std::cout << "Found MTXF" << std::endl; + file.mtxf_len = chunkData.chunkLen / sizeof(SMTextureFlags); + chunkData.readValues(file.mtxf, file.mtxf_len); + } + } + }, + { + 'MAMP', + { + [](AdtFile& file, ChunkData& chunkData){ + debuglog("Entered MAMP"); +// std::cout << "Found MAMP" << std::endl; + file.mamp_len = chunkData.chunkLen / sizeof(char); + chunkData.readValues(file.mamp, file.mamp_len); + } + } + }, { 'MMDX', { @@ -146,6 +168,18 @@ chunkDef AdtFile::adtFileTable = { } } }, + { + 'MLMX', + { + [](AdtFile& file, ChunkData& chunkData){ + debuglog("Entered MLMX"); + + file.wmoLodExtents_len = chunkData.chunkLen / sizeof(LodExtents); + chunkData.readValues(file.wmoLodExtents, file.wmoLodExtents_len); + } + } + }, + { 'MLDL', { @@ -385,6 +419,16 @@ chunkDef AdtFile::adtFileTable = { } } }, + { + 'MLHD', + { + [](AdtFile& file, ChunkData& chunkData){ + debuglog("Entered MLHD "); + + chunkData.readValue(file.mlHeader); + } + } + }, { 'MLVH', { diff --git a/wowViewerLib/src/engine/persistance/adtFile.h b/wowViewerLib/src/engine/persistance/adtFile.h index 61deede26..d2536c619 100644 --- a/wowViewerLib/src/engine/persistance/adtFile.h +++ b/wowViewerLib/src/engine/persistance/adtFile.h @@ -65,12 +65,21 @@ class AdtFile: public PersistentFile { PointerChecker mtxp = mtxp_len; int mtxp_len = 0; + PointerChecker mtxf = mtxf_len; + int mtxf_len = 0; + + PointerChecker mamp = mamp_len; + int mamp_len = 0; + PointerChecker doodadDef = doodadDef_len; int doodadDef_len = 0; PointerChecker mapObjDef = mapObjDef_len; int mapObjDef_len = 0; + PointerChecker wmoLodExtents = wmoLodExtents_len; + int wmoLodExtents_len = 0; + PointerChecker doodadDefObj1 = doodadDefObj1_len; int doodadDefObj1_len = 0; @@ -83,6 +92,8 @@ class AdtFile: public PersistentFile { PointerChecker floatDataBlob = floatDataBlob_len; int floatDataBlob_len = 0; + MLHeader * mlHeader = nullptr; + PointerChecker mvli_indicies = mvli_len; int mvli_len = 0; diff --git a/wowViewerLib/src/engine/persistance/animFile.cpp b/wowViewerLib/src/engine/persistance/animFile.cpp index 956cf9757..a90809f8c 100644 --- a/wowViewerLib/src/engine/persistance/animFile.cpp +++ b/wowViewerLib/src/engine/persistance/animFile.cpp @@ -17,20 +17,20 @@ chunkDef AnimFile::animFileTable = { } }, { - 'AFSA', + 'ASFA', { [](AnimFile &file, ChunkData &chunkData) { - - + file.m_animFileAttachAnimDataBlob_len = chunkData.chunkLen; + chunkData.readValues(file.m_animFileAttachAnimDataBlob, chunkData.chunkLen); }, } }, { - 'AFSB', + 'BSFA', { [](AnimFile &file, ChunkData &chunkData) { - - + file.m_animFileBoneAnimDataBlob_len = chunkData.chunkLen; + chunkData.readValues(file.m_animFileBoneAnimDataBlob, chunkData.chunkLen); }, } } @@ -40,13 +40,10 @@ chunkDef AnimFile::animFileTable = { void AnimFile::process(HFileContent animFile, const std::string &fileName) { m_animFile = animFile; auto &fileVec = *m_animFile.get(); - if ( + int chunk = *(uint32_t *) &fileVec[0]; + - fileVec[0] == 'A' && - fileVec[1] == 'F' && - fileVec[2] == 'M' && - fileVec[3] == '2' - ) { + if (chunk == 'BSFA' || chunk == 'ASFA' || chunk == '2MFA') { CChunkFileReader reader(fileVec); reader.processFile(*this, &AnimFile::animFileTable); } else { diff --git a/wowViewerLib/src/engine/persistance/animFile.h b/wowViewerLib/src/engine/persistance/animFile.h index 3b080c95f..e9d3caa7c 100644 --- a/wowViewerLib/src/engine/persistance/animFile.h +++ b/wowViewerLib/src/engine/persistance/animFile.h @@ -21,9 +21,14 @@ class AnimFile : public PersistentFile { this->m_postLoadFunction = postLoadFunction; }; public: - uint8_t *m_animFileDataBlob = 0; + uint8_t *m_animFileDataBlob = nullptr; int m_animFileDataBlob_len = -1; + uint8_t *m_animFileBoneAnimDataBlob = nullptr; + int m_animFileBoneAnimDataBlob_len = -1; + + uint8_t *m_animFileAttachAnimDataBlob = nullptr; + int m_animFileAttachAnimDataBlob_len = -1; private: HFileContent m_animFile; diff --git a/wowViewerLib/src/engine/persistance/header/M2FileHeader.cpp b/wowViewerLib/src/engine/persistance/header/M2FileHeader.cpp new file mode 100644 index 000000000..21349dea7 --- /dev/null +++ b/wowViewerLib/src/engine/persistance/header/M2FileHeader.cpp @@ -0,0 +1,195 @@ +// +// Created by Deamon on 9/15/2020. +// + +#include "M2FileHeader.h" + +void initEXP2(EXP2 *exp2) { + exp2->content.initM2Array(exp2); + for (int i = 0; i < exp2->content.size; i++) { + Exp2Record *exp2Record = exp2->content.getElement(i); + exp2Record->unk3.timestamps.initM2Array(exp2); + exp2Record->unk3.values.initM2Array(exp2); + } +} + +void initM2Textures(void *sectionStart, M2Array &textures) { + int32_t texturesSize = textures.size; + for (int i = 0; i < texturesSize; i++) { + M2Texture *texture = textures.getElement(i); + texture->filename.initM2Array(sectionStart); + } +} + +void initCompBones(void *sectionStart, M2Array *bones, M2Array *sequences, + CM2SequenceLoad *cm2SequenceLoad) { + int32_t bonesSize = bones->size; + for (int i = 0; i < bonesSize; i++) { + M2CompBone *compBone = bones->getElement(i); + compBone->translation.initTrack(sectionStart, sequences, cm2SequenceLoad); + compBone->rotation.initTrack(sectionStart, sequences, cm2SequenceLoad); + compBone->scaling.initTrack(sectionStart, sequences, cm2SequenceLoad); + } +} + +void initM2Color(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { + int32_t m2ColorSize = m2Header->colors.size; + for (int i = 0; i < m2ColorSize; i++) { + M2Color * m2Color = m2Header->colors.getElement(i); + m2Color->alpha.initTrack(m2Header, sequences, cm2SequenceLoad); + m2Color->color.initTrack(m2Header, sequences, cm2SequenceLoad); + } +} + +void initM2TextureWeight(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { + int32_t textureWeightSize = m2Header->texture_weights.size; + for (int i = 0; i < textureWeightSize; i++) { + M2TextureWeight * textureWeight = m2Header->texture_weights.getElement(i); + textureWeight->weight.initTrack(m2Header, sequences, cm2SequenceLoad); + } +} + +void initM2TextureTransform(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { + int32_t textureTransformSize = m2Header->texture_transforms.size; + for (int i = 0; i < textureTransformSize; i++) { + M2TextureTransform * textureTransform = m2Header->texture_transforms.getElement(i); + textureTransform->translation.initTrack(m2Header, sequences, cm2SequenceLoad); + textureTransform->rotation.initTrack(m2Header, sequences, cm2SequenceLoad); + textureTransform->scaling.initTrack(m2Header, sequences, cm2SequenceLoad); + } +} + +void initM2Attachment(void *sectionStart, M2Array *attachments, M2Array *sequences, + CM2SequenceLoad *cm2SequenceLoad) { + int32_t attachmentCount = attachments->size; + for (int i = 0; i < attachmentCount; i++) { + M2Attachment *attachment = attachments->getElement(i); + attachment->animate_attached.initTrack(sectionStart, sequences, cm2SequenceLoad); + } +} + +void initM2Event(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { + int32_t eventCount = m2Header->events.size; + for (int i = 0; i < eventCount; i++) { + M2Event *event = m2Header->events.getElement(i); + event->enabled.initTrackBase(m2Header, sequences, cm2SequenceLoad); + } +} + +void initM2Light(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { + int32_t lightCount = m2Header->lights.size; + for (int i = 0; i < lightCount; i++) { + M2Light *light = m2Header->lights.getElement(i); + light->ambient_color.initTrack(m2Header, sequences, cm2SequenceLoad); + light->ambient_intensity.initTrack(m2Header, sequences, cm2SequenceLoad); + light->diffuse_color.initTrack(m2Header, sequences, cm2SequenceLoad); + light->diffuse_intensity.initTrack(m2Header, sequences, cm2SequenceLoad); + light->attenuation_start.initTrack(m2Header, sequences, cm2SequenceLoad); + light->attenuation_end.initTrack(m2Header, sequences, cm2SequenceLoad); + light->visibility.initTrack(m2Header, sequences, cm2SequenceLoad); + } +} + +void initM2Particle(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { + assert(sizeof(M2Particle) == 492); + int32_t particleEmitterCount = m2Header->particle_emitters.size; + for (int i = 0; i < particleEmitterCount; i++) { + M2Particle *particleEmitter = m2Header->particle_emitters.getElement(i); + + particleEmitter->old.emissionSpeed.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.speedVariation.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.verticalRange.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.horizontalRange.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.gravity.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.lifespan.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.emissionRate.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.emissionAreaLength.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.emissionAreaWidth.initTrack(m2Header, sequences, cm2SequenceLoad); + particleEmitter->old.zSource.initTrack(m2Header, sequences, cm2SequenceLoad); + + particleEmitter->old.enabledIn.initTrack(m2Header, sequences, cm2SequenceLoad); + } +} + +void initM2Ribbon(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { + int32_t ribbonEmitterCount = m2Header->ribbon_emitters.size; + for (int i = 0; i < ribbonEmitterCount; i++) { + M2Ribbon *ribbonEmitter = m2Header->ribbon_emitters.getElement(i); + + ribbonEmitter->textureIndices.initM2Array(m2Header); + ribbonEmitter->materialIndices.initM2Array(m2Header); + + ribbonEmitter->colorTrack.initTrack(m2Header, sequences, cm2SequenceLoad); + ribbonEmitter->alphaTrack.initTrack(m2Header, sequences, cm2SequenceLoad); + ribbonEmitter->heightAboveTrack.initTrack(m2Header, sequences, cm2SequenceLoad); + ribbonEmitter->heightBelowTrack.initTrack(m2Header, sequences, cm2SequenceLoad); + ribbonEmitter->texSlotTrack.initTrack(m2Header, sequences, cm2SequenceLoad); + ribbonEmitter->visibilityTrack.initTrack(m2Header, sequences, cm2SequenceLoad); + } +} + +void initM2Camera(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { + int32_t cameraCount = m2Header->cameras.size; + for (int i = 0; i < cameraCount; i++) { + M2Camera *camera = m2Header->cameras.getElement(i); + camera->positions.initTrack(m2Header, sequences, cm2SequenceLoad); + camera->target_position.initTrack(m2Header, sequences, cm2SequenceLoad); + camera->roll.initTrack(m2Header, sequences, cm2SequenceLoad); + camera->FoV.initTrack(m2Header, sequences, cm2SequenceLoad); + } +} + +void initM2ParticlePartTracks(M2Data *m2Header) { + assert(sizeof(M2Particle) == 492); + int32_t particleEmitterCount = m2Header->particle_emitters.size; + for (int i = 0; i < particleEmitterCount; i++) { + M2Particle *particleEmitter = m2Header->particle_emitters.getElement(i); + + particleEmitter->old.geometry_model_filename.initM2Array(m2Header); + particleEmitter->old.recursion_model_filename.initM2Array(m2Header); + + particleEmitter->old.alphaTrack.initPartTrack(m2Header); + particleEmitter->old.colorTrack.initPartTrack(m2Header); + particleEmitter->old.scaleTrack.initPartTrack(m2Header); + particleEmitter->old.headCellTrack.initPartTrack(m2Header); + particleEmitter->old.tailCellTrack.initPartTrack(m2Header); + + particleEmitter->old.splinePoints.initM2Array(m2Header); + } +} + +int findAnimationIndex(uint32_t anim_id, M2Array *sequence_lookupsPtr, M2Array *sequencesPtr) { + auto &sequences = *sequencesPtr; + auto &sequence_lookups = *sequence_lookupsPtr; + + if (sequence_lookups.size == 0) { + int animationIndex = -1; + if ((sequences.size > 0)) { + for (int i = 0; i < sequences.size; i++) { + const M2Sequence* animationRecord = sequences[i]; + if (animationRecord->id == anim_id) { + animationIndex = i; + break; + } + } + } + return animationIndex; + } + + size_t i (anim_id % sequence_lookups.size); + + for (size_t stride (1); true; ++stride) + { + if (*sequence_lookups[i] == -1) + { + return -1; + } + if (sequences[*sequence_lookups[i]]->id == anim_id) + { + return *sequence_lookups[i]; + } + + i = (i + stride * stride) % sequence_lookups.size; + // so original_i + 1, original_i + 1 + 4, original_i + 1 + 4 + 9, … + } +} \ No newline at end of file diff --git a/wowViewerLib/src/engine/persistance/header/M2FileHeader.h b/wowViewerLib/src/engine/persistance/header/M2FileHeader.h index 0b36d74ad..7179670e5 100644 --- a/wowViewerLib/src/engine/persistance/header/M2FileHeader.h +++ b/wowViewerLib/src/engine/persistance/header/M2FileHeader.h @@ -154,7 +154,8 @@ struct M2Ribbon M2Track visibilityTrack; int16_t priorityPlane; - uint16_t padding; + int8_t ribbonColorIndex; + int8_t textureTransformLookupIndex; }; @@ -334,11 +335,14 @@ struct M2Data { uint32_t flag_camera_related : 1; // TODO: verify version uint32_t flag_new_particle_record : 1; // In CATA: new version of ParticleEmitters. By default, length of M2ParticleOld is 476. uint32_t flag_unk_0x400 : 1; - uint32_t flag_unk_0x800 : 1; + uint32_t flag_texture_transforms_use_bone_sequences : 1; uint32_t flag_unk_0x1000 : 1; uint32_t flag_unk_0x2000 : 1; // seen in UI_MainMenu_Legion uint32_t flag_unk_0x4000 : 1; uint32_t flag_unk_0x8000 : 1; // seen in UI_MainMenu_Legion + uint32_t flag_unk_0x10000 : 1; + uint32_t flag_unk_0x20000 : 1; + uint32_t flag_unk_0x40000 : 1; } global_flags; M2Array global_loops; // Timestamps used in global looping animations. @@ -425,7 +429,52 @@ struct EXP2 M2Array content; }; +PACK( +struct WaterFallDataV3 { + float bumpScale; + float values0_x; + float values0_y; + float values0_z; + float value1_w; + float values0_w; + float value1_x; + float value1_y; + float value2_w; + float value3_y; + float value3_x; + CImVector basecolor; + uint16_t flags; + uint16_t unk0; + float values3_w; + float values3_z; + float values4_y; + float unk1; + float unk2; + float unk3; + float unk4; +}); + +struct PGD1_chunk { + M2Array pgd; +}; + +void initEXP2(EXP2 *exp2); + +void initM2Textures(void* sectionStart, M2Array &textures); + +void initCompBones(void* sectionStart, M2Array *bones, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2Color(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2TextureWeight(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2TextureTransform(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2Attachment(void* sectionStart, M2Array *attachments, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2Event(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2Light(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2Particle(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2Ribbon(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2Camera(M2Data *m2Header, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad); +void initM2ParticlePartTracks(M2Data *m2Header); +int findAnimationIndex(uint32_t anim_id, M2Array *sequence_lookups, M2Array *sequences); template void check_size() { diff --git a/wowViewerLib/src/engine/persistance/header/adtFileHeader.h b/wowViewerLib/src/engine/persistance/header/adtFileHeader.h index 6c5f02e6f..e5646879a 100644 --- a/wowViewerLib/src/engine/persistance/header/adtFileHeader.h +++ b/wowViewerLib/src/engine/persistance/header/adtFileHeader.h @@ -102,6 +102,18 @@ struct SMMapObjDefObj1 { // same as MODF but without boun uint16_t unk; }; +PACK( +struct LodExtents +{ + CAaBox bounding; + float radius; +}); + +struct MLHeader { + uint32_t unknown; + float some_kind_of_bounding[6]; +}; + struct SMLodLevelPerObject { uint32_t m2LodOffset[3]; //Index into MLDD per lod @@ -258,9 +270,9 @@ struct SMLayer struct SMTextureFlags { uint32_t do_not_load_specular_or_height_texture_but_use_cubemap : 1; // probably just 'disable_all_shading' - uint32_t : 3; // no non-zero values in 20490 + uint32_t unk1: 3; // no non-zero values in 20490 uint32_t texture_scale : 4; - uint32_t : 24; + uint32_t unk2: 24; }; struct SMTextureParams { diff --git a/wowViewerLib/src/engine/persistance/header/blpFileHeader.h b/wowViewerLib/src/engine/persistance/header/blpFileHeader.h index 539a5d132..488a7a9fc 100644 --- a/wowViewerLib/src/engine/persistance/header/blpFileHeader.h +++ b/wowViewerLib/src/engine/persistance/header/blpFileHeader.h @@ -6,12 +6,35 @@ #define WOWVIEWERLIB_BLPFILEHEADER_H #include +enum class BLPColorEncoding : uint8_t { + COLOR_JPEG = 0, // not supported + COLOR_PALETTE = 1, + COLOR_DXT = 2, + COLOR_ARGB8888 = 3, + COLOR_ARGB8888_dup = 4, // same decompression, likely other PIXEL_FORMAT +}; + +enum class BLPPixelFormat : uint8_t { + PIXEL_DXT1 = 0, + PIXEL_DXT3 = 1, + PIXEL_ARGB8888 = 2, + PIXEL_ARGB1555 = 3, + PIXEL_ARGB4444 = 4, + PIXEL_RGB565 = 5, + PIXEL_A8 = 6, + PIXEL_DXT5 = 7, + PIXEL_UNSPECIFIED = 8, + PIXEL_ARGB2565 = 9, + PIXEL_BC5 = 11, // DXGI_FORMAT_BC5_UNORM + NUM_PIXEL_FORMATS = 12, // (no idea if format=10 exists) +}; + struct BlpFile{ uint32_t fileIdent; int32_t version; - uint8_t colorEncoding; + BLPColorEncoding colorEncoding; uint8_t alphaChannelBitDepth; - uint8_t preferredFormat; + BLPPixelFormat preferredFormat; uint8_t mipmap_level_and_flags; int32_t width; int32_t height; diff --git a/wowViewerLib/src/engine/persistance/header/commonFileStructs.h b/wowViewerLib/src/engine/persistance/header/commonFileStructs.h index 875e4bcb3..1e974f1e2 100644 --- a/wowViewerLib/src/engine/persistance/header/commonFileStructs.h +++ b/wowViewerLib/src/engine/persistance/header/commonFileStructs.h @@ -4,6 +4,11 @@ #ifndef WOWVIEWERLIB_COMMONFILESTRUCTS_H #define WOWVIEWERLIB_COMMONFILESTRUCTS_H + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + #include #include #include @@ -19,7 +24,7 @@ // Check GCC #if __GNUC__ -#if __x86_64__ || __ppc64__ +#if __x86_64__ || __ppc64__ || __aarch64__ #define ENVIRONMENT64 #else #define ENVIRONMENT32 @@ -116,12 +121,16 @@ struct M2Range { float min; float max; }; +struct M2RangeInt { + uint32_t min; + uint32_t max; +}; typedef M2Range CRange; +PACK( struct CAaBox { -public: - CAaBox(){}; + CAaBox()= default; CAaBox(C3Vector pmin, C3Vector pmax) { this->min = pmin; this->max = pmax; @@ -133,9 +142,10 @@ struct CAaBox // return *this; // } - C3Vector min; - C3Vector max; -}; + C3Vector min = mathfu::vec3_packed(mathfu::vec3(20000, 20000, 20000)); + C3Vector max = mathfu::vec3_packed(mathfu::vec3(-20000, -20000, -20000)); +}); + struct CRect { float miny; @@ -202,6 +212,13 @@ struct CM2SequenceLoad { uint8_t *animFileDataBlob; }; +struct CSkelSequenceLoad { + int animationIndex; + uint8_t *animFileDataBlob; + uint8_t *animFileBoneDataBlob; + uint8_t *animFileAttDataBlob; +}; + struct M2Sequence { uint16_t id; // Animation id in AnimationData.dbc uint16_t variationIndex; // Sub-animation id: Which number in a row of animations this one is. @@ -211,7 +228,7 @@ struct M2Sequence { uint32_t flags; // See below. int16_t frequency; // This is used to determine how often the animation is played. For all animations of the same type, this adds up to 0x7FFF (32767). uint16_t _padding; - M2Range replay; // May both be 0 to not repeat. Client will pick a random number of repetitions within bounds if given. + M2RangeInt replay; // May both be 0 to not repeat. Client will pick a random number of repetitions within bounds if given. uint32_t blendtime; // The client blends (lerp) animation states between animations where the end and start values differ. This specifies how long that blending takes. Values: 0, 50, 100, 150, 200, 250, 300, 350, 500. M2Bounds bounds; int16_t variationNext; // id of the following animation of this AnimationID, points to an Index or is -1 if none. @@ -219,13 +236,15 @@ struct M2Sequence { }; template -void initAnimationArray(M2Array > &array2D, void *m2File, M2Array &sequences, CM2SequenceLoad *cm2SequenceLoad){ +void initAnimationArray(M2Array > &array2D, void *m2File, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad){ static_assert(std::is_pod > >::value, "M2Array> array2D is not POD"); if (cm2SequenceLoad == nullptr) { array2D.initM2Array(m2File); int count = array2D.size; for (int i = 0; i < count; i++) { - if ((sequences.size > 0) && (sequences.getElement(i)->flags & 0x20) == 0) continue; + if (sequences != nullptr) { + if ((sequences->size > 0) && (sequences->getElement(i)->flags & 0x20) == 0) continue; + } M2Array *array1D = array2D.getElement(i); array1D->initM2Array(m2File); @@ -243,7 +262,7 @@ struct M2TrackBase { uint16_t interpolation_type; uint16_t global_sequence; M2Array > timestamps; - void initTrackBase(void * m2File, M2Array &sequences, CM2SequenceLoad *cm2SequenceLoad) { + void initTrackBase(void * m2File, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad) { initAnimationArray(timestamps, m2File, sequences, cm2SequenceLoad); } }; @@ -265,7 +284,7 @@ struct M2Track int16_t global_sequence; M2Array > timestamps; M2Array > values; - void initTrack(void * m2File, M2Array &sequences, CM2SequenceLoad *cm2SequenceLoad){ + void initTrack(void * m2File, M2Array *sequences, CM2SequenceLoad *cm2SequenceLoad){ initAnimationArray(timestamps, m2File, sequences, cm2SequenceLoad); initAnimationArray(values, m2File, sequences, cm2SequenceLoad); }; @@ -273,6 +292,7 @@ struct M2Track + template struct M2SplineKey { T value; diff --git a/wowViewerLib/src/engine/persistance/header/skelFileHeader.h b/wowViewerLib/src/engine/persistance/header/skelFileHeader.h index 591fcc06c..6929e4447 100644 --- a/wowViewerLib/src/engine/persistance/header/skelFileHeader.h +++ b/wowViewerLib/src/engine/persistance/header/skelFileHeader.h @@ -21,13 +21,13 @@ struct skeleton_attachment_header { struct skeleton_bone_header { M2Array bones; - M2Array key_bone_lookup; + M2Array key_bone_lookup; } ; struct skeleton_sequence_header { M2Array global_loops; M2Array sequences; - M2Array sequence_lookups; + M2Array sequence_lookups; uint8_t _0x18[8]; // Is this already part of the data? Always 0 as of Legion (7.3.2.25079). } ; diff --git a/wowViewerLib/src/engine/persistance/header/wmoFileHeader.h b/wowViewerLib/src/engine/persistance/header/wmoFileHeader.h index 85ca6664b..658608c34 100644 --- a/wowViewerLib/src/engine/persistance/header/wmoFileHeader.h +++ b/wowViewerLib/src/engine/persistance/header/wmoFileHeader.h @@ -127,6 +127,25 @@ struct SMOGroupFlags { uint32_t unused10 : 1; }; + struct SMOGroupFlags2 { + //0x1 + uint32_t unk1 : 1; + //0x2 + uint32_t unk2 : 1; + //0x4 + uint32_t unk4 : 1; + //0x8 + uint32_t unk8 : 1; + //0x10 + uint32_t unk0x10 : 1; + //0x20 + uint32_t unk0x20 : 1; + //0x40 + uint32_t isSplitGroupParent : 1; + //0x80 + uint32_t isSplitGroupChild : 1; + }; + struct SMOGroupInfo { /*000h*/ SMOGroupFlags flags; // see information in in MOGP, they are equivalent @@ -226,8 +245,21 @@ struct MOGP { uint8_t fogIndicies[4];// Up to four indices into the WMO fog list uint32_t liquidType; //LiquidType, not always directly used: see below in the MLIQ chunk. uint32_t wmoGroupID; - uint32_t unused1;//&1: WoD(?)+ CanCutTerrain (by MOPL planes), others (UNUSED: 20740) - uint32_t unused2;//(UNUSED: 20740) + SMOGroupFlags2 flags2;//&1: WoD(?)+ CanCutTerrain (by MOPL planes), others (UNUSED: 20740) + int16_t parentSplitOrFirstChildGroupIndex; + int16_t nextSplitGroupChildIndex; +}; + +struct MAVG { + /*000h*/ C3Vector pos; + /*00Ch*/ float start; + /*010h*/ float end; + /*014h*/ CImVector color1; + /*018h*/ CImVector color2; + /*01Ch*/ CImVector color3; + /*020h*/ uint32_t flags; // &1: use color1 and color3 + /*024h*/ uint16_t doodadSetID; + /*026h*/ char _0x26[10]; }; struct SMOPoly diff --git a/wowViewerLib/src/engine/persistance/skelFile.cpp b/wowViewerLib/src/engine/persistance/skelFile.cpp index e16c8fa06..3e81a23b3 100644 --- a/wowViewerLib/src/engine/persistance/skelFile.cpp +++ b/wowViewerLib/src/engine/persistance/skelFile.cpp @@ -44,14 +44,30 @@ chunkDef SkelFile::skelFileTable = { } }, { - 'SKPD', + 'DPKS', { [](SkelFile &file, ChunkData &chunkData) { + debuglog("Entered SKPD"); file.m_skpd_len = chunkData.chunkLen; chunkData.readValue(file.m_skpd); }, } - } + }, + { + 'DIFA', + { + [](SkelFile &file, ChunkData &chunkData) { + debuglog("Entered AFID"); + file.animationFileDataIDs = + std::vector( + (unsigned long) (chunkData.chunkLen / sizeof(M2_AFID))); + + for (int i = 0; i < file.animationFileDataIDs.size(); i++) { + chunkData.readValue(file.animationFileDataIDs[i]); + } + } + } + }, } }; @@ -78,13 +94,100 @@ void SkelFile::process(HFileContent skelFile, const std::string &fileName) { fsStatus = FileStatus::FSLoaded; - for (int i =0; i < this->m_sks1->sequences.size; i++) { - if(this->m_sks1->sequences.getElement(i)->id == 804) { - //debuglog("KU!") - } - } + initTracks(nullptr); if (m_postLoadFunction != nullptr) { m_postLoadFunction(); } } + +void SkelFile:: initTracks(CSkelSequenceLoad *cSkelSequenceLoad) { + M2Array *sequences = nullptr; + if (this->m_sks1 != nullptr) { + sequences = &this->m_sks1->sequences; + } + + if (this->m_ska1 != nullptr) { + CM2SequenceLoad *cm2SequenceLoad = nullptr; + if (cSkelSequenceLoad != nullptr) { + CM2SequenceLoad l_cm2SequenceLoad; + l_cm2SequenceLoad.animationIndex = cSkelSequenceLoad->animationIndex; + if (cSkelSequenceLoad->animFileAttDataBlob != nullptr) { + l_cm2SequenceLoad.animFileDataBlob = cSkelSequenceLoad->animFileAttDataBlob; + } else { + l_cm2SequenceLoad.animFileDataBlob = cSkelSequenceLoad->animFileDataBlob; + } + cm2SequenceLoad = &l_cm2SequenceLoad; + } + + initM2Attachment(this->m_ska1, &this->m_ska1->attachments, sequences, cm2SequenceLoad); + } + if (this->m_skb1 != nullptr) { + CM2SequenceLoad *cm2SequenceLoad = nullptr; + if (cSkelSequenceLoad != nullptr) { + CM2SequenceLoad l_cm2SequenceLoad; + l_cm2SequenceLoad.animationIndex = cSkelSequenceLoad->animationIndex; + if (cSkelSequenceLoad->animFileBoneDataBlob != nullptr) { + l_cm2SequenceLoad.animFileDataBlob = cSkelSequenceLoad->animFileBoneDataBlob; + } else { + l_cm2SequenceLoad.animFileDataBlob = cSkelSequenceLoad->animFileDataBlob; + } + cm2SequenceLoad = &l_cm2SequenceLoad; + } + + initCompBones(this->m_skb1, &this->m_skb1->bones, sequences, cm2SequenceLoad); + } +} + + +void SkelFile::loadLowPriority(HApiContainer m_api, uint32_t animationId, uint32_t variationId) { + int animationIndex = findAnimationIndex(animationId, &m_sks1->sequence_lookups, &m_sks1->sequences); + if (animationIndex < 0) return; + + AnimCacheRecord animCacheRecord; + animCacheRecord.animationId = animationId; + animCacheRecord.variationId = variationId; + auto it = loadedAnimationMap.find(animCacheRecord); + if (it != loadedAnimationMap.end()) return; + + + while (m_sks1->sequences[animationIndex]->variationIndex != variationId) { + animationIndex = m_sks1->sequences[animationIndex]->variationNext; + + if (animationIndex <= 0) return; + } + + if ((m_sks1->sequences[animationIndex]->flags & 0x20) > 0) return; + + int animationFileDataId = 0; + if (animationFileDataIDs.size() > 0) { + for (const auto &record : animationFileDataIDs) { + if (record.anim_id == animationId && record.sub_anim_id == variationId) { + animationFileDataId = record.file_id; + } + } + } + std::shared_ptr animFile = nullptr; + if (animationFileDataId != 0) { + animFile = m_api->cacheStorage->getAnimCache()->getFileId(animationFileDataId); + } +// else if (!useFileId) { +// char buffer[1024]; +// std::snprintf(buffer, 1024, "%s%04d-%02d.anim", m_nameTemplate.c_str(), animationId, variationId); +// +// animFile = m_api->cacheStorage->getAnimCache()->get(buffer); +// } + if (animFile == nullptr) return; + + animFile->setPostLoad([this, animationIndex, animFile]() -> void { + CSkelSequenceLoad cSkelSequenceLoad; + cSkelSequenceLoad.animFileDataBlob = animFile->m_animFileDataBlob; + cSkelSequenceLoad.animFileBoneDataBlob = animFile->m_animFileBoneAnimDataBlob; + cSkelSequenceLoad.animFileAttDataBlob = animFile->m_animFileAttachAnimDataBlob; + cSkelSequenceLoad.animationIndex = animationIndex; + + initTracks(&cSkelSequenceLoad); + m_sks1->sequences[animationIndex]->flags |= 0x20; + }); + loadedAnimationMap[animCacheRecord] = animFile; +} \ No newline at end of file diff --git a/wowViewerLib/src/engine/persistance/skelFile.h b/wowViewerLib/src/engine/persistance/skelFile.h index 8e80a45bf..b254e7b46 100644 --- a/wowViewerLib/src/engine/persistance/skelFile.h +++ b/wowViewerLib/src/engine/persistance/skelFile.h @@ -9,7 +9,10 @@ #include "header/skelFileHeader.h" #include "../../include/sharedFile.h" #include "PersistentFile.h" +#include "animFile.h" #include +#include +#include "../ApiContainer.h" class SkelFile : public PersistentFile { public: @@ -37,11 +40,37 @@ class SkelFile : public PersistentFile { skeleton_parent_data *m_skpd = 0; int m_skpd_len = -1; + + std::vector animationFileDataIDs; + + void loadLowPriority(HApiContainer m_api, uint32_t animationId, uint32_t variationId); private: HFileContent m_skelFile; static chunkDef skelFileTable; std::function m_postLoadFunction = nullptr; + + + + void initTracks(CSkelSequenceLoad *cm2SequenceLoad); + struct AnimCacheRecord { + uint32_t animationId; + uint32_t variationId; + + bool operator==(const AnimCacheRecord &other) const { + return + (animationId == other.animationId) && + (variationId == other.variationId); + }; + }; + struct AnimCacheRecordHasher { + std::size_t operator()(const AnimCacheRecord& k) const { + using std::hash; + return hash{}(k.animationId) ^ (hash{}(k.variationId) << 8); + }; + }; + + std::unordered_map, AnimCacheRecordHasher> loadedAnimationMap; }; diff --git a/wowViewerLib/src/engine/shader/ShaderDefinitions.h b/wowViewerLib/src/engine/shader/ShaderDefinitions.h index a98d348ef..4a3fa032e 100644 --- a/wowViewerLib/src/engine/shader/ShaderDefinitions.h +++ b/wowViewerLib/src/engine/shader/ShaderDefinitions.h @@ -34,106 +34,107 @@ struct shaderMetaData { //Per file extern const std::unordered_map shaderMetaInfo; extern const std::unordered_map> attributesPerShaderName; -extern const std::unordered_map>> fieldDefMapPerShaderName; +extern const std::unordered_map>> fieldDefMapPerShaderNameVert; +extern const std::unordered_map>> fieldDefMapPerShaderNameFrag; struct wmoShader { enum class Attribute { aPosition = 0, aNormal = 1, aTexCoord = 2, aTexCoord2 = 3, aTexCoord3 = 4, aColor = 5, aColor2 = 6, wmoShaderAttributeEnd }; }; -struct waterShader { +struct waterfallShader { enum class Attribute { - aPosition = 0, waterShaderAttributeEnd + aPosition = 0, aNormal = 1, bones = 2, boneWeights = 3, aTexCoord = 4, aTexCoord2 = 5, waterfallShaderAttributeEnd }; }; -struct skyConus { +struct ribbonShader { enum class Attribute { - aPosition = 0, skyConusAttributeEnd + aPosition = 0, aColor = 1, aTexcoord0 = 2, ribbonShaderAttributeEnd }; }; -struct m2Shader { +struct drawQuad { enum class Attribute { - aPosition = 0, aNormal = 1, bones = 2, boneWeights = 3, aTexCoord = 4, aTexCoord2 = 5, m2ShaderAttributeEnd + position = 0, drawQuadAttributeEnd }; }; -struct adtShader { +struct m2ParticleShader { enum class Attribute { - aHeight = 0, aColor = 1, aVertexLighting = 2, aNormal = 3, aIndex = 4, adtShaderAttributeEnd + aPosition = 0, aColor = 1, aTexcoord0 = 2, aTexcoord1 = 3, aTexcoord2 = 4, m2ParticleShaderAttributeEnd }; }; -struct drawLinesShader { +struct drawPortalShader { enum class Attribute { - aPosition = 0, drawLinesShaderAttributeEnd + aPosition = 0, drawPortalShaderAttributeEnd }; }; -struct adtWater { +struct waterShader { enum class Attribute { - aPositionTransp = 0, aTexCoord = 1, adtWaterAttributeEnd + aPositionTransp = 0, aTexCoord = 1, waterShaderAttributeEnd }; }; -struct drawFrustumShader { +struct renderFrameBufferShader { enum class Attribute { - aPosition = 0, drawFrustumShaderAttributeEnd + a_position = 0, renderFrameBufferShaderAttributeEnd }; }; -struct drawPortalShader { +struct drawLinesShader { enum class Attribute { - aPosition = 0, drawPortalShaderAttributeEnd + aPosition = 0, drawLinesShaderAttributeEnd }; }; -struct adtLodShader { +struct drawFrustumShader { enum class Attribute { - aHeight = 0, aIndex = 1, adtLodShaderAttributeEnd + aPosition = 0, drawFrustumShaderAttributeEnd }; }; -struct drawBBShader { +struct skyConus { enum class Attribute { - aPosition = 0, drawBBShaderAttributeEnd + aPosition = 0, skyConusAttributeEnd }; }; -struct drawPoints { +struct adtShader { enum class Attribute { - aPosition = 0, drawPointsAttributeEnd + aHeight = 0, aColor = 1, aVertexLighting = 2, aNormal = 3, aIndex = 4, adtShaderAttributeEnd }; }; -struct drawQuad { +struct m2Shader { enum class Attribute { - position = 0, drawQuadAttributeEnd + aPosition = 0, aNormal = 1, bones = 2, boneWeights = 3, aTexCoord = 4, aTexCoord2 = 5, m2ShaderAttributeEnd }; }; -struct ribbonShader { +struct imguiShader { enum class Attribute { - aPosition = 0, aColor = 1, aTexcoord0 = 2, ribbonShaderAttributeEnd + Position = 0, UV = 1, Color = 2, imguiShaderAttributeEnd }; }; -struct renderFrameBufferShader { +struct drawPoints { enum class Attribute { - a_position = 0, renderFrameBufferShaderAttributeEnd + aPosition = 0, drawPointsAttributeEnd }; }; -struct imguiShader { +struct drawBBShader { enum class Attribute { - Position = 0, UV = 1, Color = 2, imguiShaderAttributeEnd + aPosition = 0, drawBBShaderAttributeEnd }; }; -struct m2ParticleShader { +struct adtLodShader { enum class Attribute { - aPosition = 0, aColor = 1, aTexcoord0 = 2, aTexcoord1 = 3, aTexcoord2 = 4, m2ParticleShaderAttributeEnd + aHeight = 0, aIndex = 1, adtLodShaderAttributeEnd }; }; @@ -148,75 +149,90 @@ const std::unordered_map> attributesPe {"aTexCoord3", 4}, {"aColor", 5}, {"aColor2", 6}, -}},{"waterShader", { -{"aPosition", 0}, -}},{"skyConus", { -{"aPosition", 0}, -}},{"m2Shader", { +}},{"waterfallShader", { {"aPosition", 0}, {"aNormal", 1}, {"bones", 2}, {"boneWeights", 3}, {"aTexCoord", 4}, {"aTexCoord2", 5}, -}},{"adtShader", { -{"aHeight", 0}, +}},{"ribbonShader", { +{"aPosition", 0}, {"aColor", 1}, -{"aVertexLighting", 2}, -{"aNormal", 3}, -{"aIndex", 4}, -}},{"drawLinesShader", { +{"aTexcoord0", 2}, +}},{"drawQuad", { +{"position", 0}, +}},{"m2ParticleShader", { +{"aPosition", 0}, +{"aColor", 1}, +{"aTexcoord0", 2}, +{"aTexcoord1", 3}, +{"aTexcoord2", 4}, +}},{"drawPortalShader", { {"aPosition", 0}, -}},{"adtWater", { +}},{"waterShader", { {"aPositionTransp", 0}, {"aTexCoord", 1}, +}},{"renderFrameBufferShader", { +{"a_position", 0}, +}},{"drawLinesShader", { +{"aPosition", 0}, }},{"drawFrustumShader", { {"aPosition", 0}, -}},{"drawPortalShader", { +}},{"skyConus", { {"aPosition", 0}, -}},{"adtLodShader", { +}},{"adtShader", { {"aHeight", 0}, -{"aIndex", 1}, -}},{"drawBBShader", { -{"aPosition", 0}, -}},{"drawPoints", { -{"aPosition", 0}, -}},{"drawQuad", { -{"position", 0}, -}},{"ribbonShader", { -{"aPosition", 0}, {"aColor", 1}, -{"aTexcoord0", 2}, -}},{"renderFrameBufferShader", { -{"a_position", 0}, +{"aVertexLighting", 2}, +{"aNormal", 3}, +{"aIndex", 4}, +}},{"m2Shader", { +{"aPosition", 0}, +{"aNormal", 1}, +{"bones", 2}, +{"boneWeights", 3}, +{"aTexCoord", 4}, +{"aTexCoord2", 5}, }},{"imguiShader", { {"Position", 0}, {"UV", 1}, {"Color", 2}, -}},{"m2ParticleShader", { +}},{"drawPoints", { {"aPosition", 0}, -{"aColor", 1}, -{"aTexcoord0", 2}, -{"aTexcoord1", 3}, -{"aTexcoord2", 4}, +}},{"drawBBShader", { +{"aPosition", 0}, +}},{"adtLodShader", { +{"aHeight", 0}, +{"aIndex", 1}, }},}; const std::unordered_map shaderMetaInfo = {{ "wmoShader.vert.spv", { { {0,1,64}, -{0,0,352}, +{0,0,368}, {0,2,16}, } } -},{ "waterShader.vert.spv", { +},{ "wmoShader.frag.spv", { { -{0,0,128}, -{0,1,64}, +{0,3,32}, +{0,0,368}, +{0,4,32}, } } -},{ "waterShader.frag.spv", { +},{ "waterfallShader.vert.spv", { { -{0,4,16}, +{0,2,144}, +{0,1,14144}, +{0,0,368}, +} +} +},{ "waterfallShader.frag.spv", { +{ +{0,4,96}, +{0,0,368}, +{0,1,14144}, } } },{ "skyConus.frag.spv", { @@ -225,63 +241,80 @@ const std::unordered_map shaderMetaInfo = {{ "wmoSh } },{ "ribbonShader.frag.spv", { { -{0,0,352}, +{0,4,48}, +{0,0,368}, } } -},{ "renderFrameBufferShader.frag.spv", { +},{ "renderFrameBufferShader.vert.spv", { { -{0,2,168}, +} +} +},{ "waterShader.frag.spv", { +{ +{0,4,16}, +{0,0,368}, } } },{ "m2Shader.vert.spv", { { {0,1,14144}, {0,2,160}, -{0,0,352}, +{0,0,368}, +} +} +},{ "drawLinesShader.frag.spv", { +{ +{0,1,12}, } } },{ "m2Shader.frag.spv", { { -{0,4,48}, +{0,4,64}, {0,3,256}, -{0,0,352}, +{0,0,368}, {0,1,14144}, } } -},{ "renderFrameBufferShader.vert.spv", { +},{ "adtShader.vert.spv", { +{ +{0,0,368}, +{0,2,16}, +} +} +},{ "drawLinesShader.vert.spv", { { +{0,0,128}, } } },{ "m2ParticleShader.vert.spv", { { -{0,0,352}, +{0,0,368}, } } -},{ "drawDepthShader.frag.spv", { +},{ "skyConus.vert.spv", { { -{0,2,12}, +{0,0,368}, +{0,2,96}, } } -},{ "adtWater.vert.spv", { +},{ "m2ParticleShader.frag.spv", { { -{0,0,240}, +{0,4,32}, +{0,0,368}, } } -},{ "drawQuad.vert.spv", { +},{ "imguiShader.frag.spv", { { -{0,2,16}, } } -},{ "m2ParticleShader.frag.spv", { +},{ "ffxglow.frag.spv", { { -{0,4,32}, -{0,0,352}, +{0,4,16}, } } -},{ "drawBBShader.vert.spv", { +},{ "ffxgauss4.frag.spv", { { -{0,1,112}, -{0,0,128}, +{0,4,32}, } } },{ "drawFrustumShader.frag.spv", { @@ -289,494 +322,761 @@ const std::unordered_map shaderMetaInfo = {{ "wmoSh {0,2,12}, } } -},{ "drawBBShader.frag.spv", { +},{ "drawPoints.vert.spv", { { -{0,0,112}, +{0,0,128}, +{0,1,64}, } } -},{ "adtLodShader.frag.spv", { +},{ "drawPoints.frag.spv", { { -{0,0,84}, +{0,1,12}, } } },{ "adtShader.frag.spv", { { {0,4,288}, -{0,0,352}, -{0,3,32}, -} +{0,3,16}, +{0,0,368}, } -},{ "adtLodShader.vert.spv", { -{ -{0,0,144}, } -} -},{ "adtShader.vert.spv", { +},{ "drawFrustumShader.vert.spv", { { -{0,0,352}, -{0,2,16}, +{0,0,128}, } } -},{ "ffxglow.frag.spv", { +},{ "ribbonShader.vert.spv", { { -{0,4,16}, +{0,0,368}, } } -},{ "imguiShader.frag.spv", { +},{ "drawBBShader.frag.spv", { { +{0,1,112}, } } -},{ "adtWater.frag.spv", { +},{ "drawPortalShader.frag.spv", { { {0,4,16}, } } -},{ "wmoShader.frag.spv", { -{ -{0,4,32}, -{0,0,352}, -{0,3,32}, -} -} -},{ "drawLinesShader.vert.spv", { -{ -{0,0,128}, -} -} -},{ "drawLinesShader.frag.spv", { +},{ "drawQuad.vert.spv", { { -{0,1,12}, +{0,2,16}, } } -},{ "ribbonShader.vert.spv", { +},{ "adtLodShader.frag.spv", { { -{0,0,352}, +{0,0,84}, } } -},{ "ffxgauss4.frag.spv", { +},{ "drawBBShader.vert.spv", { { -{0,4,32}, +{0,1,112}, +{0,0,368}, } } -},{ "skyConus.vert.spv", { +},{ "renderFrameBufferShader.frag.spv", { { -{0,0,240}, -{0,2,96}, +{0,2,168}, } } -},{ "drawPoints.frag.spv", { +},{ "imguiShader.vert.spv", { { -{0,1,12}, +{0,1,80}, } } -},{ "drawPoints.vert.spv", { +},{ "drawPortalShader.vert.spv", { { {0,0,128}, -{0,1,64}, -} } -},{ "drawPortalShader.frag.spv", { -{ -{0,1,16}, } -} -},{ "drawPortalShader.vert.spv", { +},{ "waterShader.vert.spv", { { -{0,0,128}, +{0,0,368}, {0,1,64}, } } -},{ "drawFrustumShader.vert.spv", { +},{ "drawDepthShader.frag.spv", { { -{0,0,128}, +{0,2,12}, } } -},{ "imguiShader.vert.spv", { +},{ "adtLodShader.vert.spv", { { -{0,1,64}, +{0,0,144}, } } },}; -const std::unordered_map>> fieldDefMapPerShaderName = { - {"wmoShader", { +const std::unordered_map>> fieldDefMapPerShaderNameVert = { + {"adtLodShader", { { - 2, { - {"_182.VertexShader_UseLitColor", false, 0, 1, 4, 0}, + 0, { + {"_55_uPos", true, 0, 1, 3, 0}, + {"_55_uLookAtMat", true, 16, 4, 4, 0}, + {"_55_uPMatrix", true, 80, 4, 4, 0}, } }, + }}, + {"drawBBShader", { { 1, { - {"_93.uPlacementMat", true, 0, 4, 4, 0}, + {"_21_uPlacementMat", true, 0, 4, 4, 0}, + {"_21_uBBScale", true, 64, 1, 4, 0}, + {"_21_uBBCenter", true, 80, 1, 4, 0}, + {"_21_uColor", true, 96, 1, 4, 0}, } }, { - 3, { - {"_852.intLight.uInteriorAmbientColorAndApplyInteriorLight", true, 0, 1, 4, 0}, - {"_852.intLight.uInteriorDirectColorAndApplyExteriorLight", true, 16, 1, 4, 0}, + 0, { + {"_62_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_62_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_62_scene_uViewUp", true, 128, 1, 4, 0}, + {"_62_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_62_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_62_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_62_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_62_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_62_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_62_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_62_fogData_densityParams", true, 256, 1, 4, 0}, + {"_62_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_62_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_62_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_62_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_62_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_62_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, + }}, + {"drawPoints", { { - 4, { - {"_383.UseLitColor_EnableAlpha_PixelShader", false, 0, 1, 4, 0}, - {"_383.FogColor_AlphaTest", true, 16, 1, 4, 0}, + 0, { + {"_19_uLookAtMat", true, 0, 4, 4, 0}, + {"_19_uPMatrix", true, 64, 4, 4, 0}, } }, { - 0, { - {"_111.scene.uLookAtMat", true, 0, 4, 4, 0}, - {"_111.scene.uPMatrix", true, 64, 4, 4, 0}, - {"_111.scene.uViewUp", true, 128, 1, 4, 0}, - {"_111.scene.uInteriorSunDir", true, 144, 1, 4, 0}, - {"_111.scene.extLight.uExteriorAmbientColor", true, 160, 1, 4, 0}, - {"_111.scene.extLight.uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, - {"_111.scene.extLight.uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, - {"_111.scene.extLight.uExteriorDirectColor", true, 208, 1, 4, 0}, - {"_111.scene.extLight.uExteriorDirectColorDir", true, 224, 1, 4, 0}, - {"_111.fogData.densityParams", true, 240, 1, 4, 0}, - {"_111.fogData.heightPlane", true, 256, 1, 4, 0}, - {"_111.fogData.color_and_heightRate", true, 272, 1, 4, 0}, - {"_111.fogData.heightDensity_and_endColor", true, 288, 1, 4, 0}, - {"_111.fogData.sunAngle_and_sunColor", true, 304, 1, 4, 0}, - {"_111.fogData.heightColor_and_endFogDistance", true, 320, 1, 4, 0}, - {"_111.fogData.sunPercentage", true, 336, 1, 4, 0}, + 1, { + {"_29_uPlacementMat", true, 0, 4, 4, 0}, } }, }}, - {"waterShader", { + {"imguiShader", { { 1, { - {"_32.uPlacementMat", true, 0, 4, 4, 0}, + {"_30_ProjMtx", true, 0, 4, 4, 0}, + {"_30_uiScale", true, 64, 1, 4, 0}, } }, + }}, + {"m2Shader", { { - 4, { - {"_12.waterTypeV", false, 0, 1, 4, 0}, + 1, { + {"_133_uPlacementMat", true, 0, 4, 4, 0}, + {"_133_uBoneMatrixes[0]", true, 64, 4, 4, 220}, + } + }, + { + 2, { + {"_230_vertexShader_IsAffectedByLight", false, 0, 1, 4, 0}, + {"_230_color_Transparency", true, 16, 1, 4, 0}, + {"_230_uTextMat[0]", true, 32, 4, 4, 2}, } }, { 0, { - {"_24.uLookAtMat", true, 0, 4, 4, 0}, - {"_24.uPMatrix", true, 64, 4, 4, 0}, + {"_240_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_240_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_240_scene_uViewUp", true, 128, 1, 4, 0}, + {"_240_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_240_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_240_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_240_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_240_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_240_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_240_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_240_fogData_densityParams", true, 256, 1, 4, 0}, + {"_240_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_240_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_240_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_240_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_240_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_240_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, }}, - {"skyConus", { + {"adtShader", { + { + 0, { + {"_91_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_91_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_91_scene_uViewUp", true, 128, 1, 4, 0}, + {"_91_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_91_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_91_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_91_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_91_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_91_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_91_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_91_fogData_densityParams", true, 256, 1, 4, 0}, + {"_91_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_91_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_91_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_91_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_91_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_91_fogData_sunPercentage", true, 352, 1, 4, 0}, + } + }, { 2, { - {"_82.skyColor[0]", true, 0, 1, 4, 6}, + {"_139_uPos", true, 0, 1, 4, 0}, } }, + }}, + {"skyConus", { { 0, { - {"_50.scene.uLookAtMat", true, 0, 4, 4, 0}, - {"_50.scene.uPMatrix", true, 64, 4, 4, 0}, - {"_50.scene.uViewUp", true, 128, 1, 4, 0}, - {"_50.scene.uInteriorSunDir", true, 144, 1, 4, 0}, - {"_50.scene.extLight.uExteriorAmbientColor", true, 160, 1, 4, 0}, - {"_50.scene.extLight.uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, - {"_50.scene.extLight.uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, - {"_50.scene.extLight.uExteriorDirectColor", true, 208, 1, 4, 0}, - {"_50.scene.extLight.uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_26_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_26_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_26_scene_uViewUp", true, 128, 1, 4, 0}, + {"_26_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_26_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_26_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_26_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_26_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_26_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_26_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_26_fogData_densityParams", true, 256, 1, 4, 0}, + {"_26_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_26_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_26_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_26_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_26_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_26_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, - }}, - {"m2Shader", { { 2, { - {"_230.vertexShader_IsAffectedByLight", false, 0, 1, 4, 0}, - {"_230.color_Transparency", true, 16, 1, 4, 0}, - {"_230.uTextMat[0]", true, 32, 4, 4, 2}, + {"_67_skyColor[0]", true, 0, 1, 4, 6}, } }, + }}, + {"drawFrustumShader", { { - 1, { - {"_133.uPlacementMat", true, 0, 4, 4, 0}, - {"_133.uBoneMatrixes[0]", true, 64, 4, 4, 220}, + 0, { + {"_13_uLookAtMat", true, 0, 4, 4, 0}, + {"_13_uPMatrix", true, 64, 4, 4, 0}, } }, + }}, + {"drawLinesShader", { { 0, { - {"_240.scene.uLookAtMat", true, 0, 4, 4, 0}, - {"_240.scene.uPMatrix", true, 64, 4, 4, 0}, - {"_240.scene.uViewUp", true, 128, 1, 4, 0}, - {"_240.scene.uInteriorSunDir", true, 144, 1, 4, 0}, - {"_240.scene.extLight.uExteriorAmbientColor", true, 160, 1, 4, 0}, - {"_240.scene.extLight.uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, - {"_240.scene.extLight.uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, - {"_240.scene.extLight.uExteriorDirectColor", true, 208, 1, 4, 0}, - {"_240.scene.extLight.uExteriorDirectColorDir", true, 224, 1, 4, 0}, - {"_240.fogData.densityParams", true, 240, 1, 4, 0}, - {"_240.fogData.heightPlane", true, 256, 1, 4, 0}, - {"_240.fogData.color_and_heightRate", true, 272, 1, 4, 0}, - {"_240.fogData.heightDensity_and_endColor", true, 288, 1, 4, 0}, - {"_240.fogData.sunAngle_and_sunColor", true, 304, 1, 4, 0}, - {"_240.fogData.heightColor_and_endFogDistance", true, 320, 1, 4, 0}, - {"_240.fogData.sunPercentage", true, 336, 1, 4, 0}, + {"_19_uLookAtMat", true, 0, 4, 4, 0}, + {"_19_uPMatrix", true, 64, 4, 4, 0}, } }, + }}, + {"renderFrameBufferShader", { + }}, + {"waterShader", { { - 4, { - {"_409.PixelShader_UnFogged_IsAffectedByLight", false, 0, 1, 4, 0}, - {"_409.uFogColorAndAlphaTest", true, 16, 1, 4, 0}, - {"_409.uPcColor", true, 32, 1, 4, 0}, + 0, { + {"_28_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_28_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_28_scene_uViewUp", true, 128, 1, 4, 0}, + {"_28_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_28_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_28_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_28_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_28_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_28_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_28_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_28_fogData_densityParams", true, 256, 1, 4, 0}, + {"_28_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_28_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_28_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_28_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_28_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_28_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, { - 3, { - {"_433.intLight.uInteriorAmbientColorAndApplyInteriorLight", true, 0, 1, 4, 0}, - {"_433.intLight.uInteriorDirectColorAndApplyExteriorLight", true, 16, 1, 4, 0}, - {"_433.pc_lights[0].color", true, 32, 1, 4, 0}, - {"_433.pc_lights[0].position", true, 48, 1, 4, 0}, - {"_433.pc_lights[0].attenuation", true, 64, 1, 4, 0}, - {"_433.pc_lights[1].color", true, 80, 1, 4, 0}, - {"_433.pc_lights[1].position", true, 96, 1, 4, 0}, - {"_433.pc_lights[1].attenuation", true, 112, 1, 4, 0}, - {"_433.pc_lights[2].color", true, 128, 1, 4, 0}, - {"_433.pc_lights[2].position", true, 144, 1, 4, 0}, - {"_433.pc_lights[2].attenuation", true, 160, 1, 4, 0}, - {"_433.pc_lights[3].color", true, 176, 1, 4, 0}, - {"_433.pc_lights[3].position", true, 192, 1, 4, 0}, - {"_433.pc_lights[3].attenuation", true, 208, 1, 4, 0}, - {"_433.lightCountAndBcHack", false, 224, 1, 4, 0}, - {"_433.interiorExteriorBlend", true, 240, 1, 4, 0}, + 1, { + {"_36_uPlacementMat", true, 0, 4, 4, 0}, + } + }, + }}, + {"drawPortalShader", { + { + 0, { + {"_30_uLookAtMat", true, 0, 4, 4, 0}, + {"_30_uPMatrix", true, 64, 4, 4, 0}, } }, }}, {"m2ParticleShader", { { 0, { - {"_43.scene.uLookAtMat", true, 0, 4, 4, 0}, - {"_43.scene.uPMatrix", true, 64, 4, 4, 0}, - {"_43.scene.uViewUp", true, 128, 1, 4, 0}, - {"_43.scene.uInteriorSunDir", true, 144, 1, 4, 0}, - {"_43.scene.extLight.uExteriorAmbientColor", true, 160, 1, 4, 0}, - {"_43.scene.extLight.uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, - {"_43.scene.extLight.uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, - {"_43.scene.extLight.uExteriorDirectColor", true, 208, 1, 4, 0}, - {"_43.scene.extLight.uExteriorDirectColorDir", true, 224, 1, 4, 0}, - {"_43.fogData.densityParams", true, 240, 1, 4, 0}, - {"_43.fogData.heightPlane", true, 256, 1, 4, 0}, - {"_43.fogData.color_and_heightRate", true, 272, 1, 4, 0}, - {"_43.fogData.heightDensity_and_endColor", true, 288, 1, 4, 0}, - {"_43.fogData.sunAngle_and_sunColor", true, 304, 1, 4, 0}, - {"_43.fogData.heightColor_and_endFogDistance", true, 320, 1, 4, 0}, - {"_43.fogData.sunPercentage", true, 336, 1, 4, 0}, + {"_43_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_43_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_43_scene_uViewUp", true, 128, 1, 4, 0}, + {"_43_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_43_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_43_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_43_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_43_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_43_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_43_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_43_fogData_densityParams", true, 256, 1, 4, 0}, + {"_43_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_43_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_43_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_43_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_43_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_43_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, + }}, + {"drawQuad", { { - 4, { - {"_212.uAlphaTestv", true, 0, 1, 4, 0}, - {"_212.uPixelShaderv", false, 16, 1, 4, 0}, + 2, { + {"_12_uWidth_uHeight_uX_uY", true, 0, 1, 4, 0}, } }, }}, {"ribbonShader", { { 0, { - {"_37.scene.uLookAtMat", true, 0, 4, 4, 0}, - {"_37.scene.uPMatrix", true, 64, 4, 4, 0}, - {"_37.scene.uViewUp", true, 128, 1, 4, 0}, - {"_37.scene.uInteriorSunDir", true, 144, 1, 4, 0}, - {"_37.scene.extLight.uExteriorAmbientColor", true, 160, 1, 4, 0}, - {"_37.scene.extLight.uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, - {"_37.scene.extLight.uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, - {"_37.scene.extLight.uExteriorDirectColor", true, 208, 1, 4, 0}, - {"_37.scene.extLight.uExteriorDirectColorDir", true, 224, 1, 4, 0}, - {"_37.fogData.densityParams", true, 240, 1, 4, 0}, - {"_37.fogData.heightPlane", true, 256, 1, 4, 0}, - {"_37.fogData.color_and_heightRate", true, 272, 1, 4, 0}, - {"_37.fogData.heightDensity_and_endColor", true, 288, 1, 4, 0}, - {"_37.fogData.sunAngle_and_sunColor", true, 304, 1, 4, 0}, - {"_37.fogData.heightColor_and_endFogDistance", true, 320, 1, 4, 0}, - {"_37.fogData.sunPercentage", true, 336, 1, 4, 0}, + {"_37_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_37_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_37_scene_uViewUp", true, 128, 1, 4, 0}, + {"_37_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_37_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_37_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_37_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_37_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_37_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_37_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_37_fogData_densityParams", true, 256, 1, 4, 0}, + {"_37_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_37_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_37_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_37_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_37_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_37_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, }}, - {"renderFrameBufferShader", { + {"waterfallShader", { { 2, { - {"_34.gauss_offsets[0]", true, 0, 1, 1, 5}, - {"_34.gauss_weights[0]", true, 80, 1, 1, 5}, - {"_34.uResolution", true, 160, 1, 2, 0}, + {"_55_bumpScale", true, 0, 1, 4, 0}, + {"_55_uTextMat[0]", true, 16, 4, 4, 2}, } }, - }}, - {"imguiShader", { { 1, { - {"_30.ProjMtx", true, 0, 4, 4, 0}, + {"_104_uPlacementMat", true, 0, 4, 4, 0}, + {"_104_uBoneMatrixes[0]", true, 64, 4, 4, 220}, } }, - }}, - {"ffxglow", { { - 4, { - {"_34.blurAmount", true, 0, 1, 4, 0}, + 0, { + {"_199_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_199_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_199_scene_uViewUp", true, 128, 1, 4, 0}, + {"_199_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_199_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_199_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_199_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_199_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_199_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_199_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_199_fogData_densityParams", true, 256, 1, 4, 0}, + {"_199_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_199_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_199_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_199_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_199_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_199_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, }}, - {"drawPoints", { + {"wmoShader", { + { + 1, { + {"_93_uPlacementMat", true, 0, 4, 4, 0}, + } + }, { 0, { - {"_19.uLookAtMat", true, 0, 4, 4, 0}, - {"_19.uPMatrix", true, 64, 4, 4, 0}, + {"_111_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_111_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_111_scene_uViewUp", true, 128, 1, 4, 0}, + {"_111_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_111_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_111_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_111_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_111_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_111_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_111_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_111_fogData_densityParams", true, 256, 1, 4, 0}, + {"_111_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_111_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_111_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_111_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_111_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_111_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, { - 1, { - {"_29.uPlacementMat", true, 0, 4, 4, 0}, + 2, { + {"_170_VertexShader_UseLitColor", false, 0, 1, 4, 0}, } }, }}, - {"adtWater", { +}; +const std::unordered_map>> fieldDefMapPerShaderNameFrag = { + {"adtLodShader", { { 0, { - {"_27.scene.uLookAtMat", true, 0, 4, 4, 0}, - {"_27.scene.uPMatrix", true, 64, 4, 4, 0}, - {"_27.scene.uViewUp", true, 128, 1, 4, 0}, - {"_27.scene.uInteriorSunDir", true, 144, 1, 4, 0}, - {"_27.scene.extLight.uExteriorAmbientColor", true, 160, 1, 4, 0}, - {"_27.scene.extLight.uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, - {"_27.scene.extLight.uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, - {"_27.scene.extLight.uExteriorDirectColor", true, 208, 1, 4, 0}, - {"_27.scene.extLight.uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_65_uViewUp", true, 0, 1, 4, 0}, + {"_65_uSunDir_FogStart", true, 16, 1, 4, 0}, + {"_65_uSunColor_uFogEnd", true, 32, 1, 4, 0}, + {"_65_uAmbientLight", true, 48, 1, 4, 0}, + {"_65_FogColor", true, 64, 1, 4, 0}, + {"_65_uNewFormula", false, 80, 1, 1, 0}, } }, + }}, + {"ffxglow", { { 4, { - {"_12.color", true, 0, 1, 4, 0}, + {"_34_blurAmount", true, 0, 1, 4, 0}, } }, }}, - {"adtLodShader", { + {"adtShader", { + { + 4, { + {"_466_uHeightScale", true, 0, 1, 4, 0}, + {"_466_uHeightOffset", true, 16, 1, 4, 0}, + {"_466_animationMat[0]", true, 32, 4, 4, 4}, + } + }, + { + 3, { + {"_506_uUseHeightMixFormula", false, 0, 1, 4, 0}, + } + }, { 0, { - {"_55.uPos", true, 0, 1, 3, 0}, - {"_55.uLookAtMat", true, 16, 4, 4, 0}, - {"_55.uPMatrix", true, 80, 4, 4, 0}, + {"_748_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_748_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_748_scene_uViewUp", true, 128, 1, 4, 0}, + {"_748_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_748_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_748_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_748_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_748_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_748_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_748_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_748_fogData_densityParams", true, 256, 1, 4, 0}, + {"_748_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_748_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_748_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_748_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_748_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_748_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, }}, - {"drawBBShader", { + {"m2Shader", { { - 1, { - {"_21.uPlacementMat", true, 0, 4, 4, 0}, - {"_21.uBBScale", true, 64, 1, 4, 0}, - {"_21.uBBCenter", true, 80, 1, 4, 0}, - {"_21.uColor", true, 96, 1, 4, 0}, + 4, { + {"_473_PixelShader_UnFogged_IsAffectedByLight_blendMode", false, 0, 1, 4, 0}, + {"_473_uFogColorAndAlphaTest", true, 16, 1, 4, 0}, + {"_473_uTexSampleAlpha", true, 32, 1, 4, 0}, + {"_473_uPcColor", true, 48, 1, 4, 0}, + } + }, + { + 3, { + {"_496_intLight_uInteriorAmbientColorAndApplyInteriorLight", true, 0, 1, 4, 0}, + {"_496_intLight_uInteriorDirectColorAndApplyExteriorLight", true, 16, 1, 4, 0}, + {"_496_pc_lights[0].color", true, 32, 1, 4, 0}, + {"_496_pc_lights[0].position", true, 48, 1, 4, 0}, + {"_496_pc_lights[0].attenuation", true, 64, 1, 4, 0}, + {"_496_pc_lights[1].color", true, 80, 1, 4, 0}, + {"_496_pc_lights[1].position", true, 96, 1, 4, 0}, + {"_496_pc_lights[1].attenuation", true, 112, 1, 4, 0}, + {"_496_pc_lights[2].color", true, 128, 1, 4, 0}, + {"_496_pc_lights[2].position", true, 144, 1, 4, 0}, + {"_496_pc_lights[2].attenuation", true, 160, 1, 4, 0}, + {"_496_pc_lights[3].color", true, 176, 1, 4, 0}, + {"_496_pc_lights[3].position", true, 192, 1, 4, 0}, + {"_496_pc_lights[3].attenuation", true, 208, 1, 4, 0}, + {"_496_lightCountAndBcHack", false, 224, 1, 4, 0}, + {"_496_interiorExteriorBlend", true, 240, 1, 4, 0}, } }, { 0, { - {"_59.uLookAtMat", true, 0, 4, 4, 0}, - {"_59.uPMatrix", true, 64, 4, 4, 0}, + {"_535_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_535_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_535_scene_uViewUp", true, 128, 1, 4, 0}, + {"_535_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_535_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_535_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_535_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_535_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_535_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_535_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_535_fogData_densityParams", true, 256, 1, 4, 0}, + {"_535_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_535_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_535_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_535_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_535_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_535_fogData_sunPercentage", true, 352, 1, 4, 0}, + } + }, + { + 1, { + {"_543_uPlacementMat", true, 0, 4, 4, 0}, + {"_543_uBoneMatrixes[0]", true, 64, 4, 4, 220}, } }, }}, - {"adtShader", { + {"skyConus", { + }}, + {"ffxgauss4", { { - 2, { - {"_131.uPos", true, 0, 1, 4, 0}, + 4, { + {"_33_texOffsetX", true, 0, 1, 4, 0}, + {"_33_texOffsetY", true, 16, 1, 4, 0}, } }, + }}, + {"drawDepthShader", { { - 3, { - {"_741.uFogStartAndFogEnd", true, 0, 1, 4, 0}, - {"_741.uFogColor", true, 16, 1, 4, 0}, + 2, { + {"_10_drawDepth", false, 0, 1, 1, 0}, + {"_10_uFarPlane", true, 4, 1, 1, 0}, + {"_10_uNearPlane", true, 8, 1, 1, 0}, } }, + }}, + {"waterfallShader", { { 4, { - {"_387.uHeightScale", true, 0, 1, 4, 0}, - {"_387.uHeightOffset", true, 16, 1, 4, 0}, - {"_387.animationMat[0]", true, 32, 4, 4, 4}, + {"_220_values0", true, 0, 1, 4, 0}, + {"_220_values1", true, 16, 1, 4, 0}, + {"_220_values2", true, 32, 1, 4, 0}, + {"_220_values3", true, 48, 1, 4, 0}, + {"_220_values4", true, 64, 1, 4, 0}, + {"_220_baseColor", true, 80, 1, 4, 0}, } }, { 0, { - {"_91.scene.uLookAtMat", true, 0, 4, 4, 0}, - {"_91.scene.uPMatrix", true, 64, 4, 4, 0}, - {"_91.scene.uViewUp", true, 128, 1, 4, 0}, - {"_91.scene.uInteriorSunDir", true, 144, 1, 4, 0}, - {"_91.scene.extLight.uExteriorAmbientColor", true, 160, 1, 4, 0}, - {"_91.scene.extLight.uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, - {"_91.scene.extLight.uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, - {"_91.scene.extLight.uExteriorDirectColor", true, 208, 1, 4, 0}, - {"_91.scene.extLight.uExteriorDirectColorDir", true, 224, 1, 4, 0}, - {"_91.fogData.densityParams", true, 240, 1, 4, 0}, - {"_91.fogData.heightPlane", true, 256, 1, 4, 0}, - {"_91.fogData.color_and_heightRate", true, 272, 1, 4, 0}, - {"_91.fogData.heightDensity_and_endColor", true, 288, 1, 4, 0}, - {"_91.fogData.sunAngle_and_sunColor", true, 304, 1, 4, 0}, - {"_91.fogData.heightColor_and_endFogDistance", true, 320, 1, 4, 0}, - {"_91.fogData.sunPercentage", true, 336, 1, 4, 0}, + {"_488_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_488_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_488_scene_uViewUp", true, 128, 1, 4, 0}, + {"_488_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_488_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_488_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_488_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_488_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_488_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_488_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_488_fogData_densityParams", true, 256, 1, 4, 0}, + {"_488_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_488_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_488_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_488_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_488_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_488_fogData_sunPercentage", true, 352, 1, 4, 0}, + } + }, + { + 1, { + {"_571_uPlacementMat", true, 0, 4, 4, 0}, + {"_571_uBoneMatrixes[0]", true, 64, 4, 4, 220}, } }, }}, - {"drawPortalShader", { + {"drawBBShader", { { - 0, { - {"_30.uLookAtMat", true, 0, 4, 4, 0}, - {"_30.uPMatrix", true, 64, 4, 4, 0}, + 1, { + {"_13_uPlacementMat", true, 0, 4, 4, 0}, + {"_13_uBBScale", true, 64, 1, 4, 0}, + {"_13_uBBCenter", true, 80, 1, 4, 0}, + {"_13_uColor", true, 96, 1, 4, 0}, } }, + }}, + {"drawPoints", { { 1, { - {"_40.uPlacementMat", true, 0, 4, 4, 0}, + {"_13_uColor", true, 0, 1, 3, 0}, } }, }}, + {"imguiShader", { + }}, {"drawFrustumShader", { { - 0, { - {"_13.uLookAtMat", true, 0, 4, 4, 0}, - {"_13.uPMatrix", true, 64, 4, 4, 0}, + 2, { + {"_22_uColor", true, 0, 1, 3, 0}, + } + }, + }}, + {"drawLinesShader", { + { + 1, { + {"_19_uColor", true, 0, 1, 3, 0}, } }, + }}, + {"renderFrameBufferShader", { { 2, { - {"_22.uColor", true, 0, 1, 3, 0}, + {"_34_gauss_offsets[0]", true, 0, 1, 1, 5}, + {"_34_gauss_weights[0]", true, 80, 1, 1, 5}, + {"_34_uResolution", true, 160, 1, 2, 0}, } }, }}, - {"drawLinesShader", { + {"waterShader", { + { + 4, { + {"_253_color", true, 0, 1, 4, 0}, + } + }, { 0, { - {"_19.uLookAtMat", true, 0, 4, 4, 0}, - {"_19.uPMatrix", true, 64, 4, 4, 0}, + {"_277_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_277_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_277_scene_uViewUp", true, 128, 1, 4, 0}, + {"_277_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_277_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_277_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_277_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_277_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_277_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_277_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_277_fogData_densityParams", true, 256, 1, 4, 0}, + {"_277_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_277_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_277_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_277_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_277_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_277_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, + }}, + {"drawPortalShader", { { - 1, { - {"_19.uColor", true, 0, 1, 3, 0}, + 4, { + {"_12_uColor", true, 0, 1, 4, 0}, } }, }}, - {"drawQuad", { + {"m2ParticleShader", { { - 2, { - {"_36.uWidth", true, 0, 1, 1, 0}, - {"_36.uHeight", true, 4, 1, 1, 0}, - {"_36.uX", true, 8, 1, 1, 0}, - {"_36.uY", true, 12, 1, 1, 0}, + 4, { + {"_277_uAlphaTestv", true, 0, 1, 4, 0}, + {"_277_uPixelShaderBlendModev", false, 16, 1, 4, 0}, + } + }, + { + 0, { + {"_485_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_485_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_485_scene_uViewUp", true, 128, 1, 4, 0}, + {"_485_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_485_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_485_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_485_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_485_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_485_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_485_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_485_fogData_densityParams", true, 256, 1, 4, 0}, + {"_485_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_485_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_485_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_485_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_485_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_485_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, }}, - {"drawDepthShader", { + {"ribbonShader", { { - 2, { - {"_10.drawDepth", false, 0, 1, 1, 0}, - {"_10.uFarPlane", true, 4, 1, 1, 0}, - {"_10.uNearPlane", true, 8, 1, 1, 0}, + 4, { + {"_256_uAlphaTestScalev", true, 0, 1, 4, 0}, + {"_256_uPixelShaderv", false, 16, 1, 4, 0}, + {"_256_uTextureTranslate", true, 32, 1, 4, 0}, + } + }, + { + 0, { + {"_304_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_304_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_304_scene_uViewUp", true, 128, 1, 4, 0}, + {"_304_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_304_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_304_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_304_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_304_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_304_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_304_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_304_fogData_densityParams", true, 256, 1, 4, 0}, + {"_304_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_304_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_304_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_304_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_304_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_304_fogData_sunPercentage", true, 352, 1, 4, 0}, } }, }}, - {"ffxgauss4", { + {"wmoShader", { + { + 3, { + {"_489_intLight_uInteriorAmbientColorAndApplyInteriorLight", true, 0, 1, 4, 0}, + {"_489_intLight_uInteriorDirectColorAndApplyExteriorLight", true, 16, 1, 4, 0}, + } + }, + { + 0, { + {"_501_scene_uLookAtMat", true, 0, 4, 4, 0}, + {"_501_scene_uPMatrix", true, 64, 4, 4, 0}, + {"_501_scene_uViewUp", true, 128, 1, 4, 0}, + {"_501_scene_uInteriorSunDir", true, 144, 1, 4, 0}, + {"_501_scene_extLight_uExteriorAmbientColor", true, 160, 1, 4, 0}, + {"_501_scene_extLight_uExteriorHorizontAmbientColor", true, 176, 1, 4, 0}, + {"_501_scene_extLight_uExteriorGroundAmbientColor", true, 192, 1, 4, 0}, + {"_501_scene_extLight_uExteriorDirectColor", true, 208, 1, 4, 0}, + {"_501_scene_extLight_uExteriorDirectColorDir", true, 224, 1, 4, 0}, + {"_501_scene_extLight_adtSpecMult", true, 240, 1, 4, 0}, + {"_501_fogData_densityParams", true, 256, 1, 4, 0}, + {"_501_fogData_heightPlane", true, 272, 1, 4, 0}, + {"_501_fogData_color_and_heightRate", true, 288, 1, 4, 0}, + {"_501_fogData_heightDensity_and_endColor", true, 304, 1, 4, 0}, + {"_501_fogData_sunAngle_and_sunColor", true, 320, 1, 4, 0}, + {"_501_fogData_heightColor_and_endFogDistance", true, 336, 1, 4, 0}, + {"_501_fogData_sunPercentage", true, 352, 1, 4, 0}, + } + }, { 4, { - {"_33.texOffsetX", true, 0, 1, 4, 0}, - {"_33.texOffsetY", true, 16, 1, 4, 0}, + {"_622_UseLitColor_EnableAlpha_PixelShader_BlendMode", false, 0, 1, 4, 0}, + {"_622_FogColor_AlphaTest", true, 16, 1, 4, 0}, } }, }}, diff --git a/wowViewerLib/src/engine/texture/BlpTexture.cpp b/wowViewerLib/src/engine/texture/BlpTexture.cpp index b02f413cd..afcb9be37 100644 --- a/wowViewerLib/src/engine/texture/BlpTexture.cpp +++ b/wowViewerLib/src/engine/texture/BlpTexture.cpp @@ -14,36 +14,36 @@ TextureFormat getTextureType(BlpFile *blpFile) { TextureFormat textureFormat = TextureFormat::None; switch (blpFile->preferredFormat) { - case 0: + case BLPPixelFormat::PIXEL_DXT1: if (blpFile->alphaChannelBitDepth > 0) { textureFormat = TextureFormat::S3TC_RGBA_DXT1; } else { textureFormat = TextureFormat::S3TC_RGB_DXT1; } break; - case 1: + case BLPPixelFormat::PIXEL_DXT3: textureFormat = TextureFormat::S3TC_RGBA_DXT3; break; - case 2: - textureFormat = TextureFormat::BGRA; + case BLPPixelFormat::PIXEL_ARGB8888: + textureFormat = TextureFormat::RGBA; break; - case 3: - textureFormat = TextureFormat::BGRA; - break; - case 4: + case BLPPixelFormat::PIXEL_ARGB1555: textureFormat = TextureFormat::PalARGB1555DitherFloydSteinberg; break; - case 5: + case BLPPixelFormat::PIXEL_ARGB4444: textureFormat = TextureFormat::PalARGB4444DitherFloydSteinberg; break; - case 7: + case BLPPixelFormat::PIXEL_RGB565: + textureFormat = TextureFormat::RGBA; + break; + case BLPPixelFormat::PIXEL_DXT5: textureFormat = TextureFormat::S3TC_RGBA_DXT5; break; - case 8: - textureFormat = TextureFormat::BGRA; + case BLPPixelFormat::PIXEL_UNSPECIFIED: + textureFormat = TextureFormat::RGBA; break; - case 9: + case BLPPixelFormat::PIXEL_ARGB2565: textureFormat = TextureFormat::PalARGB2565DitherFloydSteinberg; break; @@ -52,7 +52,7 @@ TextureFormat getTextureType(BlpFile *blpFile) { } return textureFormat; } -void parseMipmaps(BlpFile *blpFile, TextureFormat textureFormat, MipmapsVector &mipmaps) { +HMipmapsVector parseMipmaps(BlpFile *blpFile, TextureFormat textureFormat) { int32_t width = blpFile->width; int32_t height = blpFile->height; @@ -64,8 +64,17 @@ void parseMipmaps(BlpFile *blpFile, TextureFormat textureFormat, MipmapsVector & minSize = (int32_t) (floor((1 + 3) / 4) * floor((1 + 3) / 4) * 8); } + int mipmapsCnt = 0; for (int i = 0; i < 15; i++) { if ((blpFile->lengths[i] == 0) || (blpFile->offsets[i] == 0)) break; + mipmapsCnt++; + } + auto mipmaps = std::make_shared>(); + + mipmaps->resize(mipmapsCnt); + + for (int i = 0; i < mipmapsCnt; i++) { + if ((blpFile->lengths[i] == 0) || (blpFile->offsets[i] == 0)) break; uint8_t *data = ((uint8_t *) blpFile)+blpFile->offsets[i]; //blpFile->lengths[i]); @@ -80,48 +89,68 @@ void parseMipmaps(BlpFile *blpFile, TextureFormat textureFormat, MipmapsVector & // if (minSize == validSize) break; - mipmapStruct_t mipmapStruct; + mipmapStruct_t &mipmapStruct = (*mipmaps)[i]; mipmapStruct.height = height; mipmapStruct.width = width; - mipmapStruct.texture = std::vector(validSize, 0); - - if ((blpFile->colorEncoding == 1) && (blpFile->preferredFormat == 8)) {//Unk format && pixel format 8 - uint8_t *paleteData = data; - validSize = 4 * width * height; - mipmapStruct.texture = std::vector(validSize, 0); - - for (int j = 0; j< width*height; j++) { - uint8_t colIndex = paleteData[j]; - uint8_t b = blpFile->palette[colIndex*4 + 0]; - uint8_t g = blpFile->palette[colIndex*4 + 1]; - uint8_t r = blpFile->palette[colIndex*4 + 2]; - uint8_t a = paleteData[width*height + j]; - - mipmapStruct.texture[j*4 + 0] = r; - mipmapStruct.texture[j*4 + 1] = g; - mipmapStruct.texture[j*4 + 2] = b; - mipmapStruct.texture[j*4 + 3] = a; + mipmapStruct.texture.resize(validSize, 0); + + //If the bytes are not compressed + if (blpFile->preferredFormat == BLPPixelFormat::PIXEL_UNSPECIFIED) { + //If the + if (blpFile->colorEncoding == BLPColorEncoding::COLOR_PALETTE) { + uint8_t *paleteData = data; + validSize = 4 * width * height; + mipmapStruct.texture = std::vector(validSize, 0); + + for (int j = 0; j < width * height; j++) { + uint8_t colIndex = paleteData[j]; + uint8_t b = blpFile->palette[colIndex * 4 + 0]; + uint8_t g = blpFile->palette[colIndex * 4 + 1]; + uint8_t r = blpFile->palette[colIndex * 4 + 2]; + uint8_t a = paleteData[width * height + j]; + + mipmapStruct.texture[j * 4 + 0] = r; + mipmapStruct.texture[j * 4 + 1] = g; + mipmapStruct.texture[j * 4 + 2] = b; + mipmapStruct.texture[j * 4 + 3] = a; + } + } else if (blpFile->colorEncoding == BLPColorEncoding::COLOR_ARGB8888) { + //Turn BGRA into RGBA + + validSize = 4 * width * height; + mipmapStruct.texture = std::vector(validSize, 0); + for (int j = 0; j < width * height; j++) { + uint8_t b = data[j * 4 + 0]; + uint8_t g = data[j * 4 + 1]; + uint8_t r = data[j * 4 + 2]; + uint8_t a = data[j * 4 + 3]; + + mipmapStruct.texture[j * 4 + 0] = r; + mipmapStruct.texture[j * 4 + 1] = g; + mipmapStruct.texture[j * 4 + 2] = b; + mipmapStruct.texture[j * 4 + 3] = a; + } + } else { + std::copy(data, data + blpFile->lengths[i], &mipmapStruct.texture[0]); } } else { - std::copy(data, data+blpFile->lengths[i], &mipmapStruct.texture[0]); + std::copy(data, data + blpFile->lengths[i], &mipmapStruct.texture[0]); } - if (textureFormat == TextureFormat::BGRA) { - - } - - mipmaps.push_back(mipmapStruct); - height = height / 2; width = width / 2; height = (height == 0) ? 1 : height; width = (width == 0) ? 1 : width; } + return mipmaps; } void BlpTexture::process(HFileContent blpFile, const std::string &fileName) { /* Post load for texture data. Can't define them through declarative definition */ /* Determine texture format */ +// std::cout << fileName << std::endl; + int fileSize = blpFile->size(); +// std::cout << fileSize << std::endl; BlpFile *pBlpFile = (BlpFile *) &(*blpFile.get())[0]; if (pBlpFile->fileIdent != '2PLB') { std::cout << pBlpFile->fileIdent; @@ -130,7 +159,7 @@ void BlpTexture::process(HFileContent blpFile, const std::string &fileName) { /* Load texture by mipmaps */ assert(this->m_textureFormat != TextureFormat::None); - parseMipmaps(pBlpFile, m_textureFormat, m_mipmaps); + m_mipmaps = parseMipmaps(pBlpFile, m_textureFormat); // /* Load texture into GL memory */ // this->texture = createGlTexture(pBlpFile, textureFormat, mipmaps, fileName); diff --git a/wowViewerLib/src/engine/texture/BlpTexture.h b/wowViewerLib/src/engine/texture/BlpTexture.h index cf37c874a..bd1c65d61 100644 --- a/wowViewerLib/src/engine/texture/BlpTexture.h +++ b/wowViewerLib/src/engine/texture/BlpTexture.h @@ -27,16 +27,16 @@ struct mipmapStruct_t { int32_t width; int32_t height; }; -typedef std::vector MipmapsVector; +typedef std::shared_ptr> HMipmapsVector; class BlpTexture : public PersistentFile{ public: - BlpTexture(std::string fileName){}; - BlpTexture(int fileDataId){}; + BlpTexture(std::string fileName){ m_textureName = fileName;}; + BlpTexture(int fileDataId){m_textureName = std::to_string(fileDataId);}; std::string getTextureName() { return m_textureName; }; void process(HFileContent blpFile, const std::string &fileName) override; - const MipmapsVector& getMipmapsVector() { + const HMipmapsVector& getMipmapsVector() { return m_mipmaps; } @@ -46,7 +46,7 @@ class BlpTexture : public PersistentFile{ private: std::string m_textureName; - MipmapsVector m_mipmaps; + HMipmapsVector m_mipmaps; TextureFormat m_textureFormat = TextureFormat::None; }; diff --git a/wowViewerLib/src/engine/wowScene.cpp b/wowViewerLib/src/engine/wowScene.cpp deleted file mode 100644 index 0ddeb93bb..000000000 --- a/wowViewerLib/src/engine/wowScene.cpp +++ /dev/null @@ -1,850 +0,0 @@ -#include "wowScene.h" -#include "algorithms/mathHelper.h" -#include "objects/scenes/m2Scene.h" -#include "objects/scenes/wmoScene.h" -#include "androidLogSupport.h" - -#include "mathfu/glsl_mappings.h" -#include "./../gapi/UniformBufferStructures.h" -#include "../gapi/IDeviceFactory.h" -#include "algorithms/FrameCounter.h" -//#include "objects/scenes/creatureScene.h" -#include -#include -#include -#include -#include -#include - - - -void WoWSceneImpl::processCaches(int limit) { -// std::cout << "WoWSceneImpl::processCaches called " << std::endl; -// std::cout << "this->adtObjectCache.m_cache.size() = " << this->adtObjectCache.m_cache.size()<< std::endl; - if (cacheStorage) { - cacheStorage->processCaches(limit); - } -} -void WoWSceneImpl::DoUpdate() { - FrameCounter frameCounter; - - FrameCounter singleUpdateCNT; - FrameCounter meshesCollectCNT; - - frameCounter.beginMeasurement(); - - IDevice *device = getDevice(); - int updateObjFrame = device->getUpdateFrameNumber(); - WoWFrameData *objFrameParam = &m_FrameParams[updateObjFrame]; - updateFrameIndex = updateObjFrame; - - device->startUpdateForNextFrame(); - - singleUpdateCNT.beginMeasurement(); - currentScene->update(objFrameParam); - singleUpdateCNT.endMeasurement("single update "); - - meshesCollectCNT.beginMeasurement(); - currentScene->collectMeshes(objFrameParam); - meshesCollectCNT.endMeasurement("collectMeshes "); - - device->prepearMemoryForBuffers(objFrameParam->renderedThisFrame); - m_sceneWideUniformBuffer->setUpdateHandler([objFrameParam](IUniformBufferChunk *chunk) -> void { - auto *blockPSVS = &chunk->getObject(); - blockPSVS->uLookAtMat = objFrameParam->m_lookAtMat4; - blockPSVS->uPMatrix = objFrameParam->m_perspectiveMatrix; - }); - - currentScene->updateBuffers(objFrameParam); - device->updateBuffers(objFrameParam->renderedThisFrame); - - currentScene->doPostLoad(objFrameParam); //Do post load after rendering is done! - device->uploadTextureForMeshes(objFrameParam->renderedThisFrame); - - device->endUpdateForNextFrame(); - frameCounter.endMeasurement("Update Thread"); - -} -void WoWSceneImpl::DoCulling() { - if (currentScene == nullptr) { - return; - } - - float farPlane = m_config->getFarPlane(); - float nearPlane = 1.0; - float fov = toRadian(45.0); - - static const mathfu::vec3 upVector(0,0,1); - - IDevice *device = getDevice(); - int currentFrame = device->getCullingFrameNumber(); - WoWFrameData *frameParam = &m_FrameParams[currentFrame]; - - M2CameraResult cameraResult; - mathfu::mat4 lookAtMat4; - mathfu::vec4 cameraVec4; - - m_firstCamera.setMovementSpeed(m_config->getMovementSpeed()); - if (controllable == nullptr) return; -// if (!m_config->getUseSecondCamera()){ - ((ICamera *)this->controllable)->tick(frameParam->deltaTime); -// } else { -// this->m_secondCamera.tick(frameParam->deltaTime); -// } - - if ( /*currentScene->getCameraSettings(cameraResult)*/ false) { -// farPlane = cameraResult.far_clip * 100; -// farPlane = 300; -// nearPlane = cameraResult.near_clip; -// -// fov = cameraResult.diagFov/ sqrt(1 + canvAspect*canvAspect); -// -// lookAtMat4 = -// mathfu::mat4::LookAt( -// -cameraResult.target_position.xyz()+cameraResult.position.xyz(), -// mathfu::vec3(0,0,0), -// upVector) * mathfu::mat4::FromTranslationVector(-cameraResult.position.xyz()); -// cameraVec4 = cameraResult.position; -// frameParam->m_lookAtMat4 = lookAtMat4; - - } else { - - cameraVec4 = mathfu::vec4(((ICamera *)controllable)->getCameraPosition(), 1); - lookAtMat4 = ((ICamera *)this->controllable)->getLookatMat(); - - frameParam->m_lookAtMat4 = lookAtMat4; - } - - frameParam->m_perspectiveMatrixForCulling = - mathfu::mat4::Perspective( - fov, - this->canvAspect, - nearPlane, - m_config->getFarPlaneForCulling()); - //Camera for rendering - mathfu::mat4 perspectiveMatrixForCameraRender = - mathfu::mat4::Perspective(fov, - this->canvAspect, - nearPlane, - farPlane); - mathfu::mat4 viewCameraForRender = - perspectiveMatrixForCameraRender * lookAtMat4; - - frameParam->m_secondLookAtMat = - mathfu::mat4::LookAt( - this->m_secondCamera.getCameraPosition(), - this->m_secondCamera.getCameraLookAt(), - upVector); - - mathfu::mat4 perspectiveMatrix = - mathfu::mat4::Perspective( - fov, - this->canvAspect, - nearPlane, - farPlane); - - static const mathfu::mat4 vulkanMatrixFix = mathfu::mat4(1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, 1.0/2.0, 1/2.0, - 0, 0, 0, 1).Transpose(); - - if (device->getIsVulkanAxisSystem()) { - perspectiveMatrix = vulkanMatrixFix * perspectiveMatrix; - } - - frameParam->m_perspectiveMatrix = perspectiveMatrix; - - frameParam->m_viewCameraForRender = viewCameraForRender; - - frameParam->m_cameraVec3 = cameraVec4.xyz(); - - float ambient[4]; - m_config->getAmbientColor(ambient); - frameParam->m_globalAmbientColor = mathfu::vec4(ambient[0],ambient[1],ambient[2],ambient[3]); - - float sunColor[4]; - m_config->getSunColor(sunColor); - frameParam->m_globalSunColor = mathfu::vec4(sunColor[0],sunColor[1],sunColor[2],sunColor[3]); - - float fogColor[4]; - m_config->getFogColor(fogColor); - frameParam->m_fogColor = mathfu::vec4(fogColor); - - if (frameParam->uFogStart < 0) { - frameParam->uFogStart = 3.0 * farPlane; - } - if (frameParam->uFogEnd < 0) { - frameParam->uFogEnd = 4.0 * farPlane; - } - - this->SetDirection(*frameParam); - - - currentScene->checkCulling(frameParam); -} - -void WoWSceneImpl::setScene(int sceneType, std::string name, int cameraNum) { - if (sceneType == -1) { - newScene = new NullScene(); - } else if (sceneType == 0) { - m_usePlanarCamera = cameraNum == -1; - if (m_usePlanarCamera) { - controllable = &m_planarCamera; - } - newScene = new M2Scene(this, name , cameraNum); - } else if (sceneType == 1) { - controllable = &m_firstCamera; - m_usePlanarCamera = false; - newScene = new WmoScene(this, name); - } else if (sceneType == 2) { - std::string &adtFileName = name; - - size_t lastSlashPos = adtFileName.find_last_of("/"); - size_t underscorePosFirst = adtFileName.find_last_of("_"); - size_t underscorePosSecond = adtFileName.find_last_of("_", underscorePosFirst-1); - std::string mapName = adtFileName.substr(lastSlashPos+1, underscorePosSecond-lastSlashPos-1); - - int i = std::stoi(adtFileName.substr(underscorePosSecond+1, underscorePosFirst-underscorePosSecond-1)); - int j = std::stoi(adtFileName.substr(underscorePosFirst+1, adtFileName.size()-underscorePosFirst-5)); - - float adt_x_min = AdtIndexToWorldCoordinate(j); - float adt_x_max = AdtIndexToWorldCoordinate(j+1); - - float adt_y_min = AdtIndexToWorldCoordinate(i); - float adt_y_max = AdtIndexToWorldCoordinate(i+1); - - m_firstCamera.setCameraPos( - (adt_x_min+adt_x_max) / 2.0, - (adt_y_min+adt_y_max) / 2.0, - 200 - ); - - controllable = &m_firstCamera; - m_usePlanarCamera = false; - - newScene = new Map(this, adtFileName, i, j, mapName); - } -} - -void WoWSceneImpl::setMap(int mapId, int wdtFileId, float x, float y, float z) { - controllable = &m_firstCamera; - m_usePlanarCamera = false; - - m_firstCamera.setCameraPos( - x, - y, - z - ); - - controllable = &m_firstCamera; - m_usePlanarCamera = false; - - newScene = new Map(this, mapId, wdtFileId); -} - - -void WoWSceneImpl::setReplaceTextureArray(std::vector &replaceTextureArray) { - if (newScene != nullptr) { - newScene->setReplaceTextureArray(replaceTextureArray); - } else { - currentScene->setReplaceTextureArray(replaceTextureArray); - } -} - -void WoWSceneImpl::setAnimationId(int animationId) { - if (newScene != nullptr) { - newScene->setAnimationId(animationId); - } else { - currentScene->setAnimationId(animationId); - } -} - -void WoWSceneImpl::setSceneWithFileDataId(int sceneType, int fileDataId, int cameraNum) { - if (sceneType == -1) { - newScene = new NullScene(); - } else if (sceneType == 0) { - m_usePlanarCamera = cameraNum == -1; - if (m_usePlanarCamera) { - controllable = &m_planarCamera; - } - newScene = new M2Scene(this, fileDataId , cameraNum); - } else if (sceneType == 1) { - controllable = &m_firstCamera; - m_usePlanarCamera = false; - newScene = new WmoScene(this, fileDataId); - } -} - -WoWSceneImpl::WoWSceneImpl(Config *config, WoWFilesCacheStorage * woWFilesCacheStorage, IClientDatabase * clientDatabase, IDevice * device, int canvWidth, int canvHeight) : - cacheStorage(woWFilesCacheStorage), m_clientDatabase(clientDatabase) -{ - m_gdevice.reset(device); -// m_gdevice.reset(IDeviceFactory::createDevice("ogl4")); - -#ifdef __EMSCRIPTEN__ - m_supportThreads = false; -#endif - -// std::ofstream *out = new std::ofstream("log_output.txt"); -// std::streambuf *coutbuf = std::cout.rdbuf(); //save old buf -// std::cout.rdbuf(out->rdbuf()); //redirect std::cout to out.txt! -// - m_sceneWideUniformBuffer = m_gdevice->createUniformBufferChunk(sizeof(sceneWideBlockVSPS)); - - this->m_config = config; - - this->canvWidth = canvWidth; - this->canvHeight = canvHeight; - this->canvAspect = (float)canvWidth / (float)canvHeight; - - //Init caches - - //Test scene 1: Shattrath -// m_firstCamera.setCameraPos(-1663, 5098, 27); //Shattrath -// m_firstCamera.setCameraPos(-241, 1176, 256); //Dark Portal - -// currentScene = new Map(this, 530, "Expansion01"); -// m_firstCamera.setCameraPos(972, 2083, 0); //Lost isles template -// m_firstCamera.setCameraPos(-834, 4500, 0); //Dalaran 2 -// m_firstCamera.setCameraPos(-719, 2772, 317); //Near the black tower -// m_firstCamera.setCameraPos( 4054, 7370, 27); // Druid class hall -// currentScene = new Map(this, 1220, "Troll Raid"); -// currentScene = new Map(this, 0, "BrokenShoreBattleshipFinale"); - -// m_firstCamera.setCameraPos(-1663, 5098, 27); -// m_firstCamera.setCameraPos( -7134, 931, 27); // THE WOUND -// currentScene = new Map(this, 1817, "silithusphase01"); -// -// m_firstCamera.setCameraPos( -594, 4664, 200); -// currentScene = new Map(this, 1513, "Artifact-MageOrderHall"); - -// m_firstCamera.setCameraPos( 3733.33325, 2666.66675, 0); -// currentScene = new Map(this, "BLTestMap"); - -// currentScene = new Map(this, 1779, "argus_rifts"); - -// m_secondCamera.setCameraPos(-1663, 5098, 27); -// -// m_firstCamera.setCameraPos(5243.2461346537075f, 1938.6550422193939f, 717.0332923206179f); //HallsOfReflection -// currentScene = new Map(this, 668, "HallsOfReflection"); -// .go 668 5243 1938 760 - // .go 668 0 0 0 - -// m_firstCamera.setCameraPos( 2290, -9.475f, 470); // Ulduar Raid -// currentScene = new Map(this, 603, "UlduarRaid"); -// -// m_firstCamera.setCameraPos( -8192, -4819, 200); // Ulduar Raid -// currentScene = new Map(this, 1803, "Kalimdor"); -// -// m_firstCamera.setCameraPos( 2843, 847, 200); // Ulduar Raid -// currentScene = new Map(this, "Islands_7VR_Swamp_Prototype2"); - -// m_firstCamera.setCameraPos( -2925, 8997, 200); // Antorus -// m_firstCamera.setCameraPos( 5333.3335, 10666.667, 1); //Strange WMO -// currentScene = new Map(this, 1669, "argus 1"); - -// m_firstCamera.setCameraPos( 4266.67, -2133.33, 200); // VoidElf -// currentScene = new Map(this, "VoildElf"); -// currentScene = new Map(this, "silithusphase01"); - -// m_firstCamera.setCameraPos(-3000, -3000, 0); //Broken shore -// currentScene = new Map(this, "stormgarde keep"); -// -// m_firstCamera.setCameraPos(-963, 977, 80); -// currentScene = new Map(this, "dungeonblockout"); -// -// m_firstCamera.setCameraPos(939, -4813 , 0); //Scholozar Basin -// m_firstCamera.setCameraPos(5783, 850, 200); //Near Dalaran -// m_firstCamera.setCameraPos(3608, 5961, 292); //Near Dalaran -// currentScene = new Map(this, 571, "Northrend"); -// -// m_firstCamera.setCameraPos(-8517, 1104, 200); //Stormwind -// m_firstCamera.setCameraPos(0, 0, 200); //Stormwind -// currentScene = new Map(this, 0, "Azeroth"); -// -// m_firstCamera.setCameraPos(-5025, -807, 500); //Ironforge -// m_firstCamera.setCameraPos(-921, 767, 200); -// currentScene = new Map(this, 0, "Zandalar"); -// -// m_firstCamera.setCameraPos(0, 0, 200); //Zaldalar -// currentScene = new Map(this, 1642, "test_01"); -// -// -// m_firstCamera.setCameraPos(570, 979, 200); //Maelstorm Shaman -// currentScene = new Map(this, "MaelstromShaman"); - -// m_firstCamera.setCameraPos(11733, 3200, 200); //Kalimdor 2 -// m_firstCamera.setCameraPos(6947, 408, 162); //Kalimdor 2 -// currentScene = new Map(this, 1, "Kalimdor 2"); -// -// m_firstCamera.setCameraPos(1825.32, 376.095, 70.0652); //LordaeronScenario -// currentScene = new Map(this, 1, "LordaeronScenario"); -// -// m_firstCamera.setCameraPos( 2652, 1083, 200) ; //LordaeronScenario -// currentScene = new Map(this, 1, "legionshiphorizontalstormheim"); - -// m_firstCamera.setCameraPos( -9169.86, 1604.42, 26.84) ; //LordaeronScenario -// currentScene = new Map(this, 1, "ahnqirajtemple"); -// -// m_firstCamera.setCameraPos(3062, 495, 200 ); //Valhalla -// m_firstCamera.setCameraPos(2979, 3525, 200); //Field of the Eternal Hunt -// currentScene = new Map(this, 1477, "Valhallas"); -// -// m_firstCamera.setCameraPos(2902, 2525, 200db); //Field of the Eternal Hunt -// m_firstCamera.setCameraPos(3993, 2302, 1043); //Field of the Eternal Hunt -// currentScene = new Map(this, "NagaDungeon"); -// m_firstCamera.setCameraPos(829, -296, 200 ); //Field of the Eternal Hunt -// currentScene = new Map(this, "unused"); - -// m_firstCamera.setCameraPos(-2825, -4546, 200 ); //Field of the Eternal Hunt -// currentScene = new Map(this, "ScenarioAlcazIsland"); - -// m_firstCamera.setCameraPos( 0, 0, 470); // Panda ria -// currentScene = new Map(this, 0, "HawaiiMainLand"); - -// m_firstCamera.setCameraPos(-12886, -165, 200); // Pandaria -// currentScene = new Map(this, "Azeroth"); -// -// m_firstCamera.setCameraPos(-12017, 3100, 200); // Pandaria -// currentScene = new Map(this, 0, "Kalimdor"); -// -// m_firstCamera.setCameraPos( -8517, 1104, 200); -// currentScene = new Map(this, 0, "escapefromstockades"); - - - //Test scene 2: tree from shattrath -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "WORLD\\EXPANSION01\\DOODADS\\TEROKKAR\\TREES\\TEROKKARTREEMEDIUMPINECONES.m2"); -// currentScene = new M2Scene(this, -// "creature/akama/akama.m2"); -// -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "WORLD\\GENERIC\\PASSIVEDOODADS\\SHIPS\\SHIPANIMATION\\TRANSPORTSHIP_SAILS.m2"); -// -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "world\\generic\\human\\passive doodads\\gryphonroost\\gryphonroost01.m2"); -// currentScene = new M2Scene(this, -// "world/expansion07/doodads/dungeon/doodads/8hu_waycrest_painting10.m2"); -// -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "creature/twilightascendantwater/twilightascendantwater.m2"); -// -// Test scene 2: tree from shattrath -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "WORLD\\AZEROTH\\ELWYNN\\PASSIVEDOODADS\\WATERFALL\\ELWYNNTALLWATERFALL01.m2"); - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "creature/celestialdragonwyrm/celestialdragonwyrm.m2"); - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "creature/lorthemar/lorthemar.m2"); - m_usePlanarCamera = false; - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "creature\\wingedhorse\\wingedhorse.m2"); -// currentScene = new M2Scene(this, -// "environments/stars/hellfireskybox.m2"); -// -// -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "world/expansion06/doodads/brokenshore/7bs_tombofsargerasfx_01_reduced.m2"); - -// currentScene = new M2Scene(this, -// "world/expansion07/doodads/8xp_burningteldrassil01.m2"); -//currentScene = new M2Scene(this, -// "world/expansion06/doodads/brokenshore/7bs_tombofsargerasfxbeam_01_reduced.m2"); -// -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "WORLD\\EXPANSION02\\DOODADS\\ULDUAR\\UL_SMALLSTATUE_DRUID.m2"); -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_mainmenu_northrend/ui_mainmenu_northrend.m2", 0); -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_mainmenu_legion/ui_mainmenu_legion.m2", 0); -// -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_mainmenu_battleforazeroth/ui_mainmenu_battleforazeroth.m2", 0); -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_mainmenu_warlords/ui_mainmenu_warlords.m2", 0); - -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_mainmenu_pandaria/ui_mainmenu_pandaria.m2", 0); -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_mainmenu_cataclysm/ui_mainmenu_cataclysm.m2", 0); -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_mainmenu_burningcrusade/ui_mainmenu_burningcrusade.m2", 0); -// mathfu::vec4 ambientColorOver = mathfu::vec4(0.3929412066936493f, 0.26823532581329346f, 0.3082353174686432f, 0); -// currentScene->setAmbientColorOverride(ambientColorOver, true); -// config->setBCLightHack(true); - -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_mainmenu/ui_mainmenu.m2", 0); -// config->setBCLightHack(true); - -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_worgen/ui_worgen.m2", 0); - -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_pandaren/ui_pandaren.m2", 0); -// -// currentScene = new M2Scene(this, -// "interface/glues/models/ui_nightelf/ui_nightelf.m2", 0); - -// currentScene = new M2Scene(this, -// "world/khazmodan/ironforge/passivedoodads/throne/dwarventhrone01.m2"); -// -// currentScene = new M2Scene(this, -// "creature/murloc/murloc.m2"); -// -// -// currentScene = new M2Scene(this, -// "WORLD\\EXPANSION02\\DOODADS\\GENERIC\\SCOURGE\\SC_EYEOFACHERUS_02.m2"); - -// currentScene = new M2Scene(this, -// "world/lordaeron/alteracmountains/passivedoodads/dalaran/dalarandome.m2"); - -// currentScene = new M2Scene(this, -// "WORLD/EXPANSION02/DOODADS/CRYSTALSONGFOREST/BUBBLE/CAMOUFLAGEBUBBLE_CRYSTALSONG.m2"); - -// m_usePlanarCamera = true; -// if (m_usePlanarCamera) { -// controllable = &m_planarCamera; -// } - -// currentScene = new M2Scene(this, 2200968, 0); - - //Test scene 3: Ironforge -// m_firstCamera.setCameraPos(1.78252912f, 33.4062042f, -126.937592f); //Room under dalaran -// m_firstCamera.setCameraPos(-32.1193314, 0.432947099, 9.5181284); //Room with transparent window -// currentScene = new WmoScene(this, -// "world\\wmo\\brokenisles\\dalaran2.wmo"); -// currentScene = new WmoScene(this, -// "World\\wmo\\Dungeon\\test\\test.wmo"); - - - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "WORLD/WMO/NORTHREND/BUILDINGS/HUMAN/ND_HUMAN_INN/ND_HUMAN_INN.WMO"); - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "World\\wmo\\BrokenIsles\\Suramar\\7SR_SuramarCity_Single_B_Core_C.wmo"); -// -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "world\\wmo\\dungeon\\karazhanb\\7du_karazhanb_castle.wmo"); -// m_firstCamera.setCameraPos(-1161.35, -798.59, 835.05); -// currentScene = new Map(this, "grimbatolraid"); - -// currentScene = new WmoScene(this, -// "world\\wmo\\dungeon\\karazhanb\\7du_karazhanb_tower.wmo"); - -// currentScene = new WmoScene(this, -// "world\\wmo\\dungeon\\karazhanb\\7du_karazhanb_nether.wmo"); -// currentScene = new WmoScene(this, -// "world\\wmo\\dungeon\\mantidraid\\pa_mantid_raid.wmo"); -// currentScene = new WmoScene(this, -// "world/wmo/dungeon/grimbatol/kz_grimbatol.wmo"); -// currentScene = new WmoScene(this, -// "world/wmo/dungeon/grimbatol/kz_grimbatol_raid.wmo");// -// currentScene = new WmoScene(this, -// "\tworld/wmo/dungeon/thunderkingraid/pa_thunderking_raid.wmo"); - - -// currentScene = new WmoScene(this, - // "World/wmo/Dungeon/AZ_Subway/Subway.wmo"); -// currentScene = new WmoScene(this, -// "world/wmo/azeroth/buildings/stranglethorn_bootybay/bootybay.wmo"); //bootybay -// 2324175); -// -// currentScene = new WmoScene(this, -// 2198682); -// currentScene = new WmoScene(this, -// "world/wmo/kultiras/nightelf/8ne_nightelf_dockbroken01.wmo"); - - -// m_firstCamera.setCameraPos(136.784775,-42.097565,33.5634689); -// currentScene = new WmoScene(this, -// "world\\wmo\\dungeon\\tombofsargerasraid\\7du_tombofsargeras_raid.wmo"); -// currentScene = new WmoScene(this, -// "world\\wmo\\khazmodan\\cities\\ironforge\\ironforge.wmo"); - -// currentScene = new WmoScene(this, -// "WORLD\\WMO\\PANDARIA\\VALEOFETERNALBLOSSOMS\\TEMPLES\\MG_RAIDBUILDING_LD.WMO"); -// -// currentScene = new WmoScene(this, -// "world\\wmo\\draenor\\tanaanjungle\\6tj_darkportal_antiportal.wmo"); -// -// currentScene = new WmoScene(this, -// "world\\wmo\\azeroth\\buildings\\stormwind\\stormwind.WMO"); - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "world\\wmo\\dungeon\\argusraid\\7du_argusraid_shivantemple.wmo"); - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "world/wmo/brokenisles/valsharah/7vs_nightmare_worldtree.wmo"); - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "WORLD\\WMO\\TRANSPORTS\\TRANSPORT_SHIP\\TRANSPORTSHIP.WMO"); -// -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "world/wmo/brokenisles/brokenshore/7bs_tombofsargerasfx_01_wmo.wmo"); -// -// currentScene = new WmoScene(this, -// "world/wmo/brokenisles/brokenshore/7bs_tombofsargeras.wmo"); -// - - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "world/wmo/azeroth/buildings/worldtree/theworldtreehyjal.wmo"); - -// m_firstCamera.setCameraPos(0, 0, 0); -// currentScene = new WmoScene(this, -// "world/wmo/dungeon/argusraid/7du_argusraid_pantheon.wmo"); -// -// currentScene = new WmoScene(this, -// "world/wmo/lorderon/undercity/8xp_undercity.wmo"); - -// currentScene = new WmoScene(this, -// "world/wmo/zuldazar/forsaken/8fk_forsaken_shiplarge01.wmo"); - -// currentScene = new WmoScene(this, -// "world/wmo/dungeon/ulduar/ulduar_raid.wmo"); - -// setScene(2, "world/maps/ahnqiraj/ahnqiraj_26_46.adt", -1); -// setScene(2, "WORLD/MAPTEXTURES/MAELSTROMDEATHWINGFIGHT/MAELSTROMDEATHWINGFIGHT_32_32.adt", -1); -// setScene(2, "WORLD/MAPTEXTURES/Expansion01/Expansion01_44_8.adt", -1); -// setSceneWithFileDataId(1, 1846142, -1); // wmo with horde symbol -// setSceneWithFileDataId(1, 324981, -1); -// setSceneWithFileDataId(1, 1120838, -1); -// setSceneWithFileDataId(1, 1699872, -1); -// setScene(2, "world/maps/nzoth/nzoth_32_27.adt", -1); -// setScene(2, "world/maps/Kalimdor/Kalimdor_41_47.adt", -1); -// setScene(0, "interface/glues/models/ui_mainmenu_northrend/ui_mainmenu_northrend.m2", 0); -// setMap(1, 782779, -8183, -4708, 200); -// setMap(530, 828395, -1663, 5098, 27); //Sharrath -// setScene(2, "world/maps/SilithusPhase01/SilithusPhase01_30_45.adt", -1); -// setSceneWithFileDataId(1, 113992, -1); //Ironforge -// setSceneWithFileDataId(1, 108803, -1); -// setSceneWithFileDataId(0, 352511, -1); // arthas souls -// setSceneWithFileDataId(0, 3180291, -1); // arthas souls -// setSceneWithFileDataId(0, 125407, -1); // phoneix -// setSceneWithFileDataId(0, 2500382, -1); // galliwix mount -// setSceneWithFileDataId(0, 125995, -1); //portal -// setSceneWithFileDataId(0, 418699, -1); //turtle -// setSceneWithFileDataId(0, 1612576, -1); //portal -// setSceneWithFileDataId(1, 108803, -1); //caverns of time in Tanaris - -// setSceneWithFileDataId(0, 1100087, -1); //bloodelfMale_hd -// setSceneWithFileDataId(0, 1416430, -1); //illidan crystal -// setSceneWithFileDataId(0, 341893, -1); //bone spike - - -// setSceneWithFileDataId(0, 1814471, -1); //nightbornemale -// setwthFileDataId(0, 1269330, -1); //nightbornemale creature - - -} - -void WoWSceneImpl::setScreenSize(int canvWidth, int canvHeight) { - this->canvWidth = canvWidth; - this->canvHeight = canvHeight; - this->canvAspect = (float)canvWidth / (float)canvHeight; -} - -/****************/ - -void WoWSceneImpl::drawCamera () { - /* - glDisable(GL_DEPTH_TEST); - - mathfu::mat4 invViewFrustum = this->m_viewCameraForRender.Inverse(); - - glUniformMatrix4fv(drawFrustumShader->getUnf("uInverseViewProjection"), 1, GL_FALSE, &invViewFrustum[0]); - - glDrawElements(GL_LINES, 48, GL_UNSIGNED_SHORT, 0); - glEnable(GL_DEPTH_TEST); - */ -} - -void print_timediff(const char* prefix, const struct timespec& start, const -struct timespec& end) -{ - double milliseconds = (end.tv_nsec - start.tv_nsec) / 1e6 + (end.tv_sec - start.tv_sec) * 1e3; - printf("%s: %lf milliseconds\n", prefix, milliseconds); -} - -void WoWSceneImpl::draw(animTime_t deltaTime) { - //Replace the scene - if (newScene != nullptr) { - if (currentScene != nullptr) delete currentScene; - for (int i = 0; i < 4; i++) { - m_FrameParams[i] = WoWFrameData(); - } - getDevice()->shrinkData(); - - currentScene = newScene; - newScene = nullptr; - } - - std::future cullingFuture; - std::future updateFuture; - if (m_supportThreads) { - cullingFuture = cullingFinished.get_future(); - - nextDeltaTime.set_value(deltaTime); - if (getDevice()->getIsAsynBuffUploadSupported()) { - nextDeltaTimeForUpdate.set_value(deltaTime); - updateFuture = updateFinished.get_future(); - } - } - - if (currentScene == nullptr) return; - - if (needToDropCache) { - if (cacheStorage) { - cacheStorage->actuallDropCache(); - } - needToDropCache = false; - } - - - IDevice *device = getDevice(); - device->reset(); - int currentFrame = device->getDrawFrameNumber(); - WoWFrameData *frameParam = &m_FrameParams[currentFrame]; - - if (!m_supportThreads) { - processCaches(10); - frameParam->deltaTime = deltaTime; - DoCulling(); - } - - float clearColor[4]; - m_config->getClearColor(clearColor); - device->setClearScreenColor(clearColor[0], clearColor[1], clearColor[2]); - device->setViewPortDimensions(0,0,this->canvWidth, this->canvHeight); - device->beginFrame(); - - device->drawMeshes(frameParam->renderedThisFrame); - m_isDebugCamera = false; - - - device->commitFrame(); - device->reset(); - - if (!device->getIsAsynBuffUploadSupported()) { - DoUpdate(); - } - - - if (m_supportThreads) { - cullingFuture.wait(); - cullingFinished = std::promise(); - - if (device->getIsAsynBuffUploadSupported()) { - updateFuture.wait(); - updateFinished = std::promise(); - } - } - - - device->increaseFrameNumber(); -} -mathfu::mat3 blizzTranspose(mathfu::mat4 &value) { - - return mathfu::mat3( - value.GetColumn(0).x,value.GetColumn(1).x,value.GetColumn(2).x, - value.GetColumn(0).y,value.GetColumn(1).y,value.GetColumn(2).y, - value.GetColumn(0).z,value.GetColumn(1).z,value.GetColumn(2).z - ); -} - -void WoWSceneImpl::SetDirection(WoWFrameData &frameParamHolder) { - - // Phi Table - static const float phiTable[4][2] = { - { 0.0, 2.2165682f }, - { 0.25, 1.9198623f }, - { 0.5, 2.2165682f }, - { 0.75, 1.9198623f } - }; - - // Theta Table - - - static const float thetaTable[4][2] = { - { 0.0, 3.926991f }, - { 0.25, 3.926991f }, - { 0.5, 3.926991f }, - { 0.75, 3.926991f } - }; - -// float phi = DayNight::InterpTable(&DayNight::phiTable, 4u, DayNight::g_dnInfo.dayProgression); -// float theta = DayNight::InterpTable(&DayNight::thetaTable, 4u, DayNight::g_dnInfo.dayProgression); - - float phi = phiTable[0][1]; - float theta = thetaTable[0][1]; - - // Convert from spherical coordinates to XYZ - // x = rho * sin(phi) * cos(theta) - // y = rho * sin(phi) * sin(theta) - // z = rho * cos(phi) - - float sinPhi = (float) sin(phi); - float cosPhi = (float) cos(phi); - - float sinTheta = (float) sin(theta); - float cosTheta = (float) cos(theta); - - mathfu::mat3 lookAtRotation = mathfu::mat4::ToRotationMatrix(frameParamHolder.m_lookAtMat4); - - mathfu::vec4 sunDirWorld = mathfu::vec4(sinPhi * cosTheta, sinPhi * sinTheta, cosPhi, 0); -// mathfu::vec4 sunDirWorld = mathfu::vec4(-0.30822, -0.30822, -0.89999998, 0); - frameParamHolder.m_sunDir = (lookAtRotation * sunDirWorld.xyz()); -// frameParamHolder.m_sunDir = frameParamHolder.m_sunDir.Normalized(); - - mathfu::vec4 upVector ( 0.0, 0.0 , 1.0 , 0.0); -// frameParamHolder.m_upVector = (frameParamHolder.m_lookAtMat4.Transpose() * upVector).xyz(); - frameParamHolder.m_upVector = (lookAtRotation * upVector.xyz());; -} - -WoWSceneImpl::~WoWSceneImpl() { - m_isTerminating = true; -} - -WoWScene *createWoWScene(Config *config, WoWFilesCacheStorage * cacheStorage, IClientDatabase * clientDatabase, IDevice *device, int canvWidth, int canvHeight) { -#ifdef __ANDROID_API__ - std::cout.rdbuf(new androidbuf()); -#endif - - return new WoWSceneImpl(config, cacheStorage, clientDatabase, device, canvWidth, canvHeight); -} - -void WoWSceneImpl::clearCache() { -// std::cout << "Called " << __PRETTY_FUNCTION__ << std::endl; -// needToDropCache = true; - if (cacheStorage) { - cacheStorage->actuallDropCache(); - }; -} - - - - diff --git a/wowViewerLib/src/engine/wowScene.h b/wowViewerLib/src/engine/wowScene.h deleted file mode 100644 index 971e91906..000000000 --- a/wowViewerLib/src/engine/wowScene.h +++ /dev/null @@ -1,193 +0,0 @@ -#ifndef WOWMAPVIEWERREVIVED_WOWSCENEIMPL_H_H -#define WOWMAPVIEWERREVIVED_WOWSCENEIMPL_H_H - -#include -#include -#include -#include -#include -#include -#include -#include "wowInnerApi.h" - -#include "../include/wowScene.h" -#include "../include/config.h" -#include "mathfu/glsl_mappings.h" -#include "camera/firstPersonOrthoCamera.h" -#include "camera/firstPersonCamera.h" -#include "texture/BlpTexture.h" -#include "geometry/skinGeom.h" -#include "geometry/m2Geom.h" -#include "cache/cache.h" -#include "stringTrim.h" -#include "geometry/wmoGroupGeom.h" -#include "geometry/wmoMainGeom.h" -#include "objects/scenes/map.h" -#include "persistance/wdtFile.h" -#include "persistance/wdlFile.h" -#include "../gapi/interface/IDevice.h" -#include "objects/wowFrameData.h" -#include "camera/planarCamera.h" -#include "persistance/animFile.h" -#include "persistance/skelFile.h" -#include "objects/scenes/NullScene.h" -#include "WowFilesCacheStorage.h" - -class WoWSceneImpl: public WoWScene, public IWoWInnerApi { -private: - HWoWFilesCacheStorage cacheStorage = nullptr; -public: - WoWSceneImpl(Config *config, WoWFilesCacheStorage * cacheStorage, IClientDatabase * clientDatabase, IDevice * device, int canvWidth, int canvHeight); - ~WoWSceneImpl() override; - - void draw(animTime_t deltaTime) override; - void setScreenSize(int canvWidth, int canvHeight) override; - void setCacheStorage(WoWFilesCacheStorage * pcacheStorage) override { - cacheStorage = pcacheStorage; - }; - - IControllable* controllable = &m_firstCamera; - - IControllable* getCurrentCamera() override { - return this->controllable; - }; - - void switchCameras() override { - if (controllable == &m_firstCamera) { - this->m_config->setUseSecondCamera(true); - controllable = &m_secondCamera; - mathfu::vec3 camPos = m_firstCamera.getCameraPosition(); - m_secondCamera.setCameraPos(camPos.x, camPos.y, camPos.z); - } else { - this->m_config->setUseSecondCamera(false); - controllable = &m_firstCamera; - } - } -public: - virtual HGUniformBufferChunk getSceneWideUniformBuffer() override { - return m_sceneWideUniformBuffer; - } - - virtual Cache *getAdtGeomCache() override { - return cacheStorage->getAdtGeomCache(); - } - virtual Cache *getM2GeomCache() override { - return cacheStorage->getM2GeomCache(); - }; - virtual Cache* getSkinGeomCache() override { - return cacheStorage->getSkinGeomCache(); - }; - virtual Cache* getAnimCache() override { - return cacheStorage->getAnimCache(); - }; - virtual Cache* getSkelCache() override { - return cacheStorage->getSkelCache(); - }; - virtual Cache *getTextureCache() override { - return cacheStorage->getTextureCache(); - }; - virtual Cache* getWmoMainCache() override { - return cacheStorage->getWmoMainCache(); - }; - virtual Cache* getWmoGroupGeomCache() override { - return cacheStorage->getWmoGroupGeomCache(); - }; - virtual Cache* getWdtFileCache() override { - return cacheStorage->getWdtFileCache(); - }; - - virtual Cache* getWdlFileCache() override { - return cacheStorage->getWdlFileCache(); - }; - - virtual mathfu::mat4& getViewMat() override { - return m_FrameParams[updateFrameIndex].m_lookAtMat4; - } - virtual mathfu::vec4 getGlobalAmbientColor() override { - return m_FrameParams[updateFrameIndex].m_globalAmbientColor ; - } - virtual mathfu::vec3 getGlobalSunDir() override { - return m_FrameParams[updateFrameIndex].m_sunDir; - }; - virtual mathfu::vec4 getGlobalSunColor() override { - return m_FrameParams[updateFrameIndex].m_globalSunColor; - } - virtual mathfu::vec4 getGlobalFogColor() override { - return m_FrameParams[updateFrameIndex].m_fogColor; - } - virtual float getGlobalFogStart() override { return m_FrameParams[updateFrameIndex].uFogStart; }; - virtual float getGlobalFogEnd() override { return m_FrameParams[updateFrameIndex].uFogEnd; }; - virtual mathfu::vec3 getViewUp() override { return m_FrameParams[updateFrameIndex].m_upVector; }; - - int updateFrameIndex = 0; - - virtual Config *getConfig() override { - return m_config; - } - virtual IClientDatabase *getDatabaseHandler() override { - return m_clientDatabase; - } - void setScenePos(float x, float y, float z) override { - m_firstCamera.setCameraPos(x,y,z); - }; - void setCameraPosition(float x, float y, float z) override { - m_planarCamera.setCameraPos(x,y,z); - } - void setCameraOffset(float x, float y, float z) override { - m_planarCamera.setCameraOffset(x,y,z); - }; - void clearCache() override; - - void setAnimationId(int animationId) override; - void setReplaceTextureArray(std::vector &replaceTextureArray) override; - - void setScene(int sceneType, std::string fileName, int cameraNum) override; - void setSceneWithFileDataId(int sceneType, int fileDataId, int cameraNum) override; - void setMap(int mapId, int wdtFileId, float x, float y, float z) override; -private: - void DoCulling(); - void DoUpdate(); -private: - bool m_enable; - - bool m_isTerminating = false; - Config * m_config; - IClientDatabase * m_clientDatabase; - - HGUniformBufferChunk m_sceneWideUniformBuffer; - - WoWFrameData m_FrameParams[4]; - - bool m_usePlanarCamera = false; - - FirstPersonCamera m_firstCamera; - PlanarCamera m_planarCamera; - FirstPersonCamera m_secondCamera; - - int canvWidth; - int canvHeight; - float canvAspect; - - - bool m_isDebugCamera = false; - - - IScene *currentScene = new NullScene(); - IScene *newScene = nullptr; - - bool needToDropCache = false; - - bool getIsDebugCamera() override { - return m_isDebugCamera; - } - - bool m_supportThreads = true; - - void SetDirection(WoWFrameData &frameParamHolder); - - void processCaches(int limit); -}; - - - -#endif //WOWMAPVIEWERREVIVED_WOWSCENEIMPL_H_H diff --git a/wowViewerLib/src/exporters/IExporter.h b/wowViewerLib/src/exporters/IExporter.h new file mode 100644 index 000000000..fa2ec800e --- /dev/null +++ b/wowViewerLib/src/exporters/IExporter.h @@ -0,0 +1,36 @@ +// +// Created by Deamon on 12/22/2020. +// + +#ifndef AWEBWOWVIEWERCPP_IEXPORTER_H +#define AWEBWOWVIEWERCPP_IEXPORTER_H + +class M2Object; + +#include +#include "../engine/objects/m2/m2Object.h" + +class IExporter { +public: + virtual ~IExporter() = default; + +public: + virtual void addM2Object(std::shared_ptr &m2Object) = 0; + virtual void saveToFile(std::string filePath) = 0; +protected: + static auto getM2Geom(std::shared_ptr &m2Object) { + return m2Object->m_m2Geom; + } + static auto getM2BlpTextureData(std::shared_ptr &m2Object, int index) { + return m2Object->getBlpTextureData(index); + } + static auto getBoneMasterData(std::shared_ptr &m2Object) { + return m2Object->m_boneMasterData; + } + static auto getSkinGeom(std::shared_ptr &m2Object) { + return m2Object->m_skinGeom; + } +}; + + +#endif //AWEBWOWVIEWERCPP_IEXPORTER_H diff --git a/wowViewerLib/src/gapi/IDeviceFactory.cpp b/wowViewerLib/src/gapi/IDeviceFactory.cpp index eafe6a581..b2177340a 100644 --- a/wowViewerLib/src/gapi/IDeviceFactory.cpp +++ b/wowViewerLib/src/gapi/IDeviceFactory.cpp @@ -15,7 +15,7 @@ #endif void initOGLPointers(){ -#ifdef _WIN32 +#if defined(_WIN32) && (!defined(WITH_GLESv2) && !defined(__EMSCRIPTEN__)) glewExperimental = true; // Needed in core profile auto result = glewInit(); @@ -25,18 +25,24 @@ void initOGLPointers(){ #endif } -std::shared_ptr IDeviceFactory::createDevice(std::string gapiName, void * data) { +HGDevice IDeviceFactory::createDevice(std::string gapiName, void * data) { #ifdef LINK_OGL2 if (gapiName == "ogl2") { initOGLPointers(); - return std::make_shared(); + + auto device = std::make_shared(); + device->initialize(); + return device; } else #endif #ifdef LINK_OGL3 if (gapiName == "ogl3") { initOGLPointers(); - return std::make_shared(); + + auto device = std::make_shared(); + device->initialize(); + return device; } else #endif #ifdef LINK_OGL4 diff --git a/wowViewerLib/src/gapi/IDeviceFactory.h b/wowViewerLib/src/gapi/IDeviceFactory.h index 7e08a4a1d..188d50226 100644 --- a/wowViewerLib/src/gapi/IDeviceFactory.h +++ b/wowViewerLib/src/gapi/IDeviceFactory.h @@ -12,7 +12,7 @@ class IDeviceFactory { public: - static std::shared_ptr createDevice(std::string gapiName, void* data); + static HGDevice createDevice(std::string gapiName, void* data); }; diff --git a/wowViewerLib/src/gapi/UniformBufferStructures.h b/wowViewerLib/src/gapi/UniformBufferStructures.h index bb4e8bc7b..4270bb8bf 100644 --- a/wowViewerLib/src/gapi/UniformBufferStructures.h +++ b/wowViewerLib/src/gapi/UniformBufferStructures.h @@ -28,6 +28,7 @@ struct SceneExteriorLight { mathfu::vec4_packed uExteriorGroundAmbientColor; mathfu::vec4_packed uExteriorDirectColor; mathfu::vec4_packed uExteriorDirectColorDir; + mathfu::vec4_packed uAdtSpecMult; }; struct sceneWideBlockVSPS { @@ -83,12 +84,45 @@ namespace M2 { int PixelShader; int UnFogged; int IsAffectedByLight; - int LightCount; + int BlendMode; mathfu::vec4_packed uFogColorAndAlphaTest; + mathfu::vec4_packed uTexSampleAlpha; mathfu::vec4_packed uPcColor; }; + + namespace WaterfallData { + struct meshWideBlockVS { + mathfu::vec4_packed bumpScale; + mathfu::mat4 uTextMat[2]; + }; + struct meshWideBlockPS { + mathfu::vec4_packed values0; + mathfu::vec4_packed values1; + mathfu::vec4_packed m_values2; + mathfu::vec4_packed m_values3; + mathfu::vec4_packed m_values4; + mathfu::vec4_packed baseColor; + }; + } +} +namespace Particle { + struct meshParticleWideBlockPS { + float uAlphaTest; + float textureScale0; + float textureScale1; + float textureScale2; + int uPixelShader; + int uBlendMode; + int padding2; // according to std140 + int padding3; // according to std140 + float textureTranslate0; + float textureTranslate1; + float textureTranslate2; + float padding4; // according to std140 + + }; } namespace WMO { @@ -100,6 +134,7 @@ namespace WMO { struct meshWideBlockVS { int VertexShader; int UseLitColor; + int padding[2]; }; @@ -112,7 +147,7 @@ namespace WMO { int UseLitColor; int EnableAlpha; int PixelShader; - int padding; + int BlendMode; mathfu::vec4_packed uFogColor_AlphaTest; // )} }; @@ -123,8 +158,7 @@ namespace ADT { }; struct modelWideBlockPS { - mathfu::vec4_packed uFogStartAndFogEnd; - mathfu::vec4_packed uFogColor; + int useHeightMixFormula[4]; }; struct meshWideBlockPS { @@ -134,6 +168,13 @@ namespace ADT { }; } +namespace ImgUI { + struct modelWideBlockVS { + mathfu::mat4 projectionMat; + float scale[4]; + }; +} + namespace FXGauss { struct meshWideBlockPS { float texOffsetX[4]; @@ -147,6 +188,12 @@ namespace DnSky { }; } +namespace DrawPortalShader { + struct meshWideBlockPS { + mathfu::vec4_packed uColor; + }; +} + struct bbModelWideBlockVS { mathfu::mat4 uPlacementMat; diff --git a/wowViewerLib/src/gapi/interface/IDevice.cpp b/wowViewerLib/src/gapi/interface/IDevice.cpp index f891c3eb0..deb81ee3f 100644 --- a/wowViewerLib/src/gapi/interface/IDevice.cpp +++ b/wowViewerLib/src/gapi/interface/IDevice.cpp @@ -21,14 +21,18 @@ int anisFiltrationSupported = -1; bool IDevice::getIsCompressedTexturesSupported() { #ifdef __EMSCRIPTEN__ if (compressedTexturesSupported == -1){ - if (emscripten_webgl_enable_extension(emscripten_webgl_get_current_context(), "WEBGL_compressed_texture_s3tc") == EM_TRUE) { + auto res = emscripten_webgl_enable_extension(emscripten_webgl_get_current_context(), "WEBGL_compressed_texture_s3tc"); + std::cout << "texture_s3tc returned " << res << std::endl; + if (res == EM_TRUE) { compressedTexturesSupported = 1; } else { compressedTexturesSupported = 0; } } - return compressedTexturesSupported == 1; + return (compressedTexturesSupported == 1); +#elif (WITH_GLESv2) + return false; #else return true; #endif @@ -49,4 +53,14 @@ bool IDevice::getIsAnisFiltrationSupported() { #endif } +std::string IDevice::insertAfterVersion(std::string &glslShaderString, std::string stringToPaste) { + auto start = glslShaderString.find("#version"); + if (start != std::string::npos) { + auto end = glslShaderString.find("\n", start+1); + return glslShaderString.insert(end+1, stringToPaste); + } else { + return glslShaderString.insert(0, stringToPaste); + } +} + diff --git a/wowViewerLib/src/gapi/interface/IDevice.h b/wowViewerLib/src/gapi/interface/IDevice.h index 093fe1bb9..d5d3188c0 100644 --- a/wowViewerLib/src/gapi/interface/IDevice.h +++ b/wowViewerLib/src/gapi/interface/IDevice.h @@ -135,18 +135,29 @@ struct vkCallInitCallback { }; #endif +struct FramebufAvalabilityStruct { + int width; int height; + std::vector attachments; + ITextureFormat depthAttachment; + HFrameBuffer frameBuffer; + int frame; +}; + class IDevice { public: virtual ~IDevice() {}; + virtual void initialize() = 0; virtual void reset() = 0; virtual unsigned int getFrameNumber() = 0; virtual unsigned int getUpdateFrameNumber() = 0; + virtual unsigned int getOcclusionFrameNumber() = 0; virtual unsigned int getCullingFrameNumber() = 0; virtual unsigned int getDrawFrameNumber() = 0; virtual bool getIsAsynBuffUploadSupported() = 0; virtual int getMaxSamplesCnt() = 0; + virtual int getUploadSize() {return 0;}; virtual void increaseFrameNumber() = 0; @@ -162,7 +173,8 @@ class IDevice { virtual void startUpdateForNextFrame() {}; virtual void endUpdateForNextFrame() {}; - virtual void updateBuffers(std::vector &meshes, std::vector additionalChunks)= 0; + + virtual void updateBuffers(std::vector*> &bufferChunks, std::vector &frameDepedantData)= 0; virtual void uploadTextureForMeshes(std::vector &meshes) = 0; virtual void drawMeshes(std::vector &meshes) = 0; virtual void drawStageAndDeps(HDrawStage drawStage) = 0; @@ -172,6 +184,10 @@ class IDevice { virtual float getAnisLevel() = 0; virtual bool getIsVulkanAxisSystem() {return false;} virtual bool getIsRenderbufferSupported() {return false;} + + virtual void initUploadThread(){} + virtual double getWaitForUpdate() {return 0;} + public: virtual HGShaderPermutation getShader(std::string shaderName, void *permutationDescriptor) = 0; @@ -189,10 +205,10 @@ class IDevice { virtual HGIndexBuffer createIndexBuffer() = 0; virtual HGVertexBufferBindings createVertexBufferBindings() = 0; //Creates or receives framebuffer and tells it would be occupied for frameNumber frames - virtual HFrameBuffer createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int frameNumber) = 0; + virtual HFrameBuffer createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int multiSampleCnt, int frameNumber) = 0; virtual HGTexture createBlpTexture(HBlpTexture &texture, bool xWrapTex, bool yWrapTex) = 0; - virtual HGTexture createTexture() = 0; + virtual HGTexture createTexture(bool xWrapTex, bool yWrapTex) = 0; virtual HGTexture getWhiteTexturePixel() = 0; virtual HGTexture getBlackTexturePixel() {return nullptr;}; virtual HGMesh createMesh(gMeshTemplate &meshTemplate) = 0; @@ -213,8 +229,16 @@ class IDevice { virtual void commitFrame() = 0; virtual void shrinkData() {}; + virtual bool wasTexturesUploaded() = 0; + + static std::string insertAfterVersion(std::string &glslShaderString, std::string stringToPaste); + virtual void addDeallocationRecord(std::function callback) {}; + + virtual int getCurrentTextureAllocated() {return 0;} }; +typedef std::shared_ptr HGDevice; + #include #define _DEBUG diff --git a/wowViewerLib/src/gapi/interface/buffers/IUniformBufferChunk.h b/wowViewerLib/src/gapi/interface/buffers/IUniformBufferChunk.h index f07f7cef0..6529ffdfc 100644 --- a/wowViewerLib/src/gapi/interface/buffers/IUniformBufferChunk.h +++ b/wowViewerLib/src/gapi/interface/buffers/IUniformBufferChunk.h @@ -7,11 +7,14 @@ #include #include +#include "../../../engine/DrawStage.h" + +typedef std::function IChunkHandlerType; class IUniformBufferChunk { friend class IDevice; private: - std::function m_handler; + IChunkHandlerType m_handler; protected: size_t m_offset = 0; size_t m_size = 0; @@ -43,12 +46,12 @@ class IUniformBufferChunk { return m_offset; } - virtual void setUpdateHandler(std::function handler) { + virtual void setUpdateHandler(IChunkHandlerType handler) { m_handler = handler; }; - virtual void update() { + virtual void update(const HFrameDepedantData &frameDepedantData) { if (m_handler) { - m_handler(this); + m_handler(this, frameDepedantData); } }; }; diff --git a/wowViewerLib/src/gapi/interface/meshes/IMesh.h b/wowViewerLib/src/gapi/interface/meshes/IMesh.h index e700bcf19..829fea9c8 100644 --- a/wowViewerLib/src/gapi/interface/meshes/IMesh.h +++ b/wowViewerLib/src/gapi/interface/meshes/IMesh.h @@ -51,8 +51,6 @@ enum class DrawElementMode { TRIANGLE_STRIP }; -extern BlendModeDesc blendModes[(int)EGxBlendEnum::GxBlend_MAX]; - class gMeshTemplate { public: gMeshTemplate(HGVertexBufferBindings bindings, HGShaderPermutation shader) : bindings(bindings), shader(shader) {} @@ -114,9 +112,11 @@ class IMesh { int m_start; int m_end; - std::vector m_texture; + std::vector m_texture = {}; int m_textureCount; + std::array m_UniformBuffer = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + public: virtual ~IMesh(){ // std::cout << "Mesh destroyed" << std::endl; @@ -133,6 +133,7 @@ class IMesh { virtual void setEnd(int end) = 0; public: + virtual void * getM2Object() = 0; virtual void setM2Object(void * m2Object) = 0; virtual void setLayer(int layer) = 0; virtual void setPriorityPlane(int priorityPlane) = 0; diff --git a/wowViewerLib/src/gapi/interface/sortLambda.h b/wowViewerLib/src/gapi/interface/sortLambda.h index 63a864d2a..6e746978f 100644 --- a/wowViewerLib/src/gapi/interface/sortLambda.h +++ b/wowViewerLib/src/gapi/interface/sortLambda.h @@ -1,6 +1,6 @@ -[sortedArrayPtr](int indexA, int indexB) { - IMesh * pA = sortedArrayPtr[indexA].get(); - IMesh* pB = sortedArrayPtr[indexB].get(); +[](const HGMesh &indexA, const HGMesh &indexB) { + IMesh * pA = indexA.get(); + IMesh* pB = indexB.get(); // HGMesh pA = sortedArrayPtr[indexA]; // HGMesh pB = sortedArrayPtr[indexB]; @@ -14,12 +14,12 @@ return true; } - if (pA->getMeshType() > pB->getMeshType()) { - return false; - } - if (pA->getMeshType() < pB->getMeshType()) { - return true; - } +// if (pA->getMeshType() > pB->getMeshType()) { +// return false; +// } +// if (pA->getMeshType() < pB->getMeshType()) { +// return true; +// } // if (pA->m_renderOrder != pB->m_renderOrder ) { // if (!pA->getIsTransparent()) { @@ -36,35 +36,34 @@ return false; } - if (pA->getMeshType() == MeshType::eM2Mesh && pA->getIsTransparent() && pB->getIsTransparent()) { - if (pA->priorityPlane() != pB->priorityPlane()) { - return pB->priorityPlane() > pA->priorityPlane(); - } - - if (pA->getSortDistance() > pB->getSortDistance()) { - return true; - } - if (pA->getSortDistance() < pB->getSortDistance()) { - return false; - } - -// if (pA->m_m2Object > pB->m_m2Object) { -// return true; -// } -// if (pA->m_m2Object < pB->m_m2Object) { -// return false; -// } - - if (pB->layer() != pA->layer()) { - return !(pB->layer() < pA->layer()); - } - } - - if (pA->getMeshType() == MeshType::eParticleMesh && pB->getMeshType() == MeshType::eParticleMesh) { - if (pA->priorityPlane() != pB->priorityPlane()) { - return pB->priorityPlane() > pA->priorityPlane(); + if (pA->getIsTransparent() && pB->getIsTransparent()) { + if (((pA->getMeshType() == MeshType::eM2Mesh || pA->getMeshType() == MeshType::eParticleMesh) && + (pB->getMeshType() == MeshType::eM2Mesh || pB->getMeshType() == MeshType::eParticleMesh))) { + if (pA->priorityPlane()!= pB->priorityPlane()) { + return pB->priorityPlane() > pA->priorityPlane(); + } + + if (pA->getSortDistance() < pB->getSortDistance()) { + return true; + } + if (pA->getSortDistance() > pB->getSortDistance()) { + return false; + } + + if (pA->getM2Object() == pB->getM2Object()) { + if (pB->layer() != pA->layer()) { + return pB->layer() < pA->layer(); + } + } + } else { + if (pA->getSortDistance() < pB->getSortDistance()) { + return true; + } + if (pA->getSortDistance() > pB->getSortDistance()) { + return false; + } } - + }else { if (pA->getSortDistance() > pB->getSortDistance()) { return true; } diff --git a/wowViewerLib/src/gapi/interface/textures/ITexture.h b/wowViewerLib/src/gapi/interface/textures/ITexture.h index 32418960b..2d66b4dae 100644 --- a/wowViewerLib/src/gapi/interface/textures/ITexture.h +++ b/wowViewerLib/src/gapi/interface/textures/ITexture.h @@ -27,6 +27,6 @@ class ITexture { - virtual void createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) = 0; + virtual void createGlTexture(TextureFormat textureFormat, const HMipmapsVector &mipmaps) = 0; }; #endif //AWEBWOWVIEWERCPP_ITEXTURE_H diff --git a/wowViewerLib/src/gapi/ogl2.0/GDeviceGL20.cpp b/wowViewerLib/src/gapi/ogl2.0/GDeviceGL20.cpp index 581d3d028..efab3333e 100644 --- a/wowViewerLib/src/gapi/ogl2.0/GDeviceGL20.cpp +++ b/wowViewerLib/src/gapi/ogl2.0/GDeviceGL20.cpp @@ -17,23 +17,24 @@ #include "../../engine/stringTrim.h" #include "buffers/GVertexBufferDynamicGL20.h" - -BlendModeDesc blendModes[(int)EGxBlendEnum::GxBlend_MAX] = { - /*GxBlend_Opaque*/ {false,GL_ONE,GL_ZERO,GL_ONE,GL_ZERO}, - /*GxBlend_AlphaKey*/ {false,GL_ONE,GL_ZERO,GL_ONE,GL_ZERO}, - /*GxBlend_Alpha*/ {true,GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ONE_MINUS_SRC_ALPHA}, - /*GxBlend_Add*/ {true,GL_SRC_ALPHA,GL_ONE,GL_ZERO,GL_ONE}, - /*GxBlend_Mod*/ {true,GL_DST_COLOR,GL_ZERO,GL_DST_ALPHA,GL_ZERO}, - /*GxBlend_Mod2x*/ {true,GL_DST_COLOR,GL_SRC_COLOR,GL_DST_ALPHA,GL_SRC_ALPHA}, - /*GxBlend_ModAdd*/ {true,GL_DST_COLOR,GL_ONE,GL_DST_ALPHA,GL_ONE}, - /*GxBlend_InvSrcAlphaAdd*/ {true,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ONE_MINUS_SRC_ALPHA,GL_ONE}, - /*GxBlend_InvSrcAlphaOpaque*/{true,GL_ONE_MINUS_SRC_ALPHA,GL_ZERO,GL_ONE_MINUS_SRC_ALPHA,GL_ZERO}, - /*GxBlend_SrcAlphaOpaque*/ {true,GL_SRC_ALPHA,GL_ZERO,GL_SRC_ALPHA,GL_ZERO}, - /*GxBlend_NoAlphaAdd*/ {true,GL_ONE,GL_ONE,GL_ZERO,GL_ONE}, - /*GxBlend_ConstantAlpha*/ {true,GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT_ALPHA,GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT_ALPHA}, - /*GxBlend_Screen*/ {true,GL_ONE_MINUS_DST_COLOR,GL_ONE,GL_ONE,GL_ZERO}, - /*GxBlend_BlendAdd*/ {true,GL_ONE,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ONE_MINUS_SRC_ALPHA} -}; +namespace GL20 { + BlendModeDesc blendModes[(int)EGxBlendEnum::GxBlend_MAX] = { + /*GxBlend_Opaque*/ {false,GL_ONE,GL_ZERO,GL_ONE,GL_ZERO}, + /*GxBlend_AlphaKey*/ {false,GL_ONE,GL_ZERO,GL_ONE,GL_ZERO}, + /*GxBlend_Alpha*/ {true,GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ONE_MINUS_SRC_ALPHA}, + /*GxBlend_Add*/ {true,GL_SRC_ALPHA,GL_ONE,GL_ZERO,GL_ONE}, + /*GxBlend_Mod*/ {true,GL_DST_COLOR,GL_ZERO,GL_DST_ALPHA,GL_ZERO}, + /*GxBlend_Mod2x*/ {true,GL_DST_COLOR,GL_SRC_COLOR,GL_DST_ALPHA,GL_SRC_ALPHA}, + /*GxBlend_ModAdd*/ {true,GL_DST_COLOR,GL_ONE,GL_DST_ALPHA,GL_ONE}, + /*GxBlend_InvSrcAlphaAdd*/ {true,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ONE_MINUS_SRC_ALPHA,GL_ONE}, + /*GxBlend_InvSrcAlphaOpaque*/{true,GL_ONE_MINUS_SRC_ALPHA,GL_ZERO,GL_ONE_MINUS_SRC_ALPHA,GL_ZERO}, + /*GxBlend_SrcAlphaOpaque*/ {true,GL_SRC_ALPHA,GL_ZERO,GL_SRC_ALPHA,GL_ZERO}, + /*GxBlend_NoAlphaAdd*/ {true,GL_ONE,GL_ONE,GL_ZERO,GL_ONE}, + /*GxBlend_ConstantAlpha*/ {true,GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT_ALPHA,GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT_ALPHA}, + /*GxBlend_Screen*/ {true,GL_ONE_MINUS_DST_COLOR,GL_ONE,GL_ONE,GL_ZERO}, + /*GxBlend_BlendAdd*/ {true,GL_ONE,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ONE_MINUS_SRC_ALPHA} + }; +} //std::string source_to_string(KHR_debug::Source source) { // if(source == KHR_debug::Source::API) { @@ -54,17 +55,15 @@ BlendModeDesc blendModes[(int)EGxBlendEnum::GxBlend_MAX] = { //} void debug_func(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { - fprintf(stdout, "source: %u, type: %u, id: %u, severity: %u, msg: %s\n", - source, - type, - id, - severity, - std::string(message, message+length).c_str()); - if (severity == 37190) { - std::cout << "lol"; - } - - fflush(stdout); +// if (type == GL_DEBUG_TYPE_ERROR) { +// fprintf(stdout, "source: %u, type: %u, id: %u, severity: %u, msg: %s\n", +// source, +// type, +// id, +// severity, +// std::string(message, message + length).c_str()); +// fflush(stdout); +// } } void GDeviceGL20::bindIndexBuffer(IIndexBuffer *buffer) { @@ -228,40 +227,26 @@ void GDeviceGL20::drawMeshes(std::vector &meshes) { // } int j = 0; - for (auto &hgMesh : meshes) { - this->drawMesh(hgMesh); - j++; - } +// for (auto &hgMesh : meshes) { +// this->drawMesh(hgMesh); +// j++; +// } } -void GDeviceGL20::updateBuffers(std::vector &iMeshes, std::vector additionalChunks) { - std::vector &meshes = (std::vector &) iMeshes; - - //1. Collect buffers - std::vector buffers; - int renderIndex = 0; - for (const auto &mesh : meshes) { - for (int i = 0; i < 5; i++ ) { - IUniformBufferChunk * buffer = (IUniformBufferChunk *)mesh->m_UniformBuffer[i].get(); - if (buffer != nullptr) { - buffers.push_back(buffer); - - } - } - } +void GDeviceGL20::updateBuffers(std::vector*> &bufferChunks, std::vector &frameDepedantDataVec) { + int fullSize = 0; - std::sort( buffers.begin(), buffers.end()); - buffers.erase( unique( buffers.begin(), buffers.end() ), buffers.end() ); + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + for (auto &bufferChunk : *bufferVec) { + fullSize += bufferChunk->getSize(); + if (uniformBufferOffsetAlign > 0) { + int offsetDiff = fullSize % uniformBufferOffsetAlign; + if (offsetDiff != 0) { + int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; - int fullSize = 0; - for (auto &buffer : buffers) { - fullSize += buffer->getSize(); - if (uniformBufferOffsetAlign > 0) { - int offsetDiff = fullSize % uniformBufferOffsetAlign; - if (offsetDiff != 0) { - int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; - - fullSize += bytesToAdd; + fullSize += bytesToAdd; + } } } } @@ -284,23 +269,30 @@ void GDeviceGL20::updateBuffers(std::vector &iMeshes, std::vector(&aggregationBufferForUpload[0]); - for (const auto &bufferChunk : buffers) { - - bufferChunk->setOffset(currentSize); - bufferChunk->setPointer(&pointerForUpload[currentSize]); - currentSize += bufferChunk->getSize(); - - if (uniformBufferOffsetAlign > 0) { - int offsetDiff = currentSize % uniformBufferOffsetAlign; - if (offsetDiff != 0) { - int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; - - currentSize += bytesToAdd; + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + for (const auto &bufferChunk : *bufferVec) { + bufferChunk->setOffset(currentSize); + bufferChunk->setPointer(&pointerForUpload[currentSize]); + currentSize += bufferChunk->getSize(); + + if (uniformBufferOffsetAlign > 0) { + int offsetDiff = currentSize % uniformBufferOffsetAlign; + if (offsetDiff != 0) { + int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; + + currentSize += bytesToAdd; + } } } } - for (auto &buffer : buffers) { - buffer->update(); + + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + auto frameDepData = frameDepedantDataVec[i]; + for (const auto &bufferChunk : *bufferVec) { + bufferChunk->update(frameDepData); + } } if (currentSize > 0) { @@ -308,7 +300,7 @@ void GDeviceGL20::updateBuffers(std::vector &iMeshes, std::vectorm_end <= 0) return; @@ -325,9 +317,15 @@ void GDeviceGL20::drawMesh(HGMesh &hIMesh) { bindProgram(hmesh->m_shader.get()); bindVertexBufferBindings(hmesh->m_bindings.get()); - for (int i = 0; i < 5; i++) { - auto *uniformChunk = hmesh->m_UniformBuffer[i].get(); + + IUniformBufferChunk *uniformChunk = nullptr; + if (i == 0) { + uniformChunk = matrixChunk.get(); + } else { + uniformChunk = hmesh->m_UniformBuffer[i].get(); + } + if (uniformChunk != nullptr) { bindUniformBuffer(bufferForUpload.get(), i, uniformChunk->getOffset(), uniformChunk->getSize()); } @@ -408,10 +406,10 @@ void GDeviceGL20::drawMesh(HGMesh &hIMesh) { } if (m_lastBlendMode != hmesh->m_blendMode) { - BlendModeDesc &selectedBlendMode = blendModes[(char)hmesh->m_blendMode]; + BlendModeDesc &selectedBlendMode = GL20::blendModes[(char)hmesh->m_blendMode]; if ((m_lastBlendMode == EGxBlendEnum::GxBlend_UNDEFINED) || - (blendModes[(char)m_lastBlendMode].blendModeEnable != selectedBlendMode.blendModeEnable )) { + (GL20::blendModes[(char)m_lastBlendMode].blendModeEnable != selectedBlendMode.blendModeEnable )) { if (selectedBlendMode.blendModeEnable) { glEnable(GL_BLEND); } else { @@ -444,7 +442,10 @@ void GDeviceGL20::drawMesh(HGMesh &hIMesh) { ((GOcclusionQueryGL20 *)gm2Mesh->m_query.get())->beginConditionalRendering(); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" glDrawElements(hmesh->m_element, hmesh->m_end, GL_UNSIGNED_SHORT, (const void *) (hmesh->m_start )); +#pragma clang diagnostic pop if (gm2Mesh != nullptr && gm2Mesh->m_query != nullptr) { ((GOcclusionQueryGL20 *)gm2Mesh->m_query.get())->endConditionalRendering(); @@ -545,9 +546,9 @@ HGTexture GDeviceGL20::createBlpTexture(HBlpTexture &texture, bool xWrapTex, boo return hgTexture; } -HGTexture GDeviceGL20::createTexture() { +HGTexture GDeviceGL20::createTexture(bool xWrapTex, bool yWrapTex) { std::shared_ptr hgTexture; - hgTexture.reset(new GTextureGL20(*this)); + hgTexture.reset(new GTextureGL20(*this, xWrapTex, yWrapTex)); return hgTexture; } @@ -575,12 +576,16 @@ void GDeviceGL20::bindProgram(IShaderPermutation *iProgram) { } GDeviceGL20::GDeviceGL20() { + +} + +void GDeviceGL20:: initialize() { unsigned int ff = 0xFFFFFFFF; unsigned int zero = 0; - m_blackPixelTexture = createTexture(); + m_blackPixelTexture = createTexture(false, false); m_blackPixelTexture->loadData(1,1,&zero, ITextureFormat::itRGBA); - m_whitePixelTexture = createTexture(); + m_whitePixelTexture = createTexture(false, false); m_whitePixelTexture->loadData(1,1,&ff, ITextureFormat::itRGBA); m_defaultVao = this->createVertexBufferBindings(); @@ -713,6 +718,9 @@ unsigned int GDeviceGL20::getCullingFrameNumber() { return (m_frameNumber + 3) & 3; // return 0; } +unsigned int GDeviceGL20::getOcclusionFrameNumber() { + return (m_frameNumber + 2) & 3; +} unsigned int GDeviceGL20::getDrawFrameNumber() { return m_frameNumber & 3; } @@ -757,9 +765,12 @@ void GDeviceGL20::uploadTextureForMeshes(std::vector &meshes) { } #ifdef __ANDROID_API__ -#include "../androidLogSupport.h" +#include "../../engine/androidLogSupport.h" #endif - +#define logExecution {} +//#define logExecution { \ +// std::cout << "Passed "<<__FUNCTION__<<" line " << __LINE__ << std::endl;\ +//} std::string GDeviceGL20::loadShader(std::string fileName, IShaderType shaderType) { std::string fullPath; trim(fileName); @@ -791,9 +802,15 @@ std::string GDeviceGL20::loadShader(std::string fileName, IShaderType shaderType if (g_assetMgr == nullptr) { std::cout << "g_assetMgr == nullptr"; } - std::string filename = "glsl/" + shaderName + ".glsl"; + std::string filename = fullPath; + //Trim dot and slash at the start + if (filename[0] == '.') + filename = filename.substr(1, filename.length()-1); + if (filename[0] == '/') + filename = filename.substr(1, filename.length()-1); std::cout << "AAssetManager_open" << std::endl; + std::cout << "Opening assest at \"" << filename << "\n" << std::endl; AAsset* asset = AAssetManager_open(mgr, filename.c_str(), AASSET_MODE_STREAMING); std::cout << "AAssetManager_opened" << std::endl; if (asset == nullptr) { @@ -815,22 +832,16 @@ std::string GDeviceGL20::loadShader(std::string fileName, IShaderType shaderType std::cout << "file fully read" << std::endl; AAsset_close(asset); std::cout << "asset closed" << std::endl; - - return std::string(outBuf.begin(), outBuf.end()); + logExecution + std::string result = std::string(outBuf.begin(), outBuf.end()); + logExecution #else std::ifstream t(fullPath); std::string result = std::string((std::istreambuf_iterator(t)), std::istreambuf_iterator()); - - //Delete version - { - auto start = result.find("#version"); - if (start != std::string::npos) { - auto end = result.find("\n"); - result = result.substr(end); - } - } +#endif + logExecution //Hack fix for bones { @@ -844,66 +855,8 @@ std::string GDeviceGL20::loadShader(std::string fileName, IShaderType shaderType } - //Hack fix for bone matrices - { - auto boneMatStart = result.find("mat4 boneTransformMat = "); - if (boneMatStart != std::string::npos) { - - //Struct override: - { - auto structStart = result.find( "struct modelWideBlockVS"); - auto structEnd = result.find( "};", structStart); - - result = result.substr(0, structStart) + - "struct modelWideBlockVS\n" - "{\n" - " mat4 uPlacementMat;\n" - "};" - "uniform mat4 uBoneMatrixes[220];"+ - result.substr(structEnd+2); - } - - - //Get uniiform prefix - auto prefixEnd = result.find(".uBoneMatrixes"); - auto prefixStart = result.rfind( " ", prefixEnd); - std::string prefix = result.substr(prefixStart, prefixEnd-prefixStart+1); - - - auto startIterator = boneMatStart; - auto end = boneMatStart; end = 0; - while(startIterator != std::string::npos) { - end = result.find("\n", startIterator + 1); - - startIterator = result.find("boneTransformMat = ", end+1); - } - - //Replace the whole block with old info - result = result.substr(0, boneMatStart) + - "mat4 boneTransformMat = mat4(1.0);\n" - "\n" - "#if BONEINFLUENCES>0\n" - " boneTransformMat = mat4(0.0);\n" - " const float inttofloat = (1.0/255.0);\n" - " boneTransformMat += (boneWeights.x ) * uBoneMatrixes[int(bones.x)];\n" - "#endif\n" - "#if BONEINFLUENCES>1\n" - " boneTransformMat += (boneWeights.y ) * uBoneMatrixes[int(bones.y)];\n" - "#endif\n" - "#if BONEINFLUENCES>2\n" - " boneTransformMat += (boneWeights.z ) * uBoneMatrixes[int(bones.z)];\n" - "#endif\n" - "#if BONEINFLUENCES>3\n" - " boneTransformMat += (boneWeights.w ) * uBoneMatrixes[int(bones.w)];\n" - "#endif"+ - result.substr(end); - - } - } - shaderCache[hashRecord] = result; return result; -#endif } float GDeviceGL20::getAnisLevel() { @@ -911,15 +864,23 @@ float GDeviceGL20::getAnisLevel() { } void GDeviceGL20::clearScreen() { -#ifndef WITH_GLESv2 - glClearDepthf(1.0f); -#else - glClearDepthf(1.0f); -#endif + if (m_isInvertZ) { + glClearDepthf(0.0f); + } else { + glClearDepthf(1.0f); + } + glDisable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); + if (m_isInvertZ) { + glDepthFunc(GL_GEQUAL); + } else { + glDepthFunc(GL_LEQUAL); + } glDepthMask(GL_TRUE); glDisable(GL_BLEND); + + //Note: the scissor dimensions are taken from call to setViewPortDimensions + glEnable(GL_SCISSOR_TEST); // glClearColor(0.0, 0.0, 0.0, 0.0); // glClearColor(0.25, 0.06, 0.015, 0.0); glClearColor(clearColor[0], clearColor[1], clearColor[2], 1); @@ -939,7 +900,7 @@ void GDeviceGL20::setClearScreenColor(float r, float g, float b) { } void GDeviceGL20::beginFrame() { - this->clearScreen(); + } void GDeviceGL20::commitFrame() { @@ -948,6 +909,7 @@ void GDeviceGL20::commitFrame() { void GDeviceGL20::setViewPortDimensions(float x, float y, float width, float height) { glViewport(x,y,width,height); + glScissor(x,y,width,height); } void GDeviceGL20::shrinkData() { @@ -957,3 +919,51 @@ void GDeviceGL20::shrinkData() { aggregationBufferForUpload = {}; } + +void GDeviceGL20::drawStageAndDeps(HDrawStage drawStage) { + + for (int i = 0; i < drawStage->drawStageDependencies.size(); i++) { + this->drawStageAndDeps(drawStage->drawStageDependencies[i]); + } + + if (drawStage->target != nullptr) { + drawStage->target->bindFrameBuffer(); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + this->setViewPortDimensions( + drawStage->viewPortDimensions.mins[0], + drawStage->viewPortDimensions.mins[1], + drawStage->viewPortDimensions.maxs[0], + drawStage->viewPortDimensions.maxs[1] + ); + + this->setInvertZ(drawStage->invertedZ); + + if (drawStage->clearScreen) { + clearColor[0] = drawStage->clearColor[0]; + clearColor[1] = drawStage->clearColor[1]; + clearColor[2] = drawStage->clearColor[2]; + this->clearScreen(); + } + + + if (drawStage->opaqueMeshes != nullptr) { + for (auto hgMesh : drawStage->opaqueMeshes->meshes) { + this->drawMesh(hgMesh, drawStage->sceneWideBlockVSPSChunk); + } + } + + if (drawStage->transparentMeshes != nullptr) { + for (auto hgMesh : drawStage->transparentMeshes->meshes) { + this->drawMesh(hgMesh, drawStage->sceneWideBlockVSPSChunk); + } + } + + if (drawStage->target != nullptr) { + drawStage->target->copyRenderBufferToTexture(); + } +// drawMeshes(drawStage->meshesToRender->meshes); + +} diff --git a/wowViewerLib/src/gapi/ogl2.0/GDeviceGL20.h b/wowViewerLib/src/gapi/ogl2.0/GDeviceGL20.h index 09a3295b2..c5bbf7df8 100644 --- a/wowViewerLib/src/gapi/ogl2.0/GDeviceGL20.h +++ b/wowViewerLib/src/gapi/ogl2.0/GDeviceGL20.h @@ -43,13 +43,15 @@ typedef std::shared_ptr HGL20Mesh; class GDeviceGL20 : public IDevice { public: GDeviceGL20(); - ~GDeviceGL20() override {}; + ~GDeviceGL20() override = default;; + void initialize() override; void reset() override; unsigned int getFrameNumber() override { return m_frameNumber; }; unsigned int getUpdateFrameNumber() override; unsigned int getCullingFrameNumber() override; + unsigned int getOcclusionFrameNumber() override; unsigned int getDrawFrameNumber() override; @@ -72,10 +74,10 @@ class GDeviceGL20 : public IDevice { void bindTexture(ITexture *texture, int slot) override; - void updateBuffers(std::vector &meshes, std::vector additionalChunks) override; + void updateBuffers(std::vector*> &bufferChunks, std::vector &frameDepedantDataVec) override; void uploadTextureForMeshes(std::vector &meshes) override; void drawMeshes(std::vector &meshes) override; - void drawStageAndDeps(HDrawStage drawStage) override {}; + void drawStageAndDeps(HDrawStage drawStage) override; // void drawM2Meshes(std::vector &meshes); public: std::shared_ptr getShader(std::string shaderName, void *permutationDescriptor) override; @@ -85,10 +87,10 @@ class GDeviceGL20 : public IDevice { HGVertexBufferDynamic createVertexBufferDynamic(size_t size) override; HGIndexBuffer createIndexBuffer() override; HGVertexBufferBindings createVertexBufferBindings() override; - HFrameBuffer createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int frameNumber) override {return nullptr;}; + HFrameBuffer createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int multiSampleCnt, int frameNumber) override {return nullptr;}; HGTexture createBlpTexture(HBlpTexture &texture, bool xWrapTex, bool yWrapTex) override; - HGTexture createTexture() override; + HGTexture createTexture(bool xWrapTex, bool yWrapTex) override; HGTexture getWhiteTexturePixel() override { return m_whitePixelTexture; }; HGTexture getBlackTexturePixel() override { return m_blackPixelTexture; }; HGMesh createMesh(gMeshTemplate &meshTemplate) override; @@ -110,8 +112,11 @@ class GDeviceGL20 : public IDevice { void setViewPortDimensions(float x, float y, float width, float height) override; void setInvertZ(bool value) override {m_isInvertZ = value;}; void shrinkData() override; + bool wasTexturesUploaded() override { + return false; + }; private: - void drawMesh(HGMesh &hmesh); + void drawMesh(HGMesh hIMesh, HGUniformBufferChunk matrixChunk); bool isDepthPreFill = false; protected: struct BlpCacheRecord { diff --git a/wowViewerLib/src/gapi/ogl2.0/GShaderPermutationGL20.cpp b/wowViewerLib/src/gapi/ogl2.0/GShaderPermutationGL20.cpp index cff35bd7a..17f5ce1e6 100644 --- a/wowViewerLib/src/gapi/ogl2.0/GShaderPermutationGL20.cpp +++ b/wowViewerLib/src/gapi/ogl2.0/GShaderPermutationGL20.cpp @@ -88,6 +88,8 @@ GShaderPermutationGL20::GShaderPermutationGL20(std::string &shaderName, IDevice } + + void GShaderPermutationGL20::compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) { std::string shaderVertFile = m_device->loadShader(m_shaderName, IShaderType::gVertexShader); @@ -108,58 +110,15 @@ void GShaderPermutationGL20::compileShader(const std::string &vertExtraDef, cons std::string fragExtraDefStrings = fragExtraDef; - bool esVersion = false; -#ifdef __ANDROID_API__ - esVersion = true; -#endif -#ifdef __APPLE__ - #include "TargetConditionals.h" -#if TARGET_IPHONE_SIMULATOR - glsl330 = false; -#elif TARGET_OS_IPHONE - glsl330 = false; -#elif TARGET_OS_MAC - glsl330 = true; -#else -# error "Unknown Apple platform" -#endif -#endif -#ifdef __EMSCRIPTEN__ - esVersion = true; -#endif - - if (esVersion) { -//#ifndef __EMSCRIPTEN__ - vertExtraDefStrings = "#version 100 \n" + vertExtraDefStrings; -//#endif - } else { - vertExtraDefStrings = "#version 100\n" + vertExtraDefStrings; - } - - //OGL 2.0 requires this for both vertex and fragment shader - vertExtraDefStrings += "precision mediump float;\n"; - - - if (esVersion) { - fragExtraDefStrings = fragExtraDefStrings; - } else { - fragExtraDefStrings = "#version 100\n" + fragExtraDefStrings; - } - - //OGL 2.0 requires this for both vertex and fragment shader - fragExtraDefStrings += "precision mediump float;\n"; - - GLint maxVertexUniforms; glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniforms); int maxMatrixUniforms = MAX_MATRIX_NUM;//(maxVertexUniforms / 4) - 9; - vertExtraDefStrings = vertExtraDefStrings + "#define COMPILING_VS 1\r\n "; - fragExtraDefStrings = fragExtraDefStrings + "#define COMPILING_FS 1\r\n"; - +// vertExtraDefStrings = vertExtraDefStrings + "#define COMPILING_VS 1\r\n "; +// fragExtraDefStrings = fragExtraDefStrings + "#define COMPILING_FS 1\r\n"; - vertShaderString = vertShaderString.insert(0, vertExtraDefStrings); - fragmentShaderString = fragmentShaderString.insert(0, fragExtraDefStrings); + vertShaderString = IDevice::insertAfterVersion(vertShaderString, vertExtraDefStrings); + fragmentShaderString = IDevice::insertAfterVersion(fragmentShaderString, fragExtraDefStrings); GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); const GLchar *vertexShaderConst = (const GLchar *)vertShaderString.c_str(); @@ -244,7 +203,7 @@ void GShaderPermutationGL20::compileShader(const std::string &vertExtraDef, cons // printf("Active Uniforms: %d\n", count); for (GLint i = 0; i < count; i++) { - const GLsizei bufSize = 32; // maximum name length + const GLsizei bufSize = 256; // maximum name length GLchar name[bufSize]; // variable name in GLSL GLsizei length; // name length GLint size; // size of the variable @@ -254,7 +213,7 @@ void GShaderPermutationGL20::compileShader(const std::string &vertExtraDef, cons GLint location = glGetUniformLocation(program, name); this->setUnf(std::string(name), location); - printf("Uniform #%d Type: %u Name: %s Location: %d\n", i, type, name, location); +// printf("Uniform #%d Type: %u Name: %s Location: %d\n", i, type, name, location); } // if (!shaderName.compare("m2Shader")) { // std::cout << fragmentShaderString << std::endl << std::flush; @@ -376,7 +335,7 @@ static bool isHashStrInited = false; void GShaderPermutationGL20::bindUniformBuffer(IUniformBuffer *buffer, int slot, int offset, int length) { if (!isHashStrInited) { boneMatHashedToReplace = [](){ - for (auto shaderIter : fieldDefMapPerShaderName) { + for (auto shaderIter : fieldDefMapPerShaderNameVert) { for (auto fieldVectorIter : shaderIter.second) { for (auto const fieldDef : fieldVectorIter.second) { if (fieldDef.name.find("uBoneMatrixes[0]") != std::string::npos) { @@ -395,46 +354,60 @@ void GShaderPermutationGL20::bindUniformBuffer(IUniformBuffer *buffer, int slot, if (buffer == nullptr) { m_UniformBuffer[slot].buffer = nullptr; } else { - if (buffer != m_UniformBuffer[slot].buffer || m_UniformBuffer[slot].offset != offset) { - - auto const &shaderDef = fieldDefMapPerShaderName.at(m_shaderName); - auto const &fieldVector = shaderDef.at(slot); + if ((buffer != m_UniformBuffer[slot].buffer) || (m_UniformBuffer[slot].offset != offset)) { + + auto vec = {&fieldDefMapPerShaderNameVert, &fieldDefMapPerShaderNameFrag}; + for (auto &fieldDefMapPerShaderName : vec) { + auto const &shaderDef = fieldDefMapPerShaderName->at(m_shaderName); + { + auto it = shaderDef.find(slot); + bool found = it != shaderDef.end(); + if (!found) + continue; + } + auto const &fieldVector = shaderDef.at(slot); - for (auto const &fieldDef : fieldVector) { + for (auto const &fieldDef : fieldVector) { // const char * cstr = name.c_str(); - auto hashedStr = HashedString(fieldDef.name.c_str()); - if (hashedStr == boneMatHashedToReplace) { - hashedStr = boneMatHashed; - } + auto hashedStr = HashedString(fieldDef.name.c_str()); + if (hashedStr == boneMatHashedToReplace) { + hashedStr = boneMatHashed; + } + + if (!hasUnf(hashedStr)) continue; - if (!hasUnf(hashedStr)) continue; +// std::cout << "binding uniform field " << fieldDef.name << std::endl; - auto uniformLoc = getUnf(hashedStr); + auto uniformLoc = getUnf(hashedStr); // fieldDef.isFloat - auto fieldOffset = fieldDef.offset + offset; - char *fieldPtr = &((char *) gbuffer->getPtr())[fieldOffset]; + auto fieldOffset = fieldDef.offset + offset; + if (fieldOffset > gbuffer->getLen()) { + continue; + } + char *fieldPtr = &((char *) gbuffer->getPtr())[fieldOffset]; // fieldDef.columns // fieldDef.vecSize - auto arraySize = fieldDef.arraySize > 0 ? fieldDef.arraySize : 1; - if (fieldDef.isFloat) { - if (fieldDef.columns == 1) { - glUniform4fv(uniformLoc, arraySize, (const GLfloat*) fieldPtr); - } else if (fieldDef.columns == 2) { - glUniformMatrix2fv(uniformLoc, arraySize, GL_FALSE, (const GLfloat*) fieldPtr); - } else if (fieldDef.columns == 3) { - glUniformMatrix3fv(uniformLoc, arraySize, GL_FALSE, (const GLfloat*) fieldPtr); - } else if (fieldDef.columns == 4) { - glUniformMatrix4fv(uniformLoc, arraySize, GL_FALSE, (const GLfloat*) fieldPtr); - } - } else { - if (fieldDef.columns == 1) { - glUniform4iv(uniformLoc, arraySize, (const GLint *) fieldPtr); + auto arraySize = fieldDef.arraySize > 0 ? fieldDef.arraySize : 1; + if (fieldDef.isFloat) { + if (fieldDef.columns == 1) { + glUniform4fv(uniformLoc, arraySize, (const GLfloat *) fieldPtr); + } else if (fieldDef.columns == 2) { + glUniformMatrix2fv(uniformLoc, arraySize, GL_FALSE, (const GLfloat *) fieldPtr); + } else if (fieldDef.columns == 3) { + glUniformMatrix3fv(uniformLoc, arraySize, GL_FALSE, (const GLfloat *) fieldPtr); + } else if (fieldDef.columns == 4) { + glUniformMatrix4fv(uniformLoc, arraySize, GL_FALSE, (const GLfloat *) fieldPtr); + } + } else { + if (fieldDef.columns == 1) { + glUniform4iv(uniformLoc, arraySize, (const GLint *) fieldPtr); + } } } - } - m_UniformBuffer[slot] = {gbuffer, (uint32_t)offset}; + m_UniformBuffer[slot] = {gbuffer, (uint32_t) offset}; + } } } } diff --git a/wowViewerLib/src/gapi/ogl2.0/GVertexBufferBindingsGL20.cpp b/wowViewerLib/src/gapi/ogl2.0/GVertexBufferBindingsGL20.cpp index 130703352..f924ea328 100644 --- a/wowViewerLib/src/gapi/ogl2.0/GVertexBufferBindingsGL20.cpp +++ b/wowViewerLib/src/gapi/ogl2.0/GVertexBufferBindingsGL20.cpp @@ -25,7 +25,7 @@ GVertexBufferBindingsGL20::GVertexBufferBindingsGL20(IDevice &m_device) : m_devi } GVertexBufferBindingsGL20::~GVertexBufferBindingsGL20() { -// destroyBuffer(); + destroyBuffer(); } void GVertexBufferBindingsGL20::createBuffer() { @@ -33,7 +33,13 @@ void GVertexBufferBindingsGL20::createBuffer() { } void GVertexBufferBindingsGL20::destroyBuffer() { -// glDeleteVertexArrays(1, (GLuint *)&this->m_buffer[0]); + for (GVertexBufferBinding &binding : m_bindings) { + m_device.bindVertexBuffer(binding.vertexBuffer.get()); + + for (GBufferBinding &bufferBinding : binding.bindings) { + glDisableVertexAttribArray(bufferBinding.position); + } + } } void GVertexBufferBindingsGL20::bind() { @@ -45,6 +51,8 @@ void GVertexBufferBindingsGL20::bind() { for (GBufferBinding &bufferBinding : binding.bindings) { glEnableVertexAttribArray(bufferBinding.position); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" glVertexAttribPointer( bufferBinding.position, bufferBinding.size, @@ -53,6 +61,7 @@ void GVertexBufferBindingsGL20::bind() { bufferBinding.stride, (const void *) bufferBinding.offset ); +#pragma clang diagnostic pop } } } diff --git a/wowViewerLib/src/gapi/ogl2.0/buffers/GUniformBufferGL20.cpp b/wowViewerLib/src/gapi/ogl2.0/buffers/GUniformBufferGL20.cpp index a2bf539f3..d35cfc6bf 100644 --- a/wowViewerLib/src/gapi/ogl2.0/buffers/GUniformBufferGL20.cpp +++ b/wowViewerLib/src/gapi/ogl2.0/buffers/GUniformBufferGL20.cpp @@ -26,6 +26,7 @@ void GUniformBufferGL20::unbind() { } void GUniformBufferGL20::uploadData(void * data, int length) { - dataPtr = data; + dataPtr = data; + m_length = length; } diff --git a/wowViewerLib/src/gapi/ogl2.0/buffers/GUniformBufferGL20.h b/wowViewerLib/src/gapi/ogl2.0/buffers/GUniformBufferGL20.h index d370ad590..a38a65cfc 100644 --- a/wowViewerLib/src/gapi/ogl2.0/buffers/GUniformBufferGL20.h +++ b/wowViewerLib/src/gapi/ogl2.0/buffers/GUniformBufferGL20.h @@ -20,6 +20,7 @@ class GUniformBufferGL20 : public IUniformBuffer { void createBuffer() override; void *getPtr(){ return dataPtr; } + int getLen(){ return m_length; } private: void destroyBuffer(); @@ -33,6 +34,7 @@ class GUniformBufferGL20 : public IUniformBuffer { private: void *dataPtr; + int m_length = 0; }; diff --git a/wowViewerLib/src/gapi/ogl2.0/meshes/GM2MeshGL20.cpp b/wowViewerLib/src/gapi/ogl2.0/meshes/GM2MeshGL20.cpp index 73d000ab7..0714c8df2 100644 --- a/wowViewerLib/src/gapi/ogl2.0/meshes/GM2MeshGL20.cpp +++ b/wowViewerLib/src/gapi/ogl2.0/meshes/GM2MeshGL20.cpp @@ -11,6 +11,9 @@ GM2MeshGL20::GM2MeshGL20(IDevice &device, const gMeshTemplate &meshTemplate) : G void GM2MeshGL20::setM2Object(void *m2Object) { m_m2Object = (m2Object); } +void *GM2MeshGL20::getM2Object() { + return m_m2Object; +} void GM2MeshGL20::setLayer(int layer) { m_layer = layer; @@ -30,3 +33,5 @@ float GM2MeshGL20::getSortDistance() { void GM2MeshGL20::setQuery(const HGOcclusionQuery &query) { m_query = query; } + + diff --git a/wowViewerLib/src/gapi/ogl2.0/meshes/GM2MeshGL20.h b/wowViewerLib/src/gapi/ogl2.0/meshes/GM2MeshGL20.h index bf9d8bf28..f400bbc96 100644 --- a/wowViewerLib/src/gapi/ogl2.0/meshes/GM2MeshGL20.h +++ b/wowViewerLib/src/gapi/ogl2.0/meshes/GM2MeshGL20.h @@ -13,6 +13,7 @@ class GM2MeshGL20 : public GMeshGL20 { GM2MeshGL20(IDevice &device, const gMeshTemplate &meshTemplate); public: + void *getM2Object() override; void setM2Object(void * m2Object) override; void setLayer(int layer) override; void setPriorityPlane(int priorityPlane) override; diff --git a/wowViewerLib/src/gapi/ogl2.0/meshes/GMeshGL20.h b/wowViewerLib/src/gapi/ogl2.0/meshes/GMeshGL20.h index 35c24e882..db948ac15 100644 --- a/wowViewerLib/src/gapi/ogl2.0/meshes/GMeshGL20.h +++ b/wowViewerLib/src/gapi/ogl2.0/meshes/GMeshGL20.h @@ -28,6 +28,7 @@ class GMeshGL20 : public IMesh { void setStart(int start) override; void setEnd(int end) override; public: + void *getM2Object() override { return nullptr; }; void setM2Object(void * m2Object) override { /* throw "Not Implemented"; */}; void setLayer(int layer) override { /* throw "Not Implemented"; */}; void setPriorityPlane(int priorityPlane) override { /* throw "Not Implemented"; */}; @@ -38,11 +39,8 @@ class GMeshGL20 : public IMesh { protected: MeshType m_meshType; private: - HGShaderPermutation m_shader; - std::array m_UniformBuffer = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; - int8_t m_depthWrite; int8_t m_depthCulling; int8_t m_backFaceCulling; @@ -50,14 +48,9 @@ class GMeshGL20 : public IMesh { EGxBlendEnum m_blendMode; bool m_isTransparent; - - uint8_t m_colorMask = 0; int m_element; - - - private: IDevice &m_device; }; diff --git a/wowViewerLib/src/gapi/ogl2.0/textures/GBlpTextureGL20.cpp b/wowViewerLib/src/gapi/ogl2.0/textures/GBlpTextureGL20.cpp index c6285a169..40d21d009 100644 --- a/wowViewerLib/src/gapi/ogl2.0/textures/GBlpTextureGL20.cpp +++ b/wowViewerLib/src/gapi/ogl2.0/textures/GBlpTextureGL20.cpp @@ -9,9 +9,7 @@ #include "../../../engine/texture/DxtDecompress.h" GBlpTextureGL20::GBlpTextureGL20(IDevice &device, HBlpTexture texture, bool xWrapTex, bool yWrapTex) - : GTextureGL20(device), m_texture(texture) { - this->xWrapTex = xWrapTex; - this->yWrapTex = yWrapTex; + : GTextureGL20(device, xWrapTex, yWrapTex), m_texture(texture) { } GBlpTextureGL20::~GBlpTextureGL20() { @@ -27,7 +25,7 @@ void GBlpTextureGL20::unbind() { } static int texturesUploaded = 0; -void GBlpTextureGL20::createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) { +void GBlpTextureGL20::createGlTexture(TextureFormat textureFormat, const HMipmapsVector &hmipmaps) { // std::cout << "texturesUploaded = " << texturesUploaded++ << " " << this->m_texture->getTextureName() <xWrapTex = xWrapTex; + this->yWrapTex = yWrapTex; + createBuffer(); } @@ -45,8 +48,16 @@ void GTextureGL20::loadData(int width, int height, void *data, ITextureFormat te glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (xWrapTex) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + } + if (yWrapTex) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } glGenerateMipmap(GL_TEXTURE_2D); diff --git a/wowViewerLib/src/gapi/ogl2.0/textures/GTextureGL20.h b/wowViewerLib/src/gapi/ogl2.0/textures/GTextureGL20.h index 3a11e0551..385d0bae0 100644 --- a/wowViewerLib/src/gapi/ogl2.0/textures/GTextureGL20.h +++ b/wowViewerLib/src/gapi/ogl2.0/textures/GTextureGL20.h @@ -12,14 +12,14 @@ class GTextureGL20 : public ITexture { friend class GDeviceGL20; protected: - explicit GTextureGL20(IDevice &device); + explicit GTextureGL20(IDevice &device, bool xWrapTex, bool yWrapTex); public: ~GTextureGL20() override; void loadData(int width, int height, void *data, ITextureFormat textureFormat) override; void readData(std::vector &buff) override {}; bool getIsLoaded() override; - void createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) override { + void createGlTexture(TextureFormat textureFormat, const HMipmapsVector &mipmaps) override { // throw "Not Implemented in this class"; } bool postLoad() override { return false;}; @@ -34,6 +34,8 @@ class GTextureGL20 : public ITexture { IDevice &m_device; bool m_loaded = false; + bool xWrapTex; + bool yWrapTex; }; diff --git a/wowViewerLib/src/gapi/ogl3.3/GDeviceGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/GDeviceGL33.cpp index 143ff2af3..c0675f050 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GDeviceGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/GDeviceGL33.cpp @@ -8,6 +8,7 @@ #include "GDeviceGL33.h" #include "../../engine/algorithms/hashString.h" #include "shaders/GM2ShaderPermutationGL33.h" +#include "shaders/GWaterShaderGL33.h" #include "meshes/GM2MeshGL33.h" #include "GOcclusionQueryGL33.h" #include "meshes/GParticleMeshGL33.h" @@ -22,6 +23,7 @@ #include "GFrameBufferGL33.h" #include "shaders/GFFXGlow.h" #include "shaders/GSkyConus.h" +#include "shaders/GWaterfallShaderGL33.h" namespace GL33 { BlendModeDesc blendModes[(int)EGxBlendEnum::GxBlend_MAX] = { @@ -60,24 +62,19 @@ namespace GL33 { //} -// void debug_func(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, -// const void *userParam) { -// fprintf(stdout, "source: %u, type: %u, id: %u, severity: %u, msg: %s\n", -// source, -// type, -// id, -// severity, -// std::string(message, message + length).c_str()); -// if (severity == 37190) { -// std::cout << "lol"; + void debug_func(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, + const void *userParam) { +// if (type == GL_DEBUG_TYPE_ERROR_KHR) { +// fprintf(stdout, "source: %u, type: %u, id: %u, severity: %u, msg: %s\n", +// source, +// type, +// id, +// severity, +// std::string(message, message + length).c_str()); +// fflush(stdout); // } -// if (type == GL_DEBUG_TYPE_ERROR) { -// std::cout << "lol Error" << std::endl; -// __debugbreak; -// } -// -// fflush(stdout); -// } + + } } void GDeviceGL33::bindIndexBuffer(IIndexBuffer *buffer) { @@ -172,25 +169,29 @@ std::shared_ptr GDeviceGL33::getShader(std::string shaderNam } } - iPremutation = new GM2ShaderPermutationGL33(shaderName, this, *cacheRecord); + iPremutation = new GM2ShaderPermutationGL33(shaderName, shared_from_this(), *cacheRecord); sharedPtr.reset(iPremutation); std::weak_ptr weakPtr(sharedPtr); m2ShaderCache[*cacheRecord] = weakPtr; } else if (shaderName == "m2ParticleShader") { - iPremutation = new GM2ParticleShaderPermutationGL33(shaderName, this); + iPremutation = new GM2ParticleShaderPermutationGL33(shaderName, shared_from_this()); sharedPtr.reset(iPremutation); m_shaderPermutCache[hash] = sharedPtr; } else if (shaderName == "fullScreen_ffxgauss4") { - iPremutation = new GFFXgauss4(shaderName, this); + iPremutation = new GFFXgauss4(shaderName, shared_from_this()); sharedPtr.reset(iPremutation); m_shaderPermutCache[hash] = sharedPtr; } else if (shaderName == "skyConus") { - iPremutation = new GSkyConus(shaderName, this); + iPremutation = new GSkyConus(shaderName, shared_from_this()); + sharedPtr.reset(iPremutation); + m_shaderPermutCache[hash] = sharedPtr; + } else if (shaderName == "waterfallShader") { + iPremutation = new GWaterfallShaderGL33(shaderName, shared_from_this()); sharedPtr.reset(iPremutation); m_shaderPermutCache[hash] = sharedPtr; - } else if (shaderName == "fullScreen_quad") { - iPremutation = new GFFXGlow(shaderName, this); + } else if (shaderName == "ffxGlowQuad") { + iPremutation = new GFFXGlow(shaderName, shared_from_this()); sharedPtr.reset(iPremutation); m_shaderPermutCache[hash] = sharedPtr; } else if (shaderName == "wmoShader") { @@ -208,17 +209,21 @@ std::shared_ptr GDeviceGL33::getShader(std::string shaderNam } } - iPremutation = new GWMOShaderPermutationGL33(shaderName, this, *cacheRecord); + iPremutation = new GWMOShaderPermutationGL33(shaderName, shared_from_this(), *cacheRecord); sharedPtr.reset(iPremutation); std::weak_ptr weakPtr(sharedPtr); wmoShaderCache[*cacheRecord] = weakPtr; } else if (shaderName == "adtShader") { - iPremutation = new GAdtShaderPermutationGL33(shaderName, this); + iPremutation = new GAdtShaderPermutationGL33(shaderName, shared_from_this()); + sharedPtr.reset(iPremutation); + m_shaderPermutCache[hash] = sharedPtr; + } else if (shaderName == "waterShader") { + iPremutation = new GWaterShaderGL33(shaderName, shared_from_this()); sharedPtr.reset(iPremutation); m_shaderPermutCache[hash] = sharedPtr; } else { - iPremutation = new GShaderPermutationGL33(shaderName, this); + iPremutation = new GShaderPermutationGL33(shaderName, shared_from_this()); sharedPtr.reset(iPremutation); m_shaderPermutCache[hash] = sharedPtr; } @@ -243,7 +248,7 @@ std::shared_ptr GDeviceGL33::getShader(std::string shaderNam HGUniformBuffer GDeviceGL33::createUniformBuffer(size_t size) { std::shared_ptr h_uniformBuffer; - h_uniformBuffer.reset(new GUniformBufferGL33(*this, size)); + h_uniformBuffer.reset(new GUniformBufferGL33(shared_from_this(), size)); std::weak_ptr w_uniformBuffer = h_uniformBuffer; @@ -263,13 +268,16 @@ void GDeviceGL33::drawStageAndDeps(HDrawStage drawStage) { } else { glBindFramebuffer(GL_FRAMEBUFFER, 0); } - + logGLError this->setViewPortDimensions( drawStage->viewPortDimensions.mins[0], drawStage->viewPortDimensions.mins[1], drawStage->viewPortDimensions.maxs[0], drawStage->viewPortDimensions.maxs[1] ); + logGLError + this->setInvertZ(drawStage->invertedZ); + logGLError if (drawStage->clearScreen) { clearColor[0] = drawStage->clearColor[0]; clearColor[1] = drawStage->clearColor[1]; @@ -277,12 +285,19 @@ void GDeviceGL33::drawStageAndDeps(HDrawStage drawStage) { this->clearScreen(); } - if (drawStage->meshesToRender != nullptr) { - for (auto hgMesh : drawStage->meshesToRender->meshes) { + logGLError + if (drawStage->opaqueMeshes != nullptr) { + for (auto hgMesh : drawStage->opaqueMeshes->meshes) { this->drawMesh(hgMesh, drawStage->sceneWideBlockVSPSChunk); } } - + logGLError + if (drawStage->transparentMeshes != nullptr) { + for (auto hgMesh : drawStage->transparentMeshes->meshes) { + this->drawMesh(hgMesh, drawStage->sceneWideBlockVSPSChunk); + } + } + logGLError if (drawStage->target != nullptr) { drawStage->target->copyRenderBufferToTexture(); } @@ -290,7 +305,7 @@ void GDeviceGL33::drawStageAndDeps(HDrawStage drawStage) { } void GDeviceGL33::drawMeshes(std::vector &meshes) { - std::cout << "FILE:" << __FILE__ << " line " << __LINE__ << std::endl; +// std::cout << "FILE:" << __FILE__ << " line " << __LINE__ << std::endl; //Collect meshes into batches and create new array for performace // int meshesSize = meshes.size(); // for (int i = 0 ; i < meshesSize - 1; i++) { @@ -315,44 +330,25 @@ void GDeviceGL33::drawMeshes(std::vector &meshes) { } #ifdef SINGLE_BUFFER_UPLOAD -void GDeviceGL33::updateBuffers(std::vector &iMeshes, std::vector additionalChunks) { - std::vector &meshes = (std::vector &) iMeshes; - - //1. Collect buffers - std::vector buffers; - int renderIndex = 0; - for (const auto &mesh : meshes) { - for (int i = 0; i < 5; i++ ) { - IUniformBufferChunk *buffer = (IUniformBufferChunk *) mesh->m_UniformBuffer[i].get(); - if (buffer != nullptr) { - buffers.push_back(buffer); - } - } - } - for (const auto &bufferChunks : additionalChunks) { - if (bufferChunks != nullptr) { - buffers.push_back(bufferChunks.get()); - } - } - - - std::sort( buffers.begin(), buffers.end()); - buffers.erase( unique( buffers.begin(), buffers.end() ), buffers.end() ); - +void GDeviceGL33::updateBuffers(std::vector*> &bufferChunks, std::vector &frameDepedantDataVec) { int fullSize = 0; - for (auto &buffer : buffers) { - fullSize += buffer->getSize(); - int offsetDiff = fullSize % uniformBufferOffsetAlign; - if (offsetDiff != 0) { - int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + for (auto &buffer : *bufferVec) { + fullSize += buffer->getSize(); + int offsetDiff = fullSize % uniformBufferOffsetAlign; + if (offsetDiff != 0) { + int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; - fullSize += bytesToAdd; + fullSize += bytesToAdd; + } } } if (fullSize > aggregationBufferForUpload.size()) { aggregationBufferForUpload.resize(fullSize); } + uploadAmountInBytes = 0; //2. Create buffers and update them int currentSize = 0; int buffersIndex = 0; @@ -368,56 +364,44 @@ void GDeviceGL33::updateBuffers(std::vector &iMeshes, std::vector 0) { char *pointerForUpload = static_cast(&aggregationBufferForUpload[0]); - for (const auto &buffer : buffers) { - buffer->setOffset(currentSize); - buffer->setPointer(&pointerForUpload[currentSize]); - currentSize += buffer->getSize(); - - int offsetDiff = currentSize % uniformBufferOffsetAlign; - if (offsetDiff != 0) { - int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; - - currentSize += bytesToAdd; + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + for (auto &buffer : *bufferVec) { + buffer->setOffset(currentSize); + buffer->setPointer(&pointerForUpload[currentSize]); + currentSize += buffer->getSize(); + + int offsetDiff = currentSize % uniformBufferOffsetAlign; + if (offsetDiff != 0) { + int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; + + currentSize += bytesToAdd; + } } } assert(currentSize == fullSize); - for (auto &buffer : buffers) { - buffer->update(); + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + auto frameDepData = frameDepedantDataVec[i]; + + for (auto &buffer : *bufferVec) { + buffer->update(frameDepData); + } } if (currentSize > 0) { bufferForUploadGL->uploadData(pointerForUpload, currentSize); + uploadAmountInBytes+= currentSize; } } } #else -void GDeviceGL33::updateBuffers(std::vector &iMeshes, std::vector additionalChunks) { - std::vector &meshes = (std::vector &) iMeshes; +void GDeviceGL33::updateBuffers(std::vector*> &bufferChunks, std::vector &frameDepedantDataVec) { aggregationBufferForUpload.resize(maxUniformBufferSize); - //1. Collect buffers - std::vector buffers; - int renderIndex = 0; - for (const auto &mesh : meshes) { - for (int i = 0; i < 5; i++ ) { - IUniformBufferChunk *buffer = (IUniformBufferChunk *) mesh->m_UniformBuffer[i].get(); - if (buffer != nullptr) { - buffers.push_back(buffer); - } - } - } - for (const auto &bufferChunks : additionalChunks) { - if (bufferChunks != nullptr) { - buffers.push_back(bufferChunks.get()); - } - } - - - std::sort( buffers.begin(), buffers.end()); - buffers.erase( unique( buffers.begin(), buffers.end() ), buffers.end() ); - + uploadAmountInBytes = 0; //2. Create buffers and update them int currentSize = 0; int buffersIndex = 0; @@ -434,53 +418,65 @@ void GDeviceGL33::updateBuffers(std::vector &iMeshes, std::vector(&aggregationBufferForUpload[0]); - int lastUploadIndx = 0; - for (int i = 0; i < buffers.size(); i++) { - const auto &buffer = buffers[i]; - - if ((currentSize + buffer->getSize()) > maxUniformBufferSize) { - for (int j = lastUploadIndx; j < i; j++) { - auto &bufferUpl = buffers[j]; - bufferUpl->update(); - } + std::array lastUploadIndx = {0, 0}; + for (int k = 0; k < bufferChunks.size(); k++) { + auto &buffers = *bufferChunks[k]; + for (int i = 0; i < buffers.size(); i++) { + const auto &buffer = buffers[i]; + + if ((currentSize + buffer->getSize()) > maxUniformBufferSize) { + for (int x = lastUploadIndx[0]; x < bufferChunks.size(); x++) { + const auto &buffersUpl = *bufferChunks[x]; + for (int y = lastUploadIndx[1]; (x <= k) && (y < i); y++) { + auto &bufferUpl = buffersUpl[y]; + bufferUpl->update(frameDepedantDataVec[x]); + } + } - lastUploadIndx = i; - ((GUniformBufferGL33 *) bufferForUpload.get())->uploadData(&aggregationBufferForUpload[0], maxUniformBufferSize); + lastUploadIndx = {k, i}; + ((GUniformBufferGL33 *) bufferForUpload.get())->uploadData(&aggregationBufferForUpload[0], + maxUniformBufferSize); + uploadAmountInBytes += maxUniformBufferSize; - buffersIndex++; - currentSize = 0; + buffersIndex++; + currentSize = 0; - if (buffersIndex >= m_unfiormBuffersForUpload.size()) { - bufferForUpload = createUniformBuffer(maxUniformBufferSize); - bufferForUpload->createBuffer(); - m_unfiormBuffersForUpload.push_back(bufferForUpload); - } else { - bufferForUpload = m_unfiormBuffersForUpload.at(buffersIndex); + if (buffersIndex >= m_unfiormBuffersForUpload.size()) { + bufferForUpload = createUniformBuffer(maxUniformBufferSize); + bufferForUpload->createBuffer(); + m_unfiormBuffersForUpload.push_back(bufferForUpload); + } else { + bufferForUpload = m_unfiormBuffersForUpload.at(buffersIndex); + } } - } - buffer->setOffset(currentSize); - buffer->setPointer(&pointerForUpload[currentSize]); - ((GUniformBufferChunk33*) buffer)->setUniformBuffer(bufferForUpload); + buffer->setOffset(currentSize); + buffer->setPointer(&pointerForUpload[currentSize]); + ((GUniformBufferChunk33 *) buffer.get())->setUniformBuffer(bufferForUpload); - currentSize += buffer->getSize(); + currentSize += buffer->getSize(); - int offsetDiff = currentSize % uniformBufferOffsetAlign; - if (offsetDiff != 0) { - int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; + int offsetDiff = currentSize % uniformBufferOffsetAlign; + if (offsetDiff != 0) { + int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; - currentSize += bytesToAdd; + currentSize += bytesToAdd; + } } } if (currentSize > 0) { - for (int j = lastUploadIndx; j < buffers.size(); j++) { - auto &bufferUpl = buffers[j]; - bufferUpl->update(); + for (int x = lastUploadIndx[0]; x < bufferChunks.size(); x++) { + const auto &buffersUpl = *bufferChunks[x]; + for (int y = lastUploadIndx[1]; y < buffersUpl.size(); y++) { + auto &bufferUpl = buffersUpl[y]; + bufferUpl->update(frameDepedantDataVec[x]); + } } ((GUniformBufferGL33 *) bufferForUpload.get())->uploadData(pointerForUpload, currentSize); + uploadAmountInBytes+= currentSize; } } #endif @@ -531,6 +527,10 @@ void GDeviceGL33::drawMesh(HGMesh hIMesh, HGUniformBufferChunk matrixChunk) { if (uniformChunk != nullptr) { auto uniformBuffer = uniformChunk->getUniformBuffer().get(); +// std::cout << "Binding to " << i << +// " chunk with offset = " << uniformChunk->getOffset() << "and size " << uniformChunk->getSize() +//// " mbuffer size is "<< uniformBuffer->size() +// << std::endl; bindUniformBuffer(uniformBuffer, i, uniformChunk->getOffset(), uniformChunk->getSize()); } } @@ -579,6 +579,7 @@ void GDeviceGL33::drawMesh(HGMesh hIMesh, HGUniformBufferChunk matrixChunk) { m_lastDepthCulling = hmesh->m_depthCulling; } + if (m_backFaceCulling != hmesh->m_backFaceCulling) { if (hmesh->m_backFaceCulling > 0) { glEnable(GL_CULL_FACE); @@ -629,10 +630,10 @@ void GDeviceGL33::drawMesh(HGMesh hIMesh, HGUniformBufferChunk matrixChunk) { } if (m_lastBlendMode != hmesh->m_blendMode) { - BlendModeDesc &selectedBlendMode = blendModes[(char)hmesh->m_blendMode]; + BlendModeDesc &selectedBlendMode = GL33::blendModes[(char)hmesh->m_blendMode]; if ((m_lastBlendMode == EGxBlendEnum::GxBlend_UNDEFINED) || - (blendModes[(char)m_lastBlendMode].blendModeEnable != selectedBlendMode.blendModeEnable )) { + (GL33::blendModes[(char)m_lastBlendMode].blendModeEnable != selectedBlendMode.blendModeEnable )) { if (selectedBlendMode.blendModeEnable) { glEnable(GL_BLEND); } else { @@ -652,7 +653,6 @@ void GDeviceGL33::drawMesh(HGMesh hIMesh, HGUniformBufferChunk matrixChunk) { if (m_isInSkyBoxDepthMode != hmesh->getIsSkyBox()) { if (hmesh->getIsSkyBox()) { glDepthRange(0, 0.002); //default - } else { glDepthRange(0.002, 1.0f); } @@ -666,30 +666,51 @@ void GDeviceGL33::drawMesh(HGMesh hIMesh, HGUniformBufferChunk matrixChunk) { ((GOcclusionQueryGL33 *)gm2Mesh->m_query.get())->beginConditionalRendering(); } -#if OPENGL_DGB_MESSAGE - std::string debugMess = - "Drawing mesh " - " meshType = " + std::to_string((int)hmesh->getMeshType()) + - " priorityPlane = " + std::to_string(hmesh->priorityPlane()) + - " sortDistance = " + std::to_string(hmesh->getSortDistance()) + - " layer = " + std::to_string(hmesh->layer()) + - " blendMode = " + std::to_string((int)hmesh->getGxBlendMode()); - - glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, (GLsizei)((uint64_t)this&0xfffffff), GLsizei(debugMess.size()), debugMess.c_str()); - - glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, - GL_DEBUG_TYPE_MARKER, 1, - GL_DEBUG_SEVERITY_LOW, - GLsizei(debugMess.size()), - debugMess.c_str() - ); +//#if OPENGL_DGB_MESSAGE +// std::string debugMess = +// "Drawing mesh " +// " meshType = " + std::to_string((int)hmesh->getMeshType()) + +// " priorityPlane = " + std::to_string(hmesh->priorityPlane()) + +// " sortDistance = " + std::to_string(hmesh->getSortDistance()) + +// " layer = " + std::to_string(hmesh->layer()) + +// " blendMode = " + std::to_string((int)hmesh->getGxBlendMode()); +// +// glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, (GLsizei)((uint64_t)this&0xfffffff), GLsizei(debugMess.size()), debugMess.c_str()); +// +// glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, +// GL_DEBUG_TYPE_MARKER, 1, +// GL_DEBUG_SEVERITY_LOW, +// GLsizei(debugMess.size()), +// debugMess.c_str() +// ); +//#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" + glDrawElements(hmesh->m_element, hmesh->m_end, GL_UNSIGNED_SHORT, (const void *) (hmesh->m_start )); +#pragma clang diagnostic pop + +#ifdef __EMSCRIPTEN__ +//if (glGetError() != 0) {` +// std::string debugMess = +// "Drawing mesh " +// " meshType = " + std::to_string((int)hmesh->getMeshType()) + +// " priorityPlane = " + std::to_string(hmesh->priorityPlane()) + +// " sortDistance = " + std::to_string(hmesh->getSortDistance()) + +// " layer = " + std::to_string(hmesh->layer()) + +// " blendMode = " + std::to_string((int)hmesh->getGxBlendMode()); +// std::cout << debugMess << std::endl; +// EM_ASM( +// +// debugger; +// ); +//} #endif - glDrawElements(hmesh->m_element, hmesh->m_end, GL_UNSIGNED_SHORT, (const void *) (hmesh->m_start )); -#if OPENGL_DGB_MESSAGE - glPopDebugGroup(); -#endif +//#if OPENGL_DGB_MESSAGE +// glPopDebugGroup(); +//#endif if (gm2Mesh != nullptr && gm2Mesh->m_query != nullptr) { ((GOcclusionQueryGL33 *)gm2Mesh->m_query.get())->endConditionalRendering(); } @@ -702,13 +723,13 @@ void GDeviceGL33::drawMesh(HGMesh hIMesh, HGUniformBufferChunk matrixChunk) { HGVertexBuffer GDeviceGL33::createVertexBuffer() { bindVertexBufferBindings(nullptr); std::shared_ptr h_vertexBuffer; - h_vertexBuffer.reset(new GVertexBufferGL33(*this)); + h_vertexBuffer.reset(new GVertexBufferGL33(shared_from_this())); return h_vertexBuffer; } HGVertexBufferDynamic GDeviceGL33::createVertexBufferDynamic(size_t size) { std::shared_ptr h_vertexBuffer; - h_vertexBuffer.reset(new GVertexBufferDynamicGL33(*this, size)); + h_vertexBuffer.reset(new GVertexBufferDynamicGL33(shared_from_this(), size)); return h_vertexBuffer; }; @@ -716,22 +737,22 @@ HGVertexBufferDynamic GDeviceGL33::createVertexBufferDynamic(size_t size) { HGIndexBuffer GDeviceGL33::createIndexBuffer() { bindVertexBufferBindings(nullptr); std::shared_ptr h_indexBuffer; - h_indexBuffer.reset(new GIndexBufferGL33(*this)); + h_indexBuffer.reset(new GIndexBufferGL33(shared_from_this())); return h_indexBuffer; } HGVertexBufferBindings GDeviceGL33::createVertexBufferBindings() { std::shared_ptr h_vertexBufferBindings; - h_vertexBufferBindings.reset(new GVertexBufferBindingsGL33(*this)); + h_vertexBufferBindings.reset(new GVertexBufferBindingsGL33(shared_from_this())); return h_vertexBufferBindings; } -HFrameBuffer GDeviceGL33::createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int frameNumber) { +HFrameBuffer GDeviceGL33::createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int multiSampleCnt, int frameNumber) { if (frameNumber > -1) { for (auto &framebufAvalability : m_createdFrameBuffers) { - if (framebufAvalability.frame >= m_frameNumber && + if (framebufAvalability.frame <= m_frameNumber && framebufAvalability.attachments.size() == attachments.size() && framebufAvalability.width == width && framebufAvalability.height == height @@ -752,7 +773,7 @@ HFrameBuffer GDeviceGL33::createFrameBuffer(int width, int height, std::vector(*this, attachments, depthAttachment, width, height); + HFrameBuffer h_frameBuffer = std::make_shared(shared_from_this(), attachments, depthAttachment, width, height); if (frameNumber > -1) { FramebufAvalabilityStruct avalabilityStruct; @@ -777,20 +798,20 @@ HGUniformBufferChunk GDeviceGL33::createUniformBufferChunk(size_t size) { }; HGMesh GDeviceGL33::createMesh(gMeshTemplate &meshTemplate) { - std::shared_ptr h_mesh = std::make_shared(*this, meshTemplate); + std::shared_ptr h_mesh = std::make_shared(shared_from_this(), meshTemplate); return h_mesh; } HGM2Mesh GDeviceGL33::createM2Mesh(gMeshTemplate &meshTemplate) { - std::shared_ptr h_mesh = std::make_shared(*this, meshTemplate); + std::shared_ptr h_mesh = std::make_shared(shared_from_this(), meshTemplate); return h_mesh; } HGParticleMesh GDeviceGL33::createParticleMesh(gMeshTemplate &meshTemplate) { std::shared_ptr h_mesh; - h_mesh.reset(new GParticleMeshGL33(*this, meshTemplate)); + h_mesh.reset(new GParticleMeshGL33(shared_from_this(), meshTemplate)); return h_mesh; } @@ -813,9 +834,9 @@ void GDeviceGL33::bindTexture(ITexture *iTexture, int slot) { HGTexture GDeviceGL33::createBlpTexture(HBlpTexture &texture, bool xWrapTex, bool yWrapTex) { BlpCacheRecord blpCacheRecord; - blpCacheRecord.texture = texture.get(); - blpCacheRecord.wrapX = xWrapTex; - blpCacheRecord.wrapY = yWrapTex; + blpCacheRecord.textureFileName = texture->getTextureName(); + blpCacheRecord.wrapX = false; + blpCacheRecord.wrapY = false; auto i = loadedTextureCache[blpCacheRecord]; if (!i.expired()) { @@ -826,7 +847,7 @@ HGTexture GDeviceGL33::createBlpTexture(HBlpTexture &texture, bool xWrapTex, boo std::shared_ptr hgTexture; - hgTexture.reset(new GBlpTextureGL33(*this, texture, xWrapTex, yWrapTex)); + hgTexture.reset(new GBlpTextureGL33(shared_from_this(), texture, xWrapTex, yWrapTex)); std::weak_ptr weakPtr(hgTexture); loadedTextureCache[blpCacheRecord] = weakPtr; @@ -834,9 +855,9 @@ HGTexture GDeviceGL33::createBlpTexture(HBlpTexture &texture, bool xWrapTex, boo return hgTexture; } -HGTexture GDeviceGL33::createTexture() { +HGTexture GDeviceGL33::createTexture(bool xWrapTex, bool yWrapTex) { std::shared_ptr hgTexture; - hgTexture.reset(new GTextureGL33(*this)); + hgTexture.reset(new GTextureGL33(shared_from_this(), xWrapTex, yWrapTex)); return hgTexture; } @@ -863,24 +884,33 @@ void GDeviceGL33::bindProgram(IShaderPermutation *iProgram) { } GDeviceGL33::GDeviceGL33() { + +} + +void GDeviceGL33::initialize() { + unsigned int ff = 0xFFFFFFFF; unsigned int zero = 0; - m_blackPixelTexture = createTexture(); + m_blackPixelTexture = createTexture(false, false); m_blackPixelTexture->loadData(1,1,&zero, ITextureFormat::itRGBA); - m_whitePixelTexture = createTexture(); + m_whitePixelTexture = createTexture(false, false); m_whitePixelTexture->loadData(1,1,&ff, ITextureFormat::itRGBA); m_defaultVao = this->createVertexBufferBindings(); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBufferSize); glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniformBufferOffsetAlign); + std::cout << std::endl << "uniformBufferOffsetAlign = " << uniformBufferOffsetAlign << std::endl; + if (getIsAnisFiltrationSupported()) { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_anisotropicLevel); } glGetIntegerv ( GL_MAX_SAMPLES, &m_maxMultiSampling ); - std::cout << std::endl << "m_maxMultiSampling = " << m_maxMultiSampling << std::endl; + glGetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, 1, &m_maxMultiSamplingRGBA); + std::cout << "m_maxMultiSampling = " << m_maxMultiSampling << std::endl; + std::cout << "m_maxMultiSamplingRGBA = " << m_maxMultiSamplingRGBA << std::endl; //From https://en.wikibooks.org/wiki/OpenGL_Programming/Bounding_box static const float vertices[] = { @@ -951,7 +981,7 @@ GDeviceGL33::GDeviceGL33() { HGOcclusionQuery GDeviceGL33::createQuery(HGMesh boundingBoxMesh) { std::shared_ptr hgOcclusionQuery; - hgOcclusionQuery.reset(new GOcclusionQueryGL33(*this, boundingBoxMesh)); + hgOcclusionQuery.reset(new GOcclusionQueryGL33(shared_from_this(), boundingBoxMesh)); return hgOcclusionQuery; } @@ -997,6 +1027,9 @@ unsigned int GDeviceGL33::getUpdateFrameNumber() { return (m_frameNumber + 1) & 3; // return 0; } +unsigned int GDeviceGL33::getOcclusionFrameNumber() { + return (m_frameNumber + 2) & 3; +} unsigned int GDeviceGL33::getCullingFrameNumber() { return (m_frameNumber + 3) & 3; // return 0; @@ -1042,10 +1075,12 @@ void GDeviceGL33::uploadTextureForMeshes(std::vector &meshes) { if (texture->postLoad()) texturesLoaded++; if (texturesLoaded > 4) return; } + + m_textureWereUploaded = texturesLoaded > 0; } #ifdef __ANDROID_API__ -#include "../androidLogSupport.h" +#include "../../engine/androidLogSupport.h" #endif std::string GDeviceGL33::loadShader(std::string fileName, IShaderType shaderType) { @@ -1078,7 +1113,13 @@ std::string GDeviceGL33::loadShader(std::string fileName, IShaderType shaderType if (g_assetMgr == nullptr) { std::cout << "g_assetMgr == nullptr"; } - std::string filename = "glsl/" + shaderName + ".glsl"; + std::string filename = fullPath; + //Trim dot and slash at the start + if (filename[0] == '.') + filename = filename.substr(1, filename.length()-1); + if (filename[0] == '/') + filename = filename.substr(1, filename.length()-1); + std::cout << "AAssetManager_open" << std::endl; AAsset* asset = AAssetManager_open(mgr, filename.c_str(), AASSET_MODE_STREAMING); @@ -1103,21 +1144,13 @@ std::string GDeviceGL33::loadShader(std::string fileName, IShaderType shaderType AAsset_close(asset); std::cout << "asset closed" << std::endl; - return std::string(outBuf.begin(), outBuf.end()); + std::string result = std::string(outBuf.begin(), outBuf.end()); #else std::ifstream t(fullPath); std::string result = std::string((std::istreambuf_iterator(t)), std::istreambuf_iterator()); - - //Delete version - { - auto start = result.find("#version"); - if (start != std::string::npos) { - auto end = result.find("\n"); - result = result.substr(end); - } - } +#endif //Hack fix for bones { @@ -1133,7 +1166,7 @@ std::string GDeviceGL33::loadShader(std::string fileName, IShaderType shaderType shaderCache[hashRecord] = result; return result; -#endif + } float GDeviceGL33::getAnisLevel() { @@ -1141,15 +1174,12 @@ float GDeviceGL33::getAnisLevel() { } void GDeviceGL33::clearScreen() { -#ifndef WITH_GLESv2 if (m_isInvertZ) { glClearDepthf(0.0f); } else { glClearDepthf(1.0f); } -#else - glClearDepthf(1.0f); -#endif + glDisable(GL_DEPTH_TEST); if (m_isInvertZ) { glDepthFunc(GL_GEQUAL); @@ -1158,6 +1188,9 @@ void GDeviceGL33::clearScreen() { } glDepthMask(GL_TRUE); glDisable(GL_BLEND); + + //Note: the scissor dimensions are taken from call to setViewPortDimensions + glEnable(GL_SCISSOR_TEST); // glClearColor(0.0, 0.0, 0.0, 0.0); // glClearColor(0.25, 0.06, 0.015, 0.0); glClearColor(clearColor[0], clearColor[1], clearColor[2], 1); @@ -1167,7 +1200,6 @@ void GDeviceGL33::clearScreen() { glDisable(GL_CULL_FACE); glDepthMask(GL_FALSE); glDisable(GL_SCISSOR_TEST); - } void GDeviceGL33::setClearScreenColor(float r, float g, float b) { @@ -1177,7 +1209,7 @@ void GDeviceGL33::setClearScreenColor(float r, float g, float b) { } void GDeviceGL33::beginFrame() { - this->clearScreen(); + } void GDeviceGL33::commitFrame() { @@ -1191,6 +1223,7 @@ void GDeviceGL33::commitFrame() { void GDeviceGL33::setViewPortDimensions(float x, float y, float width, float height) { glViewport(x,y,width,height); + glScissor(x,y,width,height); } void GDeviceGL33::shrinkData() { diff --git a/wowViewerLib/src/gapi/ogl3.3/GDeviceGL33.h b/wowViewerLib/src/gapi/ogl3.3/GDeviceGL33.h index 7e4162a53..1282f08cd 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GDeviceGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/GDeviceGL33.h @@ -42,21 +42,32 @@ typedef std::shared_ptr HGL33Mesh; #include "meshes/GMeshGL33.h" #include "../interface/IDevice.h" + + #define OPENGL_DGB_MESSAGE 1 -#ifdef __EMSCRIPTEN__ +#if defined(__EMSCRIPTEN__) || defined(__APPLE__) #define OPENGL_DGB_MESSAGE 0 #endif -class GDeviceGL33 : public IDevice { +//#define logGLError { \ +// auto currentOGLError = glGetError(); \ +// if (currentOGLError != 0) \ +// std::cout << "OGL Error at "<<__FUNCTION__<<" line " << __LINE__ << " error " << currentOGLError << " " << std::endl;\ +//} +#define logGLError + +class GDeviceGL33 : public IDevice, public std::enable_shared_from_this { public: GDeviceGL33(); ~GDeviceGL33() override {}; + void initialize() override; void reset() override; unsigned int getFrameNumber() override { return m_frameNumber; }; unsigned int getUpdateFrameNumber() override; + unsigned int getOcclusionFrameNumber() override; unsigned int getCullingFrameNumber() override; unsigned int getDrawFrameNumber() override; @@ -68,6 +79,7 @@ class GDeviceGL33 : public IDevice { int getMaxSamplesCnt() override { return (m_maxMultiSampling < 8) ? m_maxMultiSampling : 8; } + int getUploadSize() override {return uploadAmountInBytes;}; virtual bool getIsRenderbufferSupported() override {return true;} float getAnisLevel() override; @@ -82,7 +94,7 @@ class GDeviceGL33 : public IDevice { void bindTexture(ITexture *texture, int slot) override; - void updateBuffers(std::vector &meshes, std::vector additionalChunks) override; + void updateBuffers(std::vector*> &bufferChunks, std::vector &frameDepedantDataVec) override; void uploadTextureForMeshes(std::vector &meshes) override; void drawMeshes(std::vector &meshes) override; void drawStageAndDeps(HDrawStage drawStage) override; @@ -96,11 +108,11 @@ class GDeviceGL33 : public IDevice { HGIndexBuffer createIndexBuffer() override; HGVertexBufferBindings createVertexBufferBindings() override; //Creates or receives framebuffer and tells it would be occupied for frameNumber frames - HFrameBuffer createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int frameNumber) override; + HFrameBuffer createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int multiSampleCnt, int frameNumber) override; HGUniformBufferChunk createUniformBufferChunk(size_t size) override; HGTexture createBlpTexture(HBlpTexture &texture, bool xWrapTex, bool yWrapTex) override; - HGTexture createTexture() override; + HGTexture createTexture(bool xWrapTex, bool yWrapTex) override; HGTexture getWhiteTexturePixel() override { return m_whitePixelTexture; }; HGTexture getBlackTexturePixel() override { return m_blackPixelTexture; }; HGMesh createMesh(gMeshTemplate &meshTemplate) override; @@ -124,12 +136,16 @@ class GDeviceGL33 : public IDevice { void shrinkData() override; + bool wasTexturesUploaded() override { + return m_textureWereUploaded; + } + struct DeallocationRecord { unsigned int frameNumberToDoAt; std::function callback; }; - void addDeallocationRecord(std::function callback) { + void addDeallocationRecord(std::function callback) override { DeallocationRecord dr; dr.frameNumberToDoAt = m_frameNumber+4; dr.callback = callback; @@ -140,13 +156,13 @@ class GDeviceGL33 : public IDevice { bool isDepthPreFill = false; protected: struct BlpCacheRecord { - BlpTexture* texture; + std::string textureFileName; bool wrapX; bool wrapY; bool operator==(const BlpCacheRecord &other) const { return - (texture == other.texture) && + (textureFileName == other.textureFileName) && (wrapX == other.wrapX) && (wrapY == other.wrapY); @@ -155,7 +171,7 @@ class GDeviceGL33 : public IDevice { struct BlpCacheRecordHasher { std::size_t operator()(const BlpCacheRecord& k) const { using std::hash; - return hash{}(k.texture) ^ (hash{}(k.wrapX) << 8) ^ (hash{}(k.wrapY) << 16); + return hash{}(k.textureFileName) ^ (hash{}(k.wrapX) << 8) ^ (hash{}(k.wrapY) << 16); }; }; std::unordered_map, BlpCacheRecordHasher> loadedTextureCache; @@ -171,9 +187,11 @@ class GDeviceGL33 : public IDevice { int uniformBufferOffsetAlign = -1; float m_anisotropicLevel = 0.0; int m_maxMultiSampling = 0; + int m_maxMultiSamplingRGBA = 0; bool m_isInSkyBoxDepthMode = false; int8_t m_isScissorsEnabled = -1; bool m_isInvertZ = false; + int uploadAmountInBytes = false; EGxBlendEnum m_lastBlendMode = EGxBlendEnum::GxBlend_UNDEFINED; GIndexBufferGL33 *m_lastBindIndexBuffer = nullptr; IVertexBuffer* m_lastBindVertexBuffer = nullptr; @@ -194,6 +212,8 @@ class GDeviceGL33 : public IDevice { HGTexture m_blackPixelTexture; HGTexture m_whitePixelTexture; + bool m_textureWereUploaded = false; + float clearColor[3] = {0,0,0}; public: @@ -220,6 +240,8 @@ class GDeviceGL33 : public IDevice { }; }; std::unordered_map, WMOShaderCacheRecordHasher> wmoShaderCache; + + int getCurrentTextureAllocated() override {return GTextureGL33::getCurrentGLTexturesAllocated();} protected: //Caches std::unordered_map m_shaderPermutCache; @@ -234,13 +256,6 @@ class GDeviceGL33 : public IDevice { }; #endif - struct FramebufAvalabilityStruct { - int width; int height; - std::vector attachments; - ITextureFormat depthAttachment; - HFrameBuffer frameBuffer; - int frame; - }; std::vector m_createdFrameBuffers; std::array m_UBOFrames = {}; @@ -251,9 +266,8 @@ class GDeviceGL33 : public IDevice { int uniformBuffersCreated = 0; std::list listOfDeallocators; -}; - +}; #endif //WEBWOWVIEWERCPP_GDEVICE_H diff --git a/wowViewerLib/src/gapi/ogl3.3/GFrameBufferGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/GFrameBufferGL33.cpp index 636680064..6aa6c3077 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GFrameBufferGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/GFrameBufferGL33.cpp @@ -6,7 +6,7 @@ GFrameBufferGL33::GFrameBufferGL33 ( - IDevice &device, + const HGDevice &device, std::vector textureAttachments, ITextureFormat depthAttachment, int width, int height) : mdevice(device), m_height(height), m_width(width) { @@ -15,18 +15,27 @@ GFrameBufferGL33::GFrameBufferGL33 ( attachmentTextures = std::vector(textureAttachments.size()); for (int i = 0; i < textureAttachments.size(); i++) { if (textureAttachments[i] != ITextureFormat::itNone) { - attachmentTextures[i] = mdevice.createTexture(); + attachmentTextures[i] = mdevice->createTexture(false, false); attachmentTextures[i]->loadData(width, height, nullptr, textureAttachments[i]); - } + } } if (depthAttachment != ITextureFormat::itNone) { - depthTexture = mdevice.createTexture(); + depthTexture = mdevice->createTexture(false, false); depthTexture->loadData(width, height, nullptr, depthAttachment); - } + } + + defaultTextureForRenderBufFBO = mdevice->createTexture(false, false); + defaultTextureForRenderBufFBO->loadData(width, height, nullptr, ITextureFormat::itRGBA); + glGenFramebuffers(+1, &m_renderBufFbo); glGenFramebuffers(+1, &m_textureFbo); - //1. First fill framebuffer with renderbuffers + + //Create m_renderBufFbo this->bindFrameBuffer(); + //1.1 To make OGL ES happy + ((GTextureGL33 *)defaultTextureForRenderBufFBO.get())->bindToCurrentFrameBufferAsColor(0); + + //1.2 First fill framebuffer with renderbuffers renderBufferAttachments = std::vector(textureAttachments.size()); for (int i = 0; i < renderBufferAttachments.size(); i++) { if (textureAttachments[i] == ITextureFormat::itDepth32) continue; @@ -35,23 +44,23 @@ GFrameBufferGL33::GFrameBufferGL33 ( glBindRenderbuffer(GL_RENDERBUFFER, renderBufferAttachments[i]); if (textureAttachments[i] == ITextureFormat::itRGBA) { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, device.getMaxSamplesCnt(), GL_RGBA8, width, height); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, device->getMaxSamplesCnt(), GL_RGBA8, width, height); } else if (textureAttachments[i] == ITextureFormat::itRGBAFloat32) { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, device.getMaxSamplesCnt(), GL_RGBA32F, width, height); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, device->getMaxSamplesCnt(), GL_RGBA32F, width, height); } glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_RENDERBUFFER, renderBufferAttachments[i]); - } + } if (depthAttachment != ITextureFormat::itNone) { glGenRenderbuffers(1, &depthBufferAttachment); glBindRenderbuffer(GL_RENDERBUFFER, depthBufferAttachment); if (depthAttachment == ITextureFormat::itDepth32) { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, device.getMaxSamplesCnt(), GL_DEPTH32F_STENCIL8, width, height); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, device->getMaxSamplesCnt(), GL_DEPTH32F_STENCIL8, width, height); } glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthBufferAttachment); - } + } // { // auto frameBuffStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); // if (frameBuffStatus != GL_FRAMEBUFFER_COMPLETE) @@ -62,11 +71,11 @@ GFrameBufferGL33::GFrameBufferGL33 ( glBindFramebuffer(GL_FRAMEBUFFER, m_textureFbo); for (int i = 0; i < textureAttachments.size(); i++) { ((GTextureGL33 *)attachmentTextures[i].get())->bindToCurrentFrameBufferAsColor(i); - } + } if (depthTexture != nullptr) { ((GTextureGL33 *) depthTexture.get())->bindToCurrentFrameBufferAsDepth(); - } else { + } else { } // { @@ -105,7 +114,7 @@ GFrameBufferGL33::~GFrameBufferGL33() { } glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &m_renderBufFbo); - } +} HGTexture GFrameBufferGL33::getAttachment(int index){ return attachmentTextures[index]; @@ -120,9 +129,17 @@ void GFrameBufferGL33::bindFrameBuffer(){ } void GFrameBufferGL33::copyRenderBufferToTexture(){ + logGLError glBindFramebuffer(GL_READ_FRAMEBUFFER, m_renderBufFbo); + logGLError glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_textureFbo); - glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + logGLError + glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_LINEAR); + logGLError +// glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, attachmentCount, attachments) + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } void GFrameBufferGL33::readRGBAPixels(int x, int y, int width, int height, void *data) { @@ -131,6 +148,8 @@ void GFrameBufferGL33::readRGBAPixels(int x, int y, int width, int height, void // ((GTextureGL33 *)attachmentTextures[0].get())->bindToCurrentFrameBufferAsDepth() + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glReadBuffer(GL_COLOR_ATTACHMENT0); glReadPixels( x,y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); diff --git a/wowViewerLib/src/gapi/ogl3.3/GFrameBufferGL33.h b/wowViewerLib/src/gapi/ogl3.3/GFrameBufferGL33.h index 6cabc7324..6d053ab29 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GFrameBufferGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/GFrameBufferGL33.h @@ -14,7 +14,7 @@ class GFrameBufferGL33 : public IFrameBuffer { public: - GFrameBufferGL33(IDevice &device, std::vector textureAttachments, ITextureFormat depthAttachment, int width, int height); + GFrameBufferGL33(const HGDevice &device, std::vector textureAttachments, ITextureFormat depthAttachment, int width, int height); ~GFrameBufferGL33() override; void readRGBAPixels(int x, int y, int width, int height, void *data) override; @@ -25,11 +25,12 @@ class GFrameBufferGL33 : public IFrameBuffer { private: - IDevice &mdevice; + HGDevice mdevice; std::vector attachmentTextures; HGTexture depthTexture; + HGTexture defaultTextureForRenderBufFBO; GLuint m_renderBufFbo; ITextureFormat m_depthAttachment; diff --git a/wowViewerLib/src/gapi/ogl3.3/GOcclusionQueryGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/GOcclusionQueryGL33.cpp index 7d9c48fca..25290441f 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GOcclusionQueryGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/GOcclusionQueryGL33.cpp @@ -5,7 +5,7 @@ #include "GOcclusionQueryGL33.h" -GOcclusionQueryGL33::GOcclusionQueryGL33(IDevice &device, HGMesh oclludee) : GMeshGL33(*(GMeshGL33 *)(oclludee.get())){ +GOcclusionQueryGL33::GOcclusionQueryGL33(const HGDevice &device, HGMesh oclludee) : GMeshGL33(*(GMeshGL33 *)(oclludee.get())){ createQuery(); m_meshType = MeshType::eOccludingQuery; diff --git a/wowViewerLib/src/gapi/ogl3.3/GOcclusionQueryGL33.h b/wowViewerLib/src/gapi/ogl3.3/GOcclusionQueryGL33.h index ff6dc98e4..0005f7d67 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GOcclusionQueryGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/GOcclusionQueryGL33.h @@ -12,7 +12,7 @@ class GOcclusionQueryGL33 : public GMeshGL33 { public: ~GOcclusionQueryGL33() override; protected: - GOcclusionQueryGL33(IDevice &device, HGMesh oclludee); + GOcclusionQueryGL33(const HGDevice &device, HGMesh oclludee); private: diff --git a/wowViewerLib/src/gapi/ogl3.3/GShaderPermutationGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/GShaderPermutationGL33.cpp index 5ef64a0e8..c913c9685 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GShaderPermutationGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/GShaderPermutationGL33.cpp @@ -135,16 +135,17 @@ namespace GL33 { return s; } } -GShaderPermutationGL33::GShaderPermutationGL33(std::string &shaderName, IDevice * device) : m_device(device), +GShaderPermutationGL33::GShaderPermutationGL33(std::string &shaderName, const HGDevice &device) : m_device(device), m_shaderNameVert(shaderName), m_shaderNameFrag(shaderName) { } -GShaderPermutationGL33::GShaderPermutationGL33(std::string &shaderNameVert, std::string &shaderNameFrag, IDevice * device) : m_device(device), +GShaderPermutationGL33::GShaderPermutationGL33(std::string &shaderNameVert, std::string &shaderNameFrag, const HGDevice &device) : m_device(device), m_shaderNameVert(shaderNameVert), m_shaderNameFrag(shaderNameFrag) { } void GShaderPermutationGL33::compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) { + logGLError std::string shaderVertFile = m_device->loadShader(m_shaderNameVert, IShaderType::gVertexShader); std::string shaderFragFile = m_device->loadShader(m_shaderNameFrag, IShaderType::gFragmentShader); if (shaderVertFile.length() == 0) { @@ -167,79 +168,27 @@ void GShaderPermutationGL33::compileShader(const std::string &vertExtraDef, cons #ifdef __ANDROID_API__ esVersion = true; #endif -#ifdef __APPLE__ - #include "TargetConditionals.h" -#if TARGET_IPHONE_SIMULATOR - glsl330 = false; -#elif TARGET_OS_IPHONE - glsl330 = false; -#elif TARGET_OS_MAC - glsl330 = true; -#else -# error "Unknown Apple platform" -#endif -#endif -#ifdef __EMSCRIPTEN__ +#if (defined(WITH_GLESv2) || defined(__EMSCRIPTEN__)) esVersion = true; #endif bool geomShaderExists = false; - if (esVersion) { - vertExtraDefStrings = "#version 300 es\n" + vertExtraDefStrings; - geomExtraDefStrings = "#version 300 es\n" + geomExtraDefStrings; - } else { - vertExtraDefStrings = "#version 330\n" + vertExtraDefStrings; - geomExtraDefStrings = "#version 330\n" + geomExtraDefStrings; - } - if (!esVersion) { - vertExtraDefStrings += - "#define precision\n" - "#define lowp\n" - "#define mediump\n" - "#define highp\n" - "#define FLOATDEC\n"; - } else { - vertExtraDefStrings += "#define FLOATDEC float;\n"; - vertExtraDefStrings += "precision mediump float;\n"; - }; geomShaderExists = vertShaderString.find("COMPILING_GS") != std::string::npos; #ifdef __EMSCRIPTEN__ geomShaderExists = false; #endif - if (esVersion) { - fragExtraDefStrings = "#version 300 es\n" + fragExtraDefStrings; - } else { - fragExtraDefStrings = "#version 330\n" + fragExtraDefStrings; - } - - if (!esVersion) { - fragExtraDefStrings += - "#define precision\n" - "#define lowp\n" - "#define mediump\n" - "#define highp\n" - "#define FLOATDEC\n"; - } else { - fragExtraDefStrings += "precision mediump float;\n"; - fragExtraDefStrings += "#define FLOATDEC float;\n"; - - }; GLint maxVertexUniforms; glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniforms); int maxMatrixUniforms = MAX_MATRIX_NUM;//(maxVertexUniforms / 4) - 9; - vertExtraDefStrings = vertExtraDefStrings + "#define COMPILING_VS 1\r\n "; - geomExtraDefStrings = geomExtraDefStrings + "#define COMPILING_GS 1\r\n"; - fragExtraDefStrings = fragExtraDefStrings + "#define COMPILING_FS 1\r\n"; - - std::string geometryShaderString = vertShaderString; + std::string geometryShaderString = ""; - vertShaderString = vertShaderString.insert(0, vertExtraDefStrings); - fragmentShaderString = fragmentShaderString.insert(0, fragExtraDefStrings); - geometryShaderString = geometryShaderString.insert(0, geomExtraDefStrings); + vertShaderString = IDevice::insertAfterVersion(vertShaderString, vertExtraDefStrings); + fragmentShaderString = IDevice::insertAfterVersion(fragmentShaderString, fragExtraDefStrings); + geometryShaderString = IDevice::insertAfterVersion(geometryShaderString, geomExtraDefStrings); GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); const GLchar *vertexShaderConst = (const GLchar *)vertShaderString.c_str(); @@ -271,7 +220,9 @@ void GShaderPermutationGL33::compileShader(const std::string &vertExtraDef, cons // Check if it compiled success = 0; + logGLError glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + logGLError if (!success) { // Something went wrong during compilation; get the error GLint maxLength = 0; @@ -320,21 +271,28 @@ void GShaderPermutationGL33::compileShader(const std::string &vertExtraDef, cons /* 1.3 Link the program */ + logGLError GLuint program = glCreateProgram(); + logGLError glAttachShader(program, vertexShader); + logGLError glAttachShader(program, fragmentShader); - if (geomShaderExists) - glAttachShader(program, geometryShader); + logGLError +// if (geomShaderExists) +// glAttachShader(program, geometryShader); // for (int i = 0; i < shaderDefinition1->attributesNum; i++) { // glBindAttribLocation(program, shaderDefinition1->attributes[i].number, shaderDefinition1->attributes[i].variableName); // } // link the program. + logGLError glLinkProgram(program); - + logGLError GLint status; + logGLError glGetProgramiv(program, GL_LINK_STATUS, &status); + logGLError if (!status) { char logbuffer[1000]; int loglen; @@ -348,23 +306,33 @@ void GShaderPermutationGL33::compileShader(const std::string &vertExtraDef, cons //Get uniforms data GLint count; + logGLError glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count); + logGLError // printf("Active Uniforms: %d\n", count); for (GLint i = 0; i < count; i++) { - const GLsizei bufSize = 32; // maximum name length - GLchar name[bufSize]; // variable name in GLSL + const GLsizei bufSize = 256; // maximum name length + GLchar name[bufSize+1]; // variable name in GLSL GLsizei length; // name length GLint size; // size of the variable GLenum type; // type of the variable (float, vec3 or mat4, etc) + logGLError glGetActiveUniform(program, (GLuint)i, bufSize, &length, &size, &type, name); + logGLError + if (length <= bufSize) + name[length] = '\0'; + + logGLError GLint location = glGetUniformLocation(program, name); + logGLError this->setUnf(std::string(name), location); // printf("Uniform #%d Type: %u Name: %s Location: %d\n", i, type, name, location); } // if (!shaderName.compare("m2Shader")) { +// if (!shaderName.compare("m2Shader")) { // std::cout << fragmentShaderString << std::endl << std::flush; // } diff --git a/wowViewerLib/src/gapi/ogl3.3/GShaderPermutationGL33.h b/wowViewerLib/src/gapi/ogl3.3/GShaderPermutationGL33.h index 9aa5cea26..6c70f3d6f 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GShaderPermutationGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/GShaderPermutationGL33.h @@ -21,8 +21,8 @@ class GShaderPermutationGL33 : public IShaderPermutation { ~GShaderPermutationGL33() override {}; protected: - explicit GShaderPermutationGL33(std::string &shaderName, IDevice *device); - explicit GShaderPermutationGL33(std::string &shaderNameVert, std::string &shaderNameFrag, IDevice *device); + explicit GShaderPermutationGL33(std::string &shaderName, const HGDevice &device); + explicit GShaderPermutationGL33(std::string &shaderNameVert, std::string &shaderNameFrag, const HGDevice &device); void compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) override; bool hasUnf(const HashedString name) { @@ -41,7 +41,7 @@ class GShaderPermutationGL33 : public IShaderPermutation { unsigned int m_programBuffer = 0; private: - IDevice *m_device; + HGDevice m_device; private: std::unordered_map m_uniformMap; std::array m_uboBlockIndex; diff --git a/wowViewerLib/src/gapi/ogl3.3/GVertexBufferBindingsGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/GVertexBufferBindingsGL33.cpp index ad41237d3..0f729a542 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GVertexBufferBindingsGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/GVertexBufferBindingsGL33.cpp @@ -19,8 +19,7 @@ constexpr GLenum toOGLEnum(GBindingType bindingType) { return 0; } -GVertexBufferBindingsGL33::GVertexBufferBindingsGL33(IDevice &m_device) : m_device( - dynamic_cast(m_device)) { +GVertexBufferBindingsGL33::GVertexBufferBindingsGL33(const HGDevice &m_device) : m_device(m_device) { m_buffer = std::vector(sizeof(GLuint)); createBuffer(); } @@ -35,7 +34,7 @@ void GVertexBufferBindingsGL33::createBuffer() { void GVertexBufferBindingsGL33::destroyBuffer() { const GLuint indent = *(const GLuint *) &this->m_buffer[0]; - m_device.addDeallocationRecord([indent]() -> void { + m_device->addDeallocationRecord([indent]() -> void { glDeleteVertexArrays(1, &indent); }); } @@ -60,11 +59,11 @@ static int VAO_updated = 0; void GVertexBufferBindingsGL33::save() { // std::cout << "VAO_updated = " << VAO_updated++ << std::endl; - m_device.bindVertexBufferBindings(this); + m_device->bindVertexBufferBindings(this); - m_device.bindIndexBuffer(m_indexBuffer.get()); + m_device->bindIndexBuffer(m_indexBuffer.get()); for (GVertexBufferBinding &binding : m_bindings) { - m_device.bindVertexBuffer(binding.vertexBuffer.get()); + m_device->bindVertexBuffer(binding.vertexBuffer.get()); for (GBufferBinding &bufferBinding : binding.bindings) { glEnableVertexAttribArray(bufferBinding.position); @@ -80,7 +79,7 @@ void GVertexBufferBindingsGL33::save() { } - m_device.bindVertexBufferBindings(nullptr); + m_device->bindVertexBufferBindings(nullptr); } diff --git a/wowViewerLib/src/gapi/ogl3.3/GVertexBufferBindingsGL33.h b/wowViewerLib/src/gapi/ogl3.3/GVertexBufferBindingsGL33.h index a2c65c4d8..9fd20532c 100644 --- a/wowViewerLib/src/gapi/ogl3.3/GVertexBufferBindingsGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/GVertexBufferBindingsGL33.h @@ -24,12 +24,12 @@ class GVertexBufferBindingsGL33 : public IVertexBufferBindings { private: - GDeviceGL33 &m_device; + HGDevice m_device; private: std::vector m_buffer; public: - explicit GVertexBufferBindingsGL33(IDevice &m_device); + explicit GVertexBufferBindingsGL33(const HGDevice &m_device); ~GVertexBufferBindingsGL33() override; private: diff --git a/wowViewerLib/src/gapi/ogl3.3/buffers/GIndexBufferGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/buffers/GIndexBufferGL33.cpp index 67856b367..3bd869263 100644 --- a/wowViewerLib/src/gapi/ogl3.3/buffers/GIndexBufferGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/buffers/GIndexBufferGL33.cpp @@ -6,7 +6,7 @@ #include "GIndexBufferGL33.h" #include "../GDeviceGL33.h" -GIndexBufferGL33::GIndexBufferGL33(IDevice &device) : m_device(dynamic_cast(device)) { +GIndexBufferGL33::GIndexBufferGL33(const HGDevice &device) : m_device(device) { buffer = std::vector(sizeof(GLuint)); createBuffer(); } @@ -20,15 +20,15 @@ void GIndexBufferGL33::createBuffer() { void GIndexBufferGL33::destroyBuffer() { const GLuint indent = *(const GLuint *) &this->buffer[0]; - m_device.addDeallocationRecord([indent]() -> void { + m_device->addDeallocationRecord([indent]() -> void { glDeleteBuffers(1, &indent); }); } static int ibo_uploaded = 0; void GIndexBufferGL33::uploadData(void * data, int length) { - m_device.bindVertexBufferBindings(nullptr); - m_device.bindIndexBuffer(this); + m_device->bindVertexBufferBindings(nullptr); + m_device->bindIndexBuffer(this); if (length <= 0) return; if (data == nullptr) return; @@ -49,7 +49,7 @@ void GIndexBufferGL33::uploadData(void * data, int length) { // glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); } - m_device.bindIndexBuffer(nullptr); + m_device->bindIndexBuffer(nullptr); m_dataUploaded = true; } diff --git a/wowViewerLib/src/gapi/ogl3.3/buffers/GIndexBufferGL33.h b/wowViewerLib/src/gapi/ogl3.3/buffers/GIndexBufferGL33.h index f82ff62ea..3c2e758c7 100644 --- a/wowViewerLib/src/gapi/ogl3.3/buffers/GIndexBufferGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/buffers/GIndexBufferGL33.h @@ -13,7 +13,7 @@ class GIndexBufferGL33 : public IIndexBuffer{ friend class GDeviceGL33; - explicit GIndexBufferGL33(IDevice &device); + explicit GIndexBufferGL33(const HGDevice &device); public: ~GIndexBufferGL33() override; @@ -27,7 +27,7 @@ class GIndexBufferGL33 : public IIndexBuffer{ void uploadData(void *, int length) override; private: - GDeviceGL33 &m_device; + const HGDevice m_device; private: std::vector buffer = {}; diff --git a/wowViewerLib/src/gapi/ogl3.3/buffers/GUniformBufferGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/buffers/GUniformBufferGL33.cpp index 0902ef53b..98eac3078 100644 --- a/wowViewerLib/src/gapi/ogl3.3/buffers/GUniformBufferGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/buffers/GUniformBufferGL33.cpp @@ -7,7 +7,7 @@ #include "GUniformBufferGL33.h" #include "../../interface/IDevice.h" -GUniformBufferGL33::GUniformBufferGL33(IDevice &device, size_t size) : m_device(dynamic_cast(device)){ +GUniformBufferGL33::GUniformBufferGL33(const HGDevice &device, size_t size) : m_device(device){ m_size = size; createBuffer(); } @@ -25,7 +25,7 @@ void GUniformBufferGL33::createBuffer() { void GUniformBufferGL33::destroyBuffer() { const GLuint indent = glBuffId; - m_device.addDeallocationRecord([indent]() -> void { + m_device->addDeallocationRecord([indent]() -> void { glDeleteBuffers(1, &indent); }); } @@ -41,7 +41,7 @@ void GUniformBufferGL33::unbind() { } void GUniformBufferGL33::uploadData(void * data, int length) { - m_device.bindUniformBuffer(this, 0, 0, 0); + m_device->bindUniformBuffer(this, 0, 0, 0); assert(m_buffCreated); #ifdef __EMSCRIPTEN__ diff --git a/wowViewerLib/src/gapi/ogl3.3/buffers/GUniformBufferGL33.h b/wowViewerLib/src/gapi/ogl3.3/buffers/GUniformBufferGL33.h index 299afc76c..ddc9ae1c9 100644 --- a/wowViewerLib/src/gapi/ogl3.3/buffers/GUniformBufferGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/buffers/GUniformBufferGL33.h @@ -15,7 +15,7 @@ class GUniformBufferGL33 : public IUniformBuffer { public: friend class GDeviceGL33; - explicit GUniformBufferGL33(IDevice &device, size_t size); + explicit GUniformBufferGL33(const HGDevice &device, size_t size); ~GUniformBufferGL33() override; void createBuffer() override; @@ -29,7 +29,7 @@ class GUniformBufferGL33 : public IUniformBuffer { void uploadData(void * data, int length); private: - GDeviceGL33 &m_device; + const HGDevice m_device; private: size_t m_size; diff --git a/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferDynamicGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferDynamicGL33.cpp index 15f94510f..189a17f79 100644 --- a/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferDynamicGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferDynamicGL33.cpp @@ -9,8 +9,7 @@ -GVertexBufferDynamicGL33::GVertexBufferDynamicGL33(IDevice &device, size_t maxSize) : m_device( - dynamic_cast(device)) { +GVertexBufferDynamicGL33::GVertexBufferDynamicGL33(const HGDevice &device, size_t maxSize) : m_device(device) { pIdentifierBuffer = std::vector(sizeof(GLuint)); m_size = maxSize; m_buffer.resize(maxSize); @@ -27,15 +26,15 @@ void GVertexBufferDynamicGL33::createBuffer() { void GVertexBufferDynamicGL33::destroyBuffer() { const GLuint indent = *(const GLuint *) &this->pIdentifierBuffer[0]; - m_device.addDeallocationRecord([indent]() -> void { + m_device->addDeallocationRecord([indent]() -> void { glDeleteBuffers(1, &indent); }); } static int vbo_uploaded = 0; void GVertexBufferDynamicGL33::uploadData(void * data, int length) { - m_device.bindVertexBufferBindings(nullptr); - m_device.bindVertexBuffer(this); + m_device->bindVertexBufferBindings(nullptr); + m_device->bindVertexBuffer(this); assert(m_buffCreated); if (!(length > 0 && length < (400*1024*1024))) { @@ -56,7 +55,7 @@ void GVertexBufferDynamicGL33::uploadData(void * data, int length) { // glUnmapBuffer(GL_ARRAY_BUFFER); } - m_device.bindVertexBuffer(nullptr); + m_device->bindVertexBuffer(nullptr); m_dataUploaded = true; } diff --git a/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferDynamicGL33.h b/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferDynamicGL33.h index 59dd601fd..713d8ec32 100644 --- a/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferDynamicGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferDynamicGL33.h @@ -13,7 +13,7 @@ class GVertexBufferDynamicGL33 : public IVertexBufferDynamic { friend class GDeviceGL33; - explicit GVertexBufferDynamicGL33(IDevice &device, size_t maxSize); + explicit GVertexBufferDynamicGL33(const HGDevice &device, size_t maxSize); public: ~GVertexBufferDynamicGL33() override; @@ -31,7 +31,7 @@ class GVertexBufferDynamicGL33 : public IVertexBufferDynamic { void uploadData(void *, int length) override; private: - GDeviceGL33 &m_device; + const HGDevice m_device; private: std::vector m_buffer; diff --git a/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferGL33.cpp index 9d5dd3089..388e501d9 100644 --- a/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferGL33.cpp @@ -8,7 +8,7 @@ -GVertexBufferGL33::GVertexBufferGL33(IDevice &device) : m_device(dynamic_cast(device)) { +GVertexBufferGL33::GVertexBufferGL33(const HGDevice &device) : m_device(device) { pIdentifierBuffer = std::vector(sizeof(GLuint)); createBuffer(); } @@ -23,15 +23,15 @@ void GVertexBufferGL33::createBuffer() { void GVertexBufferGL33::destroyBuffer() { const GLuint indent = *(const GLuint *) &this->pIdentifierBuffer[0]; - m_device.addDeallocationRecord([indent]() -> void { + m_device->addDeallocationRecord([indent]() -> void { glDeleteBuffers(1, &indent); }); } static int vbo_uploaded = 0; void GVertexBufferGL33::uploadData(void * data, int length) { - m_device.bindVertexBufferBindings(nullptr); - m_device.bindVertexBuffer(this); + m_device->bindVertexBufferBindings(nullptr); + m_device->bindVertexBuffer(this); assert(m_buffCreated); if (!(length > 0 && length < (400*1024*1024))) { @@ -52,7 +52,7 @@ void GVertexBufferGL33::uploadData(void * data, int length) { // glUnmapBuffer(GL_ARRAY_BUFFER); } - m_device.bindVertexBuffer(nullptr); + m_device->bindVertexBuffer(nullptr); m_dataUploaded = true; } diff --git a/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferGL33.h b/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferGL33.h index 220307e48..47a78f4ea 100644 --- a/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/buffers/GVertexBufferGL33.h @@ -15,7 +15,7 @@ class GDeviceGL33; class GVertexBufferGL33 : public IVertexBuffer { friend class GDeviceGL33; - explicit GVertexBufferGL33(IDevice &device); + explicit GVertexBufferGL33(const HGDevice &device); public: ~GVertexBufferGL33() override; private: @@ -28,7 +28,7 @@ class GVertexBufferGL33 : public IVertexBuffer { void uploadData(void *, int length) override; private: - GDeviceGL33 &m_device; + const HGDevice m_device; private: std::vector pIdentifierBuffer {}; diff --git a/wowViewerLib/src/gapi/ogl3.3/meshes/GM2MeshGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/meshes/GM2MeshGL33.cpp index c7787f035..1435d9231 100644 --- a/wowViewerLib/src/gapi/ogl3.3/meshes/GM2MeshGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/meshes/GM2MeshGL33.cpp @@ -4,7 +4,7 @@ #include "GM2MeshGL33.h" -GM2MeshGL33::GM2MeshGL33(IDevice &device, const gMeshTemplate &meshTemplate) : GMeshGL33(device, meshTemplate){ +GM2MeshGL33::GM2MeshGL33(const HGDevice &device, const gMeshTemplate &meshTemplate) : GMeshGL33(device, meshTemplate){ m_meshType = MeshType::eM2Mesh; } @@ -12,6 +12,11 @@ void GM2MeshGL33::setM2Object(void *m2Object) { m_m2Object = (m2Object); } +void *GM2MeshGL33::getM2Object() { + return m_m2Object; +} + + void GM2MeshGL33::setLayer(int layer) { m_layer = layer; } diff --git a/wowViewerLib/src/gapi/ogl3.3/meshes/GM2MeshGL33.h b/wowViewerLib/src/gapi/ogl3.3/meshes/GM2MeshGL33.h index e52548cba..4df854a23 100644 --- a/wowViewerLib/src/gapi/ogl3.3/meshes/GM2MeshGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/meshes/GM2MeshGL33.h @@ -11,9 +11,10 @@ class GM2MeshGL33 : public GMeshGL33 { friend class GDeviceGL33; public: - GM2MeshGL33(IDevice &device, const gMeshTemplate &meshTemplate); + GM2MeshGL33(const HGDevice &device, const gMeshTemplate &meshTemplate); public: + void *getM2Object() override; void setM2Object(void * m2Object) override; void setLayer(int layer) override; void setPriorityPlane(int priorityPlane) override; diff --git a/wowViewerLib/src/gapi/ogl3.3/meshes/GMeshGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/meshes/GMeshGL33.cpp index d177394c8..f1d19ad83 100644 --- a/wowViewerLib/src/gapi/ogl3.3/meshes/GMeshGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/meshes/GMeshGL33.cpp @@ -5,7 +5,7 @@ #include #include "GMeshGL33.h" -GMeshGL33::GMeshGL33(IDevice &device, +GMeshGL33::GMeshGL33(const HGDevice &device, const gMeshTemplate &meshTemplate ) : m_device(device), m_shader(meshTemplate.shader), m_meshType(meshTemplate.meshType) { diff --git a/wowViewerLib/src/gapi/ogl3.3/meshes/GMeshGL33.h b/wowViewerLib/src/gapi/ogl3.3/meshes/GMeshGL33.h index 1431ce70c..3030c7dd8 100644 --- a/wowViewerLib/src/gapi/ogl3.3/meshes/GMeshGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/meshes/GMeshGL33.h @@ -14,7 +14,7 @@ class GMeshGL33 : public IMesh { friend class GDeviceGL33; public: - explicit GMeshGL33(IDevice &device, + explicit GMeshGL33(const HGDevice &device, const gMeshTemplate &meshTemplate ); @@ -30,6 +30,7 @@ class GMeshGL33 : public IMesh { void setStart(int start) override; void setEnd(int end) override; public: + void *getM2Object() override { return nullptr; }; void setM2Object(void * m2Object) override { /* throw "Not Implemented"; */}; void setLayer(int layer) override { /* throw "Not Implemented"; */}; void setPriorityPlane(int priorityPlane) override { /* throw "Not Implemented"; */}; @@ -60,7 +61,7 @@ class GMeshGL33 : public IMesh { int m_element; private: - IDevice &m_device; + const HGDevice m_device; }; diff --git a/wowViewerLib/src/gapi/ogl3.3/meshes/GParticleMeshGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/meshes/GParticleMeshGL33.cpp index 1722082e6..342fd2c54 100644 --- a/wowViewerLib/src/gapi/ogl3.3/meshes/GParticleMeshGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/meshes/GParticleMeshGL33.cpp @@ -4,6 +4,6 @@ #include "GParticleMeshGL33.h" -GParticleMeshGL33::GParticleMeshGL33(IDevice &device, const gMeshTemplate &meshTemplate) : GMeshGL33(device, meshTemplate){ +GParticleMeshGL33::GParticleMeshGL33(const HGDevice &device, const gMeshTemplate &meshTemplate) : GMeshGL33(device, meshTemplate){ m_meshType = MeshType::eParticleMesh; } diff --git a/wowViewerLib/src/gapi/ogl3.3/meshes/GParticleMeshGL33.h b/wowViewerLib/src/gapi/ogl3.3/meshes/GParticleMeshGL33.h index 4b473acf4..f2f091cd3 100644 --- a/wowViewerLib/src/gapi/ogl3.3/meshes/GParticleMeshGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/meshes/GParticleMeshGL33.h @@ -10,7 +10,7 @@ class GParticleMeshGL33 : public GMeshGL33 { friend class GDeviceGL33; protected: - GParticleMeshGL33(IDevice &device, const gMeshTemplate &meshTemplate); + GParticleMeshGL33(const HGDevice &device, const gMeshTemplate &meshTemplate); public: float getSortDistance() override { return m_sortDistance; diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GAdtShaderPermutationGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GAdtShaderPermutationGL33.cpp index 194fdc8d8..206994789 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GAdtShaderPermutationGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GAdtShaderPermutationGL33.cpp @@ -4,7 +4,7 @@ #include "GAdtShaderPermutationGL33.h" -GAdtShaderPermutationGL33::GAdtShaderPermutationGL33(std::string &shaderName, IDevice *device) : GShaderPermutationGL33(shaderName, +GAdtShaderPermutationGL33::GAdtShaderPermutationGL33(std::string &shaderName,const HGDevice &device) : GShaderPermutationGL33(shaderName, device) {} void GAdtShaderPermutationGL33::compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) { diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GAdtShaderPermutationGL33.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GAdtShaderPermutationGL33.h index e7f44f799..6e61b4314 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GAdtShaderPermutationGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GAdtShaderPermutationGL33.h @@ -10,7 +10,7 @@ class GAdtShaderPermutationGL33 : public GShaderPermutationGL33 { friend class GDeviceGL33; protected: - explicit GAdtShaderPermutationGL33(std::string &shaderName, IDevice *device); + explicit GAdtShaderPermutationGL33(std::string &shaderName, const HGDevice &device); void compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) override; diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXGlow.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXGlow.cpp index d58a6f426..305117b7e 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXGlow.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXGlow.cpp @@ -7,7 +7,7 @@ auto GFFXGlow_vertShaderName = std::string("drawQuad"); auto GFFXGlow_fragShaderName = std::string("ffxglow"); -GFFXGlow::GFFXGlow(std::string &shaderName, IDevice *device) : +GFFXGlow::GFFXGlow(std::string &shaderName, const HGDevice &device) : GShaderPermutationGL33(GFFXGlow_vertShaderName, GFFXGlow_fragShaderName, device) { } diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXGlow.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXGlow.h index dafaf3674..99bad830d 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXGlow.h +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXGlow.h @@ -10,7 +10,7 @@ class GFFXGlow : public GShaderPermutationGL33 { friend class GDeviceGL33; protected: - explicit GFFXGlow(std::string &shaderName, IDevice *device); + explicit GFFXGlow(std::string &shaderName, const HGDevice &device); void compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) override; }; diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXgauss4.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXgauss4.cpp index 1863bd1c5..2eba4a00e 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXgauss4.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXgauss4.cpp @@ -7,7 +7,7 @@ auto vertShaderName = std::string("drawQuad"); auto fragShaderName = std::string("ffxgauss4"); -GFFXgauss4::GFFXgauss4(std::string &shaderName, IDevice *device) : +GFFXgauss4::GFFXgauss4(std::string &shaderName, const HGDevice &device) : GShaderPermutationGL33(vertShaderName, fragShaderName, device) { diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXgauss4.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXgauss4.h index e7e6ad266..8aa2b1e78 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXgauss4.h +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GFFXgauss4.h @@ -10,7 +10,7 @@ class GFFXgauss4 : public GShaderPermutationGL33 { friend class GDeviceGL33; protected: - explicit GFFXgauss4(std::string &shaderName, IDevice *device); + explicit GFFXgauss4(std::string &shaderName, const HGDevice &device); void compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) override; diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ParticleShaderPermutationGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ParticleShaderPermutationGL33.cpp index 0a08837bc..99876824e 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ParticleShaderPermutationGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ParticleShaderPermutationGL33.cpp @@ -3,7 +3,7 @@ // #include "GM2ParticleShaderPermutationGL33.h" -GM2ParticleShaderPermutationGL33::GM2ParticleShaderPermutationGL33(std::string &shaderName, IDevice *device) : GShaderPermutationGL33(shaderName, +GM2ParticleShaderPermutationGL33::GM2ParticleShaderPermutationGL33(std::string &shaderName, const HGDevice &device) : GShaderPermutationGL33(shaderName, device) {} void GM2ParticleShaderPermutationGL33::compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) { diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ParticleShaderPermutationGL33.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ParticleShaderPermutationGL33.h index 391811699..ecc8ec6ab 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ParticleShaderPermutationGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ParticleShaderPermutationGL33.h @@ -10,7 +10,7 @@ class GM2ParticleShaderPermutationGL33 : public GShaderPermutationGL33 { friend class GDeviceGL33; protected: - explicit GM2ParticleShaderPermutationGL33(std::string &shaderName, IDevice *device); + explicit GM2ParticleShaderPermutationGL33(std::string &shaderName, const HGDevice &device); virtual void compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) override; }; diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ShaderPermutationGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ShaderPermutationGL33.cpp index e342f6f18..70e082eb0 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ShaderPermutationGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ShaderPermutationGL33.cpp @@ -5,7 +5,7 @@ #include "GM2ShaderPermutationGL33.h" #include -GM2ShaderPermutationGL33::GM2ShaderPermutationGL33(std::string &shaderName, IDevice *device, M2ShaderCacheRecord &permutation) : +GM2ShaderPermutationGL33::GM2ShaderPermutationGL33(std::string &shaderName, const HGDevice &device, M2ShaderCacheRecord &permutation) : GShaderPermutationGL33(shaderName, device) , permutation(permutation) {} void GM2ShaderPermutationGL33::compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) { @@ -29,8 +29,10 @@ void GM2ShaderPermutationGL33::compileShader(const std::string &vertExtraDef, co //Init newly created shader glUseProgram(this->m_programBuffer); +// std::cout << "before setting uTextures" << std::endl; if (hasUnf("uTexture")) { glUniform1i(this->getUnf("uTexture"), 0); +// std::cout << "uTexture location " << this->getUnf("uTexture") << "was set" << std::endl; } if (hasUnf("uTexture2")) { glUniform1i(this->getUnf("uTexture2"), 1); diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ShaderPermutationGL33.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ShaderPermutationGL33.h index 1ad892d5e..dae46f8a2 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ShaderPermutationGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GM2ShaderPermutationGL33.h @@ -13,7 +13,7 @@ class GM2ShaderPermutationGL33 : public GShaderPermutationGL33{ ~GM2ShaderPermutationGL33() override {}; protected: - explicit GM2ShaderPermutationGL33(std::string &shaderName, IDevice *device, M2ShaderCacheRecord &permutation); + explicit GM2ShaderPermutationGL33(std::string &shaderName, const HGDevice &device, M2ShaderCacheRecord &permutation); void compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) override; M2ShaderCacheRecord &permutation; diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GSkyConus.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GSkyConus.cpp index d8f375549..87c87ad06 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GSkyConus.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GSkyConus.cpp @@ -7,7 +7,7 @@ auto GSkyConus_vertShaderName = std::string("skyConus"); auto GSkyConus_fragShaderName = std::string("skyConus"); -GSkyConus::GSkyConus(std::string &shaderName, IDevice *device) : +GSkyConus::GSkyConus(std::string &shaderName, const HGDevice &device) : GShaderPermutationGL33(GSkyConus_vertShaderName, GSkyConus_fragShaderName, device) { diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GSkyConus.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GSkyConus.h index a22e3c25b..096f8f7b6 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GSkyConus.h +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GSkyConus.h @@ -10,7 +10,7 @@ class GSkyConus : public GShaderPermutationGL33 { friend class GDeviceGL33; protected: - explicit GSkyConus(std::string &shaderName, IDevice *device); + explicit GSkyConus(std::string &shaderName, const HGDevice &device); void compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) override; diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GWMOShaderPermutationGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GWMOShaderPermutationGL33.cpp index 3739e3dfc..bc9aaeacd 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GWMOShaderPermutationGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GWMOShaderPermutationGL33.cpp @@ -5,7 +5,7 @@ #include "GWMOShaderPermutationGL33.h" -GWMOShaderPermutationGL33::GWMOShaderPermutationGL33(std::string &shaderName, IDevice *device, WMOShaderCacheRecord &permutation) : +GWMOShaderPermutationGL33::GWMOShaderPermutationGL33(std::string &shaderName, const HGDevice &device, WMOShaderCacheRecord &permutation) : GShaderPermutationGL33(shaderName,device), permutation(permutation) {} void GWMOShaderPermutationGL33::compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) { diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GWMOShaderPermutationGL33.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GWMOShaderPermutationGL33.h index b0757bd82..41a51a7ea 100644 --- a/wowViewerLib/src/gapi/ogl3.3/shaders/GWMOShaderPermutationGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GWMOShaderPermutationGL33.h @@ -10,7 +10,7 @@ class GWMOShaderPermutationGL33 : public GShaderPermutationGL33 { friend class GDeviceGL33; protected: - explicit GWMOShaderPermutationGL33(std::string &shaderName, IDevice *device, WMOShaderCacheRecord &permutation); + explicit GWMOShaderPermutationGL33(std::string &shaderName, const HGDevice &device, WMOShaderCacheRecord &permutation); virtual void compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) override; WMOShaderCacheRecord &permutation; diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterShaderGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterShaderGL33.cpp new file mode 100644 index 000000000..fdd6f5042 --- /dev/null +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterShaderGL33.cpp @@ -0,0 +1,19 @@ +// +// Created by Deamon on 9/13/2020. +// + +#include "GWaterShaderGL33.h" + +GWaterShaderGL33::GWaterShaderGL33(std::string &shaderName, const HGDevice &device) : GShaderPermutationGL33(shaderName, + device) { + +} + +void GWaterShaderGL33::compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) { + GShaderPermutationGL33::compileShader("", ""); + + glUseProgram(this->m_programBuffer); + + glUniform1i(this->getUnf("uTexture"), 0); + glUseProgram(0); +}; \ No newline at end of file diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterShaderGL33.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterShaderGL33.h new file mode 100644 index 000000000..16a552e5e --- /dev/null +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterShaderGL33.h @@ -0,0 +1,19 @@ +// +// Created by Deamon on 9/13/2020. +// + +#ifndef AWEBWOWVIEWERCPP_GWATERSHADER33_H +#define AWEBWOWVIEWERCPP_GWATERSHADER33_H + +#include "../GShaderPermutationGL33.h" + +class GWaterShaderGL33 : public GShaderPermutationGL33 { + friend class GDeviceGL33; +protected: + explicit GWaterShaderGL33(std::string &shaderName, const HGDevice &device); + + virtual void compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) override; +}; + + +#endif //AWEBWOWVIEWERCPP_GWATERSHADER33_H diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterfallShaderGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterfallShaderGL33.cpp new file mode 100644 index 000000000..c93d106c5 --- /dev/null +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterfallShaderGL33.cpp @@ -0,0 +1,33 @@ +// +// Created by Deamon on 1/31/2021. +// + +#include "GWaterfallShaderGL33.h" + +GWaterfallShaderGL33::GWaterfallShaderGL33(std::string &shaderName, const HGDevice &device) : GShaderPermutationGL33(shaderName, + device) { + +} + +void GWaterfallShaderGL33::compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) { + GShaderPermutationGL33::compileShader("", ""); + + glUseProgram(this->m_programBuffer); + + if (this->hasUnf("uMask")) { + glUniform1i(this->getUnf("uMask"), 0); + } + if (this->hasUnf("uWhiteWater")) { + glUniform1i(this->getUnf("uWhiteWater"), 1); + } + if (this->hasUnf("uNoise")) { + glUniform1i(this->getUnf("uNoise"), 2); + } + if (this->hasUnf("uBumpTexture")) { + glUniform1i(this->getUnf("uBumpTexture"), 3); + } + if (this->hasUnf("uNormalTex")) { + glUniform1i(this->getUnf("uNormalTex"), 4); + } + glUseProgram(0); +}; \ No newline at end of file diff --git a/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterfallShaderGL33.h b/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterfallShaderGL33.h new file mode 100644 index 000000000..f42d74b82 --- /dev/null +++ b/wowViewerLib/src/gapi/ogl3.3/shaders/GWaterfallShaderGL33.h @@ -0,0 +1,19 @@ +// +// Created by Deamon on 1/31/2021. +// + +#ifndef AWEBWOWVIEWERCPP_GWATERFALLSHADERGL33_H +#define AWEBWOWVIEWERCPP_GWATERFALLSHADERGL33_H + +#include "../GShaderPermutationGL33.h" + +class GWaterfallShaderGL33 : public GShaderPermutationGL33 { + friend class GDeviceGL33; +protected: + explicit GWaterfallShaderGL33(std::string &shaderName, const HGDevice &device); + + void compileShader(const std::string &vertExtraDefStrings, const std::string &fragExtraDefStrings) override; +}; + + +#endif //AWEBWOWVIEWERCPP_GWATERFALLSHADERGL33_H diff --git a/wowViewerLib/src/gapi/ogl3.3/textures/GBlpTextureGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/textures/GBlpTextureGL33.cpp index 29ce65813..bcc7de770 100644 --- a/wowViewerLib/src/gapi/ogl3.3/textures/GBlpTextureGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/textures/GBlpTextureGL33.cpp @@ -8,10 +8,11 @@ #include "../../../engine/persistance/helper/ChunkFileReader.h" #include "../../../engine/texture/DxtDecompress.h" -GBlpTextureGL33::GBlpTextureGL33(IDevice &device, HBlpTexture texture, bool xWrapTex, bool yWrapTex) - : GTextureGL33(device), m_texture(texture) { - this->xWrapTex = xWrapTex; - this->yWrapTex = yWrapTex; + + +GBlpTextureGL33::GBlpTextureGL33(HGDevice device, HBlpTexture texture, bool xWrapTex, bool yWrapTex) + : GTextureGL33(device, xWrapTex, yWrapTex), m_texture(texture) { + } GBlpTextureGL33::~GBlpTextureGL33() { @@ -20,17 +21,17 @@ GBlpTextureGL33::~GBlpTextureGL33() { void GBlpTextureGL33::bind() { #if OPENGL_DGB_MESSAGE - std::string debugMess = "Binding Texture "+m_texture->getTextureName(); + std::string debugMess = "Binding Texture "+m_texture->getTextureName() + "(texture id = " + std::to_string(this->textureIdentifier)+")"; glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, (GLsizei)((uint64_t)this&0xfffffff), GLsizei(debugMess.size()), debugMess.c_str()); - glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, + glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 1, GL_DEBUG_SEVERITY_LOW, GLsizei(debugMess.size()), debugMess.c_str() - ); + ); #endif - + //std::cout << "Binding Texture "+m_texture->getTextureName() + "(texture id = " + std::to_string(this->textureIdentifier)+")" << std::endl; glBindTexture(GL_TEXTURE_2D, textureIdentifier); #if OPENGL_DGB_MESSAGE glPopDebugGroup(); @@ -42,7 +43,7 @@ void GBlpTextureGL33::unbind() { } static int texturesUploaded = 0; -void GBlpTextureGL33::createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) { +void GBlpTextureGL33::createGlTexture(TextureFormat textureFormat, const HMipmapsVector &hmipmaps) { // std::cout << "texturesUploaded = " << texturesUploaded++ << " " << this->m_texture->getTextureName() <getIsCompressedTexturesSupported(); + bool useDXT3Decoding = !m_device->getIsCompressedTexturesSupported(); + bool useDXT5Decoding = !m_device->getIsCompressedTexturesSupported(); + +// std::cout +// << "useDXT1Decoding = " << useDXT1Decoding << " " +// << "useDXT3Decoding = " << useDXT3Decoding << " " +// << "useDXT5Decoding = " << useDXT5Decoding << " " << std::endl; -// useDXT1Decoding = true; // Note: manual DXT1 decompression loses alpha channel for S3TC_RGBA_DXT1 textures -// useDXT3Decoding = true; -// useDXT5Decoding = true; + useDXT1Decoding = true; // Note: manual DXT1 decompression loses alpha channel for S3TC_RGBA_DXT1 textures + useDXT3Decoding = true; + useDXT5Decoding = true; // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + bool skipGenerationOfMipMaps = true; + auto &mipmaps = *hmipmaps; + + logGLError bool generateMipMaps = false; switch (textureFormat) { case TextureFormat::S3TC_RGB_DXT1: case TextureFormat::S3TC_RGBA_DXT1: { - unsigned char *decodedResult = nullptr; + std::vector decodedResult; if (useDXT1Decoding) - decodedResult = new unsigned char[mipmaps[0].width * mipmaps[0].height * 4]; + decodedResult = std::vector(mipmaps[0].width * mipmaps[0].height * 4); for (int k = 0; k < mipmaps.size(); k++) { + logGLError if (useDXT1Decoding) { bool hasAlpha = (textureFormat == TextureFormat::S3TC_RGBA_DXT1); - DecompressBC1( mipmaps[k].width, mipmaps[k].height, &mipmaps[k].texture[0], decodedResult, hasAlpha); + DecompressBC1( mipmaps[k].width, mipmaps[k].height, &mipmaps[k].texture[0], decodedResult.data(), hasAlpha); glTexImage2D(GL_TEXTURE_2D, k, GL_RGBA, mipmaps[k].width, mipmaps[k].height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, decodedResult); + GL_RGBA, GL_UNSIGNED_BYTE, decodedResult.data()); } else { glCompressedTexImage2D(GL_TEXTURE_2D, k, textureGPUFormat, mipmaps[k].width, mipmaps[k].height, 0, (GLsizei) mipmaps[k].texture.size(), &mipmaps[k].texture[0]); } + logGLError } - if (useDXT1Decoding) - delete decodedResult; break; } case TextureFormat::S3TC_RGBA_DXT3: { - unsigned char *decodedResult = nullptr; + std::vector decodedResult; if (useDXT3Decoding) - decodedResult = new unsigned char[mipmaps[0].width * mipmaps[0].height * 4]; + decodedResult = std::vector(mipmaps[0].width * mipmaps[0].height * 4); + logGLError for (int k = 0; k < mipmaps.size(); k++) { if (useDXT3Decoding) { - DecompressBC2(mipmaps[k].width, mipmaps[k].height, &mipmaps[k].texture[0], decodedResult); + DecompressBC2(mipmaps[k].width, mipmaps[k].height, &mipmaps[k].texture[0], decodedResult.data()); glTexImage2D(GL_TEXTURE_2D, k, GL_RGBA, mipmaps[k].width, mipmaps[k].height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, decodedResult); + GL_RGBA, GL_UNSIGNED_BYTE, decodedResult.data()); } else { glCompressedTexImage2D(GL_TEXTURE_2D, k, textureGPUFormat, mipmaps[k].width, mipmaps[k].height, 0, (GLsizei) mipmaps[k].texture.size(), &mipmaps[k].texture[0]); } + logGLError } - if (useDXT3Decoding) - delete decodedResult; break; } case TextureFormat::S3TC_RGBA_DXT5: { - unsigned char *decodedResult = nullptr; + std::vector decodedResult; if (useDXT5Decoding) - decodedResult = new unsigned char[mipmaps[0].width * mipmaps[0].height * 4]; + decodedResult = std::vector(mipmaps[0].width * mipmaps[0].height * 4); + + logGLError for (int k = 0; k < mipmaps.size(); k++) { if (useDXT5Decoding) { - DecompressBC3(mipmaps[k].width, mipmaps[k].height, &mipmaps[k].texture[0], decodedResult); + DecompressBC3(mipmaps[k].width, mipmaps[k].height, &mipmaps[k].texture[0], decodedResult.data()); glTexImage2D(GL_TEXTURE_2D, k, GL_RGBA, mipmaps[k].width, mipmaps[k].height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, decodedResult); + GL_RGBA, GL_UNSIGNED_BYTE, decodedResult.data()); } else { glCompressedTexImage2D(GL_TEXTURE_2D, k, textureGPUFormat, mipmaps[k].width, mipmaps[k].height, 0, (GLsizei) mipmaps[k].texture.size(), &mipmaps[k].texture[0]); } + logGLError } - if (useDXT5Decoding) - delete decodedResult; break; } - case TextureFormat::BGRA: + case TextureFormat::RGBA: + logGLError for( int k = 0; k < mipmaps.size(); k++) { - glTexImage2D(GL_TEXTURE_2D, k, GL_RGBA, mipmaps[k].width, mipmaps[k].height, 0, GL_BGRA, GL_UNSIGNED_BYTE, + glTexImage2D(GL_TEXTURE_2D, k, GL_RGBA, mipmaps[k].width, mipmaps[k].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &mipmaps[k].texture[0]); + logGLError } + skipGenerationOfMipMaps = false; break; - case TextureFormat::None: - case TextureFormat::RGBA: - case TextureFormat::PalARGB1555DitherFloydSteinberg: - case TextureFormat::PalARGB4444DitherFloydSteinberg: - case TextureFormat::PalARGB2565DitherFloydSteinberg: + + default: std::cout << "Detected unhandled texture format" << std::endl; - break; + break; } #ifndef WITH_GLESv2 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, (GLint) 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, (GLint) mipmaps.size()-1); #endif + logGLError glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + logGLError glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + logGLError if (xWrapTex) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); } + logGLError if (yWrapTex) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } + logGLError bool anisFilterExt = true; #ifndef WITH_GLESv2 - if (m_device.getIsAnisFiltrationSupported()) { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_device.getAnisLevel()); + if (m_device->getIsAnisFiltrationSupported()) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, m_device->getAnisLevel()); } -#ifndef __EMSCRIPTEN__ - glGenerateMipmap(GL_TEXTURE_2D); #endif -#endif - - + logGLError + if (!skipGenerationOfMipMaps) { + glGenerateMipmap(GL_TEXTURE_2D); + } + logGLError } bool GBlpTextureGL33::getIsLoaded() { @@ -208,9 +225,11 @@ bool GBlpTextureGL33::postLoad() { if (m_texture == nullptr) return false; if (m_texture->getStatus() != FileStatus::FSLoaded) return false; - m_device.bindTexture(this, 0); + m_device->bindTexture(this, 0); this->createGlTexture(m_texture->getTextureFormat(), m_texture->getMipmapsVector()); - m_device.bindTexture(nullptr, 0); + m_device->bindTexture(nullptr, 0); + +// m_texture = nullptr; m_loaded = true; return true; diff --git a/wowViewerLib/src/gapi/ogl3.3/textures/GBlpTextureGL33.h b/wowViewerLib/src/gapi/ogl3.3/textures/GBlpTextureGL33.h index 3ad98a1a8..1a9440036 100644 --- a/wowViewerLib/src/gapi/ogl3.3/textures/GBlpTextureGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/textures/GBlpTextureGL33.h @@ -11,10 +11,10 @@ class GBlpTextureGL33 : public GTextureGL33 { friend class GDeviceGL33; - explicit GBlpTextureGL33(IDevice &device, HBlpTexture texture, bool xWrapTex, bool yWrapTex); + explicit GBlpTextureGL33(HGDevice device, HBlpTexture texture, bool xWrapTex, bool yWrapTex); public: ~GBlpTextureGL33() override; - void createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) override; + void createGlTexture(TextureFormat textureFormat, const HMipmapsVector &mipmaps) override; bool getIsLoaded() override; bool postLoad() override; @@ -24,9 +24,6 @@ class GBlpTextureGL33 : public GTextureGL33 { private: HBlpTexture m_texture; - bool xWrapTex; - bool yWrapTex; - bool m_loaded = false; }; diff --git a/wowViewerLib/src/gapi/ogl3.3/textures/GTextureGL33.cpp b/wowViewerLib/src/gapi/ogl3.3/textures/GTextureGL33.cpp index 9b760e638..b3dd31847 100644 --- a/wowViewerLib/src/gapi/ogl3.3/textures/GTextureGL33.cpp +++ b/wowViewerLib/src/gapi/ogl3.3/textures/GTextureGL33.cpp @@ -7,7 +7,12 @@ #include "../../../engine/opengl/header.h" #include "../../interface/IDevice.h" -GTextureGL33::GTextureGL33(IDevice &device) : m_device(dynamic_cast(device)) { +int GTextureGL33::currentGLTexturesAllocated = 0; + +GTextureGL33::GTextureGL33(const HGDevice &device, bool xWrapTex, bool yWrapTex) : m_device(device) { + this->xWrapTex = xWrapTex; + this->yWrapTex = yWrapTex; + createBuffer(); } @@ -16,23 +21,30 @@ GTextureGL33::~GTextureGL33() { } void GTextureGL33::createBuffer() { + logGLError glGenTextures(1, &textureIdentifier); + logGLError + GTextureGL33::currentGLTexturesAllocated++; } void GTextureGL33::destroyBuffer() { const GLuint indent = textureIdentifier; - m_device.addDeallocationRecord([indent]() -> void { + m_device->addDeallocationRecord([indent]() -> void { glDeleteTextures(1, &indent); + GTextureGL33::currentGLTexturesAllocated--; }); } void GTextureGL33::bind() { + logGLError glBindTexture(GL_TEXTURE_2D, textureIdentifier); - + logGLError } void GTextureGL33::unbind() { + logGLError glBindTexture(GL_TEXTURE_2D, 0); + logGLError } bool GTextureGL33::getIsLoaded() { @@ -46,7 +58,7 @@ void GTextureGL33::loadData(int width, int height, void *data, ITextureFormat te this->height = height; - m_device.bindTexture(this, 0); + m_device->bindTexture(this, 0); if (textureFormat == ITextureFormat::itRGBA) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } else if (textureFormat == ITextureFormat::itRGBAFloat32) { @@ -54,32 +66,45 @@ void GTextureGL33::loadData(int width, int height, void *data, ITextureFormat te }else if (textureFormat == ITextureFormat::itDepth32) { glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH32F_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, data); } + if (data != nullptr) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, (GLint) 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, (GLint) 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (xWrapTex) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + } + if (yWrapTex) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } if (data != nullptr) { glGenerateMipmap(GL_TEXTURE_2D); } - m_device.bindTexture(nullptr, 0); + m_device->bindTexture(nullptr, 0); + + m_loaded = true; } void GTextureGL33::readData(std::vector &buff) { -#ifndef __EMSCRIPTEN__ +#if !(defined(__EMSCRIPTEN__) || defined(WITH_GLESv2)) if (buff.size() < width*height*4) { } - m_device.bindTexture(this, 0); + m_device->bindTexture(this, 0); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_BYTE, buff.data()); - m_device.bindTexture(nullptr, 0); + m_device->bindTexture(nullptr, 0); #else GLuint fbo; glGenFramebuffers(1, &fbo); diff --git a/wowViewerLib/src/gapi/ogl3.3/textures/GTextureGL33.h b/wowViewerLib/src/gapi/ogl3.3/textures/GTextureGL33.h index cdb2944cc..13681d825 100644 --- a/wowViewerLib/src/gapi/ogl3.3/textures/GTextureGL33.h +++ b/wowViewerLib/src/gapi/ogl3.3/textures/GTextureGL33.h @@ -12,32 +12,39 @@ class GTextureGL33 : public ITexture { friend class GDeviceGL33; protected: - explicit GTextureGL33(IDevice &device); + explicit GTextureGL33(const HGDevice &device, bool xWrapTex, bool yWrapTex); public: ~GTextureGL33() override; void loadData(int width, int height, void *data, ITextureFormat textureFormat) override; void readData(std::vector &buff) override; bool getIsLoaded() override; - void createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) override { + void createGlTexture(TextureFormat textureFormat, const HMipmapsVector &mipmaps) override { // throw "Not Implemented in this class"; } bool postLoad() override { return false;}; void bindToCurrentFrameBufferAsColor(uint8_t attachmentIndex); void bindToCurrentFrameBufferAsDepth(); + + static int getCurrentGLTexturesAllocated() {return currentGLTexturesAllocated;} private: + + static int currentGLTexturesAllocated; void createBuffer(); void destroyBuffer(); virtual void bind(); //Should be called only by GDevice void unbind(); protected: - GLuint textureIdentifier; + GLuint textureIdentifier = 0; - GDeviceGL33 &m_device; + HGDevice m_device; bool m_loaded = false; int width = 0; int height = 0; + + bool xWrapTex; + bool yWrapTex; }; diff --git a/wowViewerLib/src/gapi/ogl4.x/GDeviceGL4x.cpp b/wowViewerLib/src/gapi/ogl4.x/GDeviceGL4x.cpp index 277ec13aa..adf6fdc35 100644 --- a/wowViewerLib/src/gapi/ogl4.x/GDeviceGL4x.cpp +++ b/wowViewerLib/src/gapi/ogl4.x/GDeviceGL4x.cpp @@ -416,7 +416,7 @@ void GDeviceGL4x::drawMesh(HGMesh hIMesh) { } if (m_lastBlendMode != hmesh->m_blendMode) { - BlendModeDesc &selectedBlendMode = blendModes[(char)hmesh->m_blendMode]; + BlendModeDesc &selectedBlendMode = GL33::blendModes[(char)hmesh->m_blendMode]; if ((m_lastBlendMode == EGxBlendEnum::GxBlend_UNDEFINED) || (blendModes[(char)m_lastBlendMode].blendModeEnable != selectedBlendMode.blendModeEnable )) { if (selectedBlendMode.blendModeEnable) { diff --git a/wowViewerLib/src/gapi/ogl4.x/textures/GBlpTextureGL4x.cpp b/wowViewerLib/src/gapi/ogl4.x/textures/GBlpTextureGL4x.cpp index 567f758c6..66b83f243 100644 --- a/wowViewerLib/src/gapi/ogl4.x/textures/GBlpTextureGL4x.cpp +++ b/wowViewerLib/src/gapi/ogl4.x/textures/GBlpTextureGL4x.cpp @@ -26,7 +26,7 @@ void GBlpTextureGL4x::unbind() { glBindTexture(GL_TEXTURE_2D, 0); } -void GBlpTextureGL4x::createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) { +void GBlpTextureGL4x::createGlTexture(TextureFormat textureFormat, const HMipmapsVector &hmipmaps) { GLuint textureGPUFormat = 0; // if (ext) { switch (textureFormat) { @@ -63,6 +63,7 @@ void GBlpTextureGL4x::createGlTexture(TextureFormat textureFormat, const Mipmaps bool useDXT5Decoding = false; // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + auto &mipmaps = *hmipmaps; glTexStorage2D(GL_TEXTURE_2D, mipmaps.size(), textureGPUFormat, mipmaps[0].width, mipmaps[0].height); diff --git a/wowViewerLib/src/gapi/ogl4.x/textures/GBlpTextureGL4x.h b/wowViewerLib/src/gapi/ogl4.x/textures/GBlpTextureGL4x.h index f0970b376..796596b73 100644 --- a/wowViewerLib/src/gapi/ogl4.x/textures/GBlpTextureGL4x.h +++ b/wowViewerLib/src/gapi/ogl4.x/textures/GBlpTextureGL4x.h @@ -14,7 +14,7 @@ class GBlpTextureGL4x : public GTextureGL4x { explicit GBlpTextureGL4x(IDevice &device, HBlpTexture texture, bool xWrapTex, bool yWrapTex); public: ~GBlpTextureGL4x() override; - void createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) override; + void createGlTexture(TextureFormat textureFormat, const HMipmapsVector &hmipmaps) override; bool getIsLoaded() override; bool postLoad() override {return false;}; diff --git a/wowViewerLib/src/gapi/ogl4.x/textures/GTextureGL4x.h b/wowViewerLib/src/gapi/ogl4.x/textures/GTextureGL4x.h index 9834777a1..840205d6e 100644 --- a/wowViewerLib/src/gapi/ogl4.x/textures/GTextureGL4x.h +++ b/wowViewerLib/src/gapi/ogl4.x/textures/GTextureGL4x.h @@ -17,7 +17,7 @@ class GTextureGL4x : public ITexture { void loadData(int width, int height, void *data) override; bool getIsLoaded() override; - void createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) override { + void createGlTexture(TextureFormat textureFormat, const HMipmapsVector &hmipmaps) override { throw "Not Implemented in this class"; } bool postLoad() override {return false;}; diff --git a/wowViewerLib/src/gapi/vulkan/GDeviceVulkan.cpp b/wowViewerLib/src/gapi/vulkan/GDeviceVulkan.cpp index d49961739..4e93b15f0 100644 --- a/wowViewerLib/src/gapi/vulkan/GDeviceVulkan.cpp +++ b/wowViewerLib/src/gapi/vulkan/GDeviceVulkan.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "GDeviceVulkan.h" #include "../../include/vulkancontext.h" @@ -27,10 +28,19 @@ #include "../../engine/algorithms/hashString.h" #include "shaders/GAdtShaderPermutationVLK.h" #include "shaders/GWMOShaderPermutationVLK.h" -#include "shaders/GWMOWaterShaderVLK.h" +#include "shaders/GWaterShaderPermutation.h" #include "shaders/GImguiShaderPermutation.h" #include "shaders/GM2RibbonShaderPermutationVLK.h" +#include "shaders/GSkyConusShaderVLK.h" +#include "shaders/GDrawBoundingBoxVLK.h" +#include "GFrameBufferVLK.h" +#include "shaders/GFFXgauss4VLK.h" +#include "shaders/GFFXGlowVLK.h" +#include "shaders/GWaterfallShaderVLK.h" +#include "GRenderPassVLK.h" +#include "../../engine/algorithms/FrameCounter.h" //#include "fastmemcp.h" +#include const int WIDTH = 1900; const int HEIGHT = 1000; @@ -76,7 +86,7 @@ bool checkValidationLayerSupport() { bool layerFound = false; for (const auto& layerProperties : availableLayers) { - if (strcmp(layerName, layerProperties.layerName) == 0) { + if (strncmp(layerName, layerProperties.layerName, 255) == 0) { layerFound = true; break; } @@ -162,15 +172,19 @@ void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& create createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; createInfo.pNext = NULL; + createInfo.flags = NULL; createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; createInfo.pfnUserCallback = debugCallback; + createInfo.pUserData = 0; } GDeviceVLK::GDeviceVLK(vkCallInitCallback * callback) { enableValidationLayers = false; + this->threadCount = std::max((int)std::thread::hardware_concurrency() - 3, 1); + if (enableValidationLayers && !checkValidationLayerSupport()) { throw std::runtime_error("validation layers requested, but not available!"); } @@ -277,6 +291,10 @@ GDeviceVLK::GDeviceVLK(vkCallInitCallback * callback) { } +void GDeviceVLK::initialize() { + +} + void GDeviceVLK::recreateSwapChain() { createSwapChain(); createImageViews(); @@ -317,6 +335,7 @@ void GDeviceVLK::createSwapChain() { createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; createInfo.pNext = nullptr; createInfo.surface = vkSurface; + createInfo.flags = 0; createInfo.minImageCount = imageCount; createInfo.imageFormat = surfaceFormat.format; @@ -407,63 +426,11 @@ VkFormat GDeviceVLK::findDepthFormat() { } void GDeviceVLK::createRenderPass() { - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = swapChainImageFormat; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentDescription depthAttachment = {}; - depthAttachment.format = findDepthFormat(); - depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depthAttachmentRef = {}; - depthAttachmentRef.attachment = 1; - depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - subpass.pDepthStencilAttachment = &depthAttachmentRef; - - VkSubpassDependency dependency = {}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - std::array attachments = {colorAttachment, depthAttachment}; - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.pNext = nullptr; - renderPassInfo.attachmentCount = 2; - renderPassInfo.pAttachments = attachments.data(); - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = 1; - renderPassInfo.pDependencies = &dependency; - - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { - throw std::runtime_error("failed to create render pass!"); - } + swapchainRenderPass = std::make_shared(*this, + std::vector({swapChainImageFormat}), + findDepthFormat(), + VK_SAMPLE_COUNT_1_BIT, + true); } void GDeviceVLK::createColorResources() { @@ -575,7 +542,8 @@ void GDeviceVLK::createFramebuffers() { VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.pNext = NULL; - framebufferInfo.renderPass = renderPass; + framebufferInfo.flags = 0; + framebufferInfo.renderPass = swapchainRenderPass->getRenderPass(); framebufferInfo.attachmentCount = 2; framebufferInfo.pAttachments = &attachments[0]; framebufferInfo.width = swapChainExtent.width; @@ -631,6 +599,7 @@ void GDeviceVLK::createLogicalDevice() { } VkPhysicalDeviceFeatures deviceFeatures = {}; + deviceFeatures.samplerAnisotropy = true; VkDeviceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -715,6 +684,13 @@ void GDeviceVLK::createCommandPool() { throw std::runtime_error("failed to create graphics command pool!"); } + if (!getIsAsynBuffUploadSupported()) { + createCommandPoolForUpload(); + } +} +void GDeviceVLK::createCommandPoolForUpload(){ + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + VkCommandPoolCreateInfo renderPoolInfo = {}; renderPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; renderPoolInfo.pNext = NULL; @@ -734,7 +710,18 @@ void GDeviceVLK::createCommandPool() { if (vkCreateCommandPool(device, &uploadPoolInfo, nullptr, &uploadCommandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics command pool!"); } + + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.pNext = NULL; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPoolForImageTransfer) != VK_SUCCESS) { + throw std::runtime_error("failed to create graphics command pool!"); + } } + void GDeviceVLK::createCommandBuffers() { commandBuffers.resize(4); @@ -749,20 +736,39 @@ void GDeviceVLK::createCommandBuffers() { throw std::runtime_error("failed to allocate command buffers!"); } + if (!getIsAsynBuffUploadSupported()) { + createCommandBuffersForUpload(); + } +} +void GDeviceVLK::createCommandBuffersForUpload() { renderCommandBuffers.resize(4); - renderCommandBuffersNull.resize(4); - for (int i = 0; i < 4; i++) renderCommandBuffersNull[i] = true; - allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = renderCommandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - allocInfo.commandBufferCount = (uint32_t) renderCommandBuffers.size(); - - if (vkAllocateCommandBuffers(device, &allocInfo, renderCommandBuffers.data()) != VK_SUCCESS) { - throw std::runtime_error("failed to allocate command buffers!"); + renderCommandBuffersNotNull.resize(4); + renderCommandBuffersForFrameBuffers.resize(4); + renderCommandBuffersForFrameBuffersNotNull.resize(4); + for (int i = 0; i < 4; i++) renderCommandBuffersNotNull[i] = false; + { + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = renderCommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + allocInfo.commandBufferCount = (uint32_t) renderCommandBuffers.size(); + + if (vkAllocateCommandBuffers(device, &allocInfo, renderCommandBuffers.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate command buffers!"); + } } - + { + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = renderCommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t) renderCommandBuffersForFrameBuffers.size(); + + if (vkAllocateCommandBuffers(device, &allocInfo, renderCommandBuffersForFrameBuffers.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate command buffers!"); + } + } uploadCommandBuffers.resize(4); VkCommandBufferAllocateInfo allocInfoUpload = {}; @@ -780,7 +786,7 @@ void GDeviceVLK::createCommandBuffers() { for (int i = 0; i < 4; i++) textureTransferCommandBufferNull[i] = true; VkCommandBufferAllocateInfo texttrAllocInfoUpload = {}; texttrAllocInfoUpload.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - texttrAllocInfoUpload.commandPool = commandPool; + texttrAllocInfoUpload.commandPool = commandPoolForImageTransfer; texttrAllocInfoUpload.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; texttrAllocInfoUpload.commandBufferCount = (uint32_t) textureTransferCommandBuffers.size(); @@ -788,6 +794,7 @@ void GDeviceVLK::createCommandBuffers() { throw std::runtime_error("failed to allocate upload command buffers!"); } } + void GDeviceVLK::createSyncObjects() { imageAvailableSemaphores.resize(commandBuffers.size()); renderFinishedSemaphores.resize(commandBuffers.size()); @@ -850,7 +857,9 @@ unsigned int GDeviceVLK::getDrawFrameNumber() { unsigned int GDeviceVLK::getUpdateFrameNumber() { return (m_frameNumber + 1) & 3; } - +unsigned int GDeviceVLK::getOcclusionFrameNumber() { + return (m_frameNumber + 2) & 3; +} unsigned int GDeviceVLK::getCullingFrameNumber() { return (m_frameNumber + 3) & 3; } @@ -894,9 +903,11 @@ void GDeviceVLK::startUpdateForNextFrame() { // std::cout << "updateBuffers: updateFrame = " << uploadFrame << std::endl; + this->waitInDrawStageAndDeps.beginMeasurement(); vkWaitForFences(device, 1, &uploadFences[uploadFrame], VK_TRUE, std::numeric_limits::max()); vkWaitForFences(device, 1, &inFlightFences[uploadFrame], VK_TRUE, std::numeric_limits::max()); vkResetFences(device, 1, &uploadFences[uploadFrame]); + this->waitInDrawStageAndDeps.endMeasurement(""); if (vkBeginCommandBuffer(uploadCommandBuffers[uploadFrame], &beginInfo) != VK_SUCCESS) { std::cout << "failed to begin recording uploadCommandBuffer command buffer!" << std::endl; @@ -933,62 +944,47 @@ void GDeviceVLK::endUpdateForNextFrame() { } } - while ((!listOfDeallocators.empty())&&(listOfDeallocators.front().frameNumberToDoAt <= m_frameNumber)) { - listOfDeallocators.front().callback(); + { + std::lock_guard lock(m_listOfDeallocatorsAccessMtx); + while ((!listOfDeallocators.empty()) && (listOfDeallocators.front().frameNumberToDoAt <= m_frameNumber)) { + auto stuff = listOfDeallocators.front(); + if (stuff.callback != nullptr) { + stuff.callback(); + } - listOfDeallocators.pop_front(); + listOfDeallocators.pop_front(); + } } } typedef std::shared_ptr HVKMesh; -void GDeviceVLK::updateBuffers(std::vector &iMeshes, std::vector additionalChunks) { +void GDeviceVLK::updateBuffers(std::vector*> &bufferChunks, std::vector &frameDepedantData) { // aggregationBufferForUpload.resize(maxUniformBufferSize); if (!m_blackPixelTexture) { - m_blackPixelTexture = createTexture(); + m_blackPixelTexture = createTexture(false, false); unsigned int zero = 0; m_blackPixelTexture->loadData(1,1,&zero, ITextureFormat::itRGBA); } if (!m_whitePixelTexture) { - m_whitePixelTexture = createTexture(); + m_whitePixelTexture = createTexture(false, false); unsigned int ff = 0xffffffff; m_whitePixelTexture->loadData(1,1,&ff, ITextureFormat::itRGBA); } - std::vector &meshes = (std::vector &) iMeshes; - - //1. Collect buffers - std::vector buffers; - int renderIndex = 0; - for (const auto &mesh : meshes) { - for (int i = 0; i < 6; i++ ) { - IUniformBufferChunk *buffer = (IUniformBufferChunk *) mesh->getUniformBuffer(i).get(); - if (buffer != nullptr) { - buffers.push_back(buffer); - } - } - } - for (const auto &bufferChunks : additionalChunks) { - if (bufferChunks != nullptr) { - buffers.push_back(bufferChunks.get()); - } - } - - - std::sort( buffers.begin(), buffers.end()); - buffers.erase( unique( buffers.begin(), buffers.end() ), buffers.end() ); - int fullSize = 0; - for (auto buffer : buffers) { - fullSize += buffer->getSize(); - int offsetDiff = fullSize % uniformBufferOffsetAlign; - if (offsetDiff != 0) { - int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; - - fullSize += bytesToAdd; + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + for (auto &buffer : *bufferVec) { + fullSize += buffer->getSize(); + int offsetDiff = fullSize % uniformBufferOffsetAlign; + if (offsetDiff != 0) { + int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; + + fullSize += bytesToAdd; + } } } - //2. Create buffers and update them int currentSize = 0; int buffersIndex = 0; @@ -1011,28 +1007,48 @@ void GDeviceVLK::updateBuffers(std::vector &iMeshes, std::vector(bufferForUploadVLK->stagingUBOBufferAllocInfo.pMappedData); - for (const auto &buffer : buffers) { - buffer->setOffset(currentSize); - buffer->setPointer(&pointerForUpload[currentSize]); - currentSize += buffer->getSize(); - - int offsetDiff = currentSize % uniformBufferOffsetAlign; - if (offsetDiff != 0) { - int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; - currentSize += bytesToAdd; - } - } - for (auto &buffer : buffers) { - buffer->update(); - } + if (fullSize > 0) { + char *pointerForUpload = static_cast(bufferForUploadVLK->stagingUBOBufferAllocInfo.pMappedData); - if (currentSize > 0) { - bufferForUploadVLK->uploadFromStaging(currentSize); - } + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + for (auto &buffer : *bufferVec) { + buffer->setOffset(currentSize); + buffer->setPointer(&pointerForUpload[currentSize]); + currentSize += buffer->getSize(); + int offsetDiff = currentSize % uniformBufferOffsetAlign; + if (offsetDiff != 0) { + int bytesToAdd = uniformBufferOffsetAlign - offsetDiff; + currentSize += bytesToAdd; + } + } + } + assert(currentSize == fullSize); + for (int i = 0; i < bufferChunks.size(); i++) { + auto &bufferVec = bufferChunks[i]; + auto frameDepData = frameDepedantData[i]; + + int gran = std::max(bufferVec->size() / this->threadCount, 1); + + tbb::parallel_for(tbb::blocked_range(0, bufferVec->size(), gran), + [&](tbb::blocked_range r) { + for (size_t i = r.begin(); i != r.end(); ++i) { + auto& buffer = (*bufferVec)[i]; + buffer->update(frameDepData); + } + }, tbb::simple_partitioner()); + +// for (auto &buffer : *bufferVec) { +// buffer->update(frameDepData); +// } + } + if (currentSize > 0) { + bufferForUploadVLK->uploadFromStaging(currentSize); + } + } } void GDeviceVLK::uploadTextureForMeshes(std::vector &meshes) { @@ -1059,6 +1075,7 @@ void GDeviceVLK::uploadTextureForMeshes(std::vector &meshes) { if (texture->postLoad()) texturesLoaded++; if (texturesLoaded > 4) break; } + m_texturesWereUploaded = texturesLoaded > 0; } void GDeviceVLK::drawMeshes(std::vector &meshes) { @@ -1092,17 +1109,37 @@ std::shared_ptr GDeviceVLK::getShader(std::string shaderName sharedPtr.reset(iPremutation); sharedPtr->compileShader("",""); } else if (shaderName == "waterShader"){ - IShaderPermutation *iPremutation = new GWmoWaterShaderPermutationVLK(shaderName, this); + IShaderPermutation *iPremutation = new GWaterShaderPermutation(shaderName, this); sharedPtr.reset(iPremutation); sharedPtr->compileShader("",""); } else if (shaderName == "adtShader"){ IShaderPermutation *iPremutation = new GAdtShaderPermutationVLK(shaderName, this); sharedPtr.reset(iPremutation); sharedPtr->compileShader("",""); - } else if (shaderName == "adtWater"){ - IShaderPermutation *iPremutation = new GAdtShaderPermutationVLK(shaderName, this); + } else if (shaderName == "skyConus"){ + IShaderPermutation *iPremutation = new GSkyConusShaderVLK(shaderName, this); + sharedPtr.reset(iPremutation); + sharedPtr->compileShader("",""); + } else if (shaderName == "fullScreen_ffxgauss4") { + IShaderPermutation *iPremutation = new GFFXgauss4VLK(shaderName, this); + sharedPtr.reset(iPremutation); + sharedPtr->compileShader("",""); + m_shaderPermutCache[hash] = sharedPtr; + } else if (shaderName == "ffxGlowQuad") { + IShaderPermutation *iPremutation = new GFFXGlowVLK(shaderName, this); + sharedPtr.reset(iPremutation); + sharedPtr->compileShader("",""); + m_shaderPermutCache[hash] = sharedPtr; + } else if (shaderName == "waterfallShader") { + IShaderPermutation *iPremutation = new GWaterfallShaderVLK(shaderName, this); + sharedPtr.reset(iPremutation); + sharedPtr->compileShader("",""); + m_shaderPermutCache[hash] = sharedPtr; + } else if (shaderName == "drawBBShader") { + IShaderPermutation *iPremutation = new GDrawBoundingBoxVLK(shaderName, this); sharedPtr.reset(iPremutation); sharedPtr->compileShader("",""); + m_shaderPermutCache[hash] = sharedPtr; } else if (shaderName == "imguiShader") { IShaderPermutation *iPremutation = new GImguiShaderPermutation(shaderName, this); sharedPtr.reset(iPremutation); @@ -1181,9 +1218,9 @@ HGTexture GDeviceVLK::createBlpTexture(HBlpTexture &texture, bool xWrapTex, bool return hgTexture; } -HGTexture GDeviceVLK::createTexture() { +HGTexture GDeviceVLK::createTexture(bool xWrapTex, bool yWrapTex) { std::shared_ptr h_texture; - h_texture.reset(new GTextureVLK(*this)); + h_texture.reset(new GTextureVLK(*this, xWrapTex, yWrapTex)); return h_texture; } @@ -1340,12 +1377,12 @@ void GDeviceVLK::commitFrame() { std::array clearValues = {}; clearValues[0].color = {clearColor[0], clearColor[1], clearColor[2], 1.0f}; - clearValues[1].depthStencil = {1.0f, 0}; + clearValues[1].depthStencil = {getInvertZ() ? 0.0f : 1.0f, 0}; VkRenderPassBeginInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.pNext = NULL; - renderPassInfo.renderPass = renderPass; + renderPassInfo.renderPass = swapchainRenderPass->getRenderPass(); renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex]; renderPassInfo.renderArea.offset = {0, 0}; renderPassInfo.renderArea.extent = swapChainExtent; @@ -1354,7 +1391,7 @@ void GDeviceVLK::commitFrame() { vkCmdBeginRenderPass(commandBufferForFilling, &renderPassInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - if (!renderCommandBuffersNull[currentDrawFrame]) { + if (renderCommandBuffersNotNull[currentDrawFrame]) { vkCmdExecuteCommands(commandBufferForFilling, 1, &renderCommandBuffers[currentDrawFrame]); } @@ -1383,28 +1420,24 @@ void GDeviceVLK::commitFrame() { submitInfo.waitSemaphoreCount = 1; } - - - submitInfo.pWaitSemaphores = &waitSemaphores[0]; submitInfo.pWaitDstStageMask = &waitStages[0]; - std::array grCommandBuffers; + std::vector grCommandBuffers = {}; if (!textureTransferCommandBufferNull[currentDrawFrame]) { - grCommandBuffers = {textureTransferCommandBuffers[currentDrawFrame], commandBufferForFilling}; - - submitInfo.commandBufferCount = 2; - } else { - grCommandBuffers = {commandBufferForFilling}; - - submitInfo.commandBufferCount = 1; + grCommandBuffers.push_back(textureTransferCommandBuffers[currentDrawFrame]); + } + if (renderCommandBuffersForFrameBuffersNotNull[currentDrawFrame]) { + grCommandBuffers.push_back(renderCommandBuffersForFrameBuffers[currentDrawFrame]); } - submitInfo.pCommandBuffers = &grCommandBuffers[0]; + grCommandBuffers.push_back(commandBufferForFilling); + + submitInfo.commandBufferCount = grCommandBuffers.size(); + submitInfo.pCommandBuffers = grCommandBuffers.data(); submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderFinishedSemaphores[currentDrawFrame]; - // if (!renderCommandBuffersNull[currentDrawFrame]) { { auto result = vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentDrawFrame]); @@ -1451,23 +1484,78 @@ void GDeviceVLK::setViewPortDimensions(float x, float y, float width, float heig } +std::shared_ptr GDeviceVLK::getRenderPass( + std::vector textureAttachments, + ITextureFormat depthAttachment, + VkSampleCountFlagBits sampleCountFlagBits, + bool isSwapChainPass +) { + for (auto &renderPassAvalability : m_createdRenderPasses) { + if (renderPassAvalability.attachments.size() == textureAttachments.size() && + renderPassAvalability.depthAttachment == depthAttachment && + renderPassAvalability.sampleCountFlagBits == sampleCountFlagBits && + renderPassAvalability.isSwapChainPass == isSwapChainPass) + { + //Check frame definition + bool notEqual = false; + for (int i = 0; i < textureAttachments.size(); i++) { + if (textureAttachments[i] != renderPassAvalability.attachments[i]) { + notEqual = true; + break; + } + } + if (!notEqual) { + return renderPassAvalability.renderPass; + } + } + } + + std::vector attachmentFormats = {}; + + GFrameBufferVLK::iterateOverAttachments(textureAttachments, [&](int i, VkFormat textureFormat) { + attachmentFormats.push_back(textureFormat); + }); + VkFormat fbDepthFormat = findDepthFormat(); + + auto renderPass = std::make_shared(*this, + attachmentFormats, + findDepthFormat(), + sampleCountFlagBits, + false); + + RenderPassAvalabilityStruct avalabilityStruct; + avalabilityStruct.attachments = textureAttachments; + avalabilityStruct.depthAttachment = depthAttachment; + avalabilityStruct.renderPass = renderPass; + avalabilityStruct.sampleCountFlagBits = sampleCountFlagBits; + avalabilityStruct.isSwapChainPass = isSwapChainPass; + + m_createdRenderPasses.push_back(avalabilityStruct); + + return renderPass; +} + HPipelineVLK GDeviceVLK::createPipeline(HGVertexBufferBindings m_bindings, HGShaderPermutation shader, + std::shared_ptr renderPass, DrawElementMode element, int8_t backFaceCulling, int8_t triCCW, EGxBlendEnum blendMode, int8_t depthCulling, - int8_t depthWrite) { + int8_t depthWrite, + bool invertZ) { PipelineCacheRecord pipelineCacheRecord; pipelineCacheRecord.shader = shader; + pipelineCacheRecord.renderPass = renderPass; pipelineCacheRecord.element = element; pipelineCacheRecord.backFaceCulling = backFaceCulling; pipelineCacheRecord.triCCW = triCCW; pipelineCacheRecord.blendMode = blendMode; pipelineCacheRecord.depthCulling = depthCulling; pipelineCacheRecord.depthWrite = depthWrite; + pipelineCacheRecord.invertZ = invertZ; auto i = loadedPipeLines.find(pipelineCacheRecord); if (i != loadedPipeLines.end()) { @@ -1479,7 +1567,9 @@ HPipelineVLK GDeviceVLK::createPipeline(HGVertexBufferBindings m_bindings, } std::shared_ptr hgPipeline; - hgPipeline.reset(new GPipelineVLK(*this, m_bindings, shader, element, backFaceCulling, triCCW, blendMode, depthCulling, depthWrite)); + hgPipeline.reset(new GPipelineVLK(*this, m_bindings, renderPass, + shader, element, backFaceCulling, triCCW, blendMode, + depthCulling, depthWrite, invertZ)); std::weak_ptr weakPtr(hgPipeline); loadedPipeLines[pipelineCacheRecord] = weakPtr; @@ -1512,8 +1602,7 @@ void GDeviceVLK::internalDrawStageAndDeps(HDrawStage drawStage) { this->internalDrawStageAndDeps(drawStage->drawStageDependencies[i]); } - if (drawStage->meshesToRender == nullptr) - return; + this->setInvertZ(drawStage->invertedZ); if (drawStage->clearScreen) { clearColor[0] = drawStage->clearColor[0]; @@ -1523,86 +1612,189 @@ void GDeviceVLK::internalDrawStageAndDeps(HDrawStage drawStage) { int updateFrame = getUpdateFrameNumber(); auto commandBufferForFilling = renderCommandBuffers[updateFrame]; + //Default renderPass for rendering to screen framebuffers + std::shared_ptr renderPass = swapchainRenderPass; - VkBuffer lastIndexBuffer = VK_NULL_HANDLE; - VkBuffer lastVertexBuffer = VK_NULL_HANDLE; - int8_t lastIsScissorsEnabled = -1; + if (drawStage->target != nullptr) { + commandBufferForFilling = renderCommandBuffersForFrameBuffers[updateFrame]; + + GFrameBufferVLK *frameBufferVlk = dynamic_cast(drawStage->target.get()); + + renderPass = frameBufferVlk->m_renderPass; + + std::vector clearValues = renderPass->produceClearColorVec( + { clearColor[0], clearColor[1], clearColor[2] }, + drawStage->invertedZ ? 0.0f : 1.0f + ); + + VkRenderPassBeginInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.pNext = NULL; + renderPassInfo.renderPass = renderPass->getRenderPass(); + renderPassInfo.framebuffer = frameBufferVlk->m_frameBuffer; + renderPassInfo.renderArea.offset = {drawStage->viewPortDimensions.mins[0], drawStage->viewPortDimensions.mins[1]}; + renderPassInfo.renderArea.extent = {static_cast(drawStage->viewPortDimensions.maxs[0]), static_cast(drawStage->viewPortDimensions.maxs[1])}; + renderPassInfo.clearValueCount = clearValues.size(); + renderPassInfo.pClearValues = clearValues.data(); - uint32_t dynamicOffset[7] = {}; + vkCmdBeginRenderPass(commandBufferForFilling, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + } - VkViewport usualViewport; + std::array viewportsForThisStage; + + VkViewport &usualViewport = viewportsForThisStage[(int)ViewportType::vp_usual]; usualViewport.width = drawStage->viewPortDimensions.maxs[0]; usualViewport.height = drawStage->viewPortDimensions.maxs[1]; usualViewport.x = drawStage->viewPortDimensions.mins[0]; usualViewport.y = drawStage->viewPortDimensions.mins[1]; - usualViewport.minDepth = 0; - usualViewport.maxDepth = 0.990f; + if (!getInvertZ()) { + usualViewport.minDepth = 0; + usualViewport.maxDepth = 0.990f; + } else { + usualViewport.minDepth = 0.06f; + usualViewport.maxDepth = 1.0f; + } - VkViewport mapAreaViewport = usualViewport; - mapAreaViewport.minDepth = 0.991f; - mapAreaViewport.maxDepth = 0.996f; + VkViewport &mapAreaViewport = viewportsForThisStage[(int)ViewportType::vp_mapArea]; + mapAreaViewport = usualViewport; + if (!getInvertZ()) { + mapAreaViewport.minDepth = 0.991f; + mapAreaViewport.maxDepth = 0.996f; + } else { + mapAreaViewport.minDepth = 0.04f; + mapAreaViewport.maxDepth = 0.05f; + } - VkViewport skyBoxViewport = usualViewport; - skyBoxViewport.minDepth = 0.997f; - skyBoxViewport.maxDepth = 1.0f; + VkViewport &skyBoxViewport = viewportsForThisStage[(int)ViewportType::vp_skyBox]; + skyBoxViewport = usualViewport; + if (!getInvertZ()) { + skyBoxViewport.minDepth = 0.997f; + skyBoxViewport.maxDepth = 1.0f; + } else { + skyBoxViewport.minDepth = 0; + skyBoxViewport.maxDepth = 0.03f; + } //Set scissors VkRect2D defaultScissor = {}; defaultScissor.offset = {0, 0}; - defaultScissor.extent = swapChainExtent; + defaultScissor.extent = { + static_cast(drawStage->viewPortDimensions.maxs[0]), + static_cast(drawStage->viewPortDimensions.maxs[1]) + }; vkCmdSetScissor(commandBufferForFilling, 0, 1, &defaultScissor); //Set new viewport - enum class ViewportType {vp_usual, vp_mapArea, vp_skyBox}; - ViewportType lastViewPort = ViewportType::vp_usual; vkCmdSetViewport(commandBufferForFilling, 0, 1, &usualViewport); - std::shared_ptr lastPipeline = nullptr; + bool atLeastOneDrawCall = false; + if (drawStage->opaqueMeshes != nullptr) + atLeastOneDrawCall = drawMeshesInternal(drawStage, commandBufferForFilling, renderPass, + drawStage->opaqueMeshes, viewportsForThisStage, + defaultScissor) || atLeastOneDrawCall; + if (drawStage->transparentMeshes != nullptr) + atLeastOneDrawCall = drawMeshesInternal(drawStage, commandBufferForFilling, renderPass, + drawStage->transparentMeshes, viewportsForThisStage, + defaultScissor) || atLeastOneDrawCall; + + if (drawStage->target != nullptr) { + vkCmdEndRenderPass(commandBufferForFilling); + renderCommandBuffersForFrameBuffersNotNull[updateFrame] = renderCommandBuffersForFrameBuffersNotNull[updateFrame] || atLeastOneDrawCall; + } else { + renderCommandBuffersNotNull[updateFrame] = renderCommandBuffersNotNull[updateFrame] || atLeastOneDrawCall; + } + +} - auto iMeshes = drawStage->meshesToRender->meshes; +//Returns true if at least once command was written to buffer +bool GDeviceVLK::drawMeshesInternal( + const HDrawStage &drawStage, + VkCommandBuffer commandBufferForFilling, + std::shared_ptr renderPass, + const HMeshesToRender &meshes, + const std::array &viewportsForThisStage, + VkRect2D &defaultScissor) { - for (auto &mesh: iMeshes) { - auto *meshVLK = ((GMeshVLK *)mesh.get()); + int updateFrame = getUpdateFrameNumber(); + + ViewportType lastViewPort = ViewportType::vp_none; + + VkBuffer lastIndexBuffer = VK_NULL_HANDLE; + VkBuffer lastVertexBuffer = VK_NULL_HANDLE; + int8_t lastIsScissorsEnabled = -1; + std::shared_ptr lastPipeline = nullptr; +// uint32_t dynamicOffset[7] = {}; + + auto &iMeshes = meshes->meshes; + + auto dynamicOffsetPerMesh = std::vector>(iMeshes.size()); + auto uboIndPerMesh = std::vector(iMeshes.size()); + + tbb::parallel_for( + tbb::blocked_range(0, iMeshes.size(), 500), + [&](tbb::blocked_range r) { + for (size_t i = r.begin(); i != r.end(); ++i) { + auto *meshVLK = ((GMeshVLK *)iMeshes[i].get()); + auto *shaderVLK = ((GShaderPermutationVLK*)meshVLK->m_shader.get()); + uint32_t uboInd = 0; + auto *uboB = drawStage->sceneWideBlockVSPSChunk.get(); + if (uboB) { + dynamicOffsetPerMesh[i][uboInd++] = (uboB)->getOffset(); + } + + for (int k = 1; k < 6; k++) { + if (shaderVLK->hasBondUBO[k]) { + auto *uboB = (meshVLK->getUniformBuffer(k).get()); + if (uboB) { + dynamicOffsetPerMesh[i][uboInd++] = (uboB)->getOffset(); + } + } + } + uboIndPerMesh[i] = uboInd; + } + }, tbb::simple_partitioner()); + + bool atLeastOneDrawcall = false; + VkDeviceSize offsets[] = {0}; + + for (int i = 0; i < iMeshes.size(); i++) { + auto *meshVLK = ((GMeshVLK *)iMeshes[i].get()); +// auto *meshVLK = ((GMeshVLK *)mesh.get()); auto *binding = ((GVertexBufferBindingsVLK *)meshVLK->m_bindings.get()); auto *shaderVLK = ((GShaderPermutationVLK*)meshVLK->m_shader.get()); - uint32_t uboInd = 0; - auto *uboB = drawStage->sceneWideBlockVSPSChunk.get(); - if (uboB) { - dynamicOffset[uboInd++] = (uboB)->getOffset(); - } +// uint32_t uboInd = 0; +// auto *uboB = drawStage->sceneWideBlockVSPSChunk.get(); +// if (uboB) { +// dynamicOffset[uboInd++] = (uboB)->getOffset(); +// } +// +// for (int k = 1; k < 6; k++) { +// if (shaderVLK->hasBondUBO[k]) { +// auto *uboB = (meshVLK->getUniformBuffer(k).get()); +// if (uboB) { +// dynamicOffset[uboInd++] = (uboB)->getOffset(); +// } +// } +// } - for (int k = 1; k < 6; k++) { - if (shaderVLK->hasBondUBO[k]) { - auto *uboB = (meshVLK->getUniformBuffer(k).get()); - if (uboB) { - dynamicOffset[uboInd++] = (uboB)->getOffset(); - } - } - } + std::shared_ptr h_pipeLine = meshVLK->getPipeLineForRenderPass(renderPass, drawStage->invertedZ); - if (lastPipeline != meshVLK->hgPipelineVLK) { + if (lastPipeline != h_pipeLine) { vkCmdBindPipeline(commandBufferForFilling, VK_PIPELINE_BIND_POINT_GRAPHICS, - meshVLK->hgPipelineVLK->graphicsPipeline); + h_pipeLine->graphicsPipeline); - lastPipeline = meshVLK->hgPipelineVLK; + lastPipeline = h_pipeLine; } ViewportType newViewPort = meshVLK->m_isSkyBox ? ViewportType::vp_skyBox : ViewportType::vp_usual; if (lastViewPort != newViewPort) { - switch (newViewPort) { - case ViewportType::vp_usual: - vkCmdSetViewport(commandBufferForFilling, 0, 1, &usualViewport); - break; - case ViewportType::vp_skyBox: - vkCmdSetViewport(commandBufferForFilling, 0, 1, &skyBoxViewport); - break; - case ViewportType::vp_mapArea: - vkCmdSetViewport(commandBufferForFilling, 0, 1, &mapAreaViewport); - break; + if (viewportsForThisStage[+newViewPort].height > 32768) { + std::cout << "newViewPort = " << +newViewPort << std::endl; } + vkCmdSetViewport(commandBufferForFilling, 0, 1, &viewportsForThisStage[+newViewPort]); lastViewPort = newViewPort; } @@ -1624,7 +1816,7 @@ void GDeviceVLK::internalDrawStageAndDeps(HDrawStage drawStage) { auto indexBuffer = ((GIndexBufferVLK *)binding->m_indexBuffer.get())->g_hIndexBuffer; auto vertexBuffer = ((GVertexBufferVLK *)binding->m_bindings[0].vertexBuffer.get())->g_hVertexBuffer; - VkDeviceSize offsets[] = {0}; + if (lastIndexBuffer != indexBuffer) { vkCmdBindIndexBuffer(commandBufferForFilling, indexBuffer, 0, VK_INDEX_TYPE_UINT16); @@ -1641,52 +1833,94 @@ void GDeviceVLK::internalDrawStageAndDeps(HDrawStage drawStage) { auto uboDescSet = shaderVLK->uboDescriptorSets[updateFrame]->getDescSet(); auto imageDescSet = meshVLK->imageDescriptorSets[updateFrame]->getDescSet(); - renderCommandBuffersNull[updateFrame] = false; + atLeastOneDrawcall = true; + //UBO vkCmdBindDescriptorSets(commandBufferForFilling, VK_PIPELINE_BIND_POINT_GRAPHICS, - meshVLK->hgPipelineVLK->pipelineLayout, 0, 1, &uboDescSet, uboInd, &dynamicOffset[0]); + h_pipeLine->pipelineLayout, 0, 1, &uboDescSet, uboIndPerMesh[i], dynamicOffsetPerMesh[i].data()); //Image vkCmdBindDescriptorSets(commandBufferForFilling, VK_PIPELINE_BIND_POINT_GRAPHICS, - meshVLK->hgPipelineVLK->pipelineLayout, 1, 1, &imageDescSet, 0, 0); + h_pipeLine->pipelineLayout, 1, 1, &imageDescSet, 0, nullptr); vkCmdDrawIndexed(commandBufferForFilling, meshVLK->m_end, 1, meshVLK->m_start/2, 0, 0); } + + return atLeastOneDrawcall; } + void GDeviceVLK::drawStageAndDeps(HDrawStage drawStage) { int updateFrame = getUpdateFrameNumber(); - VkCommandBufferInheritanceInfo bufferInheritanceInfo; - bufferInheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; - bufferInheritanceInfo.pNext = nullptr; - bufferInheritanceInfo.renderPass = renderPass; - bufferInheritanceInfo.subpass = 0; - bufferInheritanceInfo.framebuffer = VK_NULL_HANDLE; - bufferInheritanceInfo.occlusionQueryEnable = false; - bufferInheritanceInfo.queryFlags = VK_NULL_HANDLE; - bufferInheritanceInfo.pipelineStatistics = VK_NULL_HANDLE; +// std::cout << "drawStageAndDeps: updateFrame = " << updateFrame << std::endl; - VkCommandBufferBeginInfo beginInfo = {}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; - beginInfo.pNext = NULL; - beginInfo.pInheritanceInfo = &bufferInheritanceInfo; + if (drawStage->target == nullptr && + ( + (drawStage->viewPortDimensions.maxs[0] != swapChainExtent.width) || + (drawStage->viewPortDimensions.maxs[1] != swapChainExtent.height) + ) + ) { + recreateSwapChain(); + } -// std::cout << "drawStageAndDeps: updateFrame = " << updateFrame << std::endl; vkWaitForFences(device, 1, &inFlightFences[updateFrame], VK_TRUE, std::numeric_limits::max()); //Update if (m_shaderDescriptorUpdateNeeded) { - for(auto shaderVLKRec : m_shaderPermutCache) { + for (auto shaderVLKRec : m_shaderPermutCache) { ((GShaderPermutationVLK *) shaderVLKRec.second.get())->updateDescriptorSet(updateFrame); } m_shaderDescriptorUpdateNeeded = false; } auto commandBufferForFilling = renderCommandBuffers[updateFrame]; - renderCommandBuffersNull[updateFrame] = true; - if (vkBeginCommandBuffer(commandBufferForFilling, &beginInfo) != VK_SUCCESS) { - throw std::runtime_error("failed to begin recording command buffer!"); + auto commandBufferForFillingFrameBuf = renderCommandBuffersForFrameBuffers[updateFrame]; + + renderCommandBuffersNotNull[updateFrame] = false; + renderCommandBuffersForFrameBuffersNotNull[updateFrame] = false; + + { + VkCommandBufferInheritanceInfo bufferInheritanceInfo; + bufferInheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + bufferInheritanceInfo.pNext = nullptr; + bufferInheritanceInfo.renderPass = swapchainRenderPass->getRenderPass(); + bufferInheritanceInfo.subpass = 0; + bufferInheritanceInfo.framebuffer = VK_NULL_HANDLE; + bufferInheritanceInfo.occlusionQueryEnable = false; + bufferInheritanceInfo.queryFlags = 0; + bufferInheritanceInfo.pipelineStatistics = 0; + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + beginInfo.pNext = NULL; + beginInfo.pInheritanceInfo = &bufferInheritanceInfo; + + if (vkBeginCommandBuffer(commandBufferForFilling, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("failed to begin recording command buffer!"); + } + } + + { + VkCommandBufferInheritanceInfo bufferInheritanceInfo; + bufferInheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + bufferInheritanceInfo.pNext = nullptr; + bufferInheritanceInfo.renderPass = VK_NULL_HANDLE; + bufferInheritanceInfo.subpass = 0; + bufferInheritanceInfo.framebuffer = VK_NULL_HANDLE; + bufferInheritanceInfo.occlusionQueryEnable = false; + bufferInheritanceInfo.queryFlags = 0; + bufferInheritanceInfo.pipelineStatistics = 0; + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + beginInfo.pNext = NULL; + beginInfo.pInheritanceInfo = &bufferInheritanceInfo; + + if (vkBeginCommandBuffer(commandBufferForFillingFrameBuf, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("failed to begin recording command buffer!"); + } } internalDrawStageAndDeps(drawStage); @@ -1694,4 +1928,190 @@ void GDeviceVLK::drawStageAndDeps(HDrawStage drawStage) { if (vkEndCommandBuffer(commandBufferForFilling) != VK_SUCCESS) { throw std::runtime_error("failed to record command buffer!"); } + if (vkEndCommandBuffer(commandBufferForFillingFrameBuf) != VK_SUCCESS) { + throw std::runtime_error("failed to record command buffer!"); + } +} + +void GDeviceVLK::initUploadThread() { + if (getIsAsynBuffUploadSupported()) { + createCommandPoolForUpload(); + createCommandBuffersForUpload(); + } +} + +HFrameBuffer GDeviceVLK::createFrameBuffer(int width, int height, std::vector attachments, + ITextureFormat depthAttachment, int multiSampleCnt, int frameNumber) { + + if (frameNumber > -1) { + for (auto &framebufAvalability : m_createdFrameBuffers) { + if ((framebufAvalability.frame < m_frameNumber) && + framebufAvalability.attachments.size() == attachments.size() && + framebufAvalability.depthAttachment == depthAttachment && + framebufAvalability.width == width && + framebufAvalability.height == height + ) { + //Check frame definition + bool notEqual = false; + for (int i = 0; i < attachments.size(); i++) { + if (attachments[i] != framebufAvalability.attachments[i]) { + notEqual = true; + break; + } + } + if (!notEqual) { + framebufAvalability.frame = m_frameNumber + frameNumber+3; + return framebufAvalability.frameBuffer; + } + } + } + } + + HFrameBuffer h_frameBuffer = std::make_shared(*this, attachments, depthAttachment, multiSampleCnt, width, height); + + if (frameNumber > -1) { + FramebufAvalabilityStruct avalabilityStruct; + avalabilityStruct.frameBuffer = h_frameBuffer; + avalabilityStruct.height = height; + avalabilityStruct.width = width; + avalabilityStruct.frame = m_frameNumber + frameNumber+3; + avalabilityStruct.attachments = attachments; + avalabilityStruct.depthAttachment = depthAttachment; + + m_createdFrameBuffers.push_back(avalabilityStruct); + } + + return h_frameBuffer; +} + +void GDeviceVLK::singleExecuteAndWait(std::function callback) { + //Allocate temporary command buffer + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer copyCmd; + + if (vkAllocateCommandBuffers(device, &allocInfo, ©Cmd) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate command buffers!"); + } + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + beginInfo.pNext = NULL; + beginInfo.pInheritanceInfo = NULL; + + ERR_GUARD_VULKAN(vkBeginCommandBuffer(copyCmd, &beginInfo)); + + callback(copyCmd); + + + if (copyCmd == VK_NULL_HANDLE) + { + return; + } + + ERR_GUARD_VULKAN(vkEndCommandBuffer(copyCmd)); + + VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = ©Cmd; + submitInfo.signalSemaphoreCount = 0; + submitInfo.pSignalSemaphores = nullptr; + + // Create fence to ensure that the command buffer has finished executing + VkFenceCreateInfo fenceInfo = {}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.pNext = NULL; + fenceInfo.flags = 0; + + VkFence fence; + ERR_GUARD_VULKAN(vkCreateFence(device, &fenceInfo, nullptr, &fence)); + // Submit to the queue + ERR_GUARD_VULKAN(vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence)); + // Wait for the fence to signal that command buffer has finished executing + ERR_GUARD_VULKAN(vkWaitForFences(device, 1, &fence, VK_TRUE, std::numeric_limits::max())); + + vkDestroyFence(device, fence, nullptr); + vkFreeCommandBuffers(device, commandPool, 1, ©Cmd); +} + +VkSampleCountFlagBits sampleCountToVkSampleCountFlagBits(uint8_t sampleCount) { + switch (sampleCount) { + case 1: + return VK_SAMPLE_COUNT_1_BIT; + case 2: + return VK_SAMPLE_COUNT_2_BIT; + case 4: + return VK_SAMPLE_COUNT_4_BIT; + case 8: + return VK_SAMPLE_COUNT_8_BIT; + case 16: + return VK_SAMPLE_COUNT_16_BIT; + case 32: + return VK_SAMPLE_COUNT_32_BIT; + case 64: + return VK_SAMPLE_COUNT_64_BIT; + default: + return VK_SAMPLE_COUNT_1_BIT; + } + return VK_SAMPLE_COUNT_1_BIT; +}; +static const constexpr uint8_t countFlagBitsToSampleCount(VkSampleCountFlagBits sampleCountBit) { + switch (sampleCountBit) { + case VK_SAMPLE_COUNT_1_BIT: + return 1; + case VK_SAMPLE_COUNT_2_BIT: + return 2; + case VK_SAMPLE_COUNT_4_BIT: + return 4; + case VK_SAMPLE_COUNT_8_BIT: + return 8; + case VK_SAMPLE_COUNT_16_BIT: + return 16; + case VK_SAMPLE_COUNT_32_BIT: + return 32; + case VK_SAMPLE_COUNT_64_BIT: + return 64; + default: + return 1; + } + + return 1; +} + + +int GDeviceVLK::getMaxSamplesCnt() { + if (maxMultiSample < 0) { + VkPhysicalDeviceProperties physicalDeviceProperties; + vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties); + + VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts; + if (counts & VK_SAMPLE_COUNT_64_BIT) { + maxMultiSample = countFlagBitsToSampleCount(VK_SAMPLE_COUNT_64_BIT); + } else if (counts & VK_SAMPLE_COUNT_32_BIT) { + maxMultiSample = countFlagBitsToSampleCount(VK_SAMPLE_COUNT_32_BIT); + } else if (counts & VK_SAMPLE_COUNT_16_BIT) { + maxMultiSample = countFlagBitsToSampleCount(VK_SAMPLE_COUNT_16_BIT); + } else if (counts & VK_SAMPLE_COUNT_8_BIT) { + maxMultiSample = countFlagBitsToSampleCount(VK_SAMPLE_COUNT_8_BIT); + } else if (counts & VK_SAMPLE_COUNT_4_BIT) { + maxMultiSample = countFlagBitsToSampleCount(VK_SAMPLE_COUNT_4_BIT); + } else if (counts & VK_SAMPLE_COUNT_2_BIT) { + maxMultiSample = countFlagBitsToSampleCount(VK_SAMPLE_COUNT_2_BIT); + } else if (counts & VK_SAMPLE_COUNT_1_BIT) { + maxMultiSample = countFlagBitsToSampleCount(VK_SAMPLE_COUNT_1_BIT); + } else { + maxMultiSample = countFlagBitsToSampleCount(VK_SAMPLE_COUNT_1_BIT); + } + } + + return maxMultiSample; +} + +VkSampleCountFlagBits GDeviceVLK::getMaxSamplesBit() { + return sampleCountToVkSampleCountFlagBits(getMaxSamplesCnt()); } diff --git a/wowViewerLib/src/gapi/vulkan/GDeviceVulkan.h b/wowViewerLib/src/gapi/vulkan/GDeviceVulkan.h index 2c16d4654..060455192 100644 --- a/wowViewerLib/src/gapi/vulkan/GDeviceVulkan.h +++ b/wowViewerLib/src/gapi/vulkan/GDeviceVulkan.h @@ -7,6 +7,7 @@ #include #include +#include class GVertexBufferVLK; @@ -21,6 +22,7 @@ class GM2MeshVLK; class GOcclusionQueryVLK; class GParticleMeshVLK; class GPipelineVLK; +class GRenderPassVLK; class GDescriptorPoolVLK; typedef std::shared_ptr HPipelineVLK; @@ -34,10 +36,12 @@ class gMeshTemplate; #include "../interface/IDevice.h" #include "descriptorSets/GDescriptorSet.h" #include "descriptorSets/GDescriptorPoolVLK.h" +#include "../../engine/algorithms/FrameCounter.h" #include +VkSampleCountFlagBits sampleCountToVkSampleCountFlagBits(uint8_t sampleCount); -class GDeviceVLK : public IDevice { +class GDeviceVLK : public IDevice, public std::enable_shared_from_this { struct QueueFamilyIndices { std::optional graphicsFamily; std::optional presentFamily; @@ -55,24 +59,29 @@ class GDeviceVLK : public IDevice { }; public: + enum class ViewportType {vp_none = -1, vp_usual = 0, vp_mapArea = 1, vp_skyBox = 2, vp_MAX = 3}; + explicit GDeviceVLK(vkCallInitCallback * callBacks); ~GDeviceVLK() override = default;; + void initialize() override; + void reset() override; unsigned int getFrameNumber() override { return m_frameNumber; }; unsigned int getUpdateFrameNumber() override; unsigned int getCullingFrameNumber() override; + unsigned int getOcclusionFrameNumber() override; unsigned int getDrawFrameNumber() override; + bool getIsRenderbufferSupported() override {return true;} void increaseFrameNumber() override; bool getIsAsynBuffUploadSupported() override { return true; } - int getMaxSamplesCnt() override { - return 1; - } + int getMaxSamplesCnt() override; + VkSampleCountFlagBits getMaxSamplesBit(); bool canUploadInSeparateThread() { return uploadQueue != graphicsQueue; @@ -96,13 +105,23 @@ class GDeviceVLK : public IDevice { void startUpdateForNextFrame() override; void endUpdateForNextFrame() override; - void updateBuffers(std::vector &meshes, std::vector additionalChunks) override; + void updateBuffers(std::vector*> &bufferChunks, std::vector &frameDepedantData) override; void uploadTextureForMeshes(std::vector &meshes) override; void drawMeshes(std::vector &meshes) override; void drawStageAndDeps(HDrawStage drawStage) override; + bool wasTexturesUploaded() override { + return m_texturesWereUploaded; + }; + // void drawM2Meshes(std::vector &meshes); bool getIsVulkanAxisSystem() override {return true;} + + void initUploadThread() override; public: + double getWaitForUpdate() override { + return this->waitInDrawStageAndDeps.getTimePerFrame(); + } + std::shared_ptr getShader(std::string shaderName, void *permutationDescriptor) override; HGUniformBuffer createUniformBuffer(size_t size) override; @@ -112,23 +131,30 @@ class GDeviceVLK : public IDevice { HGVertexBufferBindings createVertexBufferBindings() override; HGTexture createBlpTexture(HBlpTexture &texture, bool xWrapTex, bool yWrapTex) override; - HGTexture createTexture() override; + HGTexture createTexture(bool xWrapTex, bool yWrapTex) override; HGTexture getWhiteTexturePixel() override { return m_whitePixelTexture; }; HGMesh createMesh(gMeshTemplate &meshTemplate) override; HGM2Mesh createM2Mesh(gMeshTemplate &meshTemplate) override; HGParticleMesh createParticleMesh(gMeshTemplate &meshTemplate) override; HGPUFence createFence() override; - HFrameBuffer createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int frameNumber) override {return nullptr;}; + HFrameBuffer createFrameBuffer(int width, int height, std::vector attachments, ITextureFormat depthAttachment, int multiSampleCnt, int frameNumber) override ; HPipelineVLK createPipeline(HGVertexBufferBindings m_bindings, HGShaderPermutation shader, + std::shared_ptr renderPass, DrawElementMode element, int8_t backFaceCulling, int8_t triCCW, EGxBlendEnum blendMode, int8_t depthCulling, - int8_t depthWrite); + int8_t depthWrite, + bool invertZ); + + std::shared_ptr getRenderPass(std::vector textureAttachments, + ITextureFormat depthAttachment, + VkSampleCountFlagBits sampleCountFlagBits, + bool isSwapChainPass); HGOcclusionQuery createQuery(HGMesh boundingBoxMesh) override; @@ -143,7 +169,8 @@ class GDeviceVLK : public IDevice { virtual void setClearScreenColor(float r, float g, float b) override; virtual void setViewPortDimensions(float x, float y, float width, float height) override; - void setInvertZ(bool value) override {m_isInvertZ = value;}; + void setInvertZ(bool value) override {m_isInvertZ = true;}; + bool getInvertZ() { return m_isInvertZ;}; std::shared_ptr createDescriptorSet(VkDescriptorSetLayout layout, int uniforms, int images); @@ -151,13 +178,14 @@ class GDeviceVLK : public IDevice { virtual VkDevice getVkDevice() { return device; }; + virtual VkPhysicalDevice getVkPhysicalDevice() { + return physicalDevice; + }; virtual VkExtent2D getCurrentExtent() { return swapChainExtent; }; - virtual VkRenderPass getRenderPass() { - return renderPass; - }; -// int currentFrameSemaphore = 0; + + // int currentFrameSemaphore = 0; bool framebufferResized = false; VmaAllocator getVMAAllocator() { @@ -196,13 +224,19 @@ class GDeviceVLK : public IDevice { std::function callback; }; - void addDeallocationRecord(std::function callback) { + std::mutex m_listOfDeallocatorsAccessMtx; + + void addDeallocationRecord(std::function callback) override { + std::lock_guard lock(m_listOfDeallocatorsAccessMtx); DeallocationRecord dr; dr.frameNumberToDoAt = m_frameNumber+4; dr.callback = callback; listOfDeallocators.push_back(dr); }; + VkFormat findDepthFormat(); + + void singleExecuteAndWait(std::function callback); private: void drawMesh(HGMesh &hmesh); void internalDrawStageAndDeps(HDrawStage drawStage); @@ -221,18 +255,28 @@ class GDeviceVLK : public IDevice { void recreateSwapChain(); void createCommandPool(); + void createCommandPoolForUpload(); void createCommandBuffers(); + void createCommandBuffersForUpload(); void createSyncObjects(); void createColorResources(); void createDepthResources(); - VkFormat findDepthFormat(); + VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory, VkImageLayout vkLaylout); VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels); + bool drawMeshesInternal( + const HDrawStage &drawStage, + VkCommandBuffer commandBufferForFilling, + std::shared_ptr renderPass, + const HMeshesToRender &iMeshes, + const std::array &viewportsForThisStage, + VkRect2D &defaultScissor); + protected: struct BlpCacheRecord { BlpTexture* texture; @@ -257,23 +301,27 @@ class GDeviceVLK : public IDevice { struct PipelineCacheRecord { HGShaderPermutation shader; + std::shared_ptr renderPass; DrawElementMode element; int8_t backFaceCulling; int8_t triCCW; EGxBlendEnum blendMode; int8_t depthCulling; int8_t depthWrite; + bool invertZ; bool operator==(const PipelineCacheRecord &other) const { return (shader == other.shader) && + (renderPass == other.renderPass) && (element == other.element) && (backFaceCulling == other.backFaceCulling) && (triCCW == other.triCCW) && (blendMode == other.blendMode) && (depthCulling == other.depthCulling) && - (depthWrite == other.depthWrite); + (depthWrite == other.depthWrite) && + (invertZ == other.invertZ); }; }; @@ -281,6 +329,7 @@ class GDeviceVLK : public IDevice { std::size_t operator()(const PipelineCacheRecord& k) const { using std::hash; return hash{}(k.shader.get()) ^ + hash{}(k.renderPass.get()) ^ (hash{}(k.backFaceCulling) << 2) ^ (hash{}(k.triCCW) << 4) ^ (hash{}(k.depthCulling) << 8) ^ @@ -293,6 +342,8 @@ class GDeviceVLK : public IDevice { VkDebugUtilsMessengerEXT debugMessenger; + int threadCount = 1; + FrameCounter waitInDrawStageAndDeps; QueueFamilyIndices indices; VkInstance vkInstance; @@ -311,10 +362,11 @@ class GDeviceVLK : public IDevice { std::vector swapChainImageViews; std::vector swapChainFramebuffers; - VkRenderPass renderPass; + std::shared_ptr swapchainRenderPass; VkCommandPool commandPool; + VkCommandPool commandPoolForImageTransfer; VkCommandPool renderCommandPool; VkCommandPool uploadCommandPool; @@ -324,7 +376,9 @@ class GDeviceVLK : public IDevice { std::vector commandBuffers; std::vector renderCommandBuffers; - std::vector renderCommandBuffersNull; + std::vector renderCommandBuffersNotNull; + std::vector renderCommandBuffersForFrameBuffers; + std::vector renderCommandBuffersForFrameBuffersNotNull; std::vector uploadCommandBuffers; std::vector textureTransferCommandBuffers; std::vector textureTransferCommandBufferNull; @@ -361,6 +415,7 @@ class GDeviceVLK : public IDevice { int8_t m_triCCW = -1; int maxUniformBufferSize = -1; int uniformBufferOffsetAlign = -1; + int maxMultiSample = -1; float m_anisotropicLevel = 0.0; bool m_isInvertZ = false; EGxBlendEnum m_lastBlendMode = EGxBlendEnum::GxBlend_UNDEFINED; @@ -382,6 +437,8 @@ class GDeviceVLK : public IDevice { HGTexture m_blackPixelTexture = nullptr; HGTexture m_whitePixelTexture = nullptr; + + bool m_texturesWereUploaded = false; protected: //Caches std::unordered_map m_shaderPermutCache; @@ -390,7 +447,7 @@ class GDeviceVLK : public IDevice { HGUniformBuffer m_uniformBufferForUpload; }; - FrameUniformBuffers m_UBOFrames[4]; + std::array m_UBOFrames; std::vector aggregationBufferForUpload = std::vector(1024*1024); @@ -398,6 +455,18 @@ class GDeviceVLK : public IDevice { int uniformBuffersCreated = 0; bool attachmentsReady = false; + + std::vector m_createdFrameBuffers; + + struct RenderPassAvalabilityStruct { + std::vector attachments; + ITextureFormat depthAttachment; + std::shared_ptr renderPass; + VkSampleCountFlagBits sampleCountFlagBits; + bool isSwapChainPass; + }; + + std::vector m_createdRenderPasses; }; diff --git a/wowViewerLib/src/gapi/vulkan/GFrameBufferVLK.cpp b/wowViewerLib/src/gapi/vulkan/GFrameBufferVLK.cpp new file mode 100644 index 000000000..a0f93bd65 --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/GFrameBufferVLK.cpp @@ -0,0 +1,367 @@ +// +// Created by Deamon on 12/11/2020. +// + +#include +#include "GFrameBufferVLK.h" +#include "textures/GTextureVLK.h" +#include "GRenderPassVLK.h" + +void GFrameBufferVLK::iterateOverAttachments(std::vector textureAttachments, std::function callback) { + for (int i = 0; i < textureAttachments.size(); i++) { + if (textureAttachments[i] == ITextureFormat::itNone) continue; + if (textureAttachments[i] == ITextureFormat::itDepth32) continue; + + VkFormat textureFormat; + if (textureAttachments[i] == ITextureFormat::itRGBA) { + textureFormat = VK_FORMAT_R8G8B8A8_UNORM; + } else if (textureAttachments[i] == ITextureFormat::itRGBAFloat32) { + textureFormat = VK_FORMAT_R16G16B16_SFLOAT; + } + + callback(i, textureFormat); + } +} + +GFrameBufferVLK::GFrameBufferVLK(IDevice &device, std::vector textureAttachments, + ITextureFormat depthAttachment, + int multiSampleCnt, + int width, int height) : mdevice(dynamic_cast(device)), + m_height(height), m_width(width), + m_multiSampleCnt(multiSampleCnt){ + + std::vector attachments; + + //Encapsulated for loop + iterateOverAttachments(textureAttachments, [&](int i, VkFormat textureFormat) { + std::shared_ptr h_texture; + h_texture.reset(new GTextureVLK( + mdevice, + width, height, + false, false, + false, + textureFormat, + sampleCountToVkSampleCountFlagBits(multiSampleCnt), + 1, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + )); + attachmentTextures.push_back(h_texture); + attachments.push_back(h_texture->texture.view); + attachmentFormats.push_back(textureFormat); + + if (multiSampleCnt > 1) { + std::shared_ptr h_texture; + h_texture.reset(new GTextureVLK( + mdevice, + width, height, + false, false, + false, + textureFormat, + VK_SAMPLE_COUNT_1_BIT, + 1, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + )); + attachmentTextures.push_back(h_texture); + attachments.push_back(h_texture->texture.view); + } + + + }); + + //Depth attachment + { + // Find a suitable depth format + VkFormat fbDepthFormat = mdevice.findDepthFormat(); + + std::shared_ptr h_depthTexture; + h_depthTexture.reset(new GTextureVLK( + mdevice, + width, height, + false, false, + true, + fbDepthFormat, + sampleCountToVkSampleCountFlagBits(multiSampleCnt), + 1, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + )); + + depthTexture = h_depthTexture; + attachments.push_back(h_depthTexture->texture.view); + } + + m_renderPass = mdevice.getRenderPass(textureAttachments, depthAttachment, sampleCountToVkSampleCountFlagBits(multiSampleCnt), false); + + VkFramebufferCreateInfo fbufCreateInfo = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; + fbufCreateInfo.pNext = nullptr; + fbufCreateInfo.flags = 0; + fbufCreateInfo.renderPass = m_renderPass->getRenderPass(); + fbufCreateInfo.attachmentCount = attachments.size(); + fbufCreateInfo.pAttachments = attachments.data(); + fbufCreateInfo.width = width; + fbufCreateInfo.height = height; + fbufCreateInfo.layers = 1; + + ERR_GUARD_VULKAN(vkCreateFramebuffer(mdevice.getVkDevice(), &fbufCreateInfo, nullptr, &m_frameBuffer)); +} + +GFrameBufferVLK::~GFrameBufferVLK() { + auto *l_device = &mdevice; + auto l_frameBuffer = m_frameBuffer; + mdevice.addDeallocationRecord([l_device, l_frameBuffer]() -> void { + vkDestroyFramebuffer(l_device->getVkDevice(), l_frameBuffer, nullptr); + }); +} + +void GFrameBufferVLK::readRGBAPixels(int x, int y, int width, int height, void *outputdata) { +// Check blit support for source and destination + VkFormatProperties formatProps; + + VkPhysicalDevice physicalDevice = mdevice.getVkPhysicalDevice(); + + int attachmentIndex = 0; + + VkFormat colorFormat = attachmentFormats[attachmentIndex]; + bool supportsBlit = false; + + // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) + vkGetPhysicalDeviceFormatProperties(physicalDevice, colorFormat, &formatProps); + if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { + std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl; + supportsBlit = false; + } + + // Check if the device supports blitting to linear images + vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_R8G8B8A8_UNORM, &formatProps); + if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { + //std::cerr << "Device does not support blitting to linear tiled images, using copy instead of blit!" << std::endl; + supportsBlit = false; + } + + // Source for the copy is the last rendered swapchain image + VkImage srcImage = ((GTextureVLK *)(getAttachment(attachmentIndex).get()))->texture.image; + + // Create the linear tiled destination image to copy to and to read the memory from + VkImageCreateInfo imageCreateCI = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + imageCreateCI.imageType = VK_IMAGE_TYPE_2D; + // Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ + imageCreateCI.format = VK_FORMAT_R8G8B8A8_UNORM; + imageCreateCI.extent.width = width; + imageCreateCI.extent.height = height; + imageCreateCI.extent.depth = 1; + imageCreateCI.arrayLayers = 1; + imageCreateCI.mipLevels = 1; + imageCreateCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateCI.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateCI.tiling = VK_IMAGE_TILING_LINEAR; + imageCreateCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + // Create the image + VkImage dstImage; + + VmaAllocationCreateInfo allocImageCreateInfo = {}; + allocImageCreateInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; + + VmaAllocation imageAllocation = VK_NULL_HANDLE; + VmaAllocationInfo imageAllocationInfo = {}; + + vmaCreateImage(mdevice.getVMAAllocator(), &imageCreateCI, &allocImageCreateInfo, &dstImage, + &imageAllocation, &imageAllocationInfo); + + // Do the actual blit from the swapchain image to our host visible destination image + mdevice.singleExecuteAndWait([&](VkCommandBuffer copyCmd) { + // Transition destination image to transfer destination layout + { + VkImageSubresourceRange subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + + VkImageMemoryBarrier imageMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + imageMemoryBarrier.subresourceRange = subresourceRange; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.image = dstImage; + imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier( + copyCmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &imageMemoryBarrier); + } + + // Transition swapchain image from present to transfer source layout + { + VkImageSubresourceRange subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + + VkImageMemoryBarrier imageMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + imageMemoryBarrier.subresourceRange = subresourceRange; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.image = srcImage; + imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier( + copyCmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &imageMemoryBarrier); + } + + // If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB) + if (supportsBlit) + { + // Define the region to blit (we will blit the whole swapchain image) + VkOffset3D blitSize; + blitSize.x = width; + blitSize.y = height; + blitSize.z = 1; + VkImageBlit imageBlitRegion{}; + imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBlitRegion.srcSubresource.layerCount = 1; + imageBlitRegion.srcOffsets[1] = blitSize; + imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBlitRegion.dstSubresource.layerCount = 1; + imageBlitRegion.dstOffsets[1] = blitSize; + + // Issue the blit command + vkCmdBlitImage( + copyCmd, + srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &imageBlitRegion, + VK_FILTER_NEAREST); + } + else + { + // Otherwise use image copy (requires us to manually flip components) + VkImageCopy imageCopyRegion{}; + imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopyRegion.srcSubresource.layerCount = 1; + imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopyRegion.dstSubresource.layerCount = 1; + imageCopyRegion.extent.width = width; + imageCopyRegion.extent.height = height; + imageCopyRegion.extent.depth = 1; + + // Issue the copy command + vkCmdCopyImage( + copyCmd, + srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &imageCopyRegion); + } + + // Transition destination image to general layout, which is the required layout for mapping the image memory later on + { + VkImageSubresourceRange subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + + VkImageMemoryBarrier imageMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + imageMemoryBarrier.subresourceRange = subresourceRange; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + imageMemoryBarrier.image = dstImage; + imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier( + copyCmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &imageMemoryBarrier); + } + + // Transition back the swap chain image after the blit is done + { + VkImageSubresourceRange subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + + VkImageMemoryBarrier imageMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + imageMemoryBarrier.subresourceRange = subresourceRange; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageMemoryBarrier.image = srcImage; + imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier( + copyCmd, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &imageMemoryBarrier); + } + }); + + + // Get layout of the image (including row pitch) + VkImageSubresource subResource { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; + VkSubresourceLayout subResourceLayout; + vkGetImageSubresourceLayout(mdevice.getVkDevice(), dstImage, &subResource, &subResourceLayout); + + // Map image memory so we can start copying from it + const char* data; + vmaMapMemory(mdevice.getVMAAllocator(), imageAllocation, (void**)&data); + data += subResourceLayout.offset; + + // If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components + bool colorSwizzle = false; + // Check if source is BGR + // Note: Not complete, only contains most common and basic BGR surface formats for demonstration purposes + if (!supportsBlit) + { + std::vector formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM }; + colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), colorFormat) != formatsBGR.end()); + } + + //Flip image from vulkan + int lineLenInBytes = 4 * width; + if (lineLenInBytes != subResourceLayout.rowPitch) { + lineLenInBytes = subResourceLayout.rowPitch; + } + for (int i = 0; i < height; i++) { + std::memcpy((uint8_t *)outputdata + ((height - i - 1) * (4 * width)), (uint8_t *) data + (i *lineLenInBytes), (4 * width)); + } + + vmaUnmapMemory(mdevice.getVMAAllocator(), imageAllocation); + vmaDestroyImage(mdevice.getVMAAllocator(), dstImage, imageAllocation); +} + +HGTexture GFrameBufferVLK::getAttachment(int index) { + //If multisampling is active - return only resolved textures + if (m_multiSampleCnt > 1) { + index = 2*index+1; + } + + return attachmentTextures[index]; +} + +HGTexture GFrameBufferVLK::getDepthTexture() { + return depthTexture; +} + +void GFrameBufferVLK::bindFrameBuffer() { + +} + +void GFrameBufferVLK::copyRenderBufferToTexture() { + +} diff --git a/wowViewerLib/src/gapi/vulkan/GFrameBufferVLK.h b/wowViewerLib/src/gapi/vulkan/GFrameBufferVLK.h new file mode 100644 index 000000000..ceb09c01e --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/GFrameBufferVLK.h @@ -0,0 +1,47 @@ +// +// Created by Deamon on 12/11/2020. +// + +#ifndef AWEBWOWVIEWERCPP_GFRAMEBUFFERVLK_H +#define AWEBWOWVIEWERCPP_GFRAMEBUFFERVLK_H + +#include +#include "../interface/IFrameBuffer.h" +#include "../interface/textures/ITexture.h" +#include "GDeviceVulkan.h" + +class GFrameBufferVLK : public IFrameBuffer { +public: + GFrameBufferVLK(IDevice &device, std::vector textureAttachments, ITextureFormat depthAttachment, int multiSampleCnt, int width, int height); + ~GFrameBufferVLK() override; + + void readRGBAPixels(int x, int y, int width, int height, void *data) override; + HGTexture getAttachment(int index) override; + HGTexture getDepthTexture() override; + void bindFrameBuffer() override; + void copyRenderBufferToTexture() override; + + std::shared_ptr m_renderPass; + VkFramebuffer m_frameBuffer; + + static void iterateOverAttachments(std::vector textureAttachments, std::function callback); + + +private: + GDeviceVLK &mdevice; + + std::vector attachmentTextures; + HGTexture depthTexture; + + //Used only in readRGBAPixels function + std::vector attachmentFormats; + + int m_multiSampleCnt; + + int m_width = 0; + int m_height = 0; +}; + + + +#endif //AWEBWOWVIEWERCPP_GFRAMEBUFFERVLK_H diff --git a/wowViewerLib/src/gapi/vulkan/GPipelineVLK.cpp b/wowViewerLib/src/gapi/vulkan/GPipelineVLK.cpp index 993d37efa..d9443b097 100644 --- a/wowViewerLib/src/gapi/vulkan/GPipelineVLK.cpp +++ b/wowViewerLib/src/gapi/vulkan/GPipelineVLK.cpp @@ -6,6 +6,7 @@ #include "GPipelineVLK.h" #include "shaders/GShaderPermutationVLK.h" #include +#include "GRenderPassVLK.h" struct BlendModeDescVLK { bool blendModeEnable; @@ -34,13 +35,15 @@ BlendModeDescVLK blendModesVLK[(int)EGxBlendEnum::GxBlend_MAX] = { GPipelineVLK::GPipelineVLK(IDevice &device, HGVertexBufferBindings m_bindings, + std::shared_ptr renderPass, HGShaderPermutation shader, DrawElementMode element, int8_t backFaceCulling, int8_t triCCW, EGxBlendEnum blendMode, int8_t depthCulling, - int8_t depthWrite) : m_device(dynamic_cast(device)) { + int8_t depthWrite, + bool invertZ) : m_device(dynamic_cast(device)) { GVertexBufferBindingsVLK* bufferBindingsVlk = dynamic_cast(m_bindings.get()); @@ -57,12 +60,14 @@ GPipelineVLK::GPipelineVLK(IDevice &device, GShaderPermutationVLK* shaderVLK = reinterpret_cast(shader.get()); createPipeline(shaderVLK, + renderPass, element, backFaceCulling, triCCW, blendMode, depthCulling, depthWrite, + invertZ, vertexBindingDescriptions, vertexAttributeDescriptions); } @@ -73,18 +78,19 @@ GPipelineVLK::~GPipelineVLK() { void GPipelineVLK::createPipeline( GShaderPermutationVLK *shaderVLK, + std::shared_ptr renderPass, DrawElementMode m_element, int8_t m_backFaceCulling, int8_t m_triCCW, EGxBlendEnum m_blendMode, int8_t m_depthCulling, int8_t m_depthWrite, + bool invertZ, const std::vector &vertexBindingDescriptions, const std::vector &vertexAttributeDescriptions) { auto swapChainExtent = m_device.getCurrentExtent(); - auto renderPass = m_device.getRenderPass(); //Create Graphic pipeline for Vulkan @@ -170,7 +176,7 @@ void GPipelineVLK::createPipeline( VkPipelineMultisampleStateCreateInfo multisampling = {}; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.rasterizationSamples = renderPass->getSampleCountBit(); VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; colorBlendAttachment.colorWriteMask = @@ -200,7 +206,7 @@ void GPipelineVLK::createPipeline( depthStencil.flags = 0; depthStencil.depthTestEnable = m_depthCulling ? VK_TRUE : VK_FALSE; depthStencil.depthWriteEnable = m_depthWrite ? VK_TRUE : VK_FALSE; - depthStencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthStencil.depthCompareOp = invertZ ? VK_COMPARE_OP_GREATER_OR_EQUAL : VK_COMPARE_OP_LESS_OR_EQUAL; depthStencil.depthBoundsTestEnable = VK_FALSE; depthStencil.stencilTestEnable = VK_FALSE; @@ -243,7 +249,7 @@ void GPipelineVLK::createPipeline( pipelineInfo.pDepthStencilState = &depthStencil; pipelineInfo.pDynamicState = &dynamicState; pipelineInfo.layout = pipelineLayout; - pipelineInfo.renderPass = renderPass; + pipelineInfo.renderPass = renderPass->getRenderPass(); pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; diff --git a/wowViewerLib/src/gapi/vulkan/GPipelineVLK.h b/wowViewerLib/src/gapi/vulkan/GPipelineVLK.h index 6413b84c7..c7276669b 100644 --- a/wowViewerLib/src/gapi/vulkan/GPipelineVLK.h +++ b/wowViewerLib/src/gapi/vulkan/GPipelineVLK.h @@ -14,23 +14,27 @@ class GPipelineVLK { public: explicit GPipelineVLK(IDevice &m_device, HGVertexBufferBindings m_bindings, + std::shared_ptr renderPass, HGShaderPermutation shader, DrawElementMode element, int8_t backFaceCulling, int8_t triCCW, EGxBlendEnum blendMode, int8_t depthCulling, - int8_t depthWrite); + int8_t depthWrite, + bool invertZ); ~GPipelineVLK(); void createPipeline( GShaderPermutationVLK *shaderVLK, + std::shared_ptr renderPass, DrawElementMode m_element, int8_t m_backFaceCulling, int8_t m_triCCW, EGxBlendEnum m_blendMode, int8_t m_depthCulling, int8_t m_depthWrite, + bool invertZ, const std::vector &vertexBindingDescriptions, const std::vector &vertexAttributeDescriptions); diff --git a/wowViewerLib/src/gapi/vulkan/GRenderPassVLK.cpp b/wowViewerLib/src/gapi/vulkan/GRenderPassVLK.cpp new file mode 100644 index 000000000..b7718e968 --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/GRenderPassVLK.cpp @@ -0,0 +1,160 @@ +// +// Created by Deamon on 12/14/2020. +// + +#include "GRenderPassVLK.h" + +GRenderPassVLK::GRenderPassVLK(GDeviceVLK &device, std::vector textureAttachments, VkFormat depthAttachmentFormat, + VkSampleCountFlagBits sampleCountBit, bool isSwapChainPass) : mdevice(device) { + m_sampleCountBit = sampleCountBit; + + std::vector attachments; + std::vector colorAttachmentsResolves; + std::vector colorReferences; + std::vector colorResolveReferences; + + int attachmentIndex = 0; + for (int i = 0; i < textureAttachments.size(); i++) { + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = textureAttachments[i]; + colorAttachment.samples = sampleCountBit; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = isSwapChainPass ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + attachments.push_back(colorAttachment); + if (attachments.size() == 1) { + attachmentTypes.push_back(AttachmentType::atColor); + } else { + attachmentTypes.push_back(AttachmentType::atData); + } + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = attachmentIndex++; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + colorReferences.push_back(colorAttachmentRef); + + //Add resolves if multisampling is on + if (sampleCountBit != VK_SAMPLE_COUNT_1_BIT) { + VkAttachmentDescription colorAttachmentResolve{}; + colorAttachmentResolve.format = textureAttachments[i]; + colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachmentResolve.finalLayout = isSwapChainPass ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + attachments.push_back(colorAttachmentResolve); + attachmentTypes.push_back(AttachmentType::atData); + + VkAttachmentReference colorAttachmentResolveRef{}; + colorAttachmentResolveRef.attachment = attachmentIndex++; + colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colorResolveReferences.push_back(colorAttachmentResolveRef); + } + } + + VkAttachmentDescription depthAttachment = {}; + depthAttachment.format = depthAttachmentFormat; + depthAttachment.samples = sampleCountBit; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + attachments.push_back(depthAttachment); + attachmentTypes.push_back(AttachmentType::atDepth); + + VkAttachmentReference depthAttachmentRef = {}; + depthAttachmentRef.attachment = attachmentIndex++; + depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = colorReferences.size(); + subpass.pColorAttachments = colorReferences.data(); + subpass.pResolveAttachments = colorResolveReferences.data(); + subpass.pDepthStencilAttachment = &depthAttachmentRef; + + std::vector dependencies; + if (isSwapChainPass) { + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + dependencies.push_back(dependency); + } else { + //Assume it's framebuffer pass + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependencies.push_back(dependency); + + dependency = {}; + dependency.srcSubpass = 0; + dependency.dstSubpass = VK_SUBPASS_EXTERNAL; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependency.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependencies.push_back(dependency); + } + + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.pNext = nullptr; + renderPassInfo.attachmentCount = attachments.size(); + renderPassInfo.pAttachments = attachments.data(); + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = dependencies.size(); + renderPassInfo.pDependencies = dependencies.data(); + + if (vkCreateRenderPass(device.getVkDevice(), &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw std::runtime_error("failed to create render pass!"); + } +} + +VkSampleCountFlagBits GRenderPassVLK::getSampleCountBit() { + return m_sampleCountBit; +} + +VkRenderPass GRenderPassVLK::getRenderPass() { + return renderPass; +} + +std::vector GRenderPassVLK::produceClearColorVec(std::array colorClearColor, float depthClear) { + std::vector result; + for(int i = 0; i < attachmentTypes.size(); i++) { + VkClearValue clearValue; + if (attachmentTypes[i] == AttachmentType::atColor) { + clearValue.color = {colorClearColor[0], colorClearColor[1], colorClearColor[2], 0.f}; + } else if (attachmentTypes[i] == AttachmentType::atData) { + clearValue.color = {0,0,0,0}; + } else if (attachmentTypes[i] == AttachmentType::atDepth) { + clearValue.depthStencil = {depthClear,0}; + } + result.push_back(clearValue); + } + + return result; +} diff --git a/wowViewerLib/src/gapi/vulkan/GRenderPassVLK.h b/wowViewerLib/src/gapi/vulkan/GRenderPassVLK.h new file mode 100644 index 000000000..3b0ad6dd2 --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/GRenderPassVLK.h @@ -0,0 +1,39 @@ +// +// Created by Deamon on 12/14/2020. +// + +#ifndef AWEBWOWVIEWERCPP_GRENDERPASSVLK_H +#define AWEBWOWVIEWERCPP_GRENDERPASSVLK_H + +#include "GDeviceVulkan.h" +#include + +class GRenderPassVLK { +public: + GRenderPassVLK(GDeviceVLK &device, std::vector textureAttachments, + VkFormat depthAttachment, VkSampleCountFlagBits sampleCountBit, + bool isSwapChainPass); + + VkSampleCountFlagBits getSampleCountBit(); + VkRenderPass getRenderPass(); + + std::vector produceClearColorVec(std::array colorClearColor, float depthClear); + +private: + VkSampleCountFlagBits m_sampleCountBit; + GDeviceVLK &mdevice; + VkRenderPass renderPass; + + enum class AttachmentType { + atColor, + atData, + atDepth + }; + + //Is used to fill proper clearColor vector + std::vector attachmentTypes; + +}; + + +#endif //AWEBWOWVIEWERCPP_GRENDERPASSVLK_H diff --git a/wowViewerLib/src/gapi/vulkan/descriptorSets/GDescriptorPoolVLK.cpp b/wowViewerLib/src/gapi/vulkan/descriptorSets/GDescriptorPoolVLK.cpp index 97b5e440e..7ec4bd84f 100644 --- a/wowViewerLib/src/gapi/vulkan/descriptorSets/GDescriptorPoolVLK.cpp +++ b/wowViewerLib/src/gapi/vulkan/descriptorSets/GDescriptorPoolVLK.cpp @@ -24,6 +24,8 @@ GDescriptorPoolVLK::GDescriptorPoolVLK(IDevice &device) : m_device(dynamic_cast< poolInfo.poolSizeCount = static_cast(poolSizes.size()); poolInfo.pPoolSizes = poolSizes.data(); poolInfo.maxSets = setsAvailable; + poolInfo.pNext = nullptr; + poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT ; if (vkCreateDescriptorPool(m_device.getVkDevice(), &poolInfo, nullptr, &m_descriptorPool) != VK_SUCCESS) { throw std::runtime_error("failed to create descriptor pool!"); @@ -58,9 +60,15 @@ std::shared_ptr GDescriptorPoolVLK::allocate(VkDescriptorSetLay void GDescriptorPoolVLK::deallocate(GDescriptorSets *set) { auto descSet = set->getDescSet(); - vkFreeDescriptorSets(m_device.getVkDevice(), m_descriptorPool, 1, &descSet); + auto imgCnt = set->getImageCount(); + auto uniformCnt = set->getImageCount(); + + m_device.addDeallocationRecord([this, imgCnt, uniformCnt, descSet]() { + vkFreeDescriptorSets(m_device.getVkDevice(), m_descriptorPool, 1, &descSet); + + imageAvailable+= imgCnt; + uniformsAvailable+= uniformCnt; + setsAvailable+=1; + }); - imageAvailable+= set->getImageCount(); - uniformsAvailable+= set->getUniformCount(); - setsAvailable+=1; } diff --git a/wowViewerLib/src/gapi/vulkan/meshes/GM2MeshVLK.cpp b/wowViewerLib/src/gapi/vulkan/meshes/GM2MeshVLK.cpp index 6e4f29ebc..4b7a2887f 100644 --- a/wowViewerLib/src/gapi/vulkan/meshes/GM2MeshVLK.cpp +++ b/wowViewerLib/src/gapi/vulkan/meshes/GM2MeshVLK.cpp @@ -30,3 +30,7 @@ float GM2MeshVLK::getSortDistance() { void GM2MeshVLK::setQuery(const HGOcclusionQuery &query) { m_query = query; } + +void *GM2MeshVLK::getM2Object() { + return m_m2Object; +} diff --git a/wowViewerLib/src/gapi/vulkan/meshes/GM2MeshVLK.h b/wowViewerLib/src/gapi/vulkan/meshes/GM2MeshVLK.h index 67e477d9e..0c59b1490 100644 --- a/wowViewerLib/src/gapi/vulkan/meshes/GM2MeshVLK.h +++ b/wowViewerLib/src/gapi/vulkan/meshes/GM2MeshVLK.h @@ -13,6 +13,7 @@ class GM2MeshVLK : public GMeshVLK { GM2MeshVLK(IDevice &device, const gMeshTemplate &meshTemplate); public: + void *getM2Object() override; void setM2Object(void * m2Object) override; void setLayer(int layer) override; void setPriorityPlane(int priorityPlane) override; diff --git a/wowViewerLib/src/gapi/vulkan/meshes/GMeshVLK.cpp b/wowViewerLib/src/gapi/vulkan/meshes/GMeshVLK.cpp index 07695ed4c..f19984ad4 100644 --- a/wowViewerLib/src/gapi/vulkan/meshes/GMeshVLK.cpp +++ b/wowViewerLib/src/gapi/vulkan/meshes/GMeshVLK.cpp @@ -9,10 +9,6 @@ #include "../shaders/GShaderPermutationVLK.h" #include "../buffers/GUniformBufferVLK.h" - - - - GMeshVLK::GMeshVLK(IDevice &device, const gMeshTemplate &meshTemplate ) : m_device(dynamic_cast(device)), m_shader(meshTemplate.shader), m_meshType(meshTemplate.meshType) { @@ -54,7 +50,6 @@ GMeshVLK::GMeshVLK(IDevice &device, GShaderPermutationVLK* shaderVLK = reinterpret_cast(m_shader.get()); createDescriptorSets(shaderVLK); - hgPipelineVLK = m_device.createPipeline(m_bindings, m_shader, m_element, m_backFaceCulling, m_triCCW, m_blendMode,m_depthCulling, m_depthWrite); //Check the buffer sizes std::unordered_map shaderLayoutBindings; @@ -78,12 +73,31 @@ GMeshVLK::GMeshVLK(IDevice &device, auto it = shaderLayoutBindings.find(i); if (it != shaderLayoutBindings.end()) { if ((m_UniformBuffer[i] != nullptr) && (it->second.size != (m_UniformBuffer[i]->getSize()))) { - std::cout << "buffers missmatch!" << std::endl; + std::cout << "buffers missmatch! for shaderName = " << shaderVLK->getShaderName() << + " index = " << i << + " expected size " << (it->second.size) << + ", provided size = " << (m_UniformBuffer[i]->getSize()) << + std::endl; } } } } +//Works under assumption that meshes do not often change the renderpass on which they are rendered +std::shared_ptr GMeshVLK::getPipeLineForRenderPass(std::shared_ptr renderPass, bool invertedZ) { + if (m_lastRenderPass != renderPass) { + m_lastPipelineForRenderPass = m_device.createPipeline(m_bindings, + m_shader, renderPass, m_element, + m_backFaceCulling, m_triCCW, + m_blendMode, m_depthCulling, + m_depthWrite, invertedZ); + m_lastRenderPass = renderPass; + } + + return m_lastPipelineForRenderPass; +} + + void GMeshVLK::createDescriptorSets(GShaderPermutationVLK *shaderVLK) { auto descLayout = shaderVLK->getImageDescriptorLayout(); @@ -95,7 +109,6 @@ void GMeshVLK::createDescriptorSets(GShaderPermutationVLK *shaderVLK) { descriptorSetsUpdated = std::vector(4, false); - for (int j = 0; j < 4; j++) { std::vector descriptorWrites; diff --git a/wowViewerLib/src/gapi/vulkan/meshes/GMeshVLK.h b/wowViewerLib/src/gapi/vulkan/meshes/GMeshVLK.h index 2487e697f..b55737871 100644 --- a/wowViewerLib/src/gapi/vulkan/meshes/GMeshVLK.h +++ b/wowViewerLib/src/gapi/vulkan/meshes/GMeshVLK.h @@ -28,12 +28,16 @@ class GMeshVLK : public IMesh { void setStart(int start) override; void setEnd(int end) override; public: + void *getM2Object() override { return nullptr; }; void setM2Object(void * m2Object) override { throw "Not Implemented";}; void setLayer(int layer) override { throw "Not Implemented";}; void setPriorityPlane(int priorityPlane) override { throw "Not Implemented";}; void setQuery(const HGOcclusionQuery &query) override { throw "Not Implemented";}; - void setSortDistance(float distance) override { throw "Not Implemented";}; - float getSortDistance() override { throw "Not Implemented";}; + void setSortDistance(float distance) override { m_sortDistance = distance;}; + float getSortDistance() override { return m_sortDistance; }; + + + std::shared_ptr getPipeLineForRenderPass(std::shared_ptr renderPass, bool invertedZ); protected: MeshType m_meshType; @@ -65,7 +69,8 @@ class GMeshVLK : public IMesh { VkDescriptorPool m_descriptorPool; - std::shared_ptr hgPipelineVLK; + std::shared_ptr m_lastRenderPass = nullptr; + std::shared_ptr m_lastPipelineForRenderPass; private: diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GAdtWaterShaderPermutation.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GAdtWaterShaderPermutation.cpp deleted file mode 100644 index dd1511a5b..000000000 --- a/wowViewerLib/src/gapi/vulkan/shaders/GAdtWaterShaderPermutation.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// Created by Deamon on 26.03.2020. -// - -#include "GAdtWaterShaderPermutation.h" - -GAdtWaterShaderPermutation::GAdtWaterShaderPermutation(std::string &shaderName, IDevice *device) : - GShaderPermutationVLK(shaderName, device) {} - diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GAdtWaterShaderPermutation.h b/wowViewerLib/src/gapi/vulkan/shaders/GAdtWaterShaderPermutation.h deleted file mode 100644 index e5cabec8d..000000000 --- a/wowViewerLib/src/gapi/vulkan/shaders/GAdtWaterShaderPermutation.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Deamon on 26.03.2020. -// - -#ifndef AWEBWOWVIEWERCPP_GADTWATERSHADERPERMUTATION_H -#define AWEBWOWVIEWERCPP_GADTWATERSHADERPERMUTATION_H - - -#include "GShaderPermutationVLK.h" - -class GAdtWaterShaderPermutation: public GShaderPermutationVLK { - friend class GDeviceVLK; -public: - ~GAdtWaterShaderPermutation() override {}; - -protected: - explicit GAdtWaterShaderPermutation(std::string &shaderName, IDevice *device); -public: - int getTextureBindingStart() override { - return 5; - }; - int getTextureCount() override { - return 1; - }; -}; - - -#endif //AWEBWOWVIEWERCPP_GADTWATERSHADERPERMUTATION_H diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GDrawBoundingBoxVLK.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GDrawBoundingBoxVLK.cpp new file mode 100644 index 000000000..dc3b936d8 --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GDrawBoundingBoxVLK.cpp @@ -0,0 +1,10 @@ +// +// Created by Deamon on 1/7/2021. +// + +#include "GDrawBoundingBoxVLK.h" + +GDrawBoundingBoxVLK::GDrawBoundingBoxVLK(std::string &shaderName, IDevice *device) : GShaderPermutationVLK(shaderName, + device) { + +} \ No newline at end of file diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GDrawBoundingBoxVLK.h b/wowViewerLib/src/gapi/vulkan/shaders/GDrawBoundingBoxVLK.h new file mode 100644 index 000000000..3da468923 --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GDrawBoundingBoxVLK.h @@ -0,0 +1,26 @@ +// +// Created by Deamon on 1/7/2021. +// + +#ifndef AWEBWOWVIEWERCPP_GDRAWBOUNDINGBOXVLK_H +#define AWEBWOWVIEWERCPP_GDRAWBOUNDINGBOXVLK_H + +#include "GShaderPermutationVLK.h" + +class GDrawBoundingBoxVLK : public GShaderPermutationVLK { + friend class GDeviceVLK; +public: + ~GDrawBoundingBoxVLK() override {}; + +protected: + explicit GDrawBoundingBoxVLK(std::string &shaderName, IDevice *device); +public: + int getTextureBindingStart() override { + return 5; + }; + int getTextureCount() override { + return 0; + }; +}; + +#endif //AWEBWOWVIEWERCPP_GDRAWBOUNDINGBOXVLK_H diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GFFXGlowVLK.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GFFXGlowVLK.cpp new file mode 100644 index 000000000..08cf9ef7b --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GFFXGlowVLK.cpp @@ -0,0 +1,14 @@ +// +// Created by Deamon on 12/13/2020. +// + +#include "GFFXGlowVLK.h" + +namespace GFFXGlowVLKPrivate { + auto vertShaderName = std::string("drawQuad"); + auto fragShaderName = std::string("ffxglow"); +} + +GFFXGlowVLK::GFFXGlowVLK(std::string &shaderName, IDevice *device) : + GShaderPermutationVLK(shaderName, GFFXGlowVLKPrivate::vertShaderName, GFFXGlowVLKPrivate::fragShaderName, device) { +} diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GFFXGlowVLK.h b/wowViewerLib/src/gapi/vulkan/shaders/GFFXGlowVLK.h new file mode 100644 index 000000000..0469ad00c --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GFFXGlowVLK.h @@ -0,0 +1,27 @@ +// +// Created by Deamon on 12/13/2020. +// + +#ifndef AWEBWOWVIEWERCPP_GFFXGLOWVLK_H +#define AWEBWOWVIEWERCPP_GFFXGLOWVLK_H + +#include "GShaderPermutationVLK.h" + +class GFFXGlowVLK : public GShaderPermutationVLK { + friend class GDeviceVLK; +public: + ~GFFXGlowVLK() override {}; + +protected: + explicit GFFXGlowVLK(std::string &shaderName, IDevice *device); +public: + int getTextureBindingStart() override { + return 5; + }; + int getTextureCount() override { + return 2; + }; +}; + + +#endif //AWEBWOWVIEWERCPP_GFFXGLOWVLK_H diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GFFXgauss4VLK.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GFFXgauss4VLK.cpp new file mode 100644 index 000000000..a737a39a2 --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GFFXgauss4VLK.cpp @@ -0,0 +1,16 @@ +// +// Created by Deamon on 12/13/2020. +// + +#include "GFFXgauss4VLK.h" + +namespace GFFXgauss4VLKPrivate { + auto vertShaderName = std::string("drawQuad"); + auto fragShaderName = std::string("ffxgauss4"); +} + +GFFXgauss4VLK::GFFXgauss4VLK(std::string &shaderName, IDevice *device) : + GShaderPermutationVLK(shaderName, GFFXgauss4VLKPrivate::vertShaderName, GFFXgauss4VLKPrivate::fragShaderName, device) { + + +} diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GFFXgauss4VLK.h b/wowViewerLib/src/gapi/vulkan/shaders/GFFXgauss4VLK.h new file mode 100644 index 000000000..5d7ae1db5 --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GFFXgauss4VLK.h @@ -0,0 +1,27 @@ +// +// Created by Deamon on 12/13/2020. +// + +#ifndef AWEBWOWVIEWERCPP_GFFXGAUSS4VLK_H +#define AWEBWOWVIEWERCPP_GFFXGAUSS4VLK_H + +#include "GShaderPermutationVLK.h" + +class GFFXgauss4VLK : public GShaderPermutationVLK { + friend class GDeviceVLK; +public: + ~GFFXgauss4VLK() override {}; + +protected: + explicit GFFXgauss4VLK(std::string &shaderName, IDevice *device); +public: + int getTextureBindingStart() override { + return 5; + }; + int getTextureCount() override { + return 1; + }; +}; + + +#endif //AWEBWOWVIEWERCPP_GFFXGAUSS4VLK_H diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GShaderPermutationVLK.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GShaderPermutationVLK.cpp index d91a413ba..83e9da7d3 100644 --- a/wowViewerLib/src/gapi/vulkan/shaders/GShaderPermutationVLK.cpp +++ b/wowViewerLib/src/gapi/vulkan/shaders/GShaderPermutationVLK.cpp @@ -45,7 +45,14 @@ VkShaderModule GShaderPermutationVLK::createShaderModule(const std::vector } GShaderPermutationVLK::GShaderPermutationVLK(std::string &shaderName, IDevice * device) : - m_device(dynamic_cast(device)), m_shaderName(shaderName){ + m_device(dynamic_cast(device)), m_shaderName(shaderName), m_shaderNameVert(shaderName), m_shaderNameFrag(shaderName){ + +} + +GShaderPermutationVLK::GShaderPermutationVLK(std::string &shaderName, std::string &shaderVertName, std::string &shaderFragName, + IDevice *device) : + m_device(dynamic_cast(device)), m_shaderName(shaderName), m_shaderNameVert(shaderVertName), m_shaderNameFrag(shaderFragName){ + } @@ -117,7 +124,7 @@ void GShaderPermutationVLK::createImageDescriptorLayout() { imageLayoutBinding.descriptorCount = 1; imageLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; imageLayoutBinding.pImmutableSamplers = nullptr; - imageLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + imageLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT; shaderLayoutBindings.push_back(imageLayoutBinding); } @@ -207,14 +214,14 @@ void GShaderPermutationVLK::createUboDescriptorSets() { } void GShaderPermutationVLK::compileShader(const std::string &vertExtraDef, const std::string &fragExtraDef) { - auto vertShaderCode = readFile("spirv/" + m_shaderName + ".vert.spv"); - auto fragShaderCode = readFile("spirv/" + m_shaderName + ".frag.spv"); + auto vertShaderCode = readFile("spirv/" + m_shaderNameVert + ".vert.spv"); + auto fragShaderCode = readFile("spirv/" + m_shaderNameFrag + ".frag.spv"); vertShaderModule = createShaderModule(vertShaderCode); fragShaderModule = createShaderModule(fragShaderCode); - vertShaderMeta = &shaderMetaInfo.at(m_shaderName + ".vert.spv"); - fragShaderMeta = &shaderMetaInfo.at(m_shaderName + ".frag.spv"); + vertShaderMeta = &shaderMetaInfo.at(m_shaderNameVert + ".vert.spv"); + fragShaderMeta = &shaderMetaInfo.at(m_shaderNameFrag + ".frag.spv"); this->createUBODescriptorLayout(); diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GShaderPermutationVLK.h b/wowViewerLib/src/gapi/vulkan/shaders/GShaderPermutationVLK.h index 2ed36bf11..959ae347c 100644 --- a/wowViewerLib/src/gapi/vulkan/shaders/GShaderPermutationVLK.h +++ b/wowViewerLib/src/gapi/vulkan/shaders/GShaderPermutationVLK.h @@ -24,15 +24,20 @@ class GShaderPermutationVLK : public IShaderPermutation { VkShaderModule getFragmentModule() {return fragShaderModule;} VkDescriptorSetLayout getImageDescriptorLayout() {return imageDescriptorSetLayout;} VkDescriptorSetLayout getUboDescriptorLayout() {return uboDescriptorSetLayout;} - std::string getShaderName() {return m_shaderName; } + virtual int getTextureBindingStart() = 0; virtual int getTextureCount() = 0; const shaderMetaData *fragShaderMeta; const shaderMetaData *vertShaderMeta; + std::string getShaderName() { + return m_shaderName; + } + protected: explicit GShaderPermutationVLK(std::string &shaderName, IDevice *device); + explicit GShaderPermutationVLK(std::string &shaderName, std::string &shaderVertName, std::string &shaderFragName, IDevice *device); VkShaderModule createShaderModule(const std::vector& code); @@ -52,8 +57,13 @@ class GShaderPermutationVLK : public IShaderPermutation { private: + //Used only for logging std::string m_shaderName; + //Used for getting SPIRV + std::string m_shaderNameVert; + std::string m_shaderNameFrag; + void createUBODescriptorLayout(); void createImageDescriptorLayout(); diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GSkyConusShaderVLK.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GSkyConusShaderVLK.cpp new file mode 100644 index 000000000..6cb0cab5b --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GSkyConusShaderVLK.cpp @@ -0,0 +1,10 @@ +// +// Created by Deamon on 11/28/2020. +// + +#include "GSkyConusShaderVLK.h" + +GSkyConusShaderVLK::GSkyConusShaderVLK(std::string &shaderName, IDevice *device): + GShaderPermutationVLK(shaderName, device) { + +} \ No newline at end of file diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GSkyConusShaderVLK.h b/wowViewerLib/src/gapi/vulkan/shaders/GSkyConusShaderVLK.h new file mode 100644 index 000000000..e6000c3cf --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GSkyConusShaderVLK.h @@ -0,0 +1,27 @@ +// +// Created by Deamon on 11/28/2020. +// + +#ifndef AWEBWOWVIEWERCPP_GSKYCONUSSHADERVLK_H +#define AWEBWOWVIEWERCPP_GSKYCONUSSHADERVLK_H + +#include "GShaderPermutationVLK.h" + +class GSkyConusShaderVLK : public GShaderPermutationVLK { + friend class GDeviceVLK; +public: + ~GSkyConusShaderVLK() override {}; + +protected: + explicit GSkyConusShaderVLK(std::string &shaderName, IDevice *device); +public: + int getTextureBindingStart() override { + return 0; + }; + int getTextureCount() override { + return 0; + }; +}; + + +#endif //AWEBWOWVIEWERCPP_GSKYCONUSSHADERVLK_H diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GWMOWaterShaderVLK.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GWMOWaterShaderVLK.cpp deleted file mode 100644 index ed146d992..000000000 --- a/wowViewerLib/src/gapi/vulkan/shaders/GWMOWaterShaderVLK.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// -// Created by Deamon on 7/8/2018. -// - -#include "GWMOWaterShaderVLK.h" -#include -#include - - -GWmoWaterShaderPermutationVLK::GWmoWaterShaderPermutationVLK(std::string &shaderName, IDevice *device) : - GShaderPermutationVLK(shaderName, device) {} - diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GWMOWaterShaderVLK.h b/wowViewerLib/src/gapi/vulkan/shaders/GWMOWaterShaderVLK.h deleted file mode 100644 index c546e57e2..000000000 --- a/wowViewerLib/src/gapi/vulkan/shaders/GWMOWaterShaderVLK.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by Deamon on 7/8/2018. -// - -#ifndef AWEBWOWVIEWERCPP_GWMOWATERSHADERPERMUTATIONVLK_H -#define AWEBWOWVIEWERCPP_GWMOWATERSHADERPERMUTATIONVLK_H - -#include "GShaderPermutationVLK.h" - -class GWmoWaterShaderPermutationVLK : public GShaderPermutationVLK { - friend class GDeviceVLK; -public: - ~GWmoWaterShaderPermutationVLK() override {}; - -protected: - explicit GWmoWaterShaderPermutationVLK(std::string &shaderName, IDevice *device); -public: - int getTextureBindingStart() override { - return 0; - }; - int getTextureCount() override { - return 0; - }; -}; - - -#endif //AWEBWOWVIEWERCPP_GWMOWATERSHADERPERMUTATIONVLK_H diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GWaterShaderPermutation.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GWaterShaderPermutation.cpp new file mode 100644 index 000000000..8ea6b7fcc --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GWaterShaderPermutation.cpp @@ -0,0 +1,9 @@ +// +// Created by Deamon on 26.03.2020. +// + +#include "GWaterShaderPermutation.h" + +GWaterShaderPermutation::GWaterShaderPermutation(std::string &shaderName, IDevice *device) : + GShaderPermutationVLK(shaderName, device) {} + diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GWaterShaderPermutation.h b/wowViewerLib/src/gapi/vulkan/shaders/GWaterShaderPermutation.h new file mode 100644 index 000000000..62854fcfd --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GWaterShaderPermutation.h @@ -0,0 +1,28 @@ +// +// Created by Deamon on 26.03.2020. +// + +#ifndef AWEBWOWVIEWERCPP_GWATERSHADERPERMUTATION_H +#define AWEBWOWVIEWERCPP_GWATERSHADERPERMUTATION_H + + +#include "GShaderPermutationVLK.h" + +class GWaterShaderPermutation: public GShaderPermutationVLK { + friend class GDeviceVLK; +public: + ~GWaterShaderPermutation() override {}; + +protected: + explicit GWaterShaderPermutation(std::string &shaderName, IDevice *device); +public: + int getTextureBindingStart() override { + return 5; + }; + int getTextureCount() override { + return 1; + }; +}; + + +#endif //AWEBWOWVIEWERCPP_GWATERSHADERPERMUTATION_H diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GWaterfallShaderVLK.cpp b/wowViewerLib/src/gapi/vulkan/shaders/GWaterfallShaderVLK.cpp new file mode 100644 index 000000000..44ab3449b --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GWaterfallShaderVLK.cpp @@ -0,0 +1,10 @@ +// +// Created by Deamon on 1/31/2021. +// + +#include "GWaterfallShaderVLK.h" + +GWaterfallShaderVLK::GWaterfallShaderVLK(std::string &shaderName, IDevice *device): + GShaderPermutationVLK(shaderName, device) { + +} \ No newline at end of file diff --git a/wowViewerLib/src/gapi/vulkan/shaders/GWaterfallShaderVLK.h b/wowViewerLib/src/gapi/vulkan/shaders/GWaterfallShaderVLK.h new file mode 100644 index 000000000..7e4c4b9f7 --- /dev/null +++ b/wowViewerLib/src/gapi/vulkan/shaders/GWaterfallShaderVLK.h @@ -0,0 +1,27 @@ +// +// Created by Deamon on 1/31/2021. +// + +#ifndef AWEBWOWVIEWERCPP_GWATERFALLSHADERVLK_H +#define AWEBWOWVIEWERCPP_GWATERFALLSHADERVLK_H + +#include "GShaderPermutationVLK.h" + +class GWaterfallShaderVLK : public GShaderPermutationVLK{ + friend class GDeviceVLK; +public: + ~GWaterfallShaderVLK() override {}; + +protected: + explicit GWaterfallShaderVLK(std::string &shaderName, IDevice *device); +public: + int getTextureBindingStart() override { + return 5; + }; + int getTextureCount() override { + return 5; + }; +}; + + +#endif //AWEBWOWVIEWERCPP_GWATERFALLSHADERVLK_H diff --git a/wowViewerLib/src/gapi/vulkan/textures/GBlpTextureVLK.cpp b/wowViewerLib/src/gapi/vulkan/textures/GBlpTextureVLK.cpp index 7e0019444..0cf9d8593 100644 --- a/wowViewerLib/src/gapi/vulkan/textures/GBlpTextureVLK.cpp +++ b/wowViewerLib/src/gapi/vulkan/textures/GBlpTextureVLK.cpp @@ -7,9 +7,7 @@ #include "../../../engine/texture/DxtDecompress.h" GBlpTextureVLK::GBlpTextureVLK(IDevice &device, HBlpTexture texture, bool xWrapTex, bool yWrapTex) - : GTextureVLK(device), m_texture(texture) { - this->m_wrapX = xWrapTex; - this->m_wrapY = yWrapTex; + : GTextureVLK(device,xWrapTex,yWrapTex), m_texture(texture) { } GBlpTextureVLK::~GBlpTextureVLK() { @@ -18,7 +16,7 @@ GBlpTextureVLK::~GBlpTextureVLK() { static int texturesUploaded = 0; -void GBlpTextureVLK::createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) { +void GBlpTextureVLK::createGlTexture(TextureFormat textureFormat, const HMipmapsVector &hmipmaps) { // std::cout << "texturesUploaded = " << texturesUploaded++ << " " << this->m_texture->getTextureName() <decompressAndUpload(textureFormat, mipmaps); + this->decompressAndUpload(textureFormat, hmipmaps); return; } @@ -67,7 +67,7 @@ void GBlpTextureVLK::createGlTexture(TextureFormat textureFormat, const MipmapsV std::copy(&mipmaps[i].texture[0], &mipmaps[i].texture[0]+mipmaps[i].texture.size(), std::back_inserter(unitedBuffer)); } - createTexture(mipmaps, textureFormatGPU, unitedBuffer); + createTexture(hmipmaps, textureFormatGPU, unitedBuffer); } //bool GBlpTextureVLK::getIsLoaded() { @@ -76,18 +76,21 @@ void GBlpTextureVLK::createGlTexture(TextureFormat textureFormat, const MipmapsV bool GBlpTextureVLK::postLoad() { if (m_loaded) return false; - if (m_texture == nullptr) return false; - if (m_texture->getStatus() != FileStatus::FSLoaded) return false; + if (!m_uploaded) { + if (m_texture == nullptr) return false; + if (m_texture->getStatus() != FileStatus::FSLoaded) return false; + } if (m_uploaded) { return GTextureVLK::postLoad(); } else { this->createGlTexture(m_texture->getTextureFormat(), m_texture->getMipmapsVector()); +// m_texture = nullptr; return false; } } -void GBlpTextureVLK::decompressAndUpload(TextureFormat textureFormat, const MipmapsVector &mipmaps) { +void GBlpTextureVLK::decompressAndUpload(TextureFormat textureFormat, const HMipmapsVector &hmipmaps) { } \ No newline at end of file diff --git a/wowViewerLib/src/gapi/vulkan/textures/GBlpTextureVLK.h b/wowViewerLib/src/gapi/vulkan/textures/GBlpTextureVLK.h index e92f0819a..c0f25d16b 100644 --- a/wowViewerLib/src/gapi/vulkan/textures/GBlpTextureVLK.h +++ b/wowViewerLib/src/gapi/vulkan/textures/GBlpTextureVLK.h @@ -13,11 +13,11 @@ class GBlpTextureVLK : public GTextureVLK { explicit GBlpTextureVLK(IDevice &device, HBlpTexture texture, bool xWrapTex, bool yWrapTex); public: ~GBlpTextureVLK() override; - void createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) override; + void createGlTexture(TextureFormat textureFormat, const HMipmapsVector &hmipmaps) override; bool postLoad() override; private: - void decompressAndUpload(TextureFormat textureFormat, const MipmapsVector &mipmaps); + void decompressAndUpload(TextureFormat textureFormat, const HMipmapsVector &hmipmaps); private: HBlpTexture m_texture; }; diff --git a/wowViewerLib/src/gapi/vulkan/textures/GTextureVLK.cpp b/wowViewerLib/src/gapi/vulkan/textures/GTextureVLK.cpp index fb81f473e..82c477d11 100644 --- a/wowViewerLib/src/gapi/vulkan/textures/GTextureVLK.cpp +++ b/wowViewerLib/src/gapi/vulkan/textures/GTextureVLK.cpp @@ -6,9 +6,44 @@ #include "GTextureVLK.h" #include "../../interface/IDevice.h" -GTextureVLK::GTextureVLK(IDevice &device) : m_device(dynamic_cast(device)) { +GTextureVLK::GTextureVLK(IDevice &device, bool xWrapTex, bool yWrapTex) : m_device(dynamic_cast(device)) { + this->m_wrapX = xWrapTex; + this->m_wrapY = yWrapTex; + createBuffer(); } +GTextureVLK::GTextureVLK(IDevice &device, + int width, int height, + bool xWrapTex, bool yWrapTex, + bool isDepthTexture, + const VkFormat textureFormatGPU, + VkSampleCountFlagBits numSamples, + int vulkanMipMapCount, VkImageUsageFlags imageUsageFlags) : m_device(dynamic_cast(device)) { + //For use in frameBuffer + + this->m_wrapX = xWrapTex; + this->m_wrapY = yWrapTex; + + m_width = width; + m_height = height; + + createBuffer(); + + createVulkanImageObject( + isDepthTexture, + textureFormatGPU, + numSamples, + vulkanMipMapCount, + imageUsageFlags + ); + + //this is texture for use in framebuffer, that's why it is set as initialized + m_loaded = true; + m_uploaded = true; + stagingBufferCreated = false; +} + + GTextureVLK::~GTextureVLK() { destroyBuffer(); @@ -26,14 +61,17 @@ void GTextureVLK::destroyBuffer() { auto &l_imageAllocation = imageAllocation; auto &l_stagingBuffer = stagingBuffer; auto &l_stagingBufferAlloc = stagingBufferAlloc; + auto l_stagingBufferCreated = stagingBufferCreated; m_device.addDeallocationRecord( - [l_device, l_texture, l_imageAllocation, l_stagingBuffer, l_stagingBufferAlloc]() { + [l_device, l_texture, l_imageAllocation, l_stagingBuffer, l_stagingBufferAlloc, l_stagingBufferCreated]() { vkDestroyImageView(l_device->getVkDevice(), l_texture.view, nullptr); vkDestroySampler(l_device->getVkDevice(), l_texture.sampler, nullptr); - vmaDestroyImage(l_device->getVMAAllocator(), l_texture.image, l_imageAllocation); - vmaDestroyBuffer(l_device->getVMAAllocator(), l_stagingBuffer, l_stagingBufferAlloc); + + if (l_stagingBufferCreated) { + vmaDestroyBuffer(l_device->getVMAAllocator(), l_stagingBuffer, l_stagingBufferAlloc); + } }); } @@ -54,22 +92,28 @@ void GTextureVLK::loadData(int width, int height, void *data, ITextureFormat tex std::vector unifiedBuffer((uint8_t *)data, (uint8_t *)data + (width*height*4)); - MipmapsVector mipmapsVector; + HMipmapsVector mipmapsVector = std::make_shared>(); mipmapStruct_t mipmap; mipmap.height = height; mipmap.width = width; mipmap.texture = unifiedBuffer; - mipmapsVector.push_back(mipmap); + mipmapsVector->push_back(mipmap); + createTexture(mipmapsVector, VK_FORMAT_R8G8B8A8_UNORM, unifiedBuffer); } -void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &textureFormatGPU, std::vector unitedBuffer) {// Copy data to an optimal tiled image +void GTextureVLK::createTexture(const HMipmapsVector &hmipmaps, const VkFormat &textureFormatGPU, std::vector unitedBuffer) {// Copy data to an optimal tiled image if (m_uploaded) { std::cout << "oops!" << std::endl << std::flush; } + auto &mipmaps = *hmipmaps; + + m_width = mipmaps[0].width; + m_height = mipmaps[0].height; + // This loads the texture data into a host local buffer that is copied to the optimal tiled image on the device // Create a host-visible staging buffer that contains the raw image data @@ -102,9 +146,11 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te for (uint32_t i = 0; i < mipmaps.size(); i++) { if ( (textureFormatGPU != VK_FORMAT_B8G8R8A8_SNORM) && + (textureFormatGPU != VK_FORMAT_B8G8R8A8_UNORM) && (textureFormatGPU != VK_FORMAT_R8G8B8A8_UNORM) && ((mipmaps[i].width < 4) || (mipmaps[i].height < 4)) - ) break; + ) + break; VkBufferImageCopy bufferCopyRegion; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -117,7 +163,7 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te bufferCopyRegion.bufferRowLength = 0; bufferCopyRegion.bufferImageHeight = 0; bufferCopyRegion.bufferOffset = offset; - bufferCopyRegion.imageOffset = {0,0,0}; + bufferCopyRegion.imageOffset = {0, 0, 0}; bufferCopyRegions.push_back(bufferCopyRegion); @@ -128,31 +174,13 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te // Create optimal tiled target image on the device auto indicies = m_device.getQueueFamilyIndices(); -///3. Create Image on GPU side - VkImageCreateInfo imageCreateInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = textureFormatGPU; - imageCreateInfo.mipLevels = vulkanMipMapCount; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - // Set initial layout of the image to undefined - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = {static_cast(mipmaps[0].width), static_cast(mipmaps[0].height), 1}; - imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; -// imageCreateInfo.queueFamilyIndexCount = 2; -// std::array families = {indicies.graphicsFamily.value(), indicies.transferFamily.value()}; -// imageCreateInfo.pQueueFamilyIndices = &families[0]; - - VmaAllocationCreateInfo allocImageCreateInfo = {}; - allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - - - - vmaCreateImage(m_device.getVMAAllocator(), &imageCreateInfo, &allocImageCreateInfo, &texture.image, - &imageAllocation, &imageAllocationInfo); - + createVulkanImageObject( + false, + textureFormatGPU, + VK_SAMPLE_COUNT_1_BIT, + vulkanMipMapCount, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT + ); ///4. Fill commands to copy from CPU staging buffer to GPU buffer // Image memory barriers for the texture image @@ -170,7 +198,6 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te // Transition the texture image layout to transfer target, so we can safely copy our buffer data to it. VkImageMemoryBarrier imageMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; - imageMemoryBarrier.image = texture.image; imageMemoryBarrier.subresourceRange = subresourceRange; imageMemoryBarrier.srcAccessMask = 0; imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; @@ -214,20 +241,13 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; } - ///SeparateUploadQueue reference: https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples "Upload data from the CPU to an image sampled in a fragment shader" + ///SeparateUploadQueue reference: https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples + /// "Upload data from the CPU to an image sampled in a fragment shader" // Insert a memory dependency at the proper pipeline stages that will execute the image layout transition // Source pipeline stage stage is copy command exection (VK_PIPELINE_STAGE_TRANSFER_BIT) // Destination pipeline stage fragment shader access (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) - vkCmdPipelineBarrier( - m_device.getUploadCommandBuffer(), - VK_PIPELINE_STAGE_TRANSFER_BIT , - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - 0, - 0, nullptr, - 0, nullptr, - 1, &imageMemoryBarrier); if (m_device.canUploadInSeparateThread()) { vkCmdPipelineBarrier( @@ -240,17 +260,52 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te 1, &imageMemoryBarrier); m_device.signalTextureTransferCommandRecorded(); + } else { + vkCmdPipelineBarrier( + m_device.getUploadCommandBuffer(), + VK_PIPELINE_STAGE_TRANSFER_BIT , + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &imageMemoryBarrier); } // Store current layout for later reuse texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; -// Clean up staging resources -// vkFreeMemory(m_device.getVkDevice(), stagingMemory, nullptr); -// vkDestroyBuffer(m_device.getVkDevice(), stagingBuffer, nullptr); + m_uploaded = true; + stagingBufferCreated = true; +} + +void GTextureVLK::createVulkanImageObject(bool isDepthTexture, const VkFormat textureFormatGPU, + VkSampleCountFlagBits numSamples, int vulkanMipMapCount, + VkImageUsageFlags imageUsageFlags) { + //3. Create Image on GPU side + VkImageCreateInfo imageCreateInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = textureFormatGPU; + imageCreateInfo.mipLevels = vulkanMipMapCount; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = numSamples; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + // Set initial layout of the image to undefined + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = {static_cast(m_width), static_cast(m_height), 1}; + imageCreateInfo.usage = imageUsageFlags; + // imageCreateInfo.queueFamilyIndexCount = 2; + // std::array families = {indicies.graphicsFamily.value(), indicies.transferFamily.value()}; + // imageCreateInfo.pQueueFamilyIndices = &families[0]; + + VmaAllocationCreateInfo allocImageCreateInfo = {}; + allocImageCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + + vmaCreateImage(m_device.getVMAAllocator(), &imageCreateInfo, &allocImageCreateInfo, &texture.image, + &imageAllocation, &imageAllocationInfo); -// Create a texture sampler + // Create a texture sampler // In Vulkan textures are accessed by samplers // This separates all the sampling information from the texture data. This means you could have multiple sampler objects for the same texture with different settings // Note: Similar to the samplers available with OpenGL 3.3 @@ -269,13 +324,13 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te // Enable anisotropic filtering // This feature is optional, so we must check if it's supported on the device if (m_device.getIsAnisFiltrationSupported()) { - // Use max. level of anisotropy for this example - sampler.maxAnisotropy = m_device.getAnisLevel(); - sampler.anisotropyEnable = VK_TRUE; + // Use max. level of anisotropy for this example + sampler.maxAnisotropy = std::min(m_device.getAnisLevel(), vulkanMipMapCount); + sampler.anisotropyEnable = VK_TRUE; } else { - // The device does not support anisotropic filtering - sampler.maxAnisotropy = 1.0; - sampler.anisotropyEnable = VK_FALSE; + // The device does not support anisotropic filtering + sampler.maxAnisotropy = 1.0; + sampler.anisotropyEnable = VK_FALSE; } sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; ERR_GUARD_VULKAN(vkCreateSampler(m_device.getVkDevice(), &sampler, nullptr, &texture.sampler)); @@ -285,12 +340,21 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te // are abstracted by image views containing additional // information and sub resource ranges VkImageViewCreateInfo view = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + view.pNext = nullptr; + view.flags = 0; view.viewType = VK_IMAGE_VIEW_TYPE_2D; view.format = textureFormatGPU; - view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + if (!isDepthTexture) { + view.components = {VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A}; + } else { + view.components = {VK_COMPONENT_SWIZZLE_IDENTITY}; + } // The subresource range describes the set of mip levels (and array layers) that can be accessed through this image view // It's possible to create multiple image views for a single image referring to different (and/or overlapping) ranges of the image - view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view.subresourceRange.aspectMask = !isDepthTexture ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT); view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.layerCount = 1; @@ -300,7 +364,6 @@ void GTextureVLK::createTexture(const MipmapsVector &mipmaps, const VkFormat &te // The view will be based on the texture's image view.image = texture.image; ERR_GUARD_VULKAN(vkCreateImageView(m_device.getVkDevice(), &view, nullptr, &texture.view)); - m_uploaded = true; } bool GTextureVLK::postLoad() { diff --git a/wowViewerLib/src/gapi/vulkan/textures/GTextureVLK.h b/wowViewerLib/src/gapi/vulkan/textures/GTextureVLK.h index 8129b7f72..9b3578303 100644 --- a/wowViewerLib/src/gapi/vulkan/textures/GTextureVLK.h +++ b/wowViewerLib/src/gapi/vulkan/textures/GTextureVLK.h @@ -6,21 +6,32 @@ #define AWEBWOWVIEWERCPP_GTEXTUREVLK_H class GDeviceVLK; +class GFrameBufferVLK; #include "../GDeviceVulkan.h" #include "../../interface/textures/ITexture.h" class GTextureVLK : public ITexture { friend class GDeviceVLK; + friend class GFrameBufferVLK; protected: - explicit GTextureVLK(IDevice &device); - void createTexture(const MipmapsVector &mipmaps, const VkFormat &textureFormatGPU, std::vector unitedBuffer); + explicit GTextureVLK(IDevice &device, bool xWrapTex, bool yWrapTex); + //Used for rendering to texture in framebuffer + explicit GTextureVLK(IDevice &device, + int width, int height, + bool xWrapTex, bool yWrapTex, + bool isDepthTexture, + const VkFormat textureFormatGPU, + VkSampleCountFlagBits numSamples, + int vulkanMipMapCount, + VkImageUsageFlags imageUsageFlags); + void createTexture(const HMipmapsVector &mipmaps, const VkFormat &textureFormatGPU, std::vector unitedBuffer); public: ~GTextureVLK() override; void readData(std::vector &buff) override {}; void loadData(int width, int height, void *data, ITextureFormat textureFormat) override; bool getIsLoaded() override; - void createGlTexture(TextureFormat textureFormat, const MipmapsVector &mipmaps) override { + void createGlTexture(TextureFormat textureFormat, const HMipmapsVector &mipmaps) override { throw "Not Implemented in this class"; } bool postLoad() override;; @@ -29,7 +40,6 @@ class GTextureVLK : public ITexture { VkSampler sampler; VkImage image; VkImageLayout imageLayout; - VkDeviceMemory deviceMemory; VkImageView view; } texture; private: @@ -48,10 +58,23 @@ class GTextureVLK : public ITexture { protected: GDeviceVLK &m_device; + bool stagingBufferCreated = false; + bool m_uploaded = false; bool m_loaded = false; bool m_wrapX = true; bool m_wrapY = true; + + int m_width = 0; + int m_height = 0; + + void createVulkanImageObject( + bool isDepthTexture, + const VkFormat textureFormatGPU, + VkSampleCountFlagBits numSamples, + int vulkanMipMapCount, + VkImageUsageFlags imageUsageFlags + ); }; diff --git a/wowViewerLib/src/include/config.h b/wowViewerLib/src/include/config.h index 207557d1b..82d805990 100644 --- a/wowViewerLib/src/include/config.h +++ b/wowViewerLib/src/include/config.h @@ -6,23 +6,54 @@ #define WOWVIEWERLIB_CONFIG_H #include +#include #include +struct RiverColorOverride { + int areaId = -1; + mathfu::vec4 color = {0,0,0,0}; +}; + +typedef std::vector RiverColorOverrideHolder; +typedef std::shared_ptr HRiverColorOverrideHolder; + +enum class EParameterSource: char { + eNone, + eConfig, + eDatabase, + eM2 +}; + +enum class EFreeStrategy : char { + eTimeBased, + eFrameBase +}; + class Config { +public: + Config() { + threadCount = std::max((int)std::thread::hardware_concurrency()-2, 1); + } -private: +public: bool renderAdt = true; bool renderWMO = true; bool renderM2 = true; bool renderBSP = false; bool renderPortals = false; + bool renderPortalsIgnoreDepth = false; bool usePortalCulling = true; bool useInstancing = false; + bool disableFog = false; + bool renderSkyDom = true; + bool drawWmoBB = false; - bool drawM2BB = true; - bool secondCamera = false; + bool drawM2BB = false; + bool doubleCameraDebug = false; + bool controlSecondCamera = false; + bool swapMainAndDebug = false; bool BCLightHack = false; @@ -44,31 +75,39 @@ class Config { int currentTime = 0; bool useWotlkLogic = false; - float movementSpeed = 1.0; float nearPlane = 1; float farPlane = 1000; float farPlaneForCulling = 400; - bool useGaussBlur = true; + bool disableGlow = false; - bool useTimedGloabalLight = true; - bool useM2AmbientLight = false; + bool pauseAnimation = false; mathfu::vec4 clearColor = {0.117647, 0.207843, 0.392157, 0}; + EParameterSource globalLighting = EParameterSource::eDatabase; mathfu::vec4 exteriorAmbientColor = {1, 1, 1, 1}; mathfu::vec4 exteriorHorizontAmbientColor = {1, 1, 1, 1}; mathfu::vec4 exteriorGroundAmbientColor = {1, 1, 1, 1}; mathfu::vec4 exteriorDirectColor = {0.3,0.3,0.3, 0.3}; mathfu::vec3 exteriorDirectColorDir; + float adtSpecMult = 1.0; + mathfu::vec4 interiorAmbientColor; mathfu::vec4 interiorSunColor; mathfu::vec3 interiorSunDir; + bool useMinimapWaterColor = false; + bool useCloseRiverColorForDB = false; + EParameterSource waterColorParams = EParameterSource::eDatabase; mathfu::vec4 closeRiverColor = {1,1,1,1}; + mathfu::vec4 farRiverColor = {1,1,1,1}; + mathfu::vec4 closeOceanColor = {1,1,1,1}; + mathfu::vec4 farOceanColor = {1,1,1,1}; + EParameterSource skyParams = EParameterSource::eDatabase; mathfu::vec4 SkyTopColor; mathfu::vec4 SkyMiddleColor; mathfu::vec4 SkyBand1Color; @@ -76,443 +115,51 @@ class Config { mathfu::vec4 SkySmogColor; mathfu::vec4 SkyFogColor; - float FogEnd; - float FogScaler; - float FogDensity; - float FogHeight; - float FogHeightScaler; - float FogHeightDensity; - float SunFogAngle; - mathfu::vec3 EndFogColor; - float EndFogColorDistance; - mathfu::vec3 SunFogColor; - float SunFogStrength; - mathfu::vec3 FogHeightColor; - mathfu::vec4 FogHeightCoefficients; + EParameterSource globalFog = EParameterSource::eDatabase; + mathfu::vec4 actualFogColor = mathfu::vec4(1,1,1, 1); + float FogEnd = 0; + float FogScaler = 0; + float FogDensity = 0; + float FogHeight = 0; + float FogHeightScaler = 0; + float FogHeightDensity = 0; + float SunFogAngle = 0; + mathfu::vec3 FogColor = mathfu::vec3(0,0,0); + mathfu::vec3 EndFogColor = mathfu::vec3(0,0,0); + float EndFogColorDistance = 0; + mathfu::vec3 SunFogColor = mathfu::vec3(0,0,0); + float SunFogStrength = 0; + mathfu::vec3 FogHeightColor = mathfu::vec3(0,0,0); + mathfu::vec4 FogHeightCoefficients = mathfu::vec4(0,0,0,0); std::string areaName; -public: - float getFarPlane() { - return farPlane; - } - void setFarPlane(float value) { - farPlane = value; - } - float getFarPlaneForCulling() { - return farPlaneForCulling; - } - void setFarPlaneForCulling(float value) { - farPlaneForCulling = value; - } - - bool getRenderWMO () { - return renderWMO; - } - - void setRenderWMO(bool value) { - renderWMO = value; - } - - bool getRenderM2 () { - return renderM2; - } - bool getUseInstancing () { - return useInstancing; - } - void setRenderM2 (bool value) { - renderM2 = value; - } - bool getRenderAdt() { - return renderAdt; - } - void setRenderAdt(bool value) { - renderAdt = value; - } - bool getRenderBSP() { - return renderBSP; - } - void setRenderBSP (bool value) { - renderBSP = value; - } - bool getRenderPortals() { - return renderPortals; - } - void setRenderPortals(bool value) { - renderPortals = value; - } - bool getUsePortalCulling () { - return usePortalCulling; - } - void setUsePortalCulling(bool value) { - usePortalCulling = value; - } - - bool getDrawWmoBB (){ - return drawWmoBB; - } - void setDrawWmoBB (bool value) { - drawWmoBB = value; - } - bool getDrawM2BB (){ - return drawM2BB; - } - void setDrawM2BB( bool value) { - drawM2BB = value; - } - bool getUseSecondCamera () { - return secondCamera; - } - void setUseSecondCamera (bool value) { - secondCamera = value; - } - bool getDoubleCameraDebug () { - return doubleCameraDebug; - } - void setDoubleCameraDebug (bool value) { - doubleCameraDebug = value; - } - bool getDrawDepthBuffer () { - return drawDepthBuffer; - } - void setDrawDepthBuffer (bool value) { - drawDepthBuffer = value; - } - int getCameraM2 () { - return cameraM2; - } - void setCameraM2 (bool value) { - cameraM2 = value; - } - - bool getBCLightHack () { - return BCLightHack; - } - void setBCLightHack (bool value) { - BCLightHack = value; - } - - bool getUseWotlkLogic() { - return useWotlkLogic; - }; - - void setWmoMinBatch(int value) { - wmoMinBatch = value; - } - int getWmoMinBatch() { - return wmoMinBatch; - } - void setWmoMaxBatch(int value) { - wmoMaxBatch = value; - } - int getWmoMaxBatch() { - return wmoMaxBatch; - } - - void setM2MinBatch(int value) { - m2MinBatch = value; - } - int getM2MinBatch() { - return m2MinBatch; - } - void setM2MaxBatch(int value) { - m2MaxBatch = value; - } - int getM2MaxBatch() { - return m2MaxBatch; - } - - void setMinParticle(int value) { - minParticle = value; - } - int getMinParticle() { - return minParticle; - } - void setMaxParticle(int value) { - maxParticle = value; - } - int getMaxParticle() { - return maxParticle; - } - - void setMovementSpeed(float value) { - movementSpeed = value; - } - float getMovementSpeed() { - return movementSpeed; - } - - void setClearColor(float r, float g, float b, float a) { - clearColor[0] = r; - clearColor[1] = g; - clearColor[2] = b; - clearColor[3] = a; - } - - mathfu::vec4 getClearColor(){ - return clearColor; - } - void setExteriorAmbientColor(float r, float g, float b, float a) { - exteriorAmbientColor[0] = r; - exteriorAmbientColor[1] = g; - exteriorAmbientColor[2] = b; - exteriorAmbientColor[3] = a; - } - - mathfu::vec4 getExteriorAmbientColor(){ - return exteriorAmbientColor; - } - - mathfu::vec4 getExteriorHorizontAmbientColor(){ - return exteriorHorizontAmbientColor; - } - - void setExteriorHorizontAmbientColor(float r, float g, float b, float a) { - exteriorHorizontAmbientColor[0] = r; - exteriorHorizontAmbientColor[1] = g; - exteriorHorizontAmbientColor[2] = b; - exteriorHorizontAmbientColor[3] = a; - } - mathfu::vec4 getExteriorGroundAmbientColor(){ - return exteriorGroundAmbientColor; - } - - void setExteriorGroundAmbientColor(float r, float g, float b, float a) { - exteriorGroundAmbientColor[0] = r; - exteriorGroundAmbientColor[1] = g; - exteriorGroundAmbientColor[2] = b; - exteriorGroundAmbientColor[3] = a; - } - - - void setExteriorDirectColor(float r, float g, float b, float a) { - exteriorDirectColor[0] = r; - exteriorDirectColor[1] = g; - exteriorDirectColor[2] = b; - exteriorDirectColor[3] = a; - } - - mathfu::vec4 getExteriorDirectColor(){ - return exteriorDirectColor; - } - - void setExteriorDirectColorDir(float x, float y, float z) { - exteriorDirectColorDir[0] = x; - exteriorDirectColorDir[1] = y; - exteriorDirectColorDir[2] = z; - } - - mathfu::vec4 getSkyTopColor(){ - return SkyTopColor; - } - - void setSkyTopColor(float x, float y, float z) { - SkyTopColor[0] = x; - SkyTopColor[1] = y; - SkyTopColor[2] = z; - } - - mathfu::vec4 getSkyBand1Color(){ - return SkyBand1Color; - } - - void setSkyBand1Color(float x, float y, float z) { - SkyBand1Color[0] = x; - SkyBand1Color[1] = y; - SkyBand1Color[2] = z; - } - - mathfu::vec4 getSkyMiddleColor(){ - return SkyMiddleColor; - } - - void setSkyMiddleColor(float x, float y, float z) { - SkyMiddleColor[0] = x; - SkyMiddleColor[1] = y; - SkyMiddleColor[2] = z; - } - - mathfu::vec4 getSkyBand2Color(){ - return SkyBand2Color; - } - - void setSkyBand2Color(float x, float y, float z) { - SkyBand2Color[0] = x; - SkyBand2Color[1] = y; - SkyBand2Color[2] = z; - } - - mathfu::vec4 getSkySmogColor(){ - return SkySmogColor; - } - - void setSkySmogColor(float x, float y, float z) { - SkySmogColor[0] = x; - SkySmogColor[1] = y; - SkySmogColor[2] = z; - } - - mathfu::vec4 getSkyFogColor(){ - return SkyFogColor; - } - - void setSkyFogColor(float x, float y, float z) { - SkyFogColor[0] = x; - SkyFogColor[1] = y; - SkyFogColor[2] = z; - } - - mathfu::vec3 getExteriorDirectColorDir(){ - return exteriorDirectColorDir; - } - - void setCloseRiverColor(float r, float g, float b, float a) { - closeRiverColor[0] = r; - closeRiverColor[1] = g; - closeRiverColor[2] = b; - closeRiverColor[3] = a; - } - - mathfu::vec4 getCloseRiverColor(){ - return closeRiverColor; - } - - void setAreaName(std::string a) { - areaName = a; - } - - std::string getAreaName() { - return areaName; - } - - int getThreadCount() { - return threadCount; - } - void setThreadCount(int value) { - threadCount = value; - } - - int getQuickSortCutoff() { - return quickSortCutoff; - } - void setQuickSortCutoff(int value) { - quickSortCutoff = value; - } - - void setCurrentTime(int value) { - currentTime = value; - } - int getCurrentTime() { - return currentTime; - } - - void setUseGaussBlur(bool value) { - useGaussBlur = value; - } - bool getUseGaussBlur() { - return useGaussBlur; - } - - void setUseTimedGloabalLight(bool value) { - useTimedGloabalLight = value; - }; - bool getUseTimedGloabalLight() { - return useTimedGloabalLight; - } - - void setUseM2AmbientLight(bool value) { - useM2AmbientLight = value; - } - bool getUseM2AmbientLight() { - return useM2AmbientLight; - } - - float getFogEnd() { - return FogEnd; - } - void setFogEnd(float value) { - FogEnd = value; - } - float getFogScaler() { - return FogScaler; - } - void setFogScaler(float value) { - FogScaler = value; - } - float getFogDensity() { - return FogDensity; - } - void setFogDensity(float value) { - FogDensity = value; - } - float getFogHeight() { - return FogHeight; - } - void setFogHeight(float value ) { - FogHeight = value; - } - float getFogHeightScaler() { - return FogHeightScaler; - } - void setFogHeightScaler(float value) { - FogHeightScaler = value; - } - float getFogHeightDensity() { - return FogHeightDensity; - } - void setFogHeightDensity(float value) { - FogHeightDensity = value; - } - float getSunFogAngle() { - return SunFogAngle; - } - void setSunFogAngle(float value) { - SunFogAngle = value; - } + int diffuseColorHack = 0; - mathfu::vec3 getEndFogColor() { - return EndFogColor; - } - void setEndFogColor(float r, float g, float b) { - EndFogColor = mathfu::vec3(r, g, b); - } - float getEndFogColorDistance() { - return EndFogColorDistance; - }; - void setEndFogColorDistance(float value) { - EndFogColorDistance = value; - } + EParameterSource glowSource = EParameterSource::eDatabase; + float currentGlow = 0; - mathfu::vec3 getSunFogColor() { - return SunFogColor; - } - void setSunFogColor(float r, float g, float b) { - SunFogColor = mathfu::vec3(r, g, b); - } + //Culling preferences + double adtTTLWithoutUpdate = 10000; //10 secs by default + double adtFTLWithoutUpdate = 6; //25 frames by default + EFreeStrategy adtFreeStrategy = EFreeStrategy::eTimeBased; - float getSunFogStrength() { - return SunFogStrength; - }; - void setSunFogStrength(float value) { - SunFogStrength = value; - } - mathfu::vec3 getFogHeightColor() { - return FogHeightColor; - } - void setFogHeightColor(float r, float g, float b) { - FogHeightColor = mathfu::vec3(r, g, b); - } + //Stuff to display in UI + double cullingTimePerFrame = 0; + double updateTimePerFrame = 0; + double m2UpdateTime = 0; - mathfu::vec4 getFogHeightCoefficients() { - return FogHeightCoefficients; - } - void setFogHeightCoefficients(float x, float y, float z, float w) { - FogHeightCoefficients = mathfu::vec4(x, y, z, w); - } + double singleUpdateCNT = 0; + double meshesCollectCNT = 0; + double updateBuffersCNT = 0; + double updateBuffersDeviceCNT = 0; + double postLoadCNT = 0; + double textureUploadCNT = 0; + double drawStageAndDepsCNT = 0; + double endUpdateCNT = 0; - int diffuseColorHack = 0; - float currentGlow = 0; + HRiverColorOverrideHolder colorOverrideHolder = nullptr; }; diff --git a/wowViewerLib/src/include/controllable.h b/wowViewerLib/src/include/controllable.h index 42c1cf351..ad4ab88e7 100644 --- a/wowViewerLib/src/include/controllable.h +++ b/wowViewerLib/src/include/controllable.h @@ -30,5 +30,7 @@ class IControllable { virtual void setCameraPos(float x, float y, float z) = 0; virtual void getCameraPosition(float *position) = 0; + virtual void setCameraLookAt(float x, float y, float z) = 0; + }; #endif //WOWVIEWERLIB_CONTROLLABLE_H diff --git a/wowViewerLib/src/include/database/dbStructs.h b/wowViewerLib/src/include/database/dbStructs.h index 74cf9e3ad..ab2fd7559 100644 --- a/wowViewerLib/src/include/database/dbStructs.h +++ b/wowViewerLib/src/include/database/dbStructs.h @@ -2,6 +2,8 @@ // Created by deamon on 24.12.19. // +#include + #ifndef AWEBWOWVIEWERCPP_DBSTRUCTS_H #define AWEBWOWVIEWERCPP_DBSTRUCTS_H struct MapRecord { @@ -13,18 +15,22 @@ struct MapRecord { }; struct LightResult { + int id; float ambientColor[3]; float horizontAmbientColor[3]; float groundAmbientColor[3]; float directColor[3]; float closeRiverColor[3]; + float farRiverColor[3]; + float closeOceanColor[3]; + float farOceanColor[3]; - float SkyTopColor[3]; + std::array SkyTopColor; float SkyMiddleColor[3]; float SkyBand1Color[3]; float SkyBand2Color[3]; float SkySmogColor[3]; - float SkyFogColor[3]; + std::array SkyFogColor; //Fog float FogEnd; @@ -34,11 +40,12 @@ struct LightResult { float FogHeightScaler; float FogHeightDensity; float SunFogAngle; - float EndFogColor[3]; + + std::array EndFogColor; float EndFogColorDistance; - float SunFogColor[3]; + std::array SunFogColor; float SunFogStrength; - float FogHeightColor[3]; + std::array FogHeightColor; float FogHeightCoefficients[4]; @@ -46,7 +53,8 @@ struct LightResult { int skyBoxFdid; int skyBoxFlags; int lightSkyboxId; - float glow; + int lightParamId; + float glow = 0; float blendCoef; bool isDefault = false; }; @@ -57,6 +65,16 @@ struct LiquidMat { int OrderIndex; float color1[3]; float color2[3]; + int flags; + std::array minimapStaticCol; +}; + +struct LiquidTypeData { + int FileDataId; + float color1[3]; + float color2[3]; + int flags; + float minimapStaticCol[3]; }; struct vec2 { diff --git a/wowViewerLib/src/include/databaseHandler.h b/wowViewerLib/src/include/databaseHandler.h index 1a015427b..17ce655be 100644 --- a/wowViewerLib/src/include/databaseHandler.h +++ b/wowViewerLib/src/include/databaseHandler.h @@ -12,12 +12,13 @@ class IClientDatabase { public: virtual void getMapArray(std::vector &mapRecords) = 0; + virtual bool getMapById(int mapId, MapRecord &mapRecord) = 0; virtual AreaRecord getArea(int areaId) = 0; virtual AreaRecord getWmoArea(int wmoId, int nameId, int groupId) = 0; virtual void getLightById(int lightId, int time, LightResult &lightResult) = 0; virtual void getEnvInfo(int mapId, float x, float y, float z, int time, std::vector &lightResults) = 0; virtual void getLiquidObjectData(int liquidObjectId, std::vector &loData) = 0; - virtual void getLiquidTypeData(int liquidTypeId, std::vector &fileDataIds) = 0; + virtual void getLiquidTypeData(int liquidTypeId, std::vector &liquidTypeData) =0; virtual void getZoneLightsForMap(int mapId, std::vector &zoneLights) = 0; }; diff --git a/wowViewerLib/src/include/iostuff.h b/wowViewerLib/src/include/iostuff.h index c5f5eb25e..8d7466b19 100644 --- a/wowViewerLib/src/include/iostuff.h +++ b/wowViewerLib/src/include/iostuff.h @@ -6,6 +6,7 @@ #define AWEBWOWVIEWERCPP_IOSTUFF_H #include "sharedFile.h" +#include "../engine/persistance/PersistentFile.h" #include #include @@ -32,12 +33,12 @@ enum class CacheHolderType { class IFileRequest { public: - virtual void requestFile(const char* fileName, CacheHolderType holderType) = 0; + virtual void requestFile(std::string &fileName, CacheHolderType holderType, std::weak_ptr s_file) = 0; + virtual ~IFileRequest()= default; }; class IFileRequester { public: - virtual void provideFile(CacheHolderType holderType, const char* fileName, const HFileContent &data) = 0; virtual void rejectFile(CacheHolderType holderType, const char* fileName) = 0; }; #endif //AWEBWOWVIEWERCPP_IOSTUFF_H diff --git a/wowViewerLib/src/include/string_utils.h b/wowViewerLib/src/include/string_utils.h new file mode 100644 index 000000000..4ba416754 --- /dev/null +++ b/wowViewerLib/src/include/string_utils.h @@ -0,0 +1,20 @@ +// +// Created by Deamon on 8/2/2021. +// + +#ifndef AWEBWOWVIEWERCPP_STRING_UTILS_H +#define AWEBWOWVIEWERCPP_STRING_UTILS_H + +#include +#include + +bool endsWith(std::string_view str, std::string_view suffix); +bool endsWith2(std::string_view str, std::string_view suffix); + +bool startsWith(std::string_view str, std::string_view prefix); + +void tokenize(std::string const &str, const std::string delim, + std::vector &out); + + +#endif //AWEBWOWVIEWERCPP_STRING_UTILS_H diff --git a/wowViewerLib/src/include/string_utlis.cpp b/wowViewerLib/src/include/string_utlis.cpp new file mode 100644 index 000000000..2dc3492b6 --- /dev/null +++ b/wowViewerLib/src/include/string_utlis.cpp @@ -0,0 +1,25 @@ +#include "string_utils.h" + +bool endsWith(std::string_view str, std::string_view suffix) +{ + return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix); +} + +bool startsWith(std::string_view str, std::string_view prefix) +{ + return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix); +} + +void tokenize(std::string const &str, const std::string delim, + std::vector &out) +{ + size_t prev_start = 0; + size_t start = 0; + + while ((start = str.find(delim, start)) != std::string::npos) + { + out.push_back(str.substr(prev_start, start-prev_start)); + prev_start = ++start; + } + out.push_back(str.substr(prev_start, start-prev_start)); +}