diff --git a/.editorconfig b/.editorconfig index c8d511ec..05d09a66 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,8 @@ trim_trailing_whitespace = true [{CMakeLists.txt,*.cmake}] indent_size = 2 +insert_final_newline = true + +[*.{json,xml.in,desktop.in}] +indent_size = 2 +insert_final_newline = true diff --git a/.gitattributes b/.gitattributes index 232342c7..04926c6b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,7 @@ *.html text eol=lf diff=html *.mdp binary *.mak text eol=crlf +**/*.ico binary +**/*.png binary +**/*.svg text eol=lf +**/*.desktop text eol=lf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d688877..57d6e539 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,7 @@ jobs: build: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} + container: ${{ matrix.container || '' }} defaults: run: shell: ${{ matrix.shell || 'sh' }} @@ -33,14 +34,15 @@ jobs: fail-fast: false matrix: include: - - { name: 'Linux', os: 'ubuntu-latest', dx5: false, config: true, build-type: 'Debug', linux: true, werror: true, clang-tidy: true } - - { name: 'MSVC (x86)', os: 'windows-latest', dx5: true, config: false, build-type: 'Debug', msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64_x86' } - - { name: 'MSVC (x64)', os: 'windows-latest', dx5: false, config: false, build-type: 'Debug', msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64' } - - { name: 'MSVC (arm64)', os: 'windows-latest', dx5: false, config: false, build-type: 'Debug', msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64_arm64' } - - { name: 'msys2 mingw32', os: 'windows-latest', dx5: false, config: false, build-type: 'Debug', mingw: true, werror: true, clang-tidy: true, msystem: 'mingw32', msys-env: 'mingw-w64-i686', shell: 'msys2 {0}' } - - { name: 'msys2 mingw64', os: 'windows-latest', dx5: false, config: true, build-type: 'Debug', mingw: true, werror: true, clang-tidy: true, msystem: 'mingw64', msys-env: 'mingw-w64-x86_64', shell: 'msys2 {0}' } - - { name: 'macOS', os: 'macos-latest', dx5: false, config: true, build-type: 'Debug', brew: true, werror: true, clang-tidy: false } - - { name: 'Emscripten', os: 'ubuntu-latest', dx5: false, config: false, build-type: 'Debug', emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' } + - { name: 'Linux', os: 'ubuntu-latest', dx5: false, config: true, linux: true, werror: true, clang-tidy: true } + - { name: 'MSVC (x86)', os: 'windows-latest', dx5: true, config: false, msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64_x86' } + - { name: 'MSVC (x64)', os: 'windows-latest', dx5: false, config: false, msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64' } + - { name: 'MSVC (arm64)', os: 'windows-latest', dx5: false, config: false, msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64_arm64' } + - { name: 'msys2 mingw32', os: 'windows-latest', dx5: false, config: false, mingw: true, werror: true, clang-tidy: true, msystem: 'mingw32', msys-env: 'mingw-w64-i686', shell: 'msys2 {0}' } + - { name: 'msys2 mingw64', os: 'windows-latest', dx5: false, config: true, mingw: true, werror: true, clang-tidy: true, msystem: 'mingw64', msys-env: 'mingw-w64-x86_64', shell: 'msys2 {0}' } + - { name: 'macOS', os: 'macos-latest', dx5: false, config: true, brew: true, werror: true, clang-tidy: false } + - { name: 'Emscripten', os: 'ubuntu-latest', dx5: false, config: false, emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' } + - { name: 'Nintendo 3DS', os: 'ubuntu-latest', dx5: false, config: false, n3ds: true, werror: true, clang-tidy: false, container: 'devkitpro/devkitarm:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake' } steps: - name: Setup vcvars if: ${{ !!matrix.msvc }} @@ -66,8 +68,8 @@ jobs: sudo apt-get update sudo apt-get install -y \ libx11-dev libxext-dev libxrandr-dev libxrender-dev libxfixes-dev libxi-dev libxinerama-dev \ - libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev libglew-dev qt6-base-dev \ - libasound2-dev + libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev qt6-base-dev \ + libasound2-dev qt6-xdgdesktopportal-platformtheme - name: Install macOS dependencies (brew) if: ${{ matrix.brew }} @@ -89,26 +91,90 @@ jobs: - name: Configure (CMake) run: | ${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \ - -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ + ${{ matrix.cmake-args || '' }} \ + -DCMAKE_BUILD_TYPE=Release \ -DISLE_USE_DX5=${{ !!matrix.dx5 }} \ - -DISLE_BUILD_CONFIG=${{ matrix.config }} \ + -DISLE_BUILD_CONFIG=${{ !!matrix.config }} \ -DENABLE_CLANG_TIDY=${{ !!matrix.clang-tidy }} \ -DISLE_WERROR=${{ !!matrix.werror }} \ + -DISLE_DEBUG=OFF \ -Werror=dev - name: Build (CMake) run: cmake --build build --verbose - name: Package (CPack) + if: ${{ !matrix.n3ds }} run: | cd build cpack . + - name: Install linuxdeploy + if: ${{ matrix.linux }} + id: install-linuxdeploy + uses: miurahr/install-linuxdeploy-action@v1.8.0 + with: + plugins: qt appimage + + - name: Package (AppImage) + if: ${{ matrix.linux }} + run: | + cd build && \ + export LD_LIBRARY_PATH=".:$LD_LIBRARY_PATH" && \ + NO_STRIP=1 ${{ steps.install-linuxdeploy.outputs.linuxdeploy }} \ + -p qt \ + -e isle \ + -e isle-config \ + -d packaging/linux/org.legoisland.Isle.desktop \ + -i icons/org.legoisland.Isle.svg \ + --custom-apprun=../packaging/linux/appimage/AppRun \ + --appdir packaging/linux/appimage/AppDir \ + --output appimage && \ + mv *.AppImage dist/ + + - name: Package (3DS) + if: ${{ matrix.n3ds }} + run: | + cd build + mkdir dist + mv *.3dsx dist/ + - name: Upload Build Artifacts uses: actions/upload-artifact@v4 with: - name: '${{ matrix.name }} ${{ matrix.build-type }}' - path: build/dist/isle-* + name: '${{ matrix.name }}' + path: | + build/dist/isle-* + build/dist/*.AppImage + build/dist/*.3dsx + + flatpak: + name: "Flatpak (${{ matrix.arch }})" + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + os: ubuntu-latest + + - arch: aarch64 + os: ubuntu-22.04-arm + + container: + image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.8 + options: --privileged + + steps: + - uses: actions/checkout@v4 + + - name: Build Flatpak + uses: flatpak/flatpak-github-actions/flatpak-builder@v6 + with: + bundle: org.legoisland.Isle.${{ matrix.arch }}.flatpak + manifest-path: packaging/linux/flatpak/org.legoisland.Isle.json + arch: ${{ matrix.arch }} ncc: name: 'C++' @@ -148,3 +214,30 @@ jobs: LEGO1/omni/src/video/flic.cpp \ $action_headers \ --path LEGO1/omni LEGO1/lego/legoomni + + release: + name: 'Release' + if: ${{ github.event_name == 'push' && github.ref_name == 'master' }} + runs-on: ubuntu-latest + needs: + - build + - flatpak + steps: + - name: Download All Artifacts + uses: actions/download-artifact@main + with: + pattern: "*" + path: Release + merge-multiple: true + + - name: Checkout uploadtool + uses: actions/checkout@v4 + with: + repository: 'probonopd/uploadtool' + path: 'uploadtool' + + - name: Upload Continuous Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + ./uploadtool/upload.sh Release/* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 0e4c6cc9..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,116 +0,0 @@ -name: Release - -on: - push: - branches: - - master - -jobs: - build: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - defaults: - run: - shell: ${{ matrix.shell || 'sh' }} - - strategy: - fail-fast: false - matrix: - include: - - { name: 'Linux', os: 'ubuntu-latest', dx5: false, config: true, build-type: 'Release', linux: true, werror: true, clang-tidy: false } - - { name: 'Windows', os: 'windows-latest', dx5: false, config: false, build-type: 'Release', msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64' } - - { name: 'macOS', os: 'macos-latest', dx5: false, config: true, build-type: 'Release', brew: true, werror: true, clang-tidy: false } - steps: - - name: Setup vcvars - if: ${{ !!matrix.msvc }} - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: ${{ matrix.vc-arch }} - - - name: Set up MSYS2 - if: ${{ !!matrix.msystem }} - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.msystem }} - install: >- - ${{ matrix.msys-env }}-cc - ${{ matrix.msys-env }}-cmake - ${{ matrix.msys-env }}-ninja - ${{ matrix.msys-env }}-clang-tools-extra - ${{ (matrix.config && format('{0}-qt6-base', matrix.msys-env)) || '' }} - - - name: Install Linux dependencies (apt-get) - if: ${{ matrix.linux }} - run: | - sudo apt-get update - sudo apt-get install -y \ - libx11-dev libxext-dev libxrandr-dev libxrender-dev libxfixes-dev libxi-dev libxinerama-dev \ - libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev libglew-dev qt6-base-dev \ - libasound2-dev - - - name: Install macOS dependencies (brew) - if: ${{ matrix.brew }} - run: | - brew update - brew install cmake ninja llvm qt6 - echo "LLVM_ROOT=$(brew --prefix llvm)/bin" >> $GITHUB_ENV - - - name: Setup Emscripten - uses: mymindstorm/setup-emsdk@master - if: ${{ matrix.emsdk }} - - - name: Setup ninja - if: ${{ matrix.msvc }} - uses: ashutoshvarma/setup-ninja@master - - - uses: actions/checkout@v4 - - - name: Configure (CMake) - run: | - ${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \ - -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ - -DISLE_USE_DX5=${{ !!matrix.dx5 }} \ - -DISLE_BUILD_CONFIG=${{ matrix.config }} \ - -DENABLE_CLANG_TIDY=${{ !!matrix.clang-tidy }} \ - -DISLE_WERROR=${{ !!matrix.werror }} \ - -DISLE_DEBUG=OFF \ - -Werror=dev - - - name: Build (CMake) - run: cmake --build build --verbose - - - name: Package (CPack) - run: | - cd build - cpack . - - - name: Upload Artifact - uses: actions/upload-artifact@main - with: - name: Release-${{ matrix.name }} - path: | - build/dist/isle-* - - release: - name: 'Release' - runs-on: ubuntu-latest - needs: build - steps: - - name: Download All Artifacts - uses: actions/download-artifact@main - with: - pattern: Release-* - path: Release - merge-multiple: true - - - name: Checkout uploadtool - uses: actions/checkout@v4 - with: - repository: 'probonopd/uploadtool' - path: 'uploadtool' - - - name: Upload Continuous Release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - ./uploadtool/upload.sh Release/* diff --git a/.gitignore b/.gitignore index 662466db..1610ba3d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,9 @@ LEGO1.DLL # Kate - Text /.cache + +# Flatpak build cache +**/.flatpak-builder/ + +# Flatpak build dir +**/flatpak-build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 334580c0..95441b92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,9 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") include(CheckCXXSourceCompiles) include(CMakeDependentOption) include(CMakePushCheckState) +include(cmake/detectcpu.cmake) + +DetectTargetCPUArchitectures(ISLE_CPUS) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -35,14 +38,14 @@ option(ISLE_WERROR "Treat warnings as errors" OFF) option(ISLE_DEBUG "Enable imgui debug" ON) cmake_dependent_option(ISLE_USE_DX5 "Build with internal DirectX 5 SDK" "${NOT_MINGW}" "WIN32;CMAKE_SIZEOF_VOID_P EQUAL 4" OFF) cmake_dependent_option(ISLE_MINIWIN "Use miniwin" ON "NOT ISLE_USE_DX5" OFF) -cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN" OFF) +cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "(MSVC OR ISLE_MINIWIN) AND (NOT PSP) AND (NOT NINTENDO_3DS)" OFF) cmake_dependent_option(ISLE_COMPILE_SHADERS "Compile shaders" ON "SDL_SHADERCROSS_BIN;TARGET Python3::Interpreter" OFF) -option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON) +cmake_dependent_option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON "NOT PSP" OFF) option(ENABLE_CLANG_TIDY "Enable clang-tidy") option(DOWNLOAD_DEPENDENCIES "Download dependencies" ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" CACHE PATH "Directory where to put executables and dll") set(ISLE_EMSCRIPTEN_HOST "" CACHE STRING "Host URL for Emscripten streaming (e.g., https://test.com)") -cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN" OFF) +cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN AND NOT PSP" OFF) message(STATUS "Isle app: ${ISLE_BUILD_APP}") message(STATUS "Config app: ${ISLE_BUILD_CONFIG}") @@ -55,15 +58,19 @@ add_library(Isle::iniparser INTERFACE IMPORTED) if (DOWNLOAD_DEPENDENCIES) # FetchContent downloads and configures dependencies - message(STATUS "Fetching SDL3 and iniparser. This might take a while...") include(FetchContent) - FetchContent_Declare( - SDL3 - GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git" - GIT_TAG "main" - EXCLUDE_FROM_ALL - ) - FetchContent_MakeAvailable(SDL3) + if (NOT PSP) + message(STATUS "Fetching SDL3 and iniparser. This might take a while...") + FetchContent_Declare( + SDL3 + GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git" + GIT_TAG "main" + EXCLUDE_FROM_ALL + ) + FetchContent_MakeAvailable(SDL3) + else() + find_package(SDL3 CONFIG REQUIRED) + endif() FetchContent_Declare( iniparser @@ -345,6 +352,11 @@ if (WIN32) target_link_libraries(lego1 INTERFACE winmm) endif() target_link_libraries(lego1 PRIVATE libsmacker miniaudio) + +if (PSP) + target_link_libraries(lego1 PRIVATE atomic) +endif() + target_include_directories(lego1 PUBLIC $>) # lego1_impl sources @@ -502,6 +514,9 @@ if (ISLE_BUILD_APP) ) target_compile_definitions(isle PRIVATE ISLE_DEBUG) target_link_libraries(isle PRIVATE imgui) + if(PSP) + target_compile_definitions(imgui PRIVATE IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) + endif() find_path(valgrind_INCLUDE_PATH NAMES valgrind/callgrind.h) if(valgrind_INCLUDE_PATH) # Run isle under valgrind to create a profile. Use e.g. kcachegrind to view the profile. @@ -520,12 +535,18 @@ if (ISLE_BUILD_APP) target_compile_definitions(isle PRIVATE "ISLE_EMSCRIPTEN_HOST=\"${ISLE_EMSCRIPTEN_HOST}\"") set_property(TARGET isle PROPERTY SUFFIX ".html") endif() + if(NINTENDO_3DS) + target_sources(isle PRIVATE + ISLE/3ds/apthooks.cpp + ISLE/3ds/config.cpp + ) + endif() endif() if (ISLE_BUILD_CONFIG) find_package(Qt6 REQUIRED COMPONENTS Core Widgets) qt_standard_project_setup() - qt_add_executable(config WIN32 + qt_add_executable(isle-config WIN32 LEGO1/mxdirectx/mxdirectxinfo.cpp LEGO1/mxdirectx/legodxinfo.cpp CONFIG/config.cpp @@ -535,22 +556,22 @@ if (ISLE_BUILD_CONFIG) CONFIG/res/config.rc CONFIG/res/config.qrc ) - target_link_libraries(config PRIVATE Qt6::Core Qt6::Widgets) - set_property(TARGET config PROPERTY AUTOMOC ON) - set_property(TARGET config PROPERTY AUTORCC ON) - set_property(TARGET config PROPERTY AUTOUIC ON) - set_property(TARGET config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res") - list(APPEND isle_targets config) - target_compile_definitions(config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG) - target_include_directories(config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1") - target_include_directories(config PUBLIC "$") + target_link_libraries(isle-config PRIVATE Qt6::Core Qt6::Widgets) + set_property(TARGET isle-config PROPERTY AUTOMOC ON) + set_property(TARGET isle-config PROPERTY AUTORCC ON) + set_property(TARGET isle-config PROPERTY AUTOUIC ON) + set_property(TARGET isle-config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res") + list(APPEND isle_targets isle-config) + target_compile_definitions(isle-config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG) + target_include_directories(isle-config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1") + target_include_directories(isle-config PUBLIC "$") if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14) - target_link_libraries(config PRIVATE DirectX5::DirectX5) + target_link_libraries(isle-config PRIVATE DirectX5::DirectX5) endif() - target_compile_definitions(config PRIVATE DIRECT3D_VERSION=0x500) - target_link_libraries(config PRIVATE SDL3::SDL3 Isle::iniparser) + target_compile_definitions(isle-config PRIVATE DIRECT3D_VERSION=0x500) + target_link_libraries(isle-config PRIVATE SDL3::SDL3 Isle::iniparser) if (NOT ISLE_MINIWIN) - target_link_libraries(config PRIVATE ddraw dxguid) + target_link_libraries(isle-config PRIVATE ddraw dxguid) endif() endif() @@ -564,8 +585,8 @@ if (MSVC) if (TARGET isle) target_compile_definitions(isle PRIVATE "_CRT_SECURE_NO_WARNINGS") endif() - if (TARGET config) - target_compile_definitions(config PRIVATE "_CRT_SECURE_NO_WARNINGS") + if (TARGET isle-config) + target_compile_definitions(isle-config PRIVATE "_CRT_SECURE_NO_WARNINGS") endif() endif() # Visual Studio 2017 version 15.7 needs "/Zc:__cplusplus" for __cplusplus @@ -574,8 +595,8 @@ if (MSVC) if (TARGET isle) target_compile_options(isle PRIVATE "-Zc:__cplusplus") endif() - if (TARGET config) - target_compile_options(config PRIVATE "-Zc:__cplusplus") + if (TARGET isle-config) + target_compile_options(isle-config PRIVATE "-Zc:__cplusplus") endif() endif() endif() @@ -624,7 +645,9 @@ else() include(GNUInstallDirs) endif() -set(ISLE_PACKAGE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CACHE STRING "Platform name of the package") +string(REPLACE ";" "-" ISLE_CPUS_STRING "${ISLE_CPUS}") +string(TOLOWER "${ISLE_CPUS_STRING}" ISLE_CPUS_STRING) +set(ISLE_PACKAGE_NAME "${CMAKE_SYSTEM_NAME}-${ISLE_CPUS_STRING}" CACHE STRING "Platform name of the package") if(BUILD_SHARED_LIBS) list(APPEND install_extra_targets lego1) endif() @@ -633,7 +656,7 @@ install(TARGETS isle ${install_extra_targets} LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) if (ISLE_BUILD_CONFIG) - install(TARGETS config + install(TARGETS isle-config RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) endif() @@ -643,11 +666,39 @@ if(EMSCRIPTEN) ) endif() +add_subdirectory(packaging) + set(CPACK_PACKAGE_DIRECTORY "dist") -set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}-${CMAKE_SYSTEM_PROCESSOR}") +set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}") +if(NINTENDO_3DS) + ctr_generate_smdh(isle.smdh + NAME "LEGO Island" + TITLE "LEGO Island" + DESCRIPTION "LEGO Island for the Nintendo 3DS" + AUTHOR "isledecomp/isle-portable" + VERSION "${PROJECT_VERSION}" + ICON "ISLE/res/3ds/isle.png" + ) + + ctr_create_3dsx(isle SMDH isle.smdh) + install(FILES "$/isle.3dsx" DESTINATION "${CMAKE_INSTALL_BINDIR}") +endif() if(MSVC) set(CPACK_GENERATOR ZIP) +elseif(PSP) + # Create an EBOOT.PBP file + create_pbp_file( + TARGET ${PROJECT_NAME} + ICON_PATH "${CMAKE_SOURCE_DIR}/XMB/Game/ICON0.png" + ANIM_PATH "${CMAKE_SOURCE_DIR}/XMB/Game/ICON1.pmf" + BACKGROUND_PATH "${CMAKE_SOURCE_DIR}/XMB/Game/PIC1.png" + PREVIEW_PATH NULL + MUSIC_PATH "${CMAKE_SOURCE_DIR}/XMB/Game/SND0.at3" + TITLE ${PROJECT_NAME} + VERSION 01.00 + ) else() set(CPACK_GENERATOR TGZ) endif() + include(CPack) diff --git a/CONFIG/MainDlg.cpp b/CONFIG/MainDlg.cpp index b90f1f66..e2c526f6 100644 --- a/CONFIG/MainDlg.cpp +++ b/CONFIG/MainDlg.cpp @@ -11,6 +11,8 @@ #include "res/resource.h" #include +#include +#include #include #include @@ -59,6 +61,7 @@ CMainDialog::CMainDialog(QWidget* pParent) : QDialog(pParent) connect(m_ui->fullscreenCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxFullscreen); connect(m_ui->okButton, &QPushButton::clicked, this, &CMainDialog::accept); connect(m_ui->cancelButton, &QPushButton::clicked, this, &CMainDialog::reject); + connect(m_ui->launchButton, &QPushButton::clicked, this, &CMainDialog::launch); connect(m_ui->dataPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectDataPathDialog); connect(m_ui->savePathOpen, &QPushButton::clicked, this, &CMainDialog::SelectSavePathDialog); @@ -155,6 +158,30 @@ void CMainDialog::accept() QDialog::accept(); } +void CMainDialog::launch() +{ + if (m_modified) { + currentConfigApp->WriteRegisterSettings(); + } + + QDir::setCurrent(QCoreApplication::applicationDirPath()); + + QMessageBox msgBox = QMessageBox( + QMessageBox::Warning, + QString("Error!"), + QString("Unable to locate isle executable!"), + QMessageBox::Close + ); + + if (!QProcess::startDetached("./isle")) { // Check in isle-config directory + if (!QProcess::startDetached("isle")) { // Check in $PATH + msgBox.exec(); + } + } + + QDialog::accept(); +} + // FUNCTION: CONFIG 0x00404360 void CMainDialog::UpdateInterface() { diff --git a/CONFIG/MainDlg.h b/CONFIG/MainDlg.h index 65782037..372a1156 100644 --- a/CONFIG/MainDlg.h +++ b/CONFIG/MainDlg.h @@ -45,6 +45,7 @@ private slots: void OnCheckboxFullscreen(bool checked); void accept() override; void reject() override; + void launch(); void SelectDataPathDialog(); void SelectSavePathDialog(); void DataPathEdited(); diff --git a/CONFIG/config.cpp b/CONFIG/config.cpp index bd38e669..9030ae67 100644 --- a/CONFIG/config.cpp +++ b/CONFIG/config.cpp @@ -56,9 +56,17 @@ bool CConfigApp::InitInstance() return false; } m_device_enumerator = new LegoDeviceEnumerate; - if (m_device_enumerator->DoEnumerate()) { + SDL_Window* window = SDL_CreateWindow("Test window", 640, 480, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); + HWND hWnd; +#ifdef MINIWIN + hWnd = reinterpret_cast(window); +#else + hWnd = (HWND) SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); +#endif + if (m_device_enumerator->DoEnumerate(hWnd)) { return FALSE; } + SDL_DestroyWindow(window); m_driver = NULL; m_device = NULL; m_full_screen = TRUE; diff --git a/CONFIG/res/maindialog.ui b/CONFIG/res/maindialog.ui index 78c5a174..c2760e7c 100644 --- a/CONFIG/res/maindialog.ui +++ b/CONFIG/res/maindialog.ui @@ -508,8 +508,11 @@ + + Save configuration and close the config tool. + - OK + Save and Exit true @@ -517,9 +520,22 @@ - + + + Save configuration and launch LEGO Island. + - Cancel + Save and Launch + + + + + + + Discard changed settings and close the config tool. + + + Exit without saving diff --git a/ISLE/3ds/apthooks.cpp b/ISLE/3ds/apthooks.cpp new file mode 100644 index 00000000..3cd9c3e1 --- /dev/null +++ b/ISLE/3ds/apthooks.cpp @@ -0,0 +1,30 @@ +#include "apthooks.h" + +#include "legomain.h" +#include "misc.h" + +aptHookCookie g_aptCookie; + +void N3DS_AptHookCallback(APT_HookType hookType, void* param) +{ + switch (hookType) { + case APTHOOK_ONSLEEP: + case APTHOOK_ONSUSPEND: + Lego()->Pause(); + break; + case APTHOOK_ONWAKEUP: + case APTHOOK_ONRESTORE: + Lego()->Resume(); + break; + case APTHOOK_ONEXIT: + Lego()->CloseMainWindow(); + break; + default: + break; + } +} + +void N3DS_SetupAptHooks() +{ + aptHook(&g_aptCookie, N3DS_AptHookCallback, NULL); +} diff --git a/ISLE/3ds/apthooks.h b/ISLE/3ds/apthooks.h new file mode 100644 index 00000000..7bfa30f7 --- /dev/null +++ b/ISLE/3ds/apthooks.h @@ -0,0 +1,9 @@ +#ifndef N3DS_APTHOOKS_H +#define N3DS_APTHOOKS_H + +#include <3ds.h> + +void N3DS_AptHookCallback(APT_HookType hookType, void* param); +void N3DS_SetupAptHooks(); + +#endif // N3DS_APTHOOKS_H diff --git a/ISLE/3ds/config.cpp b/ISLE/3ds/config.cpp new file mode 100644 index 00000000..dc5dd8dc --- /dev/null +++ b/ISLE/3ds/config.cpp @@ -0,0 +1,22 @@ +#include "config.h" + +#include +#include + +void N3DS_SetupDefaultConfigOverrides(dictionary* p_dictionary) +{ + SDL_Log("Overriding default config for 3DS"); + + // We are currently not bundling the assets into romfs. + // User must place assets in sdmc:/3ds/isle where + // sdmc:/3ds/isle/LEGO/SCRIPTS/CREDITS.si exists, for example. + iniparser_set(p_dictionary, "isle:diskpath", "sdmc:/3ds/isle/LEGO/disk"); + iniparser_set(p_dictionary, "isle:cdpath", "sdmc:/3ds/isle"); + + // TODO: Save path: can we use libctru FS save data functions? Would be neat, especially for CIA install + // Extra / at the end causes some issues + iniparser_set(p_dictionary, "isle:savepath", "sdmc:/3ds/isle"); + + // Use e_noAnimation/cut transition + iniparser_set(p_dictionary, "isle:Transition Type", "1"); +} diff --git a/ISLE/3ds/config.h b/ISLE/3ds/config.h new file mode 100644 index 00000000..64d3a1b0 --- /dev/null +++ b/ISLE/3ds/config.h @@ -0,0 +1,8 @@ +#ifndef N3DS_CONFIG_H +#define N3DS_CONFIG_H + +#include "dictionary.h" + +void N3DS_SetupDefaultConfigOverrides(dictionary* p_dictionary); + +#endif // N3DS_CONFIG_H diff --git a/ISLE/emscripten/libwasmfs_fetch.js.patch b/ISLE/emscripten/libwasmfs_fetch.js.patch index eb16caf0..de8fab60 100644 --- a/ISLE/emscripten/libwasmfs_fetch.js.patch +++ b/ISLE/emscripten/libwasmfs_fetch.js.patch @@ -1,5 +1,5 @@ diff --git a/src/lib/libwasmfs_fetch.js b/src/lib/libwasmfs_fetch.js -index e8c9f7e21..1c0eea957 100644 +index e8c9f7e21..caf1971d2 100644 --- a/src/lib/libwasmfs_fetch.js +++ b/src/lib/libwasmfs_fetch.js @@ -38,36 +38,7 @@ addToLibrary({ @@ -89,7 +89,21 @@ index e8c9f7e21..1c0eea957 100644 return Promise.resolve(); } -@@ -164,6 +156,21 @@ addToLibrary({ +@@ -156,14 +148,31 @@ addToLibrary({ + return readLength; + }, + getSize: async (file) => { +- try { +- await getFileRange(file, 0, 0); +- } catch (failedResponse) { +- return 0; ++ if (!(file in wasmFS$JSMemoryRanges)) { ++ try { ++ await getFileRange(file, undefined, undefined); ++ } catch (failedResponse) { ++ return 0; ++ } + } return wasmFS$JSMemoryRanges[file].size; }, }; diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 2ca33577..8f69bf4e 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -50,6 +50,11 @@ #include "emscripten/messagebox.h" #endif +#ifdef __3DS__ +#include "3ds/apthooks.h" +#include "3ds/config.h" +#endif + DECOMP_SIZE_ASSERT(IsleApp, 0x8c) // GLOBAL: ISLE 0x410030 @@ -96,11 +101,7 @@ IsleApp::IsleApp() m_cdPath = NULL; m_deviceId = NULL; m_savePath = NULL; -#ifdef __EMSCRIPTEN__ - m_fullScreen = FALSE; -#else m_fullScreen = TRUE; -#endif m_flipSurfaces = FALSE; m_backBuffersInVram = TRUE; m_using8bit = FALSE; @@ -140,6 +141,7 @@ IsleApp::IsleApp() m_iniPath = NULL; m_maxLod = RealtimeView::GetUserMaxLOD(); m_maxAllowedExtras = m_islandQuality <= 1 ? 10 : 20; + m_transitionType = MxTransitionManager::e_mosaic; } // FUNCTION: ISLE 0x4011a0 @@ -316,6 +318,9 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) }, NULL ); +#endif +#ifdef __3DS__ + N3DS_SetupAptHooks(); #endif return SDL_APP_CONTINUE; } @@ -657,9 +662,12 @@ MxResult IsleApp::SetupWindow() SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, g_targetHeight); SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, m_fullScreen); SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE); -#ifdef MINIWIN +#if defined(MINIWIN) && !defined(__3DS__) SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); +#ifndef __PSP__ + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); +#endif #endif window = SDL_CreateWindowWithProperties(props); @@ -703,6 +711,7 @@ MxResult IsleApp::SetupWindow() return FAILURE; } + DetectGameVersion(); GameState()->SerializePlayersInfo(LegoStorage::c_read); GameState()->SerializeScoreHistory(LegoStorage::c_read); @@ -725,6 +734,7 @@ MxResult IsleApp::SetupWindow() LegoBuildingManager::configureLegoBuildingManager(m_islandQuality); LegoROI::configureLegoROI(iVar10); LegoAnimationManager::configureLegoAnimationManager(m_maxAllowedExtras); + MxTransitionManager::configureMxTransitionManager(m_transitionType); RealtimeView::SetUserMaxLOD(m_maxLod); if (LegoOmni::GetInstance()) { if (LegoOmni::GetInstance()->GetInputManager()) { @@ -823,7 +833,11 @@ bool IsleApp::LoadConfig() SDL_snprintf(buf, sizeof(buf), "%f", m_maxLod); iniparser_set(dict, "isle:Max LOD", buf); iniparser_set(dict, "isle:Max Allowed Extras", SDL_itoa(m_maxAllowedExtras, buf, 10)); + iniparser_set(dict, "isle:Transition Type", SDL_itoa(m_transitionType, buf, 10)); +#ifdef __3DS__ + N3DS_SetupDefaultConfigOverrides(dict); +#endif iniparser_dump_ini(dict, iniFP); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig); fclose(iniFP); @@ -852,7 +866,13 @@ bool IsleApp::LoadConfig() strcpy(m_mediaPath, mediaPath); m_flipSurfaces = iniparser_getboolean(dict, "isle:Flip Surfaces", m_flipSurfaces); + +#ifdef __EMSCRIPTEN__ + m_fullScreen = FALSE; +#else m_fullScreen = iniparser_getboolean(dict, "isle:Full Screen", m_fullScreen); +#endif + m_wideViewAngle = iniparser_getboolean(dict, "isle:Wide View Angle", m_wideViewAngle); m_use3dSound = iniparser_getboolean(dict, "isle:3DSound", m_use3dSound); m_useMusic = iniparser_getboolean(dict, "isle:Music", m_useMusic); @@ -879,6 +899,8 @@ bool IsleApp::LoadConfig() m_islandTexture = iniparser_getint(dict, "isle:Island Texture", m_islandTexture); m_maxLod = iniparser_getdouble(dict, "isle:Max LOD", m_maxLod); m_maxAllowedExtras = iniparser_getint(dict, "isle:Max Allowed Extras", m_maxAllowedExtras); + m_transitionType = + (MxTransitionManager::TransitionType) iniparser_getint(dict, "isle:Transition Type", m_transitionType); const char* deviceId = iniparser_getstring(dict, "isle:3D Device ID", NULL); if (deviceId != NULL) { @@ -1098,6 +1120,34 @@ MxResult IsleApp::VerifyFilesystem() return SUCCESS; } +void IsleApp::DetectGameVersion() +{ + const char* file = "/lego/scripts/infocntr/infomain.si"; + SDL_PathInfo info; + bool success = false; + + MxString path = MxString(m_hdPath) + file; + path.MapPathToFilesystem(); + if (!(success = SDL_GetPathInfo(path.GetData(), &info))) { + path = MxString(m_cdPath) + file; + path.MapPathToFilesystem(); + success = SDL_GetPathInfo(path.GetData(), &info); + } + + assert(success); + + // File sizes of INFOMAIN.SI in English 1.0 and Japanese 1.0 + Lego()->SetVersion10(info.size == 58130432 || info.size == 57737216); + + if (Lego()->IsVersion10()) { + SDL_Log("Detected game version 1.0"); + SDL_SetWindowTitle(reinterpret_cast(m_windowHandle), "Lego Island"); + } + else { + SDL_Log("Detected game version 1.1"); + } +} + IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice() { LegoVideoManager* videoManager = LegoOmni::GetInstance()->GetVideoManager(); diff --git a/ISLE/isleapp.h b/ISLE/isleapp.h index d76a291b..d0a7f523 100644 --- a/ISLE/isleapp.h +++ b/ISLE/isleapp.h @@ -3,6 +3,7 @@ #include "lego1_export.h" #include "legoutils.h" +#include "mxtransitionmanager.h" #include "mxtypes.h" #include "mxvideoparam.h" @@ -56,6 +57,7 @@ class IsleApp { MxResult ParseArguments(int argc, char** argv); MxResult VerifyFilesystem(); + void DetectGameVersion(); private: char* m_hdPath; // 0x00 @@ -90,9 +92,11 @@ class IsleApp { char* m_iniPath; MxFloat m_maxLod; MxU32 m_maxAllowedExtras; + MxTransitionManager::TransitionType m_transitionType; }; extern IsleApp* g_isle; +extern MxS32 g_closed; extern IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice(); diff --git a/ISLE/isledebug.cpp b/ISLE/isledebug.cpp index a8950fa3..ec27004b 100644 --- a/ISLE/isledebug.cpp +++ b/ISLE/isledebug.cpp @@ -309,7 +309,7 @@ void IsleDebug_Render() if (ImGui::TreeNode("Sound Manager")) { LegoSoundManager* soundManager = lego->GetSoundManager(); Sint32 oldVolume = soundManager->GetVolume(); - Sint32 volume = oldVolume; + int volume = oldVolume; ImGui::SliderInt("volume", &volume, 0, 100); if (volume != oldVolume) { soundManager->SetVolume(volume); diff --git a/ISLE/res/3ds/isle.png b/ISLE/res/3ds/isle.png new file mode 100644 index 00000000..8fd9f2c4 Binary files /dev/null and b/ISLE/res/3ds/isle.png differ diff --git a/LEGO1/lego/legoomni/include/ambulance.h b/LEGO1/lego/legoomni/include/ambulance.h index 97e6ba38..cb6a396a 100644 --- a/LEGO1/lego/legoomni/include/ambulance.h +++ b/LEGO1/lego/legoomni/include/ambulance.h @@ -11,6 +11,12 @@ class MxEndActionNotificationParam; // SIZE 0x24 class AmbulanceMissionState : public LegoState { public: + enum { + e_ready = 0, + e_enteredAmbulance = 1, + e_prepareAmbulance = 2, + }; + AmbulanceMissionState(); // FUNCTION: LEGO1 0x10037440 @@ -125,18 +131,18 @@ class AmbulanceMissionState : public LegoState { // SYNTHETIC: LEGO1 0x100376c0 // AmbulanceMissionState::`scalar deleting destructor' - undefined4 m_unk0x08; // 0x08 - MxLong m_startTime; // 0x0c - MxS16 m_peScore; // 0x10 - MxS16 m_maScore; // 0x12 - MxS16 m_paScore; // 0x14 - MxS16 m_niScore; // 0x16 - MxS16 m_laScore; // 0x18 - MxS16 m_peHighScore; // 0x1a - MxS16 m_maHighScore; // 0x1c - MxS16 m_paHighScore; // 0x1e - MxS16 m_niHighScore; // 0x20 - MxS16 m_laHighScore; // 0x22 + MxU32 m_state; // 0x08 + MxLong m_startTime; // 0x0c + MxS16 m_peScore; // 0x10 + MxS16 m_maScore; // 0x12 + MxS16 m_paScore; // 0x14 + MxS16 m_niScore; // 0x16 + MxS16 m_laScore; // 0x18 + MxS16 m_peHighScore; // 0x1a + MxS16 m_maHighScore; // 0x1c + MxS16 m_paHighScore; // 0x1e + MxS16 m_niHighScore; // 0x20 + MxS16 m_laHighScore; // 0x22 }; // VTABLE: LEGO1 0x100d71a8 @@ -177,15 +183,21 @@ class Ambulance : public IslePathActor { virtual MxLong HandleEndAction(MxEndActionNotificationParam& p_param); // vtable+0xf4 void CreateState(); - void FUN_10036e60(); + void Init(); void ActivateSceneActions(); void StopActions(); - void FUN_10037250(); + void Reset(); // SYNTHETIC: LEGO1 0x10036130 // Ambulance::`scalar deleting destructor' private: + enum { + e_none = 0, + e_waiting = 1, + e_finished = 3, + }; + void PlayAnimation(IsleScript::Script p_objectId); void PlayFinalAnimation(IsleScript::Script p_objectId); void StopAction(IsleScript::Script p_objectId); @@ -196,9 +208,9 @@ class Ambulance : public IslePathActor { AmbulanceMissionState* m_state; // 0x164 MxS16 m_unk0x168; // 0x168 MxS16 m_actorId; // 0x16a - MxS16 m_unk0x16c; // 0x16c - MxS16 m_unk0x16e; // 0x16e - MxS16 m_unk0x170; // 0x170 + MxS16 m_atPoliceTask; // 0x16c + MxS16 m_atBeachTask; // 0x16e + MxS16 m_taskState; // 0x170 MxS16 m_unk0x172; // 0x172 IsleScript::Script m_lastAction; // 0x174 IsleScript::Script m_lastAnimation; // 0x178 diff --git a/LEGO1/lego/legoomni/include/hospital.h b/LEGO1/lego/legoomni/include/hospital.h index 8629c2c2..be198776 100644 --- a/LEGO1/lego/legoomni/include/hospital.h +++ b/LEGO1/lego/legoomni/include/hospital.h @@ -123,7 +123,7 @@ class Hospital : public LegoWorld { MxLong m_copLedAnimTimer; // 0x11c MxLong m_pizzaLedAnimTimer; // 0x120 MxLong m_time; // 0x124 - undefined m_unk0x128; // 0x128 + MxBool m_exited; // 0x128 }; #endif // HOSPITAL_H diff --git a/LEGO1/lego/legoomni/include/legoanimpresenter.h b/LEGO1/lego/legoomni/include/legoanimpresenter.h index 85917cbc..a3d6cd60 100644 --- a/LEGO1/lego/legoomni/include/legoanimpresenter.h +++ b/LEGO1/lego/legoomni/include/legoanimpresenter.h @@ -121,7 +121,7 @@ class LegoAnimPresenter : public MxVideoPresenter { void SubstituteVariables(); void FUN_1006b900(LegoAnim* p_anim, MxLong p_time, Matrix4* p_matrix); void FUN_1006b9a0(LegoAnim* p_anim, MxLong p_time, Matrix4* p_matrix); - void FUN_1006c8a0(MxBool p_bool); + void SetDisabled(MxBool p_disabled); LegoAnim* m_anim; // 0x64 LegoROI** m_roiMap; // 0x68 diff --git a/LEGO1/lego/legoomni/include/legobuildingmanager.h b/LEGO1/lego/legoomni/include/legobuildingmanager.h index 5dd83221..4d27695c 100644 --- a/LEGO1/lego/legoomni/include/legobuildingmanager.h +++ b/LEGO1/lego/legoomni/include/legobuildingmanager.h @@ -79,7 +79,7 @@ class LegoBuildingManager : public MxCore { MxBool SwitchMove(LegoEntity* p_entity); MxBool SwitchMood(LegoEntity* p_entity); MxU32 GetAnimationId(LegoEntity* p_entity); - MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_state); + MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood); MxBool DecrementCounter(LegoEntity* p_entity); MxBool DecrementCounter(MxS32 p_index); MxBool DecrementCounter(LegoBuildingInfo* p_data); diff --git a/LEGO1/lego/legoomni/include/legocharactermanager.h b/LEGO1/lego/legoomni/include/legocharactermanager.h index 2f9d9551..9b4c5313 100644 --- a/LEGO1/lego/legoomni/include/legocharactermanager.h +++ b/LEGO1/lego/legoomni/include/legocharactermanager.h @@ -88,7 +88,7 @@ class LegoCharacterManager { MxBool SwitchMove(LegoROI* p_roi); MxBool SwitchMood(LegoROI* p_roi); MxU32 GetAnimationId(LegoROI* p_roi); - MxU32 GetSoundId(LegoROI* p_roi, MxBool p_und); + MxU32 GetSoundId(LegoROI* p_roi, MxBool p_basedOnMood); MxU8 GetMood(LegoROI* p_roi); LegoROI* CreateAutoROI(const char* p_name, const char* p_lodName, MxBool p_createEntity); MxResult UpdateBoundingSphereAndBox(LegoROI* p_roi); diff --git a/LEGO1/lego/legoomni/include/legoentity.h b/LEGO1/lego/legoomni/include/legoentity.h index eecf6867..a6d9d9ee 100644 --- a/LEGO1/lego/legoomni/include/legoentity.h +++ b/LEGO1/lego/legoomni/include/legoentity.h @@ -28,7 +28,7 @@ class LegoEntity : public MxEntity { }; enum { - c_altBit1 = 0x01 + c_disabled = 0x01 }; LegoEntity() { Init(); } @@ -68,13 +68,13 @@ class LegoEntity : public MxEntity { // FUNCTION: BETA10 0x10013260 virtual void SetWorldSpeed(MxFloat p_worldSpeed) { m_worldSpeed = p_worldSpeed; } // vtable+0x30 - virtual void ClickSound(MxBool p_und); // vtable+0x34 - virtual void ClickAnimation(); // vtable+0x38 - virtual void SwitchVariant(); // vtable+0x3c - virtual void SwitchSound(); // vtable+0x40 - virtual void SwitchMove(); // vtable+0x44 - virtual void SwitchColor(LegoROI* p_roi); // vtable+0x48 - virtual void SwitchMood(); // vtable+0x4c + virtual void ClickSound(MxBool p_basedOnMood); // vtable+0x34 + virtual void ClickAnimation(); // vtable+0x38 + virtual void SwitchVariant(); // vtable+0x3c + virtual void SwitchSound(); // vtable+0x40 + virtual void SwitchMove(); // vtable+0x44 + virtual void SwitchColor(LegoROI* p_roi); // vtable+0x48 + virtual void SwitchMood(); // vtable+0x4c void FUN_10010c30(); void SetType(MxU8 p_type); @@ -83,7 +83,7 @@ class LegoEntity : public MxEntity { Mx3DPointFloat GetWorldUp(); Mx3DPointFloat GetWorldPosition(); - MxBool GetUnknown0x10IsSet(MxU8 p_flag) { return m_unk0x10 & p_flag; } + MxBool IsInteraction(MxU8 p_flag) { return m_interaction & p_flag; } MxBool GetFlagsIsSet(MxU8 p_flag) { return m_flags & p_flag; } MxU8 GetFlags() { return m_flags; } @@ -101,14 +101,14 @@ class LegoEntity : public MxEntity { void SetFlags(MxU8 p_flags) { m_flags = p_flags; } void SetFlag(MxU8 p_flag) { m_flags |= p_flag; } void ClearFlag(MxU8 p_flag) { m_flags &= ~p_flag; } - void SetUnknown0x10Flag(MxU8 p_flag) { m_unk0x10 |= p_flag; } - void ClearUnknown0x10Flag(MxU8 p_flag) { m_unk0x10 &= ~p_flag; } + void SetInteractionFlag(MxU8 p_flag) { m_interaction |= p_flag; } + void ClearInteractionFlag(MxU8 p_flag) { m_interaction &= ~p_flag; } protected: void Init(); void SetWorld(); - MxU8 m_unk0x10; // 0x10 + MxU8 m_interaction; // 0x10 MxU8 m_flags; // 0x11 Mx3DPointFloat m_worldLocation; // 0x14 Mx3DPointFloat m_worldDirection; // 0x28 diff --git a/LEGO1/lego/legoomni/include/legomain.h b/LEGO1/lego/legoomni/include/legomain.h index fa9a91c5..c27be280 100644 --- a/LEGO1/lego/legoomni/include/legomain.h +++ b/LEGO1/lego/legoomni/include/legomain.h @@ -200,6 +200,9 @@ class LegoOmni : public MxOmni { SDL_PushEvent(&event); } + void SetVersion10(MxBool p_version10) { m_version10 = p_version10; } + MxBool IsVersion10() { return m_version10; } + // SYNTHETIC: LEGO1 0x10058b30 // LegoOmni::`scalar deleting destructor' @@ -221,6 +224,7 @@ class LegoOmni : public MxOmni { MxDSAction m_action; // 0xa0 MxBackgroundAudioManager* m_bkgAudioManager; // 0x134 MxTransitionManager* m_transitionManager; // 0x138 + MxBool m_version10; public: MxBool m_unk0x13c; // 0x13c diff --git a/LEGO1/lego/legoomni/include/legopathactor.h b/LEGO1/lego/legoomni/include/legopathactor.h index 6566c6fb..8df0e9db 100644 --- a/LEGO1/lego/legoomni/include/legopathactor.h +++ b/LEGO1/lego/legoomni/include/legopathactor.h @@ -13,7 +13,7 @@ struct LegoPathEdgeContainer; struct LegoOrientedEdge; class LegoWEEdge; -extern MxLong g_unk0x100f3308; +extern MxLong g_timeLastHitSoundPlayed; extern const char* g_strHIT_WALL_SOUND; // VTABLE: LEGO1 0x100d6e28 diff --git a/LEGO1/lego/legoomni/include/legoplantmanager.h b/LEGO1/lego/legoomni/include/legoplantmanager.h index 1e17d6f4..3cdb5105 100644 --- a/LEGO1/lego/legoomni/include/legoplantmanager.h +++ b/LEGO1/lego/legoomni/include/legoplantmanager.h @@ -49,12 +49,12 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore { MxBool SwitchMove(LegoEntity* p_entity); MxBool SwitchMood(LegoEntity* p_entity); MxU32 GetAnimationId(LegoEntity* p_entity); - MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_state); + MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood); LegoPlantInfo* GetInfoArray(MxS32& p_length); LegoEntity* CreatePlant(MxS32 p_index, LegoWorld* p_world, LegoOmni::World p_worldId); MxBool DecrementCounter(LegoEntity* p_entity); void ScheduleAnimation(LegoEntity* p_entity, MxLong p_length); - MxResult FUN_10026410(); + MxResult DetermineBoundaries(); void ClearCounters(); void SetInitialCounters(); @@ -77,11 +77,11 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore { static MxS32 g_maxMove[4]; static MxU32 g_maxSound; - LegoOmni::World m_worldId; // 0x08 - undefined m_unk0x0c; // 0x0c - AnimEntry* m_entries[5]; // 0x10 - MxS8 m_numEntries; // 0x24 - LegoWorld* m_world; // 0x28 + LegoOmni::World m_worldId; // 0x08 + MxBool m_boundariesDetermined; // 0x0c + AnimEntry* m_entries[5]; // 0x10 + MxS8 m_numEntries; // 0x24 + LegoWorld* m_world; // 0x28 friend class DebugViewer; }; diff --git a/LEGO1/lego/legoomni/include/legoracers.h b/LEGO1/lego/legoomni/include/legoracers.h index 76d1071d..3c8ffe07 100644 --- a/LEGO1/lego/legoomni/include/legoracers.h +++ b/LEGO1/lego/legoomni/include/legoracers.h @@ -144,10 +144,10 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { MxResult VTable0x9c() override; // vtable+0x9c virtual void SetMaxLinearVelocity(float p_maxLinearVelocity); - virtual void FUN_10012ff0(float p_param); + virtual void KickCamera(float p_param); virtual MxU32 HandleSkeletonKicks(float p_param1); - static void FUN_10012de0(); + static void InitYouCantStopSound(); static void InitSoundIndices(); // SYNTHETIC: LEGO1 0x10014240 @@ -155,7 +155,7 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { private: undefined m_userState; // 0x54 - float m_unk0x58; // 0x58 + float m_kickStart; // 0x58 Mx3DPointFloat m_unk0x5c; // 0x5c // Names verified by BETA10 0x100cb4a9 diff --git a/LEGO1/lego/legoomni/include/legoracespecial.h b/LEGO1/lego/legoomni/include/legoracespecial.h index fab200aa..fa9dd51d 100644 --- a/LEGO1/lego/legoomni/include/legoracespecial.h +++ b/LEGO1/lego/legoomni/include/legoracespecial.h @@ -44,8 +44,11 @@ class LegoCarRaceActor : public virtual LegoRaceActor { Vector3& p_v3 ) override; // vtable+0x6c void Animate(float p_time) override; // vtable+0x70 - void SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOrientedEdge*& p_edge, float& p_unk0xe4) - override; // vtable+0x98 + void SwitchBoundary( + LegoPathBoundary*& p_boundary, + LegoOrientedEdge*& p_edge, + float& p_unk0xe4 + ) override; // vtable+0x98 MxResult VTable0x9c() override; // vtable+0x9c // LegoCarRaceActor vtable @@ -83,7 +86,7 @@ class LegoCarRaceActor : public virtual LegoRaceActor { protected: MxFloat m_unk0x08; // 0x08 - MxU8 m_unk0x0c; // 0x0c + MxU8 m_animState; // 0x0c // Could be a multiplier for the maximum speed when going straight MxFloat m_unk0x10; // 0x10 diff --git a/LEGO1/lego/legoomni/include/mxtransitionmanager.h b/LEGO1/lego/legoomni/include/mxtransitionmanager.h index 50b80055..dd9dc9ed 100644 --- a/LEGO1/lego/legoomni/include/mxtransitionmanager.h +++ b/LEGO1/lego/legoomni/include/mxtransitionmanager.h @@ -55,6 +55,8 @@ class MxTransitionManager : public MxCore { TransitionType GetTransitionType() { return m_mode; } + LEGO1_EXPORT static void configureMxTransitionManager(TransitionType p_transitionManagerConfig); + // SYNTHETIC: LEGO1 0x1004b9e0 // MxTransitionManager::`scalar deleting destructor' diff --git a/LEGO1/lego/legoomni/src/actors/ambulance.cpp b/LEGO1/lego/legoomni/src/actors/ambulance.cpp index c4bded70..ff87c657 100644 --- a/LEGO1/lego/legoomni/src/actors/ambulance.cpp +++ b/LEGO1/lego/legoomni/src/actors/ambulance.cpp @@ -40,9 +40,9 @@ Ambulance::Ambulance() m_state = NULL; m_unk0x168 = 0; m_actorId = -1; - m_unk0x16c = 0; - m_unk0x16e = 0; - m_unk0x170 = 0; + m_atPoliceTask = 0; + m_atBeachTask = 0; + m_taskState = Ambulance::e_none; m_lastAction = IsleScript::c_noneIsle; m_unk0x172 = 0; m_lastAnimation = IsleScript::c_noneIsle; @@ -73,7 +73,7 @@ MxResult Ambulance::Create(MxDSAction& p_dsAction) m_state = (AmbulanceMissionState*) GameState()->GetState("AmbulanceMissionState"); if (!m_state) { m_state = new AmbulanceMissionState(); - m_state->m_unk0x08 = 0; + m_state->m_state = AmbulanceMissionState::e_ready; GameState()->RegisterState(m_state); } } @@ -173,25 +173,25 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param) m_lastAction = IsleScript::c_noneIsle; } else if (objectId == IsleScript::c_hho027en_RunAnim) { - m_state->m_unk0x08 = 1; + m_state->m_state = AmbulanceMissionState::e_enteredAmbulance; CurrentWorld()->PlaceActor(UserActor()); HandleClick(); m_unk0x172 = 0; TickleManager()->RegisterClient(this, 40000); } else if (objectId == IsleScript::c_hpz047pe_RunAnim || objectId == IsleScript::c_hpz048pe_RunAnim || objectId == IsleScript::c_hpz049bd_RunAnim || objectId == IsleScript::c_hpz053pa_RunAnim) { - if (m_unk0x170 == 3) { + if (m_taskState == Ambulance::e_finished) { PlayAnimation(IsleScript::c_hpz055pa_RunAnim); - m_unk0x170 = 0; + m_taskState = Ambulance::e_none; } else { PlayAnimation(IsleScript::c_hpz053pa_RunAnim); } } else if (objectId == IsleScript::c_hpz050bd_RunAnim || objectId == IsleScript::c_hpz052ma_RunAnim) { - if (m_unk0x170 == 3) { + if (m_taskState == Ambulance::e_finished) { PlayAnimation(IsleScript::c_hpz057ma_RunAnim); - m_unk0x170 = 0; + m_taskState = Ambulance::e_none; } else { PlayAnimation(IsleScript::c_hpz052ma_RunAnim); @@ -204,18 +204,18 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param) m_unk0x172 = 0; TickleManager()->RegisterClient(this, 40000); - if (m_unk0x16c != 0) { + if (m_atPoliceTask != 0) { StopActions(); } } else if (objectId == IsleScript::c_hps116bd_RunAnim || objectId == IsleScript::c_hps118re_RunAnim) { - if (objectId == IsleScript::c_hps116bd_RunAnim && m_unk0x170 != 3) { + if (objectId == IsleScript::c_hps116bd_RunAnim && m_taskState != Ambulance::e_finished) { PlayAction(IsleScript::c_Avo923In_PlayWav); } - if (m_unk0x170 == 3) { + if (m_taskState == Ambulance::e_finished) { PlayAnimation(IsleScript::c_hps117bd_RunAnim); - m_unk0x170 = 0; + m_taskState = Ambulance::e_none; } else { PlayAnimation(IsleScript::c_hps118re_RunAnim); @@ -228,12 +228,12 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param) m_unk0x172 = 0; TickleManager()->RegisterClient(this, 40000); - if (m_unk0x16e != 0) { + if (m_atBeachTask != 0) { StopActions(); } } else if (objectId == IsleScript::c_hho142cl_RunAnim || objectId == IsleScript::c_hho143cl_RunAnim || objectId == IsleScript::c_hho144cl_RunAnim) { - FUN_10037250(); + Reset(); } } @@ -244,18 +244,18 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param) // FUNCTION: BETA10 0x100230bf MxLong Ambulance::HandleButtonDown(LegoControlManagerNotificationParam& p_param) { - if (m_unk0x170 == 1) { + if (m_taskState == Ambulance::e_waiting) { LegoROI* roi = PickROI(p_param.GetX(), p_param.GetY()); if (roi != NULL && !SDL_strcasecmp(roi->GetName(), "ps-gate")) { - m_unk0x170 = 3; + m_taskState = Ambulance::e_finished; return 1; } roi = PickRootROI(p_param.GetX(), p_param.GetY()); if (roi != NULL && !SDL_strcasecmp(roi->GetName(), "gd")) { - m_unk0x170 = 3; + m_taskState = Ambulance::e_finished; return 1; } } @@ -273,9 +273,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) } if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && p_param.GetData() == 0x0b) { - if (m_unk0x16e != 0) { - if (m_unk0x16c != 0) { - m_state->m_unk0x08 = 2; + if (m_atBeachTask != 0) { + if (m_atPoliceTask != 0) { + m_state->m_state = AmbulanceMissionState::e_prepareAmbulance; if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); @@ -300,7 +300,7 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) return 0; } - if (m_unk0x16e != 0) { + if (m_atBeachTask != 0) { if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); } @@ -310,7 +310,7 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) } } - if (m_unk0x16c != 0) { + if (m_atPoliceTask != 0) { if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); } @@ -318,9 +318,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) PlayAction(IsleScript::c_Avo915In_PlayWav); } } - else if (p_param.GetTrigger() == LegoPathStruct::c_s && p_param.GetData() == 0x131 && m_unk0x16e == 0) { - m_unk0x16e = 1; - m_unk0x170 = 1; + else if (p_param.GetTrigger() == LegoPathStruct::c_s && p_param.GetData() == 0x131 && m_atBeachTask == 0) { + m_atBeachTask = 1; + m_taskState = Ambulance::e_waiting; if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); @@ -348,9 +348,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) break; } } - else if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && (p_param.GetData() == 0x22 || p_param.GetData() == 0x23 || p_param.GetData() == 0x24) && m_unk0x16c == 0) { - m_unk0x16c = 1; - m_unk0x170 = 1; + else if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && (p_param.GetData() == 0x22 || p_param.GetData() == 0x23 || p_param.GetData() == 0x24) && m_atPoliceTask == 0) { + m_atPoliceTask = 1; + m_taskState = Ambulance::e_waiting; if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); @@ -371,7 +371,7 @@ MxLong Ambulance::HandleClick() return 1; } - if (m_state->m_unk0x08 == 2) { + if (m_state->m_state == AmbulanceMissionState::e_prepareAmbulance) { return 1; } @@ -390,7 +390,7 @@ MxLong Ambulance::HandleClick() InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_AmbulanceDashboard, NULL); ControlManager()->Register(this); - if (m_state->m_unk0x08 == 1) { + if (m_state->m_state == AmbulanceMissionState::e_enteredAmbulance) { SpawnPlayer(LegoGameState::e_hospitalExited, TRUE, 0); m_state->m_startTime = Timer()->GetTime(); InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_pns018rd_RunAnim, NULL); @@ -401,9 +401,9 @@ MxLong Ambulance::HandleClick() // FUNCTION: LEGO1 0x10036e60 // FUNCTION: BETA10 0x100236bb -void Ambulance::FUN_10036e60() +void Ambulance::Init() { - m_state->m_unk0x08 = 2; + m_state->m_state = AmbulanceMissionState::e_prepareAmbulance; PlayAnimation(IsleScript::c_hho027en_RunAnim); m_lastAction = IsleScript::c_noneIsle; m_lastAnimation = IsleScript::c_noneIsle; @@ -414,7 +414,7 @@ void Ambulance::Exit() { GameState()->m_currentArea = LegoGameState::e_hospitalExterior; StopActions(); - FUN_10037250(); + Reset(); Leave(); } @@ -470,11 +470,11 @@ void Ambulance::ActivateSceneActions() { PlayMusic(JukeboxScript::c_Hospital_Music); - if (m_state->m_unk0x08 == 1) { - m_state->m_unk0x08 = 0; + if (m_state->m_state == AmbulanceMissionState::e_enteredAmbulance) { + m_state->m_state = AmbulanceMissionState::e_ready; PlayAction(IsleScript::c_ham033cl_PlayWav); } - else if (m_unk0x16c != 0 && m_unk0x16e != 0) { + else if (m_atPoliceTask != 0 && m_atBeachTask != 0) { IsleScript::Script objectId; switch (SDL_rand(2)) { @@ -574,14 +574,14 @@ void Ambulance::StopActions() } // FUNCTION: LEGO1 0x10037250 -void Ambulance::FUN_10037250() +void Ambulance::Reset() { StopAction(m_lastAction); BackgroundAudioManager()->RaiseVolume(); ((Act1State*) GameState()->GetState("Act1State"))->m_unk0x018 = 0; - m_state->m_unk0x08 = 0; - m_unk0x16e = 0; - m_unk0x16c = 0; + m_state->m_state = AmbulanceMissionState::e_ready; + m_atBeachTask = 0; + m_atPoliceTask = 0; g_isleFlags |= Isle::c_playMusic; AnimationManager()->EnableCamAnims(TRUE); AnimationManager()->FUN_1005f6d0(TRUE); @@ -629,7 +629,7 @@ void Ambulance::PlayAction(IsleScript::Script p_objectId) // FUNCTION: LEGO1 0x100373a0 AmbulanceMissionState::AmbulanceMissionState() { - m_unk0x08 = 0; + m_state = AmbulanceMissionState::e_ready; m_startTime = 0; m_peScore = 0; m_maScore = 0; diff --git a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp index e69415f6..fa0be996 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp @@ -488,7 +488,7 @@ void LegoCarBuild::FUN_100236d0() m_unk0x110 = NULL; m_unk0x100 = 0; - if (m_animPresenter->AllPartsPlaced()) { + if (m_animPresenter->AllPartsPlaced() && !Lego()->IsVersion10()) { // Note the code duplication with LEGO1 0x10025ee0 switch (m_carId) { case 1: diff --git a/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp b/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp index d46b8a49..8dae5d61 100644 --- a/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp @@ -199,10 +199,10 @@ LegoBuildingInfo g_buildingInfoInit[16] = { MxU32 LegoBuildingManager::g_maxSound = 6; // GLOBAL: LEGO1 0x100f373c -MxU32 g_unk0x100f373c = 0x3c; +MxU32 g_buildingSoundIdOffset = 0x3c; // GLOBAL: LEGO1 0x100f3740 -MxU32 g_unk0x100f3740 = 0x42; +MxU32 g_buildingSoundIdMoodOffset = 0x42; // clang-format off // GLOBAL: LEGO1 0x100f3788 @@ -227,6 +227,8 @@ LegoBuildingInfo g_buildingInfo[16]; // GLOBAL: LEGO1 0x100f3748 MxS32 LegoBuildingManager::g_maxMove[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0}; +#define HAUS1_INDEX 12 + // FUNCTION: LEGO1 0x1002f8b0 void LegoBuildingManager::configureLegoBuildingManager(MxS32 p_buildingManagerConfig) { @@ -392,6 +394,9 @@ MxResult LegoBuildingManager::Read(LegoStorage* p_storage) m_nextVariant = 0; } + // Bugfix: allow Pepper to change variant building after save game load + g_buildingInfo[HAUS1_INDEX].m_variant = g_buildingInfoVariants[m_nextVariant]; + result = SUCCESS; done: @@ -461,7 +466,7 @@ MxBool LegoBuildingManager::SwitchVariant(LegoEntity* p_entity) roi->SetVisibility(FALSE); info->m_variant = g_buildingInfoVariants[m_nextVariant]; - CreateBuilding(12, CurrentWorld()); + CreateBuilding(HAUS1_INDEX, CurrentWorld()); if (info->m_entity != NULL) { info->m_entity->GetROI()->SetVisibility(TRUE); @@ -548,7 +553,7 @@ MxU32 LegoBuildingManager::GetAnimationId(LegoEntity* p_entity) // FUNCTION: LEGO1 0x1002ff40 // FUNCTION: BETA10 0x10064398 -MxU32 LegoBuildingManager::GetSoundId(LegoEntity* p_entity, MxBool p_state) +MxU32 LegoBuildingManager::GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood) { LegoBuildingInfo* info = GetInfo(p_entity); @@ -556,12 +561,12 @@ MxU32 LegoBuildingManager::GetSoundId(LegoEntity* p_entity, MxBool p_state) return 0; } - if (p_state) { - return info->m_mood + g_unk0x100f3740; + if (p_basedOnMood) { + return info->m_mood + g_buildingSoundIdMoodOffset; } if (info != NULL) { - return info->m_sound + g_unk0x100f373c; + return info->m_sound + g_buildingSoundIdOffset; } return 0; diff --git a/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp b/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp index 6510dcb3..adf442eb 100644 --- a/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp @@ -39,10 +39,10 @@ MxU32 g_characterAnimationId = 10; char* LegoCharacterManager::g_customizeAnimFile = NULL; // GLOBAL: LEGO1 0x100fc4d8 -MxU32 g_soundIdOffset = 50; +MxU32 g_characterSoundIdOffset = 50; // GLOBAL: LEGO1 0x100fc4dc -MxU32 g_soundIdMoodOffset = 66; +MxU32 g_characterSoundIdMoodOffset = 66; // GLOBAL: LEGO1 0x100fc4e8 MxU32 g_headTextureCounter = 0; @@ -636,7 +636,7 @@ MxBool LegoCharacterManager::SetHeadTexture(LegoROI* p_roi, LegoTextureInfo* p_t LegoLOD* clone = lod->Clone(renderer); if (p_texture != NULL) { - clone->FUN_100aad70(p_texture); + clone->UpdateTextureInfo(p_texture); } dupLodList->PushBack(clone); @@ -933,16 +933,16 @@ MxU32 LegoCharacterManager::GetAnimationId(LegoROI* p_roi) // FUNCTION: LEGO1 0x10085140 // FUNCTION: BETA10 0x10076855 -MxU32 LegoCharacterManager::GetSoundId(LegoROI* p_roi, MxBool p_und) +MxU32 LegoCharacterManager::GetSoundId(LegoROI* p_roi, MxBool p_basedOnMood) { LegoActorInfo* info = GetActorInfo(p_roi); - if (p_und) { - return info->m_mood + g_soundIdMoodOffset; + if (p_basedOnMood) { + return info->m_mood + g_characterSoundIdMoodOffset; } if (info != NULL) { - return info->m_sound + g_soundIdOffset; + return info->m_sound + g_characterSoundIdOffset; } else { return 0; diff --git a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp index 5385a86a..a67f7e08 100644 --- a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp @@ -40,10 +40,10 @@ MxU8 g_counters[] = {1, 2, 2, 3}; MxU32 LegoPlantManager::g_maxSound = 8; // GLOBAL: LEGO1 0x100f3160 -MxU32 g_unk0x100f3160 = 56; +MxU32 g_plantSoundIdOffset = 56; // GLOBAL: LEGO1 0x100f3164 -MxU32 g_unk0x100f3164 = 66; +MxU32 g_plantSoundIdMoodOffset = 66; // GLOBAL: LEGO1 0x100f3168 MxS32 LegoPlantManager::g_maxMove[4] = {3, 3, 3, 3}; @@ -83,7 +83,7 @@ void LegoPlantManager::Init() } m_worldId = LegoOmni::e_undefined; - m_unk0x0c = 0; + m_boundariesDetermined = FALSE; m_numEntries = 0; } @@ -98,7 +98,7 @@ void LegoPlantManager::LoadWorldInfo(LegoOmni::World p_worldId) CreatePlant(i, world, p_worldId); } - m_unk0x0c = 0; + m_boundariesDetermined = FALSE; } // FUNCTION: LEGO1 0x100263a0 @@ -119,12 +119,12 @@ void LegoPlantManager::Reset(LegoOmni::World p_worldId) } m_worldId = LegoOmni::e_undefined; - m_unk0x0c = 0; + m_boundariesDetermined = FALSE; } // FUNCTION: LEGO1 0x10026410 // FUNCTION: BETA10 0x100c50e9 -MxResult LegoPlantManager::FUN_10026410() +MxResult LegoPlantManager::DetermineBoundaries() { // similar to LegoBuildingManager::FUN_10030630() @@ -192,7 +192,7 @@ MxResult LegoPlantManager::FUN_10026410() } } - m_unk0x0c = TRUE; + m_boundariesDetermined = TRUE; return SUCCESS; } @@ -200,8 +200,8 @@ MxResult LegoPlantManager::FUN_10026410() // FUNCTION: BETA10 0x100c55e0 LegoPlantInfo* LegoPlantManager::GetInfoArray(MxS32& p_length) { - if (!m_unk0x0c) { - FUN_10026410(); + if (!m_boundariesDetermined) { + DetermineBoundaries(); } p_length = sizeOfArray(g_plantInfo); @@ -514,16 +514,16 @@ MxU32 LegoPlantManager::GetAnimationId(LegoEntity* p_entity) // FUNCTION: LEGO1 0x10026ba0 // FUNCTION: BETA10 0x100c61ba -MxU32 LegoPlantManager::GetSoundId(LegoEntity* p_entity, MxBool p_state) +MxU32 LegoPlantManager::GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood) { LegoPlantInfo* info = GetInfo(p_entity); - if (p_state) { - return (info->m_mood & 1) + g_unk0x100f3164; + if (p_basedOnMood) { + return (info->m_mood & 1) + g_plantSoundIdMoodOffset; } if (info != NULL) { - return info->m_sound + g_unk0x100f3160; + return info->m_sound + g_plantSoundIdOffset; } return 0; diff --git a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp index b251c1c0..d54563ab 100644 --- a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp @@ -16,6 +16,8 @@ DECOMP_SIZE_ASSERT(MxTransitionManager, 0x900) +MxTransitionManager::TransitionType g_transitionManagerConfig = MxTransitionManager::e_mosaic; + // GLOBAL: LEGO1 0x100f4378 RECT g_fullScreenRect = {0, 0, 640, 480}; @@ -105,7 +107,7 @@ MxResult MxTransitionManager::StartTransition( backgroundAudioManager->Stop(); } - m_mode = p_animationType; + m_mode = g_transitionManagerConfig; m_copyFlags.m_bit0 = p_doCopy; @@ -632,3 +634,8 @@ void MxTransitionManager::SetupCopyRect(LPDDSURFACEDESC p_ddsc) ); } } + +void MxTransitionManager::configureMxTransitionManager(TransitionType p_transitionManagerConfig) +{ + g_transitionManagerConfig = p_transitionManagerConfig; +} diff --git a/LEGO1/lego/legoomni/src/entity/legoactor.cpp b/LEGO1/lego/legoomni/src/entity/legoactor.cpp index 34ba9fb8..477bf529 100644 --- a/LEGO1/lego/legoomni/src/entity/legoactor.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoactor.cpp @@ -20,7 +20,7 @@ LegoActor::LegoActor() m_frequencyFactor = 0.0f; m_sound = NULL; m_unk0x70 = 0.0f; - m_unk0x10 = 0; + m_interaction = 0; m_actorId = 0; } diff --git a/LEGO1/lego/legoomni/src/entity/legoentity.cpp b/LEGO1/lego/legoomni/src/entity/legoentity.cpp index 148d4c16..cce17c18 100644 --- a/LEGO1/lego/legoomni/src/entity/legoentity.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoentity.cpp @@ -31,7 +31,7 @@ void LegoEntity::Init() m_roi = NULL; m_cameraFlag = FALSE; m_siFile = NULL; - m_unk0x10 = 0; + m_interaction = 0; m_flags = 0; m_actionType = Extra::ActionType::e_unknown; m_targetEntityId = -1; @@ -266,23 +266,23 @@ void LegoEntity::ParseAction(char* p_extra) // FUNCTION: LEGO1 0x10010f10 // FUNCTION: BETA10 0x1007ee87 -void LegoEntity::ClickSound(MxBool p_und) +void LegoEntity::ClickSound(MxBool p_basedOnMood) { - if (!GetUnknown0x10IsSet(c_altBit1)) { + if (!IsInteraction(c_disabled)) { MxU32 objectId = 0; const char* name = m_roi->GetName(); switch (m_type) { case e_actor: - objectId = CharacterManager()->GetSoundId(m_roi, p_und); + objectId = CharacterManager()->GetSoundId(m_roi, p_basedOnMood); break; case e_unk1: break; case e_plant: - objectId = PlantManager()->GetSoundId(this, p_und); + objectId = PlantManager()->GetSoundId(this, p_basedOnMood); break; case e_building: - objectId = BuildingManager()->GetSoundId(this, p_und); + objectId = BuildingManager()->GetSoundId(this, p_basedOnMood); break; } @@ -300,7 +300,7 @@ void LegoEntity::ClickSound(MxBool p_und) // FUNCTION: BETA10 0x1007f062 void LegoEntity::ClickAnimation() { - if (!GetUnknown0x10IsSet(c_altBit1)) { + if (!IsInteraction(c_disabled)) { MxU32 objectId = 0; MxDSAction action; const char* name = m_roi->GetName(); @@ -332,7 +332,7 @@ void LegoEntity::ClickAnimation() action.SetObjectId(objectId); action.AppendExtra(strlen(extra) + 1, extra); LegoOmni::GetInstance()->GetAnimationManager()->StartEntityAction(action, this); - m_unk0x10 |= c_altBit1; + m_interaction |= c_disabled; } } } diff --git a/LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp b/LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp index 407fa3cb..4308717a 100644 --- a/LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp +++ b/LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp @@ -697,7 +697,7 @@ MxLong LegoNavController::Notify(MxParam& p_param) for (MxS32 i = 0; i < numPlants; i++) { LegoEntity* entity = plantMgr->CreatePlant(i, NULL, LegoOmni::e_act1); - if (entity != NULL && !entity->GetUnknown0x10IsSet(LegoEntity::c_altBit1)) { + if (entity != NULL && !entity->IsInteraction(LegoEntity::c_disabled)) { LegoROI* roi = entity->GetROI(); if (roi != NULL && roi->GetVisibility()) { diff --git a/LEGO1/lego/legoomni/src/main/legomain.cpp b/LEGO1/lego/legoomni/src/main/legomain.cpp index 3b99f0b5..1695e1c3 100644 --- a/LEGO1/lego/legoomni/src/main/legomain.cpp +++ b/LEGO1/lego/legoomni/src/main/legomain.cpp @@ -76,6 +76,7 @@ void LegoOmni::Init() m_bkgAudioManager = NULL; m_unk0x13c = TRUE; m_transitionManager = NULL; + m_version10 = FALSE; } // FUNCTION: LEGO1 0x10058c30 diff --git a/LEGO1/lego/legoomni/src/paths/legopathactor.cpp b/LEGO1/lego/legoomni/src/paths/legopathactor.cpp index adf3390a..c9ce7c6c 100644 --- a/LEGO1/lego/legoomni/src/paths/legopathactor.cpp +++ b/LEGO1/lego/legoomni/src/paths/legopathactor.cpp @@ -37,7 +37,7 @@ const char* g_strHIT_WALL_SOUND = "HIT_WALL_SOUND"; // GLOBAL: LEGO1 0x100f3308 // GLOBAL: BETA10 0x101f1e1c -MxLong g_unk0x100f3308 = 0; +MxLong g_timeLastHitSoundPlayed = 0; // FUNCTION: LEGO1 0x1002d700 // FUNCTION: BETA10 0x100ae6e0 @@ -292,8 +292,8 @@ MxS32 LegoPathActor::VTable0x8c(float p_time, Matrix4& p_transform) if (m_boundary == oldBoundary) { MxLong time = Timer()->GetTime(); - if (time - g_unk0x100f3308 > 1000) { - g_unk0x100f3308 = time; + if (time - g_timeLastHitSoundPlayed > 1000) { + g_timeLastHitSoundPlayed = time; const char* var = VariableTable()->GetVariable(g_strHIT_WALL_SOUND); if (var && var[0] != 0) { diff --git a/LEGO1/lego/legoomni/src/race/carrace.cpp b/LEGO1/lego/legoomni/src/race/carrace.cpp index 988584c5..97ae49b8 100644 --- a/LEGO1/lego/legoomni/src/race/carrace.cpp +++ b/LEGO1/lego/legoomni/src/race/carrace.cpp @@ -246,7 +246,7 @@ MxLong CarRace::HandlePathStruct(LegoPathStructNotificationParam& p_param) VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_raceState->m_unk0x28 = 2; RaceState::Entry* raceState = m_raceState->GetState(GameState()->GetActorId()); @@ -348,7 +348,7 @@ MxLong CarRace::HandleClick(LegoEventNotificationParam& p_param) VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_destLocation = LegoGameState::e_infomain; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); GameState()->GetBackgroundColor()->SetValue("reset"); @@ -360,7 +360,7 @@ MxLong CarRace::HandleClick(LegoEventNotificationParam& p_param) VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_destLocation = LegoGameState::e_carraceExterior; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); GameState()->GetBackgroundColor()->SetValue("reset"); @@ -424,7 +424,7 @@ MxBool CarRace::Escape() NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); GameState()->GetBackgroundColor()->SetValue("reset"); m_destLocation = LegoGameState::e_infomain; diff --git a/LEGO1/lego/legoomni/src/race/jetskirace.cpp b/LEGO1/lego/legoomni/src/race/jetskirace.cpp index 042090db..b520f8f3 100644 --- a/LEGO1/lego/legoomni/src/race/jetskirace.cpp +++ b/LEGO1/lego/legoomni/src/race/jetskirace.cpp @@ -132,7 +132,7 @@ MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param) m_act1State->m_unk0x018 = 0; VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_destLocation = LegoGameState::e_jetraceExterior; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); break; @@ -140,7 +140,7 @@ MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param) m_act1State->m_unk0x018 = 0; VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_destLocation = LegoGameState::e_infomain; result = 1; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); @@ -193,7 +193,7 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param) VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_raceState->m_unk0x28 = 2; RaceState::Entry* raceStateEntry = m_raceState->GetState(GameState()->GetActorId()); @@ -294,6 +294,6 @@ MxBool JetskiRace::Escape() VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); m_destLocation = LegoGameState::e_infomain; - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); return TRUE; } diff --git a/LEGO1/lego/legoomni/src/race/legoraceactor.cpp b/LEGO1/lego/legoomni/src/race/legoraceactor.cpp index 965ba326..f005d996 100644 --- a/LEGO1/lego/legoomni/src/race/legoraceactor.cpp +++ b/LEGO1/lego/legoomni/src/race/legoraceactor.cpp @@ -31,8 +31,8 @@ MxS32 LegoRaceActor::VTable0x68(Vector3& p_v1, Vector3& p_v2, Vector3& p_v3) if (m_userNavFlag && result) { MxLong time = Timer()->GetTime(); - if (time - g_unk0x100f3308 > 1000) { - g_unk0x100f3308 = time; + if (time - g_timeLastHitSoundPlayed > 1000) { + g_timeLastHitSoundPlayed = time; const char* soundKey = VariableTable()->GetVariable(g_strHIT_ACTOR_SOUND); if (soundKey && *soundKey) { diff --git a/LEGO1/lego/legoomni/src/race/legoracers.cpp b/LEGO1/lego/legoomni/src/race/legoracers.cpp index 0cffed0c..4aeaced0 100644 --- a/LEGO1/lego/legoomni/src/race/legoracers.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracers.cpp @@ -178,7 +178,7 @@ LegoRaceCar::LegoRaceCar() m_skelKick1Anim = 0; m_skelKick2Anim = 0; m_unk0x5c.Clear(); - m_unk0x58 = 0; + m_kickStart = 0; m_kick1B = 0; m_kick2B = 0; NotificationManager()->Register(this); @@ -201,10 +201,10 @@ MxLong LegoRaceCar::Notify(MxParam& p_param) // Initialized at LEGO1 0x10012db0 // GLOBAL: LEGO1 0x10102af0 // GLOBAL: BETA10 0x102114c0 -Mx3DPointFloat g_unk0x10102af0 = Mx3DPointFloat(0.0f, 2.0f, 0.0f); +Mx3DPointFloat g_hitOffset = Mx3DPointFloat(0.0f, 2.0f, 0.0f); // FUNCTION: LEGO1 0x10012de0 -void LegoRaceCar::FUN_10012de0() +void LegoRaceCar::InitYouCantStopSound() { // Init to TRUE so we don't play "you can't stop in the middle of the race!" before the player ever moves g_playedYouCantStopSound = TRUE; @@ -229,7 +229,7 @@ void LegoRaceCar::InitSoundIndices() void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed) { if (!m_userNavFlag) { - if (!LegoCarRaceActor::m_unk0x0c) { + if (!LegoCarRaceActor::m_animState) { m_maxLinearVel = p_worldSpeed; } LegoAnimActor::SetWorldSpeed(p_worldSpeed); @@ -244,7 +244,7 @@ void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed) void LegoRaceCar::SetMaxLinearVelocity(float p_maxLinearVelocity) { if (p_maxLinearVelocity < 0) { - LegoCarRaceActor::m_unk0x0c = 2; + LegoCarRaceActor::m_animState = 2; m_maxLinearVel = 0; SetWorldSpeed(0); } @@ -299,7 +299,7 @@ void LegoRaceCar::ParseAction(char* p_extra) // FUNCTION: LEGO1 0x10012ff0 // FUNCTION: BETA10 0x100cb60e -void LegoRaceCar::FUN_10012ff0(float p_param) +void LegoRaceCar::KickCamera(float p_param) { LegoAnimActorStruct* a; // called `a` in BETA10 float deltaTime; @@ -315,7 +315,7 @@ void LegoRaceCar::FUN_10012ff0(float p_param) assert(a && a->GetAnimTreePtr() && a->GetAnimTreePtr()->GetCamAnim()); if (a->GetAnimTreePtr()) { - deltaTime = p_param - m_unk0x58; + deltaTime = p_param - m_kickStart; if (a->GetDuration() <= deltaTime || deltaTime < 0.0) { if (m_userState == LEGORACECAR_KICK1) { @@ -390,7 +390,7 @@ MxU32 LegoRaceCar::HandleSkeletonKicks(float p_param1) return FALSE; } - m_unk0x58 = p_param1; + m_kickStart = p_param1; SoundManager()->GetCacheSoundManager()->Play(g_soundSkel3, NULL, FALSE); return TRUE; @@ -401,7 +401,7 @@ MxU32 LegoRaceCar::HandleSkeletonKicks(float p_param1) void LegoRaceCar::Animate(float p_time) { if (m_userNavFlag && (m_userState == LEGORACECAR_KICK1 || m_userState == LEGORACECAR_KICK2)) { - FUN_10012ff0(p_time); + KickCamera(p_time); return; } @@ -413,7 +413,7 @@ void LegoRaceCar::Animate(float p_time) } } - if (LegoCarRaceActor::m_unk0x0c == 1) { + if (LegoCarRaceActor::m_animState == 1) { FUN_1005d4b0(); if (!m_userNavFlag) { @@ -471,7 +471,7 @@ MxResult LegoRaceCar::HitActor(LegoPathActor* p_actor, MxBool p_bool) assert(roi); matr = roi->GetLocal2World(); - Vector3(matr[3]) += g_unk0x10102af0; + Vector3(matr[3]) += g_hitOffset; roi->SetLocal2World(matr); p_actor->SetActorState(c_two); @@ -516,7 +516,7 @@ MxResult LegoRaceCar::HitActor(LegoPathActor* p_actor, MxBool p_bool) if (soundKey) { SoundManager()->GetCacheSoundManager()->Play(soundKey, NULL, FALSE); - g_timeLastRaceCarSoundPlayed = g_unk0x100f3308 = time; + g_timeLastRaceCarSoundPlayed = g_timeLastHitSoundPlayed = time; } } @@ -582,7 +582,7 @@ void LegoJetski::InitSoundIndices() void LegoJetski::SetWorldSpeed(MxFloat p_worldSpeed) { if (!m_userNavFlag) { - if (!LegoCarRaceActor::m_unk0x0c) { + if (!LegoCarRaceActor::m_animState) { m_maxLinearVel = p_worldSpeed; } LegoAnimActor::SetWorldSpeed(p_worldSpeed); @@ -597,7 +597,7 @@ void LegoJetski::SetWorldSpeed(MxFloat p_worldSpeed) void LegoJetski::FUN_100136f0(float p_worldSpeed) { if (p_worldSpeed < 0) { - LegoCarRaceActor::m_unk0x0c = 2; + LegoCarRaceActor::m_animState = 2; m_maxLinearVel = 0; SetWorldSpeed(0); } @@ -612,7 +612,7 @@ void LegoJetski::Animate(float p_time) { LegoJetskiRaceActor::Animate(p_time); - if (LegoCarRaceActor::m_unk0x0c == 1) { + if (LegoCarRaceActor::m_animState == 1) { FUN_1005d4b0(); if (!m_userNavFlag) { @@ -685,7 +685,7 @@ MxResult LegoJetski::HitActor(LegoPathActor* p_actor, MxBool p_bool) LegoROI* roi = p_actor->GetROI(); matr = roi->GetLocal2World(); - Vector3(matr[3]) += g_unk0x10102af0; + Vector3(matr[3]) += g_hitOffset; roi->SetLocal2World(matr); p_actor->SetActorState(c_two); @@ -714,7 +714,7 @@ MxResult LegoJetski::HitActor(LegoPathActor* p_actor, MxBool p_bool) if (soundKey) { SoundManager()->GetCacheSoundManager()->Play(soundKey, NULL, FALSE); - g_timeLastJetskiSoundPlayed = g_unk0x100f3308 = time; + g_timeLastJetskiSoundPlayed = g_timeLastHitSoundPlayed = time; } } diff --git a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp index 935c3406..7fe58c71 100644 --- a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp @@ -45,7 +45,7 @@ LegoCarRaceActor::LegoCarRaceActor() { m_unk0x08 = 1.0f; m_unk0x70 = 0.0f; - m_unk0x0c = 0; + m_animState = 0; m_maxLinearVel = 0.0f; m_frequencyFactor = 1.0f; m_unk0x1c = 0; @@ -224,18 +224,18 @@ void LegoCarRaceActor::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOriente // FUNCTION: BETA10 0x100cdbae void LegoCarRaceActor::Animate(float p_time) { - // m_unk0x0c is not an MxBool, there are places where it is set to 2 or higher - if (m_unk0x0c == 0) { + // m_animState is not an MxBool, there are places where it is set to 2 or higher + if (m_animState == 0) { const char* value = VariableTable()->GetVariable(g_raceState); if (SDL_strcasecmp(value, g_racing) == 0) { - m_unk0x0c = 1; + m_animState = 1; m_lastTime = p_time - 1.0f; m_unk0x1c = p_time; } } - if (m_unk0x0c == 1) { + if (m_animState == 1) { LegoAnimActor::Animate(p_time); } } @@ -399,10 +399,10 @@ MxS32 LegoJetskiRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_ // FUNCTION: LEGO1 0x10081550 void LegoJetskiRaceActor::Animate(float p_time) { - if (m_unk0x0c == 0) { + if (m_animState == 0) { const LegoChar* raceState = VariableTable()->GetVariable(g_raceState); if (!SDL_strcasecmp(raceState, g_racing)) { - m_unk0x0c = 1; + m_animState = 1; m_lastTime = p_time - 1.0f; m_unk0x1c = p_time; } @@ -411,7 +411,7 @@ void LegoJetskiRaceActor::Animate(float p_time) } } - if (m_unk0x0c == 1) { + if (m_animState == 1) { LegoAnimActor::Animate(p_time); } } diff --git a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp index 28642bab..c49b766e 100644 --- a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp +++ b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp @@ -793,7 +793,7 @@ void LegoAnimPresenter::StartingTickle() } FUN_10069b10(); - FUN_1006c8a0(TRUE); + SetDisabled(TRUE); if (m_unk0x78 == NULL) { if (fabs(m_action->GetDirection()[0]) >= 0.00000047683716F || @@ -1093,7 +1093,7 @@ void LegoAnimPresenter::EndAction() } } - FUN_1006c8a0(FALSE); + SetDisabled(FALSE); FUN_1006ab70(); VTable0x90(); @@ -1154,18 +1154,18 @@ void LegoAnimPresenter::VTable0x90() } // FUNCTION: LEGO1 0x1006c8a0 -void LegoAnimPresenter::FUN_1006c8a0(MxBool p_bool) +void LegoAnimPresenter::SetDisabled(MxBool p_disabled) { if (m_roiMapSize != 0 && m_roiMap != NULL) { for (MxU32 i = 1; i <= m_roiMapSize; i++) { LegoEntity* entity = m_roiMap[i]->GetEntity(); if (entity != NULL) { - if (p_bool) { - entity->SetUnknown0x10Flag(LegoEntity::c_altBit1); + if (p_disabled) { + entity->SetInteractionFlag(LegoEntity::c_disabled); } else { - entity->ClearUnknown0x10Flag(LegoEntity::c_altBit1); + entity->ClearInteractionFlag(LegoEntity::c_disabled); } } } diff --git a/LEGO1/lego/legoomni/src/video/legovideomanager.cpp b/LEGO1/lego/legoomni/src/video/legovideomanager.cpp index a0173224..7e2f7e42 100644 --- a/LEGO1/lego/legoomni/src/video/legovideomanager.cpp +++ b/LEGO1/lego/legoomni/src/video/legovideomanager.cpp @@ -108,7 +108,7 @@ MxResult LegoVideoManager::Create(MxVideoParam& p_videoParam, MxU32 p_frequencyM goto done; } - if (deviceEnumerate.DoEnumerate() != SUCCESS) { + if (deviceEnumerate.DoEnumerate(hwnd) != SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "LegoDeviceEnumerate::DoEnumerate failed"); goto done; } diff --git a/LEGO1/lego/legoomni/src/worlds/hospital.cpp b/LEGO1/lego/legoomni/src/worlds/hospital.cpp index 66d22168..4a8fa8ac 100644 --- a/LEGO1/lego/legoomni/src/worlds/hospital.cpp +++ b/LEGO1/lego/legoomni/src/worlds/hospital.cpp @@ -48,7 +48,7 @@ Hospital::Hospital() m_flashingLeds = 0; m_copLedAnimTimer = 0; m_pizzaLedAnimTimer = 0; - m_unk0x128 = 0; + m_exited = FALSE; NotificationManager()->Register(this); } @@ -369,8 +369,8 @@ MxLong Hospital::HandleEndAction(MxEndActionNotificationParam& p_param) act1State = (Act1State*) GameState()->GetState("Act1State"); act1State->SetUnknown18(9); case HospitalState::e_exitToFront: - if (m_unk0x128 == 0) { - m_unk0x128 = 1; + if (m_exited == FALSE) { + m_exited = TRUE; m_destLocation = LegoGameState::e_hospitalExited; DeleteObjects(&m_atomId, HospitalScript::c_hho002cl_RunAnim, HospitalScript::c_hho006cl_RunAnim); @@ -378,8 +378,8 @@ MxLong Hospital::HandleEndAction(MxEndActionNotificationParam& p_param) } break; case HospitalState::e_exitToInfocenter: - if (m_unk0x128 == 0) { - m_unk0x128 = 1; + if (m_exited == FALSE) { + m_exited = TRUE; m_destLocation = LegoGameState::e_infomain; DeleteObjects(&m_atomId, HospitalScript::c_hho002cl_RunAnim, HospitalScript::c_hho006cl_RunAnim); @@ -412,8 +412,8 @@ MxLong Hospital::HandleButtonDown(LegoControlManagerNotificationParam& p_param) m_interactionMode = 3; if (m_hospitalState->m_state == HospitalState::e_explainQuestShort) { - if (m_unk0x128 == 0) { - m_unk0x128 = 1; + if (m_exited == FALSE) { + m_exited = TRUE; TickleManager()->UnregisterClient(this); @@ -568,8 +568,8 @@ MxBool Hospital::HandleControl(LegoControlManagerNotificationParam& p_param) m_currentAction = HospitalScript::c_hho016cl_RunAnim; m_setWithCurrentAction = 1; } - else if (m_unk0x128 == 0) { - m_unk0x128 = 1; + else if (m_exited == FALSE) { + m_exited = TRUE; m_hospitalState->m_state = HospitalState::e_exitImmediately; m_destLocation = LegoGameState::e_infomain; @@ -589,8 +589,8 @@ MxBool Hospital::HandleControl(LegoControlManagerNotificationParam& p_param) m_currentAction = HospitalScript::c_hho016cl_RunAnim; m_setWithCurrentAction = 1; } - else if (m_unk0x128 == 0) { - m_unk0x128 = 1; + else if (m_exited == FALSE) { + m_exited = TRUE; m_hospitalState->m_state = HospitalState::e_exitImmediately; m_destLocation = LegoGameState::e_hospitalExited; diff --git a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp index f9bfb4eb..c007dc96 100644 --- a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp +++ b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp @@ -306,29 +306,32 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) if (!m_unk0x1d4) { PlayMusic(JukeboxScript::c_InformationCenter_Music); - GameState()->SetActor(m_selectedCharacter); - switch (m_selectedCharacter) { - case e_pepper: - PlayAction(InfomainScript::c_avo901in_RunAnim); - break; - case e_mama: - PlayAction(InfomainScript::c_avo902in_RunAnim); - break; - case e_papa: - PlayAction(InfomainScript::c_avo903in_RunAnim); - break; - case e_nick: - PlayAction(InfomainScript::c_avo904in_RunAnim); - break; - case e_laura: - PlayAction(InfomainScript::c_avo905in_RunAnim); - break; - default: - break; + if (!Lego()->IsVersion10()) { + GameState()->SetActor(m_selectedCharacter); + + switch (m_selectedCharacter) { + case e_pepper: + PlayAction(InfomainScript::c_avo901in_RunAnim); + break; + case e_mama: + PlayAction(InfomainScript::c_avo902in_RunAnim); + break; + case e_papa: + PlayAction(InfomainScript::c_avo903in_RunAnim); + break; + case e_nick: + PlayAction(InfomainScript::c_avo904in_RunAnim); + break; + case e_laura: + PlayAction(InfomainScript::c_avo905in_RunAnim); + break; + default: + break; + } + + UpdateFrameHot(TRUE); } - - UpdateFrameHot(TRUE); } } @@ -338,7 +341,7 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) return result; } - if (action->GetObjectId() == InfomainScript::c_iicx26in_RunAnim) { + if (action->GetObjectId() == InfomainScript::c_iicx26in_RunAnim - Lego()->IsVersion10()) { ControlManager()->FUN_100293c0(InfomainScript::c_BigInfo_Ctl, action->GetAtomId().GetInternal(), 0); m_unk0x1d6 = 0; } @@ -478,7 +481,7 @@ void Infocenter::ReadyWorld() InfomainScript::Script script = m_infocenterState->GetNextReturnDialogue(); PlayAction(script); - if (script == InfomainScript::c_iicx26in_RunAnim) { + if (script == InfomainScript::c_iicx26in_RunAnim - Lego()->IsVersion10()) { m_unk0x1d6 = 1; } @@ -1186,13 +1189,13 @@ MxLong Infocenter::HandleNotification0(MxNotificationParam& p_param) m_currentInfomainScript == InfomainScript::c_Pepper_All_Movie || m_currentInfomainScript == InfomainScript::c_Nick_All_Movie || m_currentInfomainScript == InfomainScript::c_Laura_All_Movie || - m_currentInfomainScript == InfomainScript::c_iic007ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ijs002ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_irt001ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ipz006ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_igs004ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_iho003ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ips005ra_PlayWav) { + m_currentInfomainScript == InfomainScript::c_iic007ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ijs002ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_irt001ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ipz006ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_igs004ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_iho003ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ips005ra_PlayWav - Lego()->IsVersion10()) { StopCurrentAction(); } } @@ -1506,6 +1509,17 @@ void Infocenter::StopCredits() // FUNCTION: BETA10 0x1002ee8c void Infocenter::PlayAction(InfomainScript::Script p_script) { + if (Lego()->IsVersion10()) { + if (p_script == InfomainScript::c_iicx18in_RunAnim) { + // Alternative dialogue after signing in (1.0 version) + p_script = InfomainScript::c_iic016in_RunAnim; + } + else if (p_script > InfomainScript::c_iicx18in_RunAnim) { + // Shift all other actions by 1 + p_script = (InfomainScript::Script)((int) p_script - 1); + } + } + MxDSAction action; action.SetObjectId(p_script); action.SetAtomId(*g_infomainScript); diff --git a/LEGO1/lego/legoomni/src/worlds/isle.cpp b/LEGO1/lego/legoomni/src/worlds/isle.cpp index 4d2c5b29..8c35e1f1 100644 --- a/LEGO1/lego/legoomni/src/worlds/isle.cpp +++ b/LEGO1/lego/legoomni/src/worlds/isle.cpp @@ -810,7 +810,7 @@ void Isle::Enable(MxBool p_enable) AnimationManager()->EnableCamAnims(FALSE); g_isleFlags &= ~c_playMusic; - m_ambulance->FUN_10036e60(); + m_ambulance->Init(); break; case 11: m_act1state->m_unk0x018 = 0; @@ -1209,7 +1209,7 @@ MxBool Isle::Escape() case 10: if (UserActor() != NULL && !UserActor()->IsA("Ambulance")) { m_ambulance->StopActions(); - m_ambulance->FUN_10037250(); + m_ambulance->Reset(); } break; } @@ -1250,7 +1250,7 @@ void Isle::FUN_10033350() if (m_act1state->m_unk0x018 == 10) { if (UserActor() != NULL && !UserActor()->IsA("Ambulance")) { m_ambulance->StopActions(); - m_ambulance->FUN_10037250(); + m_ambulance->Reset(); } } diff --git a/LEGO1/lego/sources/roi/legolod.cpp b/LEGO1/lego/sources/roi/legolod.cpp index 2e0df088..94eef6ff 100644 --- a/LEGO1/lego/sources/roi/legolod.cpp +++ b/LEGO1/lego/sources/roi/legolod.cpp @@ -353,7 +353,7 @@ LegoResult LegoLOD::SetTextureInfo(LegoTextureInfo* p_textureInfo) } // FUNCTION: LEGO1 0x100aad70 -LegoResult LegoLOD::FUN_100aad70(LegoTextureInfo* p_textureInfo) +LegoResult LegoLOD::UpdateTextureInfo(LegoTextureInfo* p_textureInfo) { for (LegoU32 i = m_meshOffset; i < m_numMeshes; i++) { if (m_melems[i].m_textured) { diff --git a/LEGO1/lego/sources/roi/legolod.h b/LEGO1/lego/sources/roi/legolod.h index 114e377b..e787d377 100644 --- a/LEGO1/lego/sources/roi/legolod.h +++ b/LEGO1/lego/sources/roi/legolod.h @@ -31,7 +31,7 @@ class LegoLOD : public ViewLOD { LegoLOD* Clone(Tgl::Renderer* p_renderer); LegoResult SetColor(LegoFloat p_red, LegoFloat p_green, LegoFloat p_blue, LegoFloat p_alpha); LegoResult SetTextureInfo(LegoTextureInfo* p_textureInfo); - LegoResult FUN_100aad70(LegoTextureInfo* p_textureInfo); + LegoResult UpdateTextureInfo(LegoTextureInfo* p_textureInfo); void ClearMeshOffset(); LegoResult GetTextureInfo(LegoTextureInfo*& p_textureInfo); diff --git a/LEGO1/mxdirectx/mxdirectxinfo.cpp b/LEGO1/mxdirectx/mxdirectxinfo.cpp index c5100031..639a2864 100644 --- a/LEGO1/mxdirectx/mxdirectxinfo.cpp +++ b/LEGO1/mxdirectx/mxdirectxinfo.cpp @@ -216,27 +216,33 @@ BOOL MxDeviceEnumerate::EnumDirectDrawCallback(LPGUID p_guid, LPSTR p_driverDesc BuildErrorString("DirectDraw Create failed: %s\n", EnumerateErrorToString(result)); } else { - newDevice.m_ddCaps.dwSize = sizeof(newDevice.m_ddCaps); - result = lpDD->GetCaps(&newDevice.m_ddCaps, NULL); - + result = lpDD->SetCooperativeLevel(m_hWnd, DDSCL_NORMAL); if (result != DD_OK) { - BuildErrorString("GetCaps failed: %s\n", EnumerateErrorToString(result)); + BuildErrorString("SetCooperativeLevel failed: %s\n", EnumerateErrorToString(result)); } else { - result = lpDD->QueryInterface(IID_IDirect3D2, (LPVOID*) &lpDirect3d2); + newDevice.m_ddCaps.dwSize = sizeof(newDevice.m_ddCaps); + result = lpDD->GetCaps(&newDevice.m_ddCaps, NULL); if (result != DD_OK) { - BuildErrorString("D3D creation failed: %s\n", EnumerateErrorToString(result)); + BuildErrorString("GetCaps failed: %s\n", EnumerateErrorToString(result)); } else { - result = lpDirect3d2->EnumDevices(DevicesEnumerateCallback, this); + result = lpDD->QueryInterface(IID_IDirect3D2, (LPVOID*) &lpDirect3d2); if (result != DD_OK) { - BuildErrorString("D3D enum devices failed: %s\n", EnumerateErrorToString(result)); + BuildErrorString("D3D creation failed: %s\n", EnumerateErrorToString(result)); } else { - if (!newDevice.m_devices.size()) { - m_list.pop_back(); + result = lpDirect3d2->EnumDevices(DevicesEnumerateCallback, this); + + if (result != DD_OK) { + BuildErrorString("D3D enum devices failed: %s\n", EnumerateErrorToString(result)); + } + else { + if (!newDevice.m_devices.size()) { + m_list.pop_back(); + } } } } @@ -306,12 +312,14 @@ HRESULT MxDeviceEnumerate::EnumDevicesCallback( // FUNCTION: CONFIG 0x00401dc0 // FUNCTION: LEGO1 0x1009c6c0 // FUNCTION: BETA10 0x1011e3fa -int MxDeviceEnumerate::DoEnumerate() +int MxDeviceEnumerate::DoEnumerate(HWND hWnd) { if (IsInitialized()) { return -1; } + m_hWnd = hWnd; + HRESULT ret = DirectDrawEnumerate(DirectDrawEnumerateCallback, this); if (ret != DD_OK) { BuildErrorString("DirectDrawEnumerate returned error %s\n", EnumerateErrorToString(ret)); diff --git a/LEGO1/mxdirectx/mxdirectxinfo.h b/LEGO1/mxdirectx/mxdirectxinfo.h index a4ebef91..9bc480de 100644 --- a/LEGO1/mxdirectx/mxdirectxinfo.h +++ b/LEGO1/mxdirectx/mxdirectxinfo.h @@ -194,7 +194,7 @@ class MxDeviceEnumerate { MxDeviceEnumerate(); ~MxDeviceEnumerate(); - virtual int DoEnumerate(); // vtable+0x00 + virtual int DoEnumerate(HWND hWnd); // vtable+0x00 BOOL EnumDirectDrawCallback(LPGUID p_guid, LPSTR p_driverDesc, LPSTR p_driverName); HRESULT EnumDevicesCallback( @@ -242,6 +242,7 @@ class MxDeviceEnumerate { protected: list m_list; // 0x04 unsigned char m_initialized; // 0x10 + HWND m_hWnd; }; // TEMPLATE: BETA10 0x1011c1b0 diff --git a/LEGO1/omni/include/mxstring.h b/LEGO1/omni/include/mxstring.h index eaa58181..b4b25262 100644 --- a/LEGO1/omni/include/mxstring.h +++ b/LEGO1/omni/include/mxstring.h @@ -20,10 +20,10 @@ class MxString : public MxCore { void ToLowerCase(); void MapPathToFilesystem() { MapPathToFilesystem(m_data); } - MxString& operator=(const MxString& p_str); - const MxString& operator=(const char* p_str); - MxString operator+(const MxString& p_str) const; - MxString operator+(const char* p_str) const; + LEGO1_EXPORT MxString& operator=(const MxString& p_str); + LEGO1_EXPORT const MxString& operator=(const char* p_str); + LEGO1_EXPORT MxString operator+(const MxString& p_str) const; + LEGO1_EXPORT MxString operator+(const char* p_str) const; LEGO1_EXPORT MxString& operator+=(const char* p_str); static void CharSwap(char* p_a, char* p_b); diff --git a/LEGO1/omni/include/mxthread.h b/LEGO1/omni/include/mxthread.h index 8afa4454..ba0d89fa 100644 --- a/LEGO1/omni/include/mxthread.h +++ b/LEGO1/omni/include/mxthread.h @@ -5,7 +5,11 @@ #include "mxsemaphore.h" #include "mxtypes.h" +#ifdef PSP +#include +#else #include +#endif class MxCore; @@ -33,9 +37,15 @@ class MxThread { virtual ~MxThread(); private: +#ifdef PSP + static int ThreadProc(SceSize args, void* argp); + + int m_thread; +#else static int SDLCALL ThreadProc(void* p_thread); SDL_Thread* m_thread; +#endif MxBool m_running; // 0x0c MxSemaphore m_semaphore; // 0x10 diff --git a/LEGO1/omni/src/system/mxthread.cpp b/LEGO1/omni/src/system/mxthread.cpp index 96e64992..6c118d4d 100644 --- a/LEGO1/omni/src/system/mxthread.cpp +++ b/LEGO1/omni/src/system/mxthread.cpp @@ -3,13 +3,20 @@ #include "decomp.h" #include +#ifdef PSP +#include +#endif DECOMP_SIZE_ASSERT(MxThread, 0x1c) // FUNCTION: LEGO1 0x100bf510 MxThread::MxThread() { +#ifdef PSP + m_thread = 0; +#else m_thread = NULL; +#endif m_running = TRUE; } @@ -17,7 +24,13 @@ MxThread::MxThread() MxThread::~MxThread() { if (m_thread) { +#ifdef PSP + sceKernelWaitThreadEnd(m_thread, NULL); + sceKernelDeleteThread(m_thread); + m_thread = 0; +#else SDL_WaitThread(m_thread, NULL); +#endif } } @@ -27,6 +40,25 @@ MxResult MxThread::Start(MxS32 p_stackSize, MxS32 p_flag) MxResult result = FAILURE; if (m_semaphore.Init(0, 1) == SUCCESS) { +#ifdef PSP + int thid = sceKernelCreateThread( + "MxThread", + ThreadProc, + 0x18, // priority (0x18 is typical) + p_stackSize * 4, + 0, + NULL + ); + + if (thid >= 0) { + MxThread* self = this; + int start = sceKernelStartThread(thid, sizeof(MxThread*), &self); + if (start >= 0) { + result = SUCCESS; + m_thread = thid; // store thread ID if needed + } + } +#else const SDL_PropertiesID props = SDL_CreateProperties(); SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void*) MxThread::ThreadProc); SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, this); @@ -37,6 +69,7 @@ MxResult MxThread::Start(MxS32 p_stackSize, MxS32 p_flag) } SDL_DestroyProperties(props); +#endif } return result; @@ -55,11 +88,19 @@ void MxThread::Terminate() m_semaphore.Wait(); } +#ifdef PSP +int MxThread::ThreadProc(SceSize args, void* argp) +{ + MxThread* self = *(MxThread**) argp; + return self->Run(); +} +#else // FUNCTION: LEGO1 0x100bf680 int MxThread::ThreadProc(void* p_thread) { return static_cast(p_thread)->Run(); } +#endif // FUNCTION: LEGO1 0x100bf690 MxResult MxThread::Run() diff --git a/LEGO1/omni/src/video/mxdisplaysurface.cpp b/LEGO1/omni/src/video/mxdisplaysurface.cpp index 6c93a0e7..c2886ebe 100644 --- a/LEGO1/omni/src/video/mxdisplaysurface.cpp +++ b/LEGO1/omni/src/video/mxdisplaysurface.cpp @@ -926,7 +926,7 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::VTable0x44( transparentColor = RGB555_CREATE(0x1f, 0, 0x1f); break; default: - transparentColor = RGB8888_CREATE(0xff, 0, 0xff, 0); + transparentColor = RGB8888_CREATE(0, 0, 0, 0); break; } @@ -971,25 +971,11 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::VTable0x44( surfacePtr += adjustedPitch; } - if (p_transparent && surface) { - DDCOLORKEY key; - key.dwColorSpaceLowValue = key.dwColorSpaceHighValue = transparentColor; - surface->SetColorKey(DDCKEY_SRCBLT, &key); - } - surface->Unlock(ddsd.lpSurface); - if (p_transparent && surface) { + if (p_transparent && surface && bytesPerPixel != 4) { DDCOLORKEY key; - if (bytesPerPixel == 1) { - key.dwColorSpaceLowValue = key.dwColorSpaceHighValue = 0; - } - else if (bytesPerPixel == 2) { - key.dwColorSpaceLowValue = key.dwColorSpaceHighValue = RGB555_CREATE(0x1f, 0, 0x1f); - } - else { - key.dwColorSpaceLowValue = key.dwColorSpaceHighValue = RGB8888_CREATE(0xff, 0, 0xff, 0); - } + key.dwColorSpaceLowValue = key.dwColorSpaceHighValue = transparentColor; surface->SetColorKey(DDCKEY_SRCBLT, &key); } } diff --git a/README.md b/README.md index 33dbd232..8f5c6756 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Please note: this project is dedicated to achieving platform independence withou | Linux | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | macOS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | [Web](https://isle.pizza) | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | +| Nintendo 3DS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | We are actively working to support more platforms. If you have experience with a particular platform, we encourage you to contribute to `isle-portable`. You can find a [list of ongoing efforts](https://github.com/isledecomp/isle-portable/wiki/Work%E2%80%90in%E2%80%90progress-ports) in our Wiki. diff --git a/XMB/Game/ICON0.png b/XMB/Game/ICON0.png new file mode 100644 index 00000000..d16c278b Binary files /dev/null and b/XMB/Game/ICON0.png differ diff --git a/XMB/Game/ICON1.pmf b/XMB/Game/ICON1.pmf new file mode 100644 index 00000000..bac01979 Binary files /dev/null and b/XMB/Game/ICON1.pmf differ diff --git a/XMB/Game/PIC1.png b/XMB/Game/PIC1.png new file mode 100644 index 00000000..b0916359 Binary files /dev/null and b/XMB/Game/PIC1.png differ diff --git a/XMB/Game/SND0.at3 b/XMB/Game/SND0.at3 new file mode 100644 index 00000000..47afc159 Binary files /dev/null and b/XMB/Game/SND0.at3 differ diff --git a/XMB/Save/EMPTY.png b/XMB/Save/EMPTY.png new file mode 100644 index 00000000..284649e6 Binary files /dev/null and b/XMB/Save/EMPTY.png differ diff --git a/XMB/Save/ICON0.png b/XMB/Save/ICON0.png new file mode 100644 index 00000000..d16c278b Binary files /dev/null and b/XMB/Save/ICON0.png differ diff --git a/XMB/Save/ICON1.pmf b/XMB/Save/ICON1.pmf new file mode 100644 index 00000000..faf5a77d Binary files /dev/null and b/XMB/Save/ICON1.pmf differ diff --git a/XMB/Save/Memory Stick Model Source.txt b/XMB/Save/Memory Stick Model Source.txt new file mode 100644 index 00000000..2a465251 --- /dev/null +++ b/XMB/Save/Memory Stick Model Source.txt @@ -0,0 +1 @@ +The model was sourced from https://3dwarehouse.sketchup.com/model/c300a598e63cb2f593dcf5e22ecc3f5e/Memory-Stick-Pro-Duo. It was further modified by Sonen10 to get the results seen in the animation. \ No newline at end of file diff --git a/XMB/Save/PIC1.png b/XMB/Save/PIC1.png new file mode 100644 index 00000000..7a0a7c4d Binary files /dev/null and b/XMB/Save/PIC1.png differ diff --git a/XMB/Save/SND0.AT3 b/XMB/Save/SND0.AT3 new file mode 100644 index 00000000..6f19ec8e Binary files /dev/null and b/XMB/Save/SND0.AT3 differ diff --git a/XMB/explanation.txt b/XMB/explanation.txt new file mode 100644 index 00000000..458d206c --- /dev/null +++ b/XMB/explanation.txt @@ -0,0 +1,10 @@ +Explanation of where the assets in these folders are used +ICON0.PNG - Icon used in various scenarios depending on if it's used for a save or a game. This includes when: +> The game/save isn't selected in the XMB +> There is no ICON1.PMF present (XMB only) +> The "Group Content" mode is enabled in the XMB game selection menu +> The game is on a save management screen with UI elements shared with the XMB (note the font) +ICON1.PMF - Seen when the game/save is selected in the XMB (NOT IN-GAME) +PIC1.PNG - Seen as a background when the game/save is selected in the XMB +SND0.AT3 - Plays when the game/save is selected in the XMB (NOT IN-GAME) +EMPTY.PNG - Used as an icon for empty saves when selecting a save to write to. diff --git a/cmake/detectcpu.cmake b/cmake/detectcpu.cmake new file mode 100644 index 00000000..e0b6feae --- /dev/null +++ b/cmake/detectcpu.cmake @@ -0,0 +1,156 @@ +function(DetectTargetCPUArchitectures DETECTED_ARCHS) + + set(known_archs EMSCRIPTEN ARM32 ARM64 ARM64EC LOONGARCH64 POWERPC32 POWERPC64 X86 X64) + + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + foreach(known_arch IN LISTS known_archs) + set(CPU_${known_arch} "0" PARENT_SCOPE) + endforeach() + set(detected_archs) + foreach(osx_arch IN LISTS CMAKE_OSX_ARCHITECTURES) + if(osx_arch STREQUAL "x86_64") + set(CPU_X64 "1" PARENT_SCOPE) + list(APPEND detected_archs "X64") + elseif(osx_arch STREQUAL "arm64") + set(CPU_ARM64 "1" PARENT_SCOPE) + list(APPEND detected_archs "ARM64") + endif() + endforeach() + set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE) + return() + endif() + + set(detected_archs) + foreach(known_arch IN LISTS known_archs) + if(CPU_${known_arch}) + list(APPEND detected_archs "${known_arch}") + endif() + endforeach() + + if(detected_archs) + set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE) + return() + endif() + + set(arch_check_ARM32 "defined(__arm__) || defined(_M_ARM)") + set(arch_check_ARM64 "defined(__aarch64__) || defined(_M_ARM64)") + set(arch_check_ARM64EC "defined(_M_ARM64EC)") + set(arch_check_EMSCRIPTEN "defined(__EMSCRIPTEN__)") + set(arch_check_LOONGARCH64 "defined(__loongarch64)") + set(arch_check_POWERPC32 "(defined(__PPC__) || defined(__powerpc__)) && !defined(__powerpc64__)") + set(arch_check_POWERPC64 "defined(__PPC64__) || defined(__powerpc64__)") + set(arch_check_X86 "defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86)") + set(arch_check_X64 "(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)) && !defined(_M_ARM64EC)") + + set(src_vars "") + set(src_main "") + foreach(known_arch IN LISTS known_archs) + set(detected_${known_arch} "0") + + string(APPEND src_vars " +#if ${arch_check_${known_arch}} +#define ARCH_${known_arch} \"1\" +#else +#define ARCH_${known_arch} \"0\" +#endif +const char *arch_${known_arch} = \"INFO<${known_arch}=\" ARCH_${known_arch} \">\"; +") + string(APPEND src_main " + result += arch_${known_arch}[argc];") + endforeach() + + set(src_arch_detect "${src_vars} +int main(int argc, char *argv[]) { + int result = 0; + (void)argv; +${src_main} + return result; +}") + + if(CMAKE_C_COMPILER) + set(ext ".c") + elseif(CMAKE_CXX_COMPILER) + set(ext ".cpp") + else() + enable_language(C) + set(ext ".c") + endif() + set(path_src_arch_detect "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/detect_arch${ext}") + file(WRITE "${path_src_arch_detect}" "${src_arch_detect}") + set(path_dir_arch_detect "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/detect_arch") + set(path_bin_arch_detect "${path_dir_arch_detect}/bin") + + set(detected_archs) + + set(msg "Detecting Target CPU Architecture") + message(STATUS "${msg}") + + include(CMakePushCheckState) + + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") + + cmake_push_check_state(RESET) + try_compile(CPU_CHECK_ALL + "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/detect_arch" + SOURCES "${path_src_arch_detect}" + COPY_FILE "${path_bin_arch_detect}" + ) + cmake_pop_check_state() + if(NOT CPU_CHECK_ALL) + message(STATUS "${msg} - ") + message(WARNING "Failed to compile source detecting the target CPU architecture") + else() + set(re "INFO<([A-Z0-9]+)=([01])>") + file(STRINGS "${path_bin_arch_detect}" infos REGEX "${re}") + + foreach(info_arch_01 IN LISTS infos) + string(REGEX MATCH "${re}" A "${info_arch_01}") + if(NOT "${CMAKE_MATCH_1}" IN_LIST known_archs) + message(WARNING "Unknown architecture: \"${CMAKE_MATCH_1}\"") + continue() + endif() + set(arch "${CMAKE_MATCH_1}") + set(arch_01 "${CMAKE_MATCH_2}") + set(detected_${arch} "${arch_01}") + endforeach() + + foreach(known_arch IN LISTS known_archs) + if(detected_${known_arch}) + list(APPEND detected_archs ${known_arch}) + endif() + endforeach() + endif() + + if(detected_archs) + foreach(known_arch IN LISTS known_archs) + set("CPU_${known_arch}" "${detected_${known_arch}}" CACHE BOOL "Detected architecture ${known_arch}") + endforeach() + message(STATUS "${msg} - ${detected_archs}") + else() + include(CheckCSourceCompiles) + cmake_push_check_state(RESET) + foreach(known_arch IN LISTS known_archs) + if(NOT detected_archs) + set(cache_variable "CPU_${known_arch}") + set(test_src " + int main(int argc, char *argv[]) { + #if ${arch_check_${known_arch}} + return 0; + #else + choke + #endif + } + ") + check_c_source_compiles("${test_src}" "${cache_variable}") + if(${cache_variable}) + set(CPU_${known_arch} "1" CACHE BOOL "Detected architecture ${known_arch}") + set(detected_archs ${known_arch}) + else() + set(CPU_${known_arch} "0" CACHE BOOL "Detected architecture ${known_arch}") + endif() + endif() + endforeach() + cmake_pop_check_state() + endif() + set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE) +endfunction() diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index a1939441..851403cc 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -32,14 +32,16 @@ target_compile_definitions(miniwin PRIVATE ) find_package(OpenGL) -find_package(GLEW) -if(OpenGL_FOUND AND GLEW_FOUND) - message(STATUS "Found OpenGL and GLEW: enabling OpenGL 1.x renderer") - target_sources(miniwin PRIVATE src/d3drm/backends/opengl1/renderer.cpp) +if(OpenGL_FOUND) + message(STATUS "Found OpenGL: enabling OpenGL 1.x renderer") + target_sources(miniwin PRIVATE + src/d3drm/backends/opengl1/actual.cpp + src/d3drm/backends/opengl1/renderer.cpp + ) target_compile_definitions(miniwin PRIVATE USE_OPENGL1) - target_link_libraries(miniwin PRIVATE OpenGL::GL GLEW::GLEW) + target_link_libraries(miniwin PRIVATE OpenGL::GL) else() - message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL and GLEW") + message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL") endif() find_library(OPENGL_ES2_LIBRARY NAMES GLESv2) @@ -52,6 +54,23 @@ else() message(STATUS "🧩 OpenGL ES 2.x support not enabled") endif() +if(NINTENDO_3DS) + if(ISLE_DEBUG) + find_library(CITRO3D_LIBRARY NAMES citro3dd) + else() + find_library(CITRO3D_LIBRARY NAMES citro3d) + endif() + if(CITRO3D_LIBRARY) + message(STATUS "Found citro3d: enabling Citro3D renderer") + target_sources(miniwin PRIVATE src/d3drm/backends/citro3d/renderer.cpp) + ctr_add_shader_library(vshader src/d3drm/backends/citro3d/vshader.v.pica) + dkp_add_embedded_binary_library(3ds_shaders vshader) + target_link_libraries(miniwin PRIVATE ${CITRO3D_LIBRARY} 3ds_shaders) + else() + message(STATUS "🧩 Citro3D support not enabled") + endif() +endif() + if(WIN32) target_sources(miniwin PRIVATE src/d3drm/backends/directx9/actual.cpp diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp new file mode 100644 index 00000000..f9167401 --- /dev/null +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -0,0 +1,606 @@ +#include "d3drmrenderer.h" +#include "d3drmrenderer_citro3d.h" +#include "d3drmtexture_impl.h" +#include "ddraw_impl.h" +#include "meshutils.h" +#include "miniwin.h" +#include "vshader_shbin.h" + +#include + +static bool g_rendering = false; + +static DVLB_s* vshader_dvlb; +static shaderProgram_s program; +static int uLoc_projection; +static int uLoc_modelView; +static int uLoc_meshColor; +static int uLoc_lightVec; +static int uLoc_lightClr; +static int uLoc_shininess; + +Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) +{ + m_width = 320; + m_height = 240; + m_virtualWidth = width; + m_virtualHeight = height; + + gfxInitDefault(); + consoleInit(GFX_TOP, nullptr); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + + m_renderTarget = C3D_RenderTargetCreate(m_height, m_width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetOutput( + m_renderTarget, + GFX_BOTTOM, + GFX_LEFT, + GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO) + ); + + vshader_dvlb = DVLB_ParseFile((u32*) vshader_shbin, vshader_shbin_size); + shaderProgramInit(&program); + shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]); + C3D_BindProgram(&program); + + C3D_CullFace(GPU_CULL_FRONT_CCW); + + uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection"); + uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView"); + uLoc_meshColor = shaderInstanceGetUniformLocation(program.vertexShader, "meshColor"); + uLoc_lightVec = shaderInstanceGetUniformLocation(program.vertexShader, "lightVec"); + uLoc_lightClr = shaderInstanceGetUniformLocation(program.vertexShader, "lightClr"); + uLoc_shininess = shaderInstanceGetUniformLocation(program.vertexShader, "shininess"); + + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 3); // v2=normal + AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 2); // v1=texcoord +} + +Citro3DRenderer::~Citro3DRenderer() +{ + shaderProgramFree(&program); + DVLB_Free(vshader_dvlb); + C3D_Fini(); + gfxExit(); +} + +void Citro3DRenderer::PushLights(const SceneLight* lights, size_t count) +{ + m_lights.assign(lights, lights + count); +} + +void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) +{ + memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); +} + +void Citro3DRenderer::SetFrustumPlanes(const Plane* frustumPlanes) +{ +} + +struct Citro3DCacheDestroyContext { + Citro3DRenderer* renderer; + Uint32 id; +}; + +void Citro3DRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) +{ + auto* ctx = new Citro3DCacheDestroyContext{this, id}; + texture->AddDestroyCallback( + [](IDirect3DRMObject* obj, void* arg) { + auto* ctx = static_cast(arg); + auto& entry = ctx->renderer->m_textures[ctx->id]; + if (entry.texture) { + C3D_TexDelete(&entry.c3dTex); + entry.texture = nullptr; + } + delete ctx; + }, + ctx + ); +} + +static int NearestPowerOfTwoClamp(int val) +{ + static const int sizes[] = {8, 16, 32, 64, 128, 256, 512}; + for (int size : sizes) { + if (val <= size) { + return size; + } + } + return 512; +} + +static SDL_Surface* ConvertAndResizeSurface(SDL_Surface* original, bool isUI, float scale) +{ + SDL_Surface* converted = SDL_ConvertSurface(original, SDL_PIXELFORMAT_RGBA8888); + if (!converted) { + return nullptr; + } + if (!isUI) { + return converted; + } + + int scaledW = static_cast(converted->w * scale); + int scaledH = static_cast(converted->h * scale); + + int paddedW = NearestPowerOfTwoClamp(scaledW); + int paddedH = NearestPowerOfTwoClamp(scaledH); + + SDL_Surface* padded = SDL_CreateSurface(paddedW, paddedH, SDL_PIXELFORMAT_RGBA8888); + if (!padded) { + SDL_DestroySurface(converted); + return nullptr; + } + + SDL_Rect dstRect = {0, 0, scaledW, scaledH}; + SDL_BlitSurfaceScaled(converted, nullptr, padded, &dstRect, SDL_SCALEMODE_LINEAR); + SDL_DestroySurface(converted); + + return padded; +} + +static void EncodeTextureLayout(const u8* src, u8* dst, int width, int height) +{ + const int tileSize = 8; + const int bytesPerPixel = 4; + + int tilesPerRow = (width + tileSize - 1) / tileSize; + + static const uint8_t mortonLUT[64] = {0, 1, 4, 5, 16, 17, 20, 21, 2, 3, 6, 7, 18, 19, 22, 23, + 8, 9, 12, 13, 24, 25, 28, 29, 10, 11, 14, 15, 26, 27, 30, 31, + 32, 33, 36, 37, 48, 49, 52, 53, 34, 35, 38, 39, 50, 51, 54, 55, + 40, 41, 44, 45, 56, 57, 60, 61, 42, 43, 46, 47, 58, 59, 62, 63}; + + for (int tileY = 0; tileY < height; tileY += tileSize) { + for (int tileX = 0; tileX < width; tileX += tileSize) { + int tileIndex = (tileY / tileSize) * tilesPerRow + (tileX / tileSize); + tileIndex *= tileSize * tileSize; + + for (int y = 0; y < tileSize; ++y) { + for (int x = 0; x < tileSize; ++x) { + int srcX = tileX + x; + int srcY = tileY + y; + + if (srcX >= width || srcY >= height) { + continue; + } + + int morton = mortonLUT[y * tileSize + x]; + int dstIndex = (tileIndex + morton) * bytesPerPixel; + int srcIndex = ((height - 1 - srcY) * width + srcX); + + *(u32*) &dst[dstIndex] = ((u32*) src)[srcIndex]; + } + } + } + } +} + +static bool ConvertAndUploadTexture(C3D_Tex* tex, SDL_Surface* originalSurface, bool isUI, float scale) +{ + SDL_Surface* resized = ConvertAndResizeSurface(originalSurface, isUI, scale); + if (!resized) { + return false; + } + + int width = resized->w; + int height = resized->h; + + C3D_TexInitParams params = {}; + params.width = width; + params.height = height; + params.format = GPU_RGBA8; + params.maxLevel = isUI ? 0 : 4; + params.type = GPU_TEX_2D; + if (!C3D_TexInitWithParams(tex, nullptr, params)) { + SDL_DestroySurface(resized); + return false; + } + + uint8_t* tiledData = (uint8_t*) malloc(width * height * 4); + if (!tiledData) { + SDL_DestroySurface(resized); + return false; + } + + EncodeTextureLayout((const u8*) resized->pixels, tiledData, width, height); + SDL_DestroySurface(resized); + + C3D_TexUpload(tex, tiledData); + free(tiledData); + + if (isUI) { + C3D_TexSetFilter(tex, GPU_NEAREST, GPU_NEAREST); + C3D_TexSetWrap(tex, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE); + } + else { + C3D_TexSetFilter(tex, GPU_LINEAR, GPU_LINEAR); + C3D_TexSetWrap(tex, GPU_REPEAT, GPU_REPEAT); + C3D_TexSetFilterMipmap(tex, GPU_LINEAR); + C3D_TexGenerateMipmap(tex, GPU_TEXFACE_2D); + } + + return true; +} + +Uint32 Citro3DRenderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi) +{ + auto texture = static_cast(iTexture); + auto surface = static_cast(texture->m_surface); + SDL_Surface* originalSurface = surface->m_surface; + + int originalW = originalSurface->w; + int originalH = originalSurface->h; + + for (Uint32 i = 0; i < m_textures.size(); ++i) { + auto& tex = m_textures[i]; + if (tex.texture == texture) { + if (tex.version != texture->m_version) { + C3D_TexDelete(&tex.c3dTex); + if (!ConvertAndUploadTexture(&tex.c3dTex, originalSurface, isUi, m_viewportTransform.scale)) { + return NO_TEXTURE_ID; + } + + tex.version = texture->m_version; + tex.width = NearestPowerOfTwoClamp(originalW * m_viewportTransform.scale); + tex.height = NearestPowerOfTwoClamp(originalH * m_viewportTransform.scale); + } + return i; + } + } + + C3DTextureCacheEntry entry; + entry.texture = texture; + entry.version = texture->m_version; + entry.width = NearestPowerOfTwoClamp(originalW * m_viewportTransform.scale); + entry.height = NearestPowerOfTwoClamp(originalH * m_viewportTransform.scale); + + if (!ConvertAndUploadTexture(&entry.c3dTex, originalSurface, isUi, m_viewportTransform.scale)) { + return NO_TEXTURE_ID; + } + + for (Uint32 i = 0; i < m_textures.size(); ++i) { + if (!m_textures[i].texture) { + m_textures[i] = std::move(entry); + AddTextureDestroyCallback(i, texture); + return i; + } + } + + m_textures.push_back(std::move(entry)); + AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture); + return (Uint32) (m_textures.size() - 1); +} + +C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup) +{ + C3DMeshCacheEntry cache{&meshGroup, meshGroup.version}; + + std::vector vertexBuffer; + std::vector indexBuffer; + + if (meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT) { + FlattenSurfaces( + meshGroup.vertices.data(), + meshGroup.vertices.size(), + meshGroup.indices.data(), + meshGroup.indices.size(), + meshGroup.texture != nullptr, + vertexBuffer, + indexBuffer + ); + } + else { + vertexBuffer.assign(meshGroup.vertices.begin(), meshGroup.vertices.end()); + indexBuffer.assign(meshGroup.indices.begin(), meshGroup.indices.end()); + } + + // Flatten vertices as IBO is buggy on 3DS hardware + std::vector vertexUploadBuffer; + vertexUploadBuffer.reserve(indexBuffer.size()); + + for (size_t i = 0; i < indexBuffer.size(); ++i) { + vertexUploadBuffer.emplace_back(vertexBuffer[indexBuffer[i]]); + } + + size_t vertexBufferSize = vertexUploadBuffer.size() * sizeof(D3DRMVERTEX); + cache.vbo = linearAlloc(vertexBufferSize); + memcpy(cache.vbo, vertexUploadBuffer.data(), vertexBufferSize); + cache.vertexCount = vertexUploadBuffer.size(); + + return cache; +} + +void Citro3DRenderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) +{ + auto* ctx = new Citro3DCacheDestroyContext{this, id}; + mesh->AddDestroyCallback( + [](IDirect3DRMObject* obj, void* arg) { + auto* ctx = static_cast(arg); + auto& cacheEntry = ctx->renderer->m_meshs[ctx->id]; + if (cacheEntry.meshGroup) { + cacheEntry.meshGroup = nullptr; + linearFree(cacheEntry.vbo); + cacheEntry.vertexCount = 0; + } + delete ctx; + }, + ctx + ); +} + +Uint32 Citro3DRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) +{ + for (Uint32 i = 0; i < m_meshs.size(); ++i) { + auto& cache = m_meshs[i]; + if (cache.meshGroup == meshGroup) { + if (cache.version != meshGroup->version) { + cache = std::move(C3DUploadMesh(*meshGroup)); + } + return i; + } + } + + auto newCache = C3DUploadMesh(*meshGroup); + + for (Uint32 i = 0; i < m_meshs.size(); ++i) { + auto& cache = m_meshs[i]; + if (!cache.meshGroup) { + cache = std::move(newCache); + AddMeshDestroyCallback(i, mesh); + return i; + } + } + + m_meshs.push_back(std::move(newCache)); + AddMeshDestroyCallback((Uint32) (m_meshs.size() - 1), mesh); + return (Uint32) (m_meshs.size() - 1); +} + +void Citro3DRenderer::StartFrame() +{ + if (g_rendering) { + return; + } + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C3D_FrameDrawOn(m_renderTarget); + g_rendering = true; +} + +void ConvertPerspective(const D3DRMMATRIX4D in, C3D_Mtx* out) +{ + float f_h = in[0][0]; + float f_v = in[1][1]; + + float aspect = f_v / f_h; + float fovY = 2.0f * atanf(1.0f / f_v); + + float nearZ = -in[3][2] / in[2][2]; + float farZ = nearZ * in[2][2] / (in[2][2] - 1.0f); + + Mtx_PerspTilt(out, fovY, aspect, nearZ, farZ, true); +} + +HRESULT Citro3DRenderer::BeginFrame() +{ + StartFrame(); + C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL); + + C3D_Mtx projection; + ConvertPerspective(m_projection, &projection); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + + for (const auto& light : m_lights) { + FColor lightColor = light.color; + if (light.positional == 0.0f && light.directional == 0.0f) { + // Ambient light + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 2, lightColor.r, lightColor.g, lightColor.b, 1.0f); + } + else if (light.directional == 1.0f) { + C3D_FVUnifSet( + GPU_VERTEX_SHADER, + uLoc_lightVec + 1, + -light.direction.x, + -light.direction.y, + -light.direction.z, + 0.0f + ); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 1, lightColor.r, lightColor.g, lightColor.b, 0.0f); + } + else if (light.positional == 1.0f) { + C3D_FVUnifSet( + GPU_VERTEX_SHADER, + uLoc_lightVec + 0, + light.position.x, + light.position.y, + light.position.z, + 0.0f + ); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 0, lightColor.r, lightColor.g, lightColor.b, 0.0f); + } + } + + return S_OK; +} + +void Citro3DRenderer::EnableTransparency() +{ + C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_COLOR); +} + +void ConvertMatrix(const D3DRMMATRIX4D in, C3D_Mtx* out) +{ + for (int i = 0; i < 4; i++) { + out->r[i].x = in[0][i]; + out->r[i].y = in[1][i]; + out->r[i].z = in[2][i]; + out->r[i].w = in[3][i]; + } +} + +void Citro3DRenderer::SubmitDraw( + DWORD meshId, + const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, + const Matrix3x3& normalMatrix, + const Appearance& appearance +) +{ + C3D_Mtx modelView; + ConvertMatrix(modelViewMatrix, &modelView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + auto& mesh = m_meshs[meshId]; + + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, mesh.vbo, sizeof(D3DRMVERTEX), 3, 0x210); + + C3D_FVUnifSet( + GPU_VERTEX_SHADER, + uLoc_meshColor, + appearance.color.r / 255.0f, + appearance.color.g / 255.0f, + appearance.color.b / 255.0f, + appearance.color.a / 255.0f + ); + + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_shininess, appearance.shininess / 255.0f, 0.0f, 0.0f, 0.0f); + + if (appearance.textureId != NO_TEXTURE_ID) { + C3D_TexBind(0, &m_textures[appearance.textureId].c3dTex); + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvInit(env); + C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); + } + else { + C3D_TexBind(0, nullptr); + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvInit(env); + C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); + } + + C3D_DrawArrays(GPU_TRIANGLES, 0, mesh.vertexCount); +} + +HRESULT Citro3DRenderer::FinalizeFrame() +{ + return S_OK; +} + +void Citro3DRenderer::Resize(int width, int height, const ViewportTransform& viewportTransform) +{ + m_width = width; + m_height = height; + m_viewportTransform = viewportTransform; +} + +void Citro3DRenderer::Clear(float r, float g, float b) +{ + StartFrame(); + u32 color = + (static_cast(r * 255) << 24) | (static_cast(g * 255) << 16) | (static_cast(b * 255) << 8) | 255; + C3D_RenderTargetClear(m_renderTarget, C3D_CLEAR_ALL, color, 0); +} + +void Citro3DRenderer::Flip() +{ + C3D_FrameEnd(0); + gfxFlushBuffers(); + gspWaitForVBlank(); + g_rendering = false; +} + +void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) +{ + C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + StartFrame(); + C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_COLOR); + + float left = -m_viewportTransform.offsetX / m_viewportTransform.scale; + float right = (m_width - m_viewportTransform.offsetX) / m_viewportTransform.scale; + float top = -m_viewportTransform.offsetY / m_viewportTransform.scale; + float bottom = (m_height - m_viewportTransform.offsetY) / m_viewportTransform.scale; + + C3D_Mtx projection, modelView; + Mtx_OrthoTilt(&projection, left, right, bottom, top, 0.0f, 1.0f, true); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + Mtx_Identity(&modelView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + // Set light directions + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec + 0, 0.0f, 0.0f, 0.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec + 1, 0.0f, 0.0f, 0.0f, 0.0f); + + // Set light colors + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 0, 0.0f, 0.0f, 0.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 1, 0.0f, 0.0f, 0.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 2, 1.0f, 1.0f, 1.0f, 1.0f); // Ambient + + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_shininess, 0.0f, 0.0f, 0.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_meshColor, 1.0f, 1.0f, 1.0f, 1.0f); + + C3DTextureCacheEntry& texture = m_textures[textureId]; + + C3D_TexBind(0, &texture.c3dTex); + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvInit(env); + C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); + + float scale = m_viewportTransform.scale; + + float x1 = static_cast(dstRect.x); + float y1 = static_cast(dstRect.y); + float x2 = x1 + static_cast(dstRect.w); + float y2 = y1 + static_cast(dstRect.h); + + float u0 = (srcRect.x * scale) / texture.width; + float u1 = ((srcRect.x + srcRect.w) * scale) / texture.width; + float v0 = (srcRect.y * scale) / texture.height; + float v1 = ((srcRect.y + srcRect.h) * scale) / texture.height; + + C3D_ImmDrawBegin(GPU_TRIANGLES); + + // Triangle 1 + C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f); + + C3D_ImmSendAttrib(x2, y1, 0.5f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(u1, v0, 0.0f, 0.0f); + + C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f); + + // Triangle 2 + C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f); + + C3D_ImmSendAttrib(x1, y2, 0.5f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(u0, v1, 0.0f, 0.0f); + + C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f); + + C3D_ImmDrawEnd(); +} + +void Citro3DRenderer::Download(SDL_Surface* target) +{ + MINIWIN_NOT_IMPLEMENTED(); +} diff --git a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica new file mode 100644 index 00000000..cd7f91c4 --- /dev/null +++ b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica @@ -0,0 +1,130 @@ +; Uniforms +.fvec projection[4], modelView[4], meshColor +.fvec lightVec[2], lightClr[3], shininess + +; Constants +.constf myconst(0.0, 1.0, -1.0, -0.5) + +; Outputs +.out outpos position +.out outtc0 texcoord0 +.out outclr color + +; Inputs +.alias inpos v0 +.alias innrm v1 +.alias intex v2 + +.proc main + ; Prepare constants in usable temp regs + mov r15.x, myconst.x ; 0.0 + mov r15.y, myconst.y ; 1.0 + mov r15.z, myconst.z ; -1.0 + + ; Force the w component of inpos to be 1.0 + mov r0.xyz, inpos + mov r0.w, r15.y + + ; r1 = modelView * inpos + dp4 r1.x, modelView[0], r0 + dp4 r1.y, modelView[1], r0 + dp4 r1.z, modelView[2], r0 + dp4 r1.w, modelView[3], r0 + + ; outpos = projection * r1 + dp4 outpos.x, projection[0], r1 + dp4 outpos.y, projection[1], r1 + dp4 outpos.z, projection[2], r1 + dp4 outpos.w, projection[3], r1 + + ; outtex = intex + mov outtc0, intex + mov outtc0.zw, myconst.xy + + ; Transform normal + mov r2.xyz, innrm + mov r2.w, r15.x + dp4 r3.x, modelView[0], r2 + dp4 r3.y, modelView[1], r2 + dp4 r3.z, modelView[2], r2 + mov r3.w, r15.x + dp3 r4.x, r3, r3 + rsq r4.x, r4.x + mul r3, r4.xxxx, r3 ; r3 = normalized normal + + ; Normalize lightVec[0] + mov r5, lightVec[0] + dp3 r6.x, r5, r5 + rsq r6.x, r6.x + mul r5, r6.xxxx, r5 + + ; dot(normal, lightVec[0]) + dp3 r6.x, r3, r5 + max r6.x, r6.x, r15.xxxx + + ; Normalize lightVec[1] + mov r7, lightVec[1] + dp3 r8.x, r7, r7 + rsq r8.x, r8.x + mul r7, r8.xxxx, r7 + + ; dot(normal, lightVec[1]) + dp3 r6.y, r3, r7 + max r6.y, r6.y, r15.xxxx + + ; Load lightClr + mov r8, lightClr[2] ; ambient + mov r9, lightClr[0] ; point + mov r10, lightClr[1] ; directional + + ; diffuse = ambient + (lightClr[0] * dot0) + (lightClr[1] * dot1) + mul r11, r9, r6.xxxx + add r8, r8, r11 + mul r11, r10, r6.yyyy + add r8, r8, r11 ; r8 = diffuse + + ; Check if shininess > 0 + mov r12, shininess + slt r13.x, r15.x, r12.x + + ; viewVec = normalize(-position.xyz) + mov r14.xyz, r1.xyz + mul r14.xyz, r14.xyz, r15.zzz + dp3 r4.x, r14, r14 + rsq r4.x, r4.x + mul r14, r4.xxxx, r14 + + ; H = normalize(view + lightVec[1]) + add r11, r14, r7 + dp3 r4.x, r11, r11 + rsq r4.x, r4.x + mul r11, r4.xxxx, r11 + + ; dot(normal, H) + dp3 r4.x, r3, r11 + max r4.x, r4.x, r15.x + + ; Approximate pow(dotNH, 10) by repeated multiplication + mul r5.x, r4.x, r4.x ; dotNH^2 + mul r5.x, r5.x, r5.x ; dotNH^4 + mul r5.x, r5.x, r5.x ; dotNH^8 + mul r4.x, r5.x, r4.x ; dotNH^9 + mul r4.x, r4.x, r4.x ; dotNH^10 + + ; Multiply by shininess > 0 flag + mul r4.x, r4.x, r13.x + + ; specular = lightClr[1] * spec + mul r5, r10, r4.xxxx + + ; final = diffuse * meshColor + specular * lightClr[1] + mov r9, meshColor + mul r6, r8, r9 ; diffuse * meshColor + add r7.xyz, r6.xyz, r5.xyz ; add specular (already multiplied by lightClr) + min r7.xyz, r7.xyz, r15.yyyy + + mov outclr.xyz, r7.xyz + mov outclr.w, meshColor.w + + end +.end diff --git a/miniwin/src/d3drm/backends/directx9/renderer.cpp b/miniwin/src/d3drm/backends/directx9/renderer.cpp index 574939f5..5917cd07 100644 --- a/miniwin/src/d3drm/backends/directx9/renderer.cpp +++ b/miniwin/src/d3drm/backends/directx9/renderer.cpp @@ -76,7 +76,7 @@ void DirectX9Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* ); } -Uint32 DirectX9Renderer::GetTextureId(IDirect3DRMTexture* iTexture) +Uint32 DirectX9Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); @@ -216,24 +216,6 @@ Uint32 DirectX9Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshG return static_cast(m_meshs.size() - 1); } -void DirectX9Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) -{ - halDesc->dcmColorModel = D3DCOLORMODEL::RGB; - halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; - halDesc->dwDeviceZBufferBitDepth = DDBD_24; - helDesc->dwDeviceRenderBitDepth = DDBD_32; - halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; - halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; - halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; - - memset(helDesc, 0, sizeof(D3DDEVICEDESC)); -} - -const char* DirectX9Renderer::GetName() -{ - return "DirectX 9 HAL"; -} - HRESULT DirectX9Renderer::BeginFrame() { return Actual_BeginFrame(); diff --git a/miniwin/src/d3drm/backends/opengl1/actual.cpp b/miniwin/src/d3drm/backends/opengl1/actual.cpp new file mode 100644 index 00000000..1c12f51a --- /dev/null +++ b/miniwin/src/d3drm/backends/opengl1/actual.cpp @@ -0,0 +1,369 @@ +// This file cannot include any minwin headers. + +#include "actual.h" + +#include "structs.h" + +#include +#include +#include +#include +#include + +// GL extension API functions. +bool g_useVBOs; +PFNGLGENBUFFERSPROC mwglGenBuffers; +PFNGLBINDBUFFERPROC mwglBindBuffer; +PFNGLBUFFERDATAPROC mwglBufferData; +PFNGLDELETEBUFFERSPROC mwglDeleteBuffers; + +void GL11_InitState() +{ + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CW); +} + +void GL11_LoadExtensions() +{ + g_useVBOs = SDL_GL_ExtensionSupported("GL_ARB_vertex_buffer_object"); + + if (g_useVBOs) { + // Load the required GL function pointers. + mwglGenBuffers = (PFNGLGENBUFFERSPROC) SDL_GL_GetProcAddress("glGenBuffersARB"); + mwglBindBuffer = (PFNGLBINDBUFFERPROC) SDL_GL_GetProcAddress("glBindBufferARB"); + mwglBufferData = (PFNGLBUFFERDATAPROC) SDL_GL_GetProcAddress("glBufferDataARB"); + mwglDeleteBuffers = (PFNGLDELETEBUFFERSPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB"); + } +} + +void GL11_DestroyTexture(GLuint texId) +{ + glDeleteTextures(1, &texId); +} + +GLuint GL11_UploadTextureData(void* pixels, int width, int height, bool isUi) +{ + GLuint texId; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_2D, texId); + if (isUi) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + return texId; +} + +void GL11_UploadMesh(GLMeshCacheEntry& cache, bool hasTexture) +{ + if (g_useVBOs) { + mwglGenBuffers(1, &cache.vboPositions); + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboPositions); + mwglBufferData( + GL_ARRAY_BUFFER_ARB, + cache.positions.size() * sizeof(GL11_BridgeVector), + cache.positions.data(), + GL_STATIC_DRAW_ARB + ); + + mwglGenBuffers(1, &cache.vboNormals); + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboNormals); + mwglBufferData( + GL_ARRAY_BUFFER_ARB, + cache.normals.size() * sizeof(GL11_BridgeVector), + cache.normals.data(), + GL_STATIC_DRAW_ARB + ); + + if (hasTexture) { + mwglGenBuffers(1, &cache.vboTexcoords); + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboTexcoords); + mwglBufferData( + GL_ARRAY_BUFFER_ARB, + cache.texcoords.size() * sizeof(GL11_BridgeTexCoord), + cache.texcoords.data(), + GL_STATIC_DRAW_ARB + ); + } + + mwglGenBuffers(1, &cache.ibo); + mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, cache.ibo); + mwglBufferData( + GL_ELEMENT_ARRAY_BUFFER_ARB, + cache.indices.size() * sizeof(cache.indices[0]), + cache.indices.data(), + GL_STATIC_DRAW_ARB + ); + } +} + +void GL11_DestroyMesh(GLMeshCacheEntry& cache) +{ + if (g_useVBOs) { + mwglDeleteBuffers(1, &cache.vboPositions); + mwglDeleteBuffers(1, &cache.vboNormals); + mwglDeleteBuffers(1, &cache.vboTexcoords); + mwglDeleteBuffers(1, &cache.ibo); + } +} + +void GL11_BeginFrame(const Matrix4x4* projection) +{ + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glEnable(GL_LIGHTING); + glEnable(GL_COLOR_MATERIAL); + + // Disable all lights and reset global ambient + for (int i = 0; i < 8; ++i) { + glDisable(GL_LIGHT0 + i); + } + const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f}; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroAmbient); + glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); + + // Projection and view + glMatrixMode(GL_PROJECTION); + glLoadMatrixf((const GLfloat*) projection); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void GL11_UploadLight(int lightIdx, GL11_BridgeSceneLight* l) +{ + // Setup light + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + GLenum lightId = GL_LIGHT0 + lightIdx++; + const FColor& c = l->color; + GLfloat col[4] = {c.r, c.g, c.b, c.a}; + const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f}; + + if (l->positional == 0.f && l->directional == 0.f) { + // Ambient light only + glLightfv(lightId, GL_AMBIENT, col); + const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f}; + glLightfv(lightId, GL_DIFFUSE, black); + glLightfv(lightId, GL_SPECULAR, black); + const GLfloat dummyPos[4] = {0.f, 0.f, 1.f, 0.f}; + glLightfv(lightId, GL_POSITION, dummyPos); + } + else { + glLightfv(lightId, GL_AMBIENT, zeroAmbient); + glLightfv(lightId, GL_DIFFUSE, col); + if (l->directional == 1.0f) { + glLightfv(lightId, GL_SPECULAR, col); + } + else { + const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f}; + glLightfv(lightId, GL_SPECULAR, black); + } + + GLfloat pos[4]; + if (l->directional == 1.f) { + pos[0] = -l->direction.x; + pos[1] = -l->direction.y; + pos[2] = -l->direction.z; + pos[3] = 0.f; + } + else { + pos[0] = l->position.x; + pos[1] = l->position.y; + pos[2] = l->position.z; + pos[3] = 1.f; + } + glLightfv(lightId, GL_POSITION, pos); + } + glEnable(lightId); + + glPopMatrix(); +} + +void GL11_EnableTransparency() +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(GL_FALSE); +} + +#define NO_TEXTURE_ID 0xffffffff + +void GL11_SubmitDraw( + GLMeshCacheEntry& mesh, + const Matrix4x4& modelViewMatrix, + const Appearance& appearance, + GLuint texId +) +{ + glLoadMatrixf(&modelViewMatrix[0][0]); + glEnable(GL_NORMALIZE); + +#ifndef __PSP__ + glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); + glColor4ub(appearance.color.r, appearance.color.g, appearance.color.b, appearance.color.a); +#else + GLfloat color[4] = { + appearance.color.r / 255.0f, + appearance.color.g / 255.0f, + appearance.color.b / 255.0f, + appearance.color.a / 255.0f + }; + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); +#endif + + if (appearance.shininess != 0.0f) { + GLfloat whiteSpec[] = {1.f, 1.f, 1.f, 1.f}; + glMaterialfv(GL_FRONT, GL_SPECULAR, whiteSpec); + glMaterialf(GL_FRONT, GL_SHININESS, appearance.shininess); + } + else { + GLfloat noSpec[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glMaterialfv(GL_FRONT, GL_SPECULAR, noSpec); + glMaterialf(GL_FRONT, GL_SHININESS, 0.0f); + } + + if (mesh.flat) { + glShadeModel(GL_FLAT); + } + else { + glShadeModel(GL_SMOOTH); + } + + // Bind texture if present + if (appearance.textureId != NO_TEXTURE_ID) { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texId); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + else { + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + if (g_useVBOs) { + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboPositions); + glVertexPointer(3, GL_FLOAT, 0, nullptr); + + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboNormals); + glNormalPointer(GL_FLOAT, 0, nullptr); + + if (appearance.textureId != NO_TEXTURE_ID) { + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboTexcoords); + glTexCoordPointer(2, GL_FLOAT, 0, nullptr); + } + + mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, mesh.ibo); + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr); + + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, 0); + mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + } + else { + glVertexPointer(3, GL_FLOAT, 0, mesh.positions.data()); + glNormalPointer(GL_FLOAT, 0, mesh.normals.data()); + if (appearance.textureId != NO_TEXTURE_ID) { + glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords.data()); + } + + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, mesh.indices.data()); + } + + glPopMatrix(); +} + +void GL11_Resize(int width, int height) +{ + glViewport(0, 0, width, height); +} + +void GL11_Clear(float r, float g, float b) +{ + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glClearColor(r, g, b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void GL11_Draw2DImage( + GLTextureCacheEntry& cache, + const SDL_Rect& srcRect, + const SDL_Rect& dstRect, + float left, + float right, + float bottom, + float top +) +{ + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glOrtho(left, right, bottom, top, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glDisable(GL_LIGHTING); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, cache.glTextureId); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + GLint boundTexture = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture); + + float u1 = srcRect.x / cache.width; + float v1 = srcRect.y / cache.height; + float u2 = (srcRect.x + srcRect.w) / cache.width; + float v2 = (srcRect.y + srcRect.h) / cache.height; + + float x1 = (float) dstRect.x; + float y1 = (float) dstRect.y; + float x2 = x1 + dstRect.w; + float y2 = y1 + dstRect.h; + + glBegin(GL_QUADS); + glTexCoord2f(u1, v1); + glVertex2f(x1, y1); + glTexCoord2f(u2, v1); + glVertex2f(x2, y1); + glTexCoord2f(u2, v2); + glVertex2f(x2, y2); + glTexCoord2f(u1, v2); + glVertex2f(x1, y2); + glEnd(); + + // Restore state + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); +} + +void GL11_Download(SDL_Surface* target) +{ + glFinish(); + glReadPixels(0, 0, target->w, target->h, GL_RGBA, GL_UNSIGNED_BYTE, target->pixels); +} diff --git a/miniwin/src/d3drm/backends/opengl1/actual.h b/miniwin/src/d3drm/backends/opengl1/actual.h new file mode 100644 index 00000000..1d5e407d --- /dev/null +++ b/miniwin/src/d3drm/backends/opengl1/actual.h @@ -0,0 +1,90 @@ +#pragma once + +#include "structs.h" + +#include +#include +#include + +// We don't want to transitively include windows.h, but we need GLuint +typedef unsigned int GLuint; +struct IDirect3DRMTexture; +struct MeshGroup; + +typedef float Matrix4x4[4][4]; + +struct GL11_BridgeVector { + float x, y, z; +}; + +struct GL11_BridgeTexCoord { + float u, v; +}; + +struct GL11_BridgeSceneLight { + FColor color; + GL11_BridgeVector position; + float positional; + GL11_BridgeVector direction; + float directional; +}; + +struct GL11_BridgeSceneVertex { + GL11_BridgeVector position; + GL11_BridgeVector normal; + float tu, tv; +}; + +struct GLTextureCacheEntry { + IDirect3DRMTexture* texture; + Uint32 version; + GLuint glTextureId; + float width; + float height; +}; + +struct GLMeshCacheEntry { + const MeshGroup* meshGroup; + int version; + bool flat; + + // non-VBO cache + std::vector positions; + std::vector normals; + std::vector texcoords; + std::vector indices; + + // VBO cache + GLuint vboPositions; + GLuint vboNormals; + GLuint vboTexcoords; + GLuint ibo; +}; + +void GL11_InitState(); +void GL11_LoadExtensions(); +void GL11_DestroyTexture(GLuint texId); +GLuint GL11_UploadTextureData(void* pixels, int width, int height, bool isUI); +void GL11_UploadMesh(GLMeshCacheEntry& cache, bool hasTexture); +void GL11_DestroyMesh(GLMeshCacheEntry& cache); +void GL11_BeginFrame(const Matrix4x4* projection); +void GL11_UploadLight(int lightIdx, GL11_BridgeSceneLight* l); +void GL11_EnableTransparency(); +void GL11_SubmitDraw( + GLMeshCacheEntry& mesh, + const Matrix4x4& modelViewMatrix, + const Appearance& appearance, + GLuint texId +); +void GL11_Resize(int width, int height); +void GL11_Clear(float r, float g, float b); +void GL11_Draw2DImage( + GLTextureCacheEntry& cache, + const SDL_Rect& srcRect, + const SDL_Rect& dstRect, + float left, + float right, + float bottom, + float top +); +void GL11_Download(SDL_Surface* target); diff --git a/miniwin/src/d3drm/backends/opengl1/renderer.cpp b/miniwin/src/d3drm/backends/opengl1/renderer.cpp index cc415de8..1014315f 100644 --- a/miniwin/src/d3drm/backends/opengl1/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengl1/renderer.cpp @@ -1,5 +1,4 @@ -#include -// must come after GLEW +#include "actual.h" #include "d3drmrenderer_opengl1.h" #include "ddraw_impl.h" #include "ddsurface_impl.h" @@ -11,58 +10,44 @@ #include #include +static_assert(sizeof(Matrix4x4) == sizeof(D3DRMMATRIX4D), "Matrix4x4 is wrong size"); +static_assert(sizeof(GL11_BridgeVector) == sizeof(D3DVECTOR), "GL11_BridgeVector is wrong size"); +static_assert(sizeof(GL11_BridgeTexCoord) == sizeof(TexCoord), "GL11_BridgeTexCoord is wrong size"); +static_assert(sizeof(GL11_BridgeSceneLight) == sizeof(SceneLight), "GL11_BridgeSceneLight is wrong size"); +static_assert(sizeof(GL11_BridgeSceneVertex) == sizeof(D3DRMVERTEX), "GL11_BridgeSceneVertex is wrong size"); + Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height) { + // We have to reset the attributes here after having enumerated the + // OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE + // call below when on an EGL-based backend, and crashes with EGL_BAD_MATCH. + SDL_GL_ResetAttributes(); + // But ResetAttributes resets it to 16. +#ifndef __PSP__ + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); +#endif SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_Window* window = DDWindow; - bool testWindow = false; - if (!window) { - window = SDL_CreateWindow("OpenGL 1.1 test", width, height, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); - if (!window) { - SDL_Log("SDL_CreateWindow: %s", SDL_GetError()); - return nullptr; - } - testWindow = true; + if (!DDWindow) { + SDL_Log("No window handler"); + return nullptr; } - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - - SDL_GLContext context = SDL_GL_CreateContext(window); + SDL_GLContext context = SDL_GL_CreateContext(DDWindow); if (!context) { SDL_Log("SDL_GL_CreateContext: %s", SDL_GetError()); - if (testWindow) { - SDL_DestroyWindow(window); - } return nullptr; } - if (!SDL_GL_MakeCurrent(window, context)) { + if (!SDL_GL_MakeCurrent(DDWindow, context)) { + SDL_GL_DestroyContext(context); SDL_Log("SDL_GL_MakeCurrent: %s", SDL_GetError()); - if (testWindow) { - SDL_DestroyWindow(window); - } return nullptr; } - GLenum err = glewInit(); - if (err != GLEW_OK) { - SDL_Log("glewInit: %s", glewGetErrorString(err)); - if (testWindow) { - SDL_DestroyWindow(window); - } - return nullptr; - } - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CW); - - if (testWindow) { - SDL_DestroyWindow(window); - } + GL11_InitState(); return new OpenGL1Renderer(width, height, context); } @@ -74,12 +59,15 @@ OpenGL1Renderer::OpenGL1Renderer(DWORD width, DWORD height, SDL_GLContext contex m_virtualWidth = width; m_virtualHeight = height; m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); - m_useVBOs = GLEW_ARB_vertex_buffer_object; + GL11_LoadExtensions(); + m_useVBOs = SDL_GL_ExtensionSupported("GL_ARB_vertex_buffer_object"); + m_useNPOT = SDL_GL_ExtensionSupported("GL_OES_texture_npot"); } OpenGL1Renderer::~OpenGL1Renderer() { SDL_DestroySurface(m_renderedImage); + SDL_GL_DestroyContext(m_context); } void OpenGL1Renderer::PushLights(const SceneLight* lightsArray, size_t count) @@ -114,7 +102,7 @@ void OpenGL1Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* t auto* ctx = static_cast(arg); auto& cache = ctx->renderer->m_textures[ctx->textureId]; if (cache.glTextureId != 0) { - glDeleteTextures(1, &cache.glTextureId); + GL11_DestroyTexture(cache.glTextureId); cache.glTextureId = 0; cache.texture = nullptr; } @@ -124,7 +112,60 @@ void OpenGL1Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* t ); } -Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture) +static int NextPowerOfTwo(int v) +{ + int power = 1; + while (power < v) { + power <<= 1; + } + return power; +} + +static Uint32 UploadTextureData(SDL_Surface* src, bool useNPOT, bool isUi) +{ + SDL_Surface* working = src; + if (src->format != SDL_PIXELFORMAT_RGBA32) { + working = SDL_ConvertSurface(src, SDL_PIXELFORMAT_RGBA32); + if (!working) { + SDL_Log("SDL_ConvertSurface failed: %s", SDL_GetError()); + return NO_TEXTURE_ID; + } + } + + SDL_Surface* finalSurface = working; + + // Resize to next power-of-two if needed and NPOT isn't supported + int newW = NextPowerOfTwo(working->w); + int newH = NextPowerOfTwo(working->h); + + if (!useNPOT && (newW != working->w || newH != working->h)) { + SDL_Surface* resized = SDL_CreateSurface(newW, newH, working->format); + if (!resized) { + SDL_Log("SDL_CreateSurface (resize) failed: %s", SDL_GetError()); + if (working != src) { + SDL_DestroySurface(working); + } + return NO_TEXTURE_ID; + } + + SDL_Rect srcRect = {0, 0, working->w, working->h}; + SDL_Rect dstRect = {0, 0, newW, newH}; + SDL_BlitSurfaceScaled(working, &srcRect, resized, &dstRect, SDL_SCALEMODE_NEAREST); + + if (working != src) { + SDL_DestroySurface(working); + } + finalSurface = resized; + } + + Uint32 texId = GL11_UploadTextureData(finalSurface->pixels, finalSurface->w, finalSurface->h, isUi); + if (finalSurface != src) { + SDL_DestroySurface(finalSurface); + } + return texId; +} + +Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); @@ -133,33 +174,17 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture) auto& tex = m_textures[i]; if (tex.texture == texture) { if (tex.version != texture->m_version) { - glDeleteTextures(1, &tex.glTextureId); - glGenTextures(1, &tex.glTextureId); - glBindTexture(GL_TEXTURE_2D, tex.glTextureId); - - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); - if (!surf) { - return NO_TEXTURE_ID; - } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); - SDL_DestroySurface(surf); - + GL11_DestroyTexture(tex.glTextureId); + tex.glTextureId = UploadTextureData(surface->m_surface, m_useNPOT, isUi); tex.version = texture->m_version; + tex.width = surface->m_surface->w; + tex.height = surface->m_surface->h; } return i; } } - GLuint texId; - glGenTextures(1, &texId); - glBindTexture(GL_TEXTURE_2D, texId); - - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); - if (!surf) { - return NO_TEXTURE_ID; - } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); - SDL_DestroySurface(surf); + GLuint texId = UploadTextureData(surface->m_surface, m_useNPOT, isUi); for (Uint32 i = 0; i < m_textures.size(); ++i) { auto& tex = m_textures[i]; @@ -167,12 +192,14 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture) tex.texture = texture; tex.version = texture->m_version; tex.glTextureId = texId; + tex.width = surface->m_surface->w; + tex.height = surface->m_surface->h; AddTextureDestroyCallback(i, texture); return i; } } - m_textures.push_back({texture, texture->m_version, texId}); + m_textures.push_back({texture, texture->m_version, texId, surface->m_surface->w, surface->m_surface->h}); AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture); return (Uint32) (m_textures.size() - 1); } @@ -206,52 +233,19 @@ GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup, bool useVBOs) if (meshGroup.texture) { cache.texcoords.resize(vertices.size()); std::transform(vertices.begin(), vertices.end(), cache.texcoords.begin(), [](const D3DRMVERTEX& v) { - return v.texCoord; + return GL11_BridgeTexCoord{v.texCoord.u, v.texCoord.v}; }); } cache.positions.resize(vertices.size()); std::transform(vertices.begin(), vertices.end(), cache.positions.begin(), [](const D3DRMVERTEX& v) { - return v.position; + return GL11_BridgeVector{v.position.x, v.position.y, v.position.z}; }); cache.normals.resize(vertices.size()); std::transform(vertices.begin(), vertices.end(), cache.normals.begin(), [](const D3DRMVERTEX& v) { - return v.normal; + return GL11_BridgeVector{v.normal.x, v.normal.y, v.normal.z}; }); - if (useVBOs) { - glGenBuffers(1, &cache.vboPositions); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions); - glBufferData( - GL_ARRAY_BUFFER, - cache.positions.size() * sizeof(D3DVECTOR), - cache.positions.data(), - GL_STATIC_DRAW - ); - - glGenBuffers(1, &cache.vboNormals); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals); - glBufferData(GL_ARRAY_BUFFER, cache.normals.size() * sizeof(D3DVECTOR), cache.normals.data(), GL_STATIC_DRAW); - - if (meshGroup.texture) { - glGenBuffers(1, &cache.vboTexcoords); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords); - glBufferData( - GL_ARRAY_BUFFER, - cache.texcoords.size() * sizeof(TexCoord), - cache.texcoords.data(), - GL_STATIC_DRAW - ); - } - - glGenBuffers(1, &cache.ibo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache.ibo); - glBufferData( - GL_ELEMENT_ARRAY_BUFFER, - cache.indices.size() * sizeof(cache.indices[0]), - cache.indices.data(), - GL_STATIC_DRAW - ); - } + GL11_UploadMesh(cache, meshGroup.texture != nullptr); return cache; } @@ -269,12 +263,7 @@ void OpenGL1Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) auto* ctx = static_cast(arg); auto& cache = ctx->renderer->m_meshs[ctx->id]; cache.meshGroup = nullptr; - if (ctx->renderer->m_useVBOs) { - glDeleteBuffers(1, &cache.vboPositions); - glDeleteBuffers(1, &cache.vboNormals); - glDeleteBuffers(1, &cache.vboTexcoords); - glDeleteBuffers(1, &cache.ibo); - } + GL11_DestroyMesh(cache); delete ctx; }, ctx @@ -309,110 +298,25 @@ Uint32 OpenGL1Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGr return (Uint32) (m_meshs.size() - 1); } -void OpenGL1Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) -{ - halDesc->dcmColorModel = D3DCOLORMODEL::RGB; - halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; - halDesc->dwDeviceZBufferBitDepth = DDBD_24; - helDesc->dwDeviceRenderBitDepth = DDBD_32; - halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; - halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; - halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; - - memset(helDesc, 0, sizeof(D3DDEVICEDESC)); -} - -const char* OpenGL1Renderer::GetName() -{ - return "OpenGL 1.1 HAL"; -} - HRESULT OpenGL1Renderer::BeginFrame() { - m_dirty = true; - - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glEnable(GL_LIGHTING); - glEnable(GL_COLOR_MATERIAL); - glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); - - // Disable all lights and reset global ambient - for (int i = 0; i < 8; ++i) { - glDisable(GL_LIGHT0 + i); - } - const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f}; - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroAmbient); - glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); - - // Setup lights - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + GL11_BeginFrame((Matrix4x4*) &m_projection[0][0]); int lightIdx = 0; for (const auto& l : m_lights) { if (lightIdx > 7) { break; } - GLenum lightId = GL_LIGHT0 + lightIdx++; - const FColor& c = l.color; - GLfloat col[4] = {c.r, c.g, c.b, c.a}; + GL11_UploadLight(lightIdx, (GL11_BridgeSceneLight*) &l); - if (l.positional == 0.f && l.directional == 0.f) { - // Ambient light only - glLightfv(lightId, GL_AMBIENT, col); - const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f}; - glLightfv(lightId, GL_DIFFUSE, black); - glLightfv(lightId, GL_SPECULAR, black); - const GLfloat dummyPos[4] = {0.f, 0.f, 1.f, 0.f}; - glLightfv(lightId, GL_POSITION, dummyPos); - } - else { - glLightfv(lightId, GL_AMBIENT, zeroAmbient); - glLightfv(lightId, GL_DIFFUSE, col); - if (l.directional == 1.0f) { - glLightfv(lightId, GL_SPECULAR, col); - } - else { - const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f}; - glLightfv(lightId, GL_SPECULAR, black); - } - - GLfloat pos[4]; - if (l.directional == 1.f) { - pos[0] = -l.direction.x; - pos[1] = -l.direction.y; - pos[2] = -l.direction.z; - pos[3] = 0.f; - } - else { - pos[0] = l.position.x; - pos[1] = l.position.y; - pos[2] = l.position.z; - pos[3] = 1.f; - } - glLightfv(lightId, GL_POSITION, pos); - } - glEnable(lightId); + lightIdx++; } - glPopMatrix(); - - // Projection and view - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(&m_projection[0][0]); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - return DD_OK; } void OpenGL1Renderer::EnableTransparency() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(GL_FALSE); + GL11_EnableTransparency(); } void OpenGL1Renderer::SubmitDraw( @@ -426,75 +330,14 @@ void OpenGL1Renderer::SubmitDraw( { auto& mesh = m_meshs[meshId]; - glLoadMatrixf(&modelViewMatrix[0][0]); - glEnable(GL_NORMALIZE); - - glColor4ub(appearance.color.r, appearance.color.g, appearance.color.b, appearance.color.a); - - if (appearance.shininess != 0.0f) { - GLfloat whiteSpec[] = {1.f, 1.f, 1.f, 1.f}; - glMaterialfv(GL_FRONT, GL_SPECULAR, whiteSpec); - glMaterialf(GL_FRONT, GL_SHININESS, appearance.shininess); - } - else { - GLfloat noSpec[] = {0.0f, 0.0f, 0.0f, 0.0f}; - glMaterialfv(GL_FRONT, GL_SPECULAR, noSpec); - glMaterialf(GL_FRONT, GL_SHININESS, 0.0f); - } - - if (mesh.flat) { - glShadeModel(GL_FLAT); - } - else { - glShadeModel(GL_SMOOTH); - } - // Bind texture if present if (appearance.textureId != NO_TEXTURE_ID) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); auto& tex = m_textures[appearance.textureId]; - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, tex.glTextureId); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); + GL11_SubmitDraw(mesh, modelViewMatrix, appearance, tex.glTextureId); } else { - glDisable(GL_TEXTURE_2D); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); + GL11_SubmitDraw(mesh, modelViewMatrix, appearance, 0); } - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - if (m_useVBOs) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboPositions); - glVertexPointer(3, GL_FLOAT, 0, nullptr); - - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboNormals); - glNormalPointer(GL_FLOAT, 0, nullptr); - - if (appearance.textureId != NO_TEXTURE_ID) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboTexcoords); - glTexCoordPointer(2, GL_FLOAT, 0, nullptr); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.ibo); - glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - else { - glVertexPointer(3, GL_FLOAT, 0, mesh.positions.data()); - glNormalPointer(GL_FLOAT, 0, mesh.normals.data()); - if (appearance.textureId != NO_TEXTURE_ID) { - glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords.data()); - } - - glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, mesh.indices.data()); - } - - glPopMatrix(); } HRESULT OpenGL1Renderer::FinalizeFrame() @@ -509,16 +352,13 @@ void OpenGL1Renderer::Resize(int width, int height, const ViewportTransform& vie m_viewportTransform = viewportTransform; SDL_DestroySurface(m_renderedImage); m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); - glViewport(0, 0, m_width, m_height); + GL11_Resize(width, height); } void OpenGL1Renderer::Clear(float r, float g, float b) { m_dirty = true; - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glClearColor(r, g, b, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GL11_Clear(r, g, b); } void OpenGL1Renderer::Flip() @@ -532,73 +372,18 @@ void OpenGL1Renderer::Flip() void OpenGL1Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) { m_dirty = true; - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); float left = -m_viewportTransform.offsetX / m_viewportTransform.scale; float right = (m_width - m_viewportTransform.offsetX) / m_viewportTransform.scale; float top = -m_viewportTransform.offsetY / m_viewportTransform.scale; float bottom = (m_height - m_viewportTransform.offsetY) / m_viewportTransform.scale; - glOrtho(left, right, bottom, top, -1, 1); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glDisable(GL_LIGHTING); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, m_textures[textureId].glTextureId); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - GLint boundTexture = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture); - - GLfloat texW, texH; - glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texW); - glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texH); - - float u1 = srcRect.x / texW; - float v1 = srcRect.y / texH; - float u2 = (srcRect.x + srcRect.w) / texW; - float v2 = (srcRect.y + srcRect.h) / texH; - - float x1 = (float) dstRect.x; - float y1 = (float) dstRect.y; - float x2 = x1 + dstRect.w; - float y2 = y1 + dstRect.h; - - glBegin(GL_QUADS); - glTexCoord2f(u1, v1); - glVertex2f(x1, y1); - glTexCoord2f(u2, v1); - glVertex2f(x2, y1); - glTexCoord2f(u2, v2); - glVertex2f(x2, y2); - glTexCoord2f(u1, v2); - glVertex2f(x1, y2); - glEnd(); - - // Restore state - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); + GL11_Draw2DImage(m_textures[textureId], srcRect, dstRect, left, right, bottom, top); } void OpenGL1Renderer::Download(SDL_Surface* target) { - glFinish(); - glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); + GL11_Download(m_renderedImage); SDL_Rect srcRect = { static_cast(m_viewportTransform.offsetX), diff --git a/miniwin/src/d3drm/backends/opengles2/renderer.cpp b/miniwin/src/d3drm/backends/opengles2/renderer.cpp index 3dbabda2..9cb8455c 100644 --- a/miniwin/src/d3drm/backends/opengles2/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengles2/renderer.cpp @@ -30,31 +30,29 @@ struct SceneLightGLES2 { Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) { + // We have to reset the attributes here after having enumerated the + // OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE + // call below when on an EGL-based backend, and crashes with EGL_BAD_MATCH. + SDL_GL_ResetAttributes(); + // But ResetAttributes resets it to 16. + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - SDL_Window* window = DDWindow; - bool testWindow = false; - if (!window) { - window = SDL_CreateWindow("OpenGL ES 2.0 test", width, height, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); - testWindow = true; - } - - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - - SDL_GLContext context = SDL_GL_CreateContext(window); - if (!context) { - if (testWindow) { - SDL_DestroyWindow(window); - } + if (!DDWindow) { + SDL_Log("No window handler"); return nullptr; } - if (!SDL_GL_MakeCurrent(window, context)) { - if (testWindow) { - SDL_DestroyWindow(window); - } + SDL_GLContext context = SDL_GL_CreateContext(DDWindow); + if (!context) { + SDL_Log("SDL_GL_CreateContext: %s", SDL_GetError()); + return nullptr; + } + + if (!SDL_GL_MakeCurrent(DDWindow, context)) { + SDL_GL_DestroyContext(context); return nullptr; } @@ -170,27 +168,115 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) glDeleteShader(vs); glDeleteShader(fs); - if (testWindow) { - SDL_DestroyWindow(window); + return new OpenGLES2Renderer(width, height, context, shaderProgram); +} + +GLES2MeshCacheEntry GLES2UploadMesh(const MeshGroup& meshGroup, bool forceUV = false) +{ + GLES2MeshCacheEntry cache{&meshGroup, meshGroup.version}; + + cache.flat = meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT; + + std::vector vertices; + if (cache.flat) { + FlattenSurfaces( + meshGroup.vertices.data(), + meshGroup.vertices.size(), + meshGroup.indices.data(), + meshGroup.indices.size(), + meshGroup.texture != nullptr || forceUV, + vertices, + cache.indices + ); + } + else { + vertices = meshGroup.vertices; + cache.indices.resize(meshGroup.indices.size()); + std::transform(meshGroup.indices.begin(), meshGroup.indices.end(), cache.indices.begin(), [](DWORD index) { + return static_cast(index); + }); } - return new OpenGLES2Renderer(width, height, context, shaderProgram); + std::vector texcoords; + if (meshGroup.texture || forceUV) { + texcoords.resize(vertices.size()); + std::transform(vertices.begin(), vertices.end(), texcoords.begin(), [](const D3DRMVERTEX& v) { + return v.texCoord; + }); + } + std::vector positions(vertices.size()); + std::transform(vertices.begin(), vertices.end(), positions.begin(), [](const D3DRMVERTEX& v) { + return v.position; + }); + std::vector normals(vertices.size()); + std::transform(vertices.begin(), vertices.end(), normals.begin(), [](const D3DRMVERTEX& v) { return v.normal; }); + + glGenBuffers(1, &cache.vboPositions); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions); + glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(D3DVECTOR), positions.data(), GL_STATIC_DRAW); + + glGenBuffers(1, &cache.vboNormals); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals); + glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(D3DVECTOR), normals.data(), GL_STATIC_DRAW); + + if (meshGroup.texture || forceUV) { + glGenBuffers(1, &cache.vboTexcoords); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords); + glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(TexCoord), texcoords.data(), GL_STATIC_DRAW); + } + + glGenBuffers(1, &cache.ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache.ibo); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + cache.indices.size() * sizeof(cache.indices[0]), + cache.indices.data(), + GL_STATIC_DRAW + ); + + return cache; } OpenGLES2Renderer::OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext context, GLuint shaderProgram) : m_context(context), m_shaderProgram(shaderProgram) { - m_width = width; - m_height = height; m_virtualWidth = width; m_virtualHeight = height; - m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); + ViewportTransform viewportTransform = {1.0f, 0.0f, 0.0f}; + Resize(width, height, viewportTransform); + + m_uiMesh.vertices = { + {{0.0f, 0.0f, 0.0f}, {0, 0, -1}, {0.0f, 0.0f}}, + {{1.0f, 0.0f, 0.0f}, {0, 0, -1}, {1.0f, 0.0f}}, + {{1.0f, 1.0f, 0.0f}, {0, 0, -1}, {1.0f, 1.0f}}, + {{0.0f, 1.0f, 0.0f}, {0, 0, -1}, {0.0f, 1.0f}} + }; + m_uiMesh.indices = {0, 1, 2, 0, 2, 3}; + m_uiMeshCache = GLES2UploadMesh(m_uiMesh, true); + m_posLoc = glGetAttribLocation(m_shaderProgram, "a_position"); + m_normLoc = glGetAttribLocation(m_shaderProgram, "a_normal"); + m_texLoc = glGetAttribLocation(m_shaderProgram, "a_texCoord"); + m_colorLoc = glGetUniformLocation(m_shaderProgram, "u_color"); + m_shinLoc = glGetUniformLocation(m_shaderProgram, "u_shininess"); + m_lightCountLoc = glGetUniformLocation(m_shaderProgram, "u_lightCount"); + m_useTextureLoc = glGetUniformLocation(m_shaderProgram, "u_useTexture"); + m_textureLoc = glGetUniformLocation(m_shaderProgram, "u_texture"); + for (int i = 0; i < 3; ++i) { + std::string base = "u_lights[" + std::to_string(i) + "]"; + u_lightLocs[i][0] = glGetUniformLocation(m_shaderProgram, (base + ".color").c_str()); + u_lightLocs[i][1] = glGetUniformLocation(m_shaderProgram, (base + ".position").c_str()); + u_lightLocs[i][2] = glGetUniformLocation(m_shaderProgram, (base + ".direction").c_str()); + } + m_modelViewMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_modelViewMatrix"); + m_normalMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_normalMatrix"); + m_projectionMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_projectionMatrix"); } OpenGLES2Renderer::~OpenGLES2Renderer() { SDL_DestroySurface(m_renderedImage); glDeleteProgram(m_shaderProgram); + SDL_GL_DestroyContext(m_context); } void OpenGLES2Renderer::PushLights(const SceneLight* lightsArray, size_t count) @@ -235,7 +321,48 @@ void OpenGLES2Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* ); } -Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture) +bool UploadTexture(SDL_Surface* source, GLuint& outTexId, bool isUi) +{ + SDL_Surface* surf = source; + if (source->format != SDL_PIXELFORMAT_RGBA32) { + surf = SDL_ConvertSurface(source, SDL_PIXELFORMAT_RGBA32); + if (!surf) { + return false; + } + } + + glGenTextures(1, &outTexId); + glBindTexture(GL_TEXTURE_2D, outTexId); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + + if (isUi) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (strstr((const char*) glGetString(GL_EXTENSIONS), "GL_EXT_texture_filter_anisotropic")) { + GLfloat maxAniso = 0.0f; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso); + GLfloat desiredAniso = fminf(8.0f, maxAniso); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, desiredAniso); + } + glGenerateMipmap(GL_TEXTURE_2D); + } + + if (surf != source) { + SDL_DestroySurface(surf); + } + + return true; +} + +Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); @@ -245,31 +372,18 @@ Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture) if (tex.texture == texture) { if (tex.version != texture->m_version) { glDeleteTextures(1, &tex.glTextureId); - glGenTextures(1, &tex.glTextureId); - glBindTexture(GL_TEXTURE_2D, tex.glTextureId); - - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); - if (!surf) { - return NO_TEXTURE_ID; + if (UploadTexture(surface->m_surface, tex.glTextureId, isUi)) { + tex.version = texture->m_version; } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); - SDL_DestroySurface(surf); - - tex.version = texture->m_version; } return i; } } GLuint texId; - glGenTextures(1, &texId); - glBindTexture(GL_TEXTURE_2D, texId); - - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); - if (!surf) { + if (!UploadTexture(surface->m_surface, texId, isUi)) { return NO_TEXTURE_ID; } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); for (Uint32 i = 0; i < m_textures.size(); ++i) { auto& tex = m_textures[i]; @@ -277,85 +391,20 @@ Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture) tex.texture = texture; tex.version = texture->m_version; tex.glTextureId = texId; - tex.width = surf->w; - tex.height = surf->h; + tex.width = surface->m_surface->w; + tex.height = surface->m_surface->h; AddTextureDestroyCallback(i, texture); return i; } } - m_textures.push_back({texture, texture->m_version, texId, (uint16_t) surf->w, (uint16_t) surf->h}); - SDL_DestroySurface(surf); + m_textures.push_back( + {texture, texture->m_version, texId, (uint16_t) surface->m_surface->w, (uint16_t) surface->m_surface->h} + ); AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture); return (Uint32) (m_textures.size() - 1); } -GLES2MeshCacheEntry GLES2UploadMesh(const MeshGroup& meshGroup) -{ - GLES2MeshCacheEntry cache{&meshGroup, meshGroup.version}; - - cache.flat = meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT; - - std::vector vertices; - if (cache.flat) { - FlattenSurfaces( - meshGroup.vertices.data(), - meshGroup.vertices.size(), - meshGroup.indices.data(), - meshGroup.indices.size(), - meshGroup.texture != nullptr, - vertices, - cache.indices - ); - } - else { - vertices = meshGroup.vertices; - cache.indices.resize(meshGroup.indices.size()); - std::transform(meshGroup.indices.begin(), meshGroup.indices.end(), cache.indices.begin(), [](DWORD index) { - return static_cast(index); - }); - } - - std::vector texcoords; - if (meshGroup.texture) { - texcoords.resize(vertices.size()); - std::transform(vertices.begin(), vertices.end(), texcoords.begin(), [](const D3DRMVERTEX& v) { - return v.texCoord; - }); - } - std::vector positions(vertices.size()); - std::transform(vertices.begin(), vertices.end(), positions.begin(), [](const D3DRMVERTEX& v) { - return v.position; - }); - std::vector normals(vertices.size()); - std::transform(vertices.begin(), vertices.end(), normals.begin(), [](const D3DRMVERTEX& v) { return v.normal; }); - - glGenBuffers(1, &cache.vboPositions); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions); - glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(D3DVECTOR), positions.data(), GL_STATIC_DRAW); - - glGenBuffers(1, &cache.vboNormals); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals); - glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(D3DVECTOR), normals.data(), GL_STATIC_DRAW); - - if (meshGroup.texture) { - glGenBuffers(1, &cache.vboTexcoords); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords); - glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(TexCoord), texcoords.data(), GL_STATIC_DRAW); - } - - glGenBuffers(1, &cache.ibo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache.ibo); - glBufferData( - GL_ELEMENT_ARRAY_BUFFER, - cache.indices.size() * sizeof(cache.indices[0]), - cache.indices.data(), - GL_STATIC_DRAW - ); - - return cache; -} - struct GLES2MeshDestroyContext { OpenGLES2Renderer* renderer; Uint32 id; @@ -407,37 +456,11 @@ Uint32 OpenGLES2Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* mesh return (Uint32) (m_meshs.size() - 1); } -void OpenGLES2Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) -{ - halDesc->dcmColorModel = D3DCOLORMODEL::RGB; - halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; - halDesc->dwDeviceZBufferBitDepth = DDBD_16; - const char* extensions = (const char*) glGetString(GL_EXTENSIONS); - if (extensions) { - if (strstr(extensions, "GL_OES_depth24")) { - halDesc->dwDeviceZBufferBitDepth |= DDBD_24; - } - if (strstr(extensions, "GL_OES_depth32")) { - halDesc->dwDeviceZBufferBitDepth |= DDBD_32; - } - } - helDesc->dwDeviceRenderBitDepth = DDBD_32; - halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; - halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; - halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; - - memset(helDesc, 0, sizeof(D3DDEVICEDESC)); -} - -const char* OpenGLES2Renderer::GetName() -{ - return "OpenGL ES 2.0 HAL"; -} - HRESULT OpenGLES2Renderer::BeginFrame() { m_dirty = true; + glEnable(GL_CULL_FACE); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); @@ -466,12 +489,11 @@ HRESULT OpenGLES2Renderer::BeginFrame() } for (int i = 0; i < lightCount; ++i) { - std::string base = "u_lights[" + std::to_string(i) + "]"; - glUniform4fv(glGetUniformLocation(m_shaderProgram, (base + ".color").c_str()), 1, lightData[i].color); - glUniform4fv(glGetUniformLocation(m_shaderProgram, (base + ".position").c_str()), 1, lightData[i].position); - glUniform4fv(glGetUniformLocation(m_shaderProgram, (base + ".direction").c_str()), 1, lightData[i].direction); + glUniform4fv(u_lightLocs[i][0], 1, lightData[i].color); + glUniform4fv(u_lightLocs[i][1], 1, lightData[i].position); + glUniform4fv(u_lightLocs[i][2], 1, lightData[i].direction); } - glUniform1i(glGetUniformLocation(m_shaderProgram, "u_lightCount"), lightCount); + glUniform1i(m_lightCountLoc, lightCount); return DD_OK; } @@ -493,55 +515,49 @@ void OpenGLES2Renderer::SubmitDraw( { auto& mesh = m_meshs[meshId]; - glUniformMatrix4fv(glGetUniformLocation(m_shaderProgram, "u_modelViewMatrix"), 1, GL_FALSE, &modelViewMatrix[0][0]); - glUniformMatrix3fv(glGetUniformLocation(m_shaderProgram, "u_normalMatrix"), 1, GL_FALSE, &normalMatrix[0][0]); - glUniformMatrix4fv(glGetUniformLocation(m_shaderProgram, "u_projectionMatrix"), 1, GL_FALSE, &m_projection[0][0]); + glUniformMatrix4fv(m_modelViewMatrixLoc, 1, GL_FALSE, &modelViewMatrix[0][0]); + glUniformMatrix3fv(m_normalMatrixLoc, 1, GL_FALSE, &normalMatrix[0][0]); + glUniformMatrix4fv(m_projectionMatrixLoc, 1, GL_FALSE, &m_projection[0][0]); glUniform4f( - glGetUniformLocation(m_shaderProgram, "u_color"), + m_colorLoc, appearance.color.r / 255.0f, appearance.color.g / 255.0f, appearance.color.b / 255.0f, appearance.color.a / 255.0f ); - glUniform1f(glGetUniformLocation(m_shaderProgram, "u_shininess"), appearance.shininess); + glUniform1f(m_shinLoc, appearance.shininess); if (appearance.textureId != NO_TEXTURE_ID) { - glUniform1i(glGetUniformLocation(m_shaderProgram, "u_useTexture"), 1); + glUniform1i(m_useTextureLoc, 1); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_textures[appearance.textureId].glTextureId); - glUniform1i(glGetUniformLocation(m_shaderProgram, "u_texture"), 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glUniform1i(m_textureLoc, 0); } else { - glUniform1i(glGetUniformLocation(m_shaderProgram, "u_useTexture"), 0); + glUniform1i(m_useTextureLoc, 0); } glBindBuffer(GL_ARRAY_BUFFER, mesh.vboPositions); - GLint posLoc = glGetAttribLocation(m_shaderProgram, "a_position"); - glEnableVertexAttribArray(posLoc); - glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + glEnableVertexAttribArray(m_posLoc); + glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindBuffer(GL_ARRAY_BUFFER, mesh.vboNormals); - GLint normLoc = glGetAttribLocation(m_shaderProgram, "a_normal"); - glEnableVertexAttribArray(normLoc); - glVertexAttribPointer(normLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + glEnableVertexAttribArray(m_normLoc); + glVertexAttribPointer(m_normLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); - GLint texLoc = glGetAttribLocation(m_shaderProgram, "a_texCoord"); if (appearance.textureId != NO_TEXTURE_ID) { glBindBuffer(GL_ARRAY_BUFFER, mesh.vboTexcoords); - glEnableVertexAttribArray(texLoc); - glVertexAttribPointer(texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glEnableVertexAttribArray(m_texLoc); + glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.ibo); glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr); - glDisableVertexAttribArray(posLoc); - glDisableVertexAttribArray(normLoc); - glDisableVertexAttribArray(texLoc); + glDisableVertexAttribArray(m_normLoc); + glDisableVertexAttribArray(m_texLoc); } HRESULT OpenGLES2Renderer::FinalizeFrame() @@ -558,7 +574,9 @@ void OpenGLES2Renderer::Resize(int width, int height, const ViewportTransform& v m_width = width; m_height = height; m_viewportTransform = viewportTransform; - SDL_DestroySurface(m_renderedImage); + if (m_renderedImage) { + SDL_DestroySurface(m_renderedImage); + } m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); glViewport(0, 0, m_width, m_height); } @@ -580,35 +598,6 @@ void OpenGLES2Renderer::Flip() } } -void CreateOrthoMatrix(float left, float right, float bottom, float top, D3DRMMATRIX4D& outMatrix) -{ - float near = -1.0f; - float far = 1.0f; - float rl = right - left; - float tb = top - bottom; - float fn = far - near; - - outMatrix[0][0] = 2.0f / rl; - outMatrix[0][1] = 0.0f; - outMatrix[0][2] = 0.0f; - outMatrix[0][3] = 0.0f; - - outMatrix[1][0] = 0.0f; - outMatrix[1][1] = 2.0f / tb; - outMatrix[1][2] = 0.0f; - outMatrix[1][3] = 0.0f; - - outMatrix[2][0] = 0.0f; - outMatrix[2][1] = 0.0f; - outMatrix[2][2] = -2.0f / fn; - outMatrix[2][3] = 0.0f; - - outMatrix[3][0] = -(right + left) / rl; - outMatrix[3][1] = -(top + bottom) / tb; - outMatrix[3][2] = -(far + near) / fn; - outMatrix[3][3] = 1.0f; -} - void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) { m_dirty = true; @@ -620,68 +609,70 @@ void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, c float color[] = {1.0f, 1.0f, 1.0f, 1.0f}; float blank[] = {0.0f, 0.0f, 0.0f, 0.0f}; - glUniform4fv(glGetUniformLocation(m_shaderProgram, "u_lights[0].color"), 1, color); - glUniform4fv(glGetUniformLocation(m_shaderProgram, "u_lights[0].position"), 1, blank); - glUniform4fv(glGetUniformLocation(m_shaderProgram, "u_lights[0].direction"), 1, blank); - glUniform1i(glGetUniformLocation(m_shaderProgram, "u_lightCount"), 1); + glUniform4fv(u_lightLocs[0][0], 1, color); + glUniform4fv(u_lightLocs[0][1], 1, blank); + glUniform4fv(u_lightLocs[0][2], 1, blank); + glUniform1i(m_lightCountLoc, 1); - glUniform4f(glGetUniformLocation(m_shaderProgram, "u_color"), 1.0f, 1.0f, 1.0f, 1.0f); - glUniform1f(glGetUniformLocation(m_shaderProgram, "u_shininess"), 0.0f); + glUniform4f(m_colorLoc, 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1f(m_shinLoc, 0.0f); - float left = -m_viewportTransform.offsetX / m_viewportTransform.scale; - float right = (m_width - m_viewportTransform.offsetX) / m_viewportTransform.scale; - float top = -m_viewportTransform.offsetY / m_viewportTransform.scale; - float bottom = (m_height - m_viewportTransform.offsetY) / m_viewportTransform.scale; + const GLES2TextureCacheEntry& texture = m_textures[textureId]; + float scaleX = static_cast(dstRect.w) / srcRect.w; + float scaleY = static_cast(dstRect.h) / srcRect.h; + SDL_Rect expandedDstRect = { + static_cast(std::round(dstRect.x - srcRect.x * scaleX)), + static_cast(std::round(dstRect.y - srcRect.y * scaleY)), + static_cast(std::round(texture.width * scaleX)), + static_cast(std::round(texture.height * scaleY)) + }; - D3DRMMATRIX4D projection; - CreateOrthoMatrix(left, right, bottom, top, projection); + D3DRMMATRIX4D modelView, projection; + Create2DTransformMatrix( + expandedDstRect, + m_viewportTransform.scale, + m_viewportTransform.offsetX, + m_viewportTransform.offsetY, + modelView + ); - D3DRMMATRIX4D identity = {{1.f, 0.f, 0.f, 0.f}, {0.f, 1.f, 0.f, 0.f}, {0.f, 0.f, 1.f, 0.f}, {0.f, 0.f, 0.f, 1.f}}; - glUniformMatrix4fv(glGetUniformLocation(m_shaderProgram, "u_modelViewMatrix"), 1, GL_FALSE, &identity[0][0]); - glUniformMatrix3fv(glGetUniformLocation(m_shaderProgram, "u_normalMatrix"), 1, GL_FALSE, &identity[0][0]); - glUniformMatrix4fv(glGetUniformLocation(m_shaderProgram, "u_projectionMatrix"), 1, GL_FALSE, &projection[0][0]); + glUniformMatrix4fv(m_modelViewMatrixLoc, 1, GL_FALSE, &modelView[0][0]); + Matrix3x3 identity = {{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}}; + glUniformMatrix3fv(m_normalMatrixLoc, 1, GL_FALSE, &identity[0][0]); + CreateOrthographicProjection((float) m_width, (float) m_height, projection); + glUniformMatrix4fv(m_projectionMatrixLoc, 1, GL_FALSE, &projection[0][0]); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glActiveTexture(GL_TEXTURE0); - glUniform1i(glGetUniformLocation(m_shaderProgram, "u_useTexture"), 1); - const GLES2TextureCacheEntry& texture = m_textures[textureId]; + glUniform1i(m_useTextureLoc, 1); glBindTexture(GL_TEXTURE_2D, texture.glTextureId); - glUniform1i(glGetUniformLocation(m_shaderProgram, "u_texture"), 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - float texW = texture.width; - float texH = texture.height; + glUniform1i(m_textureLoc, 0); - float u1 = srcRect.x / texW; - float v1 = srcRect.y / texH; - float u2 = (srcRect.x + srcRect.w) / texW; - float v2 = (srcRect.y + srcRect.h) / texH; + glEnable(GL_SCISSOR_TEST); + glScissor( + static_cast(std::round(dstRect.x * m_viewportTransform.scale + m_viewportTransform.offsetX)), + m_height - static_cast( + std::round((dstRect.y + dstRect.h) * m_viewportTransform.scale + m_viewportTransform.offsetY) + ), + static_cast(std::round(dstRect.w * m_viewportTransform.scale)), + static_cast(std::round(dstRect.h * m_viewportTransform.scale)) + ); - float x1 = static_cast(dstRect.x); - float y1 = static_cast(dstRect.y); - float x2 = x1 + dstRect.w; - float y2 = y1 + dstRect.h; + glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboPositions); + glEnableVertexAttribArray(m_posLoc); + glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); - GLfloat vertices[] = {x1, y1, u1, v1, x2, y1, u2, v1, x1, y2, u1, v2, x2, y2, u2, v2}; + glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboTexcoords); + glEnableVertexAttribArray(m_texLoc); + glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); - GLint posLoc = glGetAttribLocation(m_shaderProgram, "a_position"); - GLint texLoc = glGetAttribLocation(m_shaderProgram, "a_texCoord"); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_uiMeshCache.ibo); + glDrawElements(GL_TRIANGLES, static_cast(m_uiMeshCache.indices.size()), GL_UNSIGNED_SHORT, nullptr); - glEnableVertexAttribArray(posLoc); - glEnableVertexAttribArray(texLoc); - - glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vertices); - glVertexAttribPointer(texLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vertices + 2); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(posLoc); - glDisableVertexAttribArray(texLoc); - - glBindTexture(GL_TEXTURE_2D, 0); - glUseProgram(0); + glDisableVertexAttribArray(m_texLoc); + glDisable(GL_SCISSOR_TEST); } void OpenGLES2Renderer::Download(SDL_Surface* target) diff --git a/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp b/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp index 4ee5a35c..98704c66 100644 --- a/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp +++ b/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp @@ -203,49 +203,31 @@ Direct3DRMRenderer* Direct3DRMSDL3GPURenderer::Create(DWORD width, DWORD height) return nullptr; } - SDL_Window* window = DDWindow; - bool testWindow = false; - if (!window) { - window = SDL_CreateWindow("SDL_GPU test", width, height, SDL_WINDOW_HIDDEN); - if (!window) { - SDL_Log("SDL_CreateWindow: %s", SDL_GetError()); - return nullptr; - } - testWindow = true; - } - - if (!SDL_ClaimWindowForGPUDevice(device.ptr, window)) { - SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_ClaimWindowForGPUDevice: %s", SDL_GetError()); - if (testWindow) { - SDL_DestroyWindow(window); - } + if (!DDWindow) { + SDL_Log("No window handler"); return nullptr; } - ScopedPipeline opaquePipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, window, true, true)}; + if (!SDL_ClaimWindowForGPUDevice(device.ptr, DDWindow)) { + SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_ClaimWindowForGPUDevice: %s", SDL_GetError()); + return nullptr; + } + + ScopedPipeline opaquePipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, DDWindow, true, true)}; if (!opaquePipeline.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "InitializeGraphicsPipeline for opaquePipeline"); - if (testWindow) { - SDL_DestroyWindow(window); - } return nullptr; } - ScopedPipeline transparentPipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, window, true, false)}; + ScopedPipeline transparentPipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, DDWindow, true, false)}; if (!transparentPipeline.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "InitializeGraphicsPipeline for transparentPipeline"); - if (testWindow) { - SDL_DestroyWindow(window); - } return nullptr; } - ScopedPipeline uiPipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, window, false, false)}; + ScopedPipeline uiPipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, DDWindow, false, false)}; if (!uiPipeline.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "InitializeGraphicsPipeline for uiPipeline"); - if (testWindow) { - SDL_DestroyWindow(window); - } return nullptr; } @@ -257,9 +239,6 @@ Direct3DRMRenderer* Direct3DRMSDL3GPURenderer::Create(DWORD width, DWORD height) ScopedTransferBuffer uploadBuffer{device.ptr, SDL_CreateGPUTransferBuffer(device.ptr, &uploadBufferInfo)}; if (!uploadBuffer.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_CreateGPUTransferBuffer filed for upload buffer (%s)", SDL_GetError()); - if (testWindow) { - SDL_DestroyWindow(window); - } return nullptr; } @@ -273,9 +252,6 @@ Direct3DRMRenderer* Direct3DRMSDL3GPURenderer::Create(DWORD width, DWORD height) ScopedSampler sampler{device.ptr, SDL_CreateGPUSampler(device.ptr, &samplerInfo)}; if (!sampler.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "Failed to create sampler: %s", SDL_GetError()); - if (testWindow) { - SDL_DestroyWindow(window); - } return nullptr; } @@ -289,17 +265,9 @@ Direct3DRMRenderer* Direct3DRMSDL3GPURenderer::Create(DWORD width, DWORD height) ScopedSampler uiSampler{device.ptr, SDL_CreateGPUSampler(device.ptr, &uiSamplerInfo)}; if (!uiSampler.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "Failed to create sampler: %s", SDL_GetError()); - if (testWindow) { - SDL_DestroyWindow(window); - } return nullptr; } - if (testWindow) { - SDL_ReleaseWindowFromGPUDevice(device.ptr, window); - SDL_DestroyWindow(window); - } - auto renderer = new Direct3DRMSDL3GPURenderer( width, height, @@ -383,9 +351,7 @@ Direct3DRMSDL3GPURenderer::~Direct3DRMSDL3GPURenderer() { SDL_ReleaseGPUBuffer(m_device, m_uiMeshCache.vertexBuffer); SDL_ReleaseGPUBuffer(m_device, m_uiMeshCache.indexBuffer); - if (DDWindow) { - SDL_ReleaseWindowFromGPUDevice(m_device, DDWindow); - } + SDL_ReleaseWindowFromGPUDevice(m_device, DDWindow); if (m_downloadBuffer) { SDL_ReleaseGPUTransferBuffer(m_device, m_downloadBuffer); } @@ -533,7 +499,7 @@ SDL_GPUTexture* Direct3DRMSDL3GPURenderer::CreateTextureFromSurface(SDL_Surface* return texptr; } -Uint32 Direct3DRMSDL3GPURenderer::GetTextureId(IDirect3DRMTexture* iTexture) +Uint32 Direct3DRMSDL3GPURenderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); @@ -585,7 +551,7 @@ SDL3MeshCache Direct3DRMSDL3GPURenderer::UploadMesh(const MeshGroup& meshGroup) meshGroup.vertices.size(), meshGroup.indices.data(), meshGroup.indices.size(), - meshGroup.texture != nullptr, + true, finalVertices, newIndices ); @@ -712,24 +678,6 @@ Uint32 Direct3DRMSDL3GPURenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGro return (Uint32) (m_meshs.size() - 1); } -void Direct3DRMSDL3GPURenderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) -{ - halDesc->dcmColorModel = D3DCOLORMODEL::RGB; - halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; - halDesc->dwDeviceZBufferBitDepth = DDBD_32; // Todo add support for other depths - halDesc->dwDeviceRenderBitDepth = DDBD_32; - halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; - halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; - halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; - - memset(helDesc, 0, sizeof(D3DDEVICEDESC)); -} - -const char* Direct3DRMSDL3GPURenderer::GetName() -{ - return "SDL3 GPU HAL"; -} - void PackNormalMatrix(const Matrix3x3& normalMatrix3x3, D3DRMMATRIX4D& packedNormalMatrix4x4) { for (int row = 0; row < 3; ++row) { @@ -857,10 +805,6 @@ void Direct3DRMSDL3GPURenderer::Resize(int width, int height, const ViewportTran m_height = height; m_viewportTransform = viewportTransform; - if (!DDWindow) { - return; - } - if (m_transferTexture) { SDL_ReleaseGPUTexture(m_device, m_transferTexture); } @@ -940,37 +884,6 @@ void Direct3DRMSDL3GPURenderer::Flip() m_cmdbuf = nullptr; } -// TODO use SDL_SetGPUScissor(SDL_GPURenderPass *render_pass, const SDL_Rect *scissor) when srcRect isn't 100% of -// texture - -void Create2DTransformMatrix( - const SDL_Rect& dstRect, - float scale, - float offsetX, - float offsetY, - D3DRMMATRIX4D& outMatrix -) -{ - float x = static_cast(dstRect.x) * scale + offsetX; - float y = static_cast(dstRect.y) * scale + offsetY; - float w = static_cast(dstRect.w) * scale; - float h = static_cast(dstRect.h) * scale; - - D3DVALUE tmp[4][4] = {{w, 0, 0, 0}, {0, h, 0, 0}, {0, 0, 1, 0}, {x, y, 0, 1}}; - memcpy(outMatrix, tmp, sizeof(tmp)); -} - -void CreateOrthographicProjection(float width, float height, D3DRMMATRIX4D& outProj) -{ - D3DVALUE tmp[4][4] = { - {2.0f / width, 0.0f, 0.0f, 0.0f}, - {0.0f, -2.0f / height, 0.0f, 0.0f}, - {0.0f, 0.0f, 1.0f, 0.0f}, - {-1.0f, 1.0f, 0.0f, 1.0f} - }; - memcpy(outProj, tmp, sizeof(tmp)); -} - void Direct3DRMSDL3GPURenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) { if (!m_renderPass) { diff --git a/miniwin/src/d3drm/backends/software/renderer.cpp b/miniwin/src/d3drm/backends/software/renderer.cpp index 64adc93b..02c5bfc0 100644 --- a/miniwin/src/d3drm/backends/software/renderer.cpp +++ b/miniwin/src/d3drm/backends/software/renderer.cpp @@ -80,7 +80,7 @@ void Direct3DRMSoftwareRenderer::ClearZBuffer() _mm_empty(); } #endif -#elif defined(__arm__) || defined(__aarch64__) +#elif (defined(__arm__) || defined(__aarch64__)) && !defined(__3DS__) if (SDL_HasNEON()) { float32x4_t inf4 = vdupq_n_f32(inf); for (; i + 4 <= size; i += 4) { @@ -554,7 +554,7 @@ void Direct3DRMSoftwareRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DR ); } -Uint32 Direct3DRMSoftwareRenderer::GetTextureId(IDirect3DRMTexture* iTexture) +Uint32 Direct3DRMSoftwareRenderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); @@ -664,24 +664,6 @@ Uint32 Direct3DRMSoftwareRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGr return (Uint32) (m_meshs.size() - 1); } -void Direct3DRMSoftwareRenderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) -{ - memset(halDesc, 0, sizeof(D3DDEVICEDESC)); - - helDesc->dcmColorModel = D3DCOLORMODEL::RGB; - helDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; - helDesc->dwDeviceZBufferBitDepth = DDBD_32; - helDesc->dwDeviceRenderBitDepth = DDBD_32; - helDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; - helDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; - helDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; -} - -const char* Direct3DRMSoftwareRenderer::GetName() -{ - return "Miniwin Emulation"; -} - HRESULT Direct3DRMSoftwareRenderer::BeginFrame() { if (!m_renderedImage || !SDL_LockSurface(m_renderedImage)) { diff --git a/miniwin/src/d3drm/d3drm.cpp b/miniwin/src/d3drm/d3drm.cpp index e8df37b1..45a4e3ad 100644 --- a/miniwin/src/d3drm/d3drm.cpp +++ b/miniwin/src/d3drm/d3drm.cpp @@ -13,6 +13,9 @@ #ifdef USE_OPENGLES2 #include "d3drmrenderer_opengles2.h" #endif +#ifdef __3DS__ +#include "d3drmrenderer_citro3d.h" +#endif #ifdef _WIN32 #include "d3drmrenderer_directx9.h" #endif @@ -159,6 +162,11 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface( DDRenderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif +#ifdef __3DS__ + else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) { + DDRenderer = new Citro3DRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); + } +#endif #ifdef _WIN32 else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) { DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); diff --git a/miniwin/src/d3drm/d3drmdevice.cpp b/miniwin/src/d3drm/d3drmdevice.cpp index f6b2f9f7..b738c809 100644 --- a/miniwin/src/d3drm/d3drmdevice.cpp +++ b/miniwin/src/d3drm/d3drmdevice.cpp @@ -155,6 +155,10 @@ void Direct3DRMDevice2Impl::Resize() { int width, height; SDL_GetWindowSizeInPixels(DDWindow, &width, &height); +#ifdef __3DS__ + width = 320; // We are on the lower screen + height = 240; +#endif m_viewportTransform = CalculateViewportTransform(m_virtualWidth, m_virtualHeight, width, height); m_renderer->Resize(width, height, m_viewportTransform); for (int i = 0; i < m_viewports->GetSize(); i++) { diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index 2765a2a1..a3bc5d4d 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -4,6 +4,9 @@ #ifdef USE_OPENGLES2 #include "d3drmrenderer_opengles2.h" #endif +#ifdef __3DS__ +#include "d3drmrenderer_citro3d.h" +#endif #ifdef _WIN32 #include "d3drmrenderer_directx9.h" #endif @@ -18,6 +21,7 @@ #include #include +#include #include #include #include @@ -75,7 +79,7 @@ HRESULT DirectDrawImpl::CreateSurface( if ((lpDDSurfaceDesc->dwFlags & DDSD_ZBUFFERBITDEPTH) != DDSD_ZBUFFERBITDEPTH) { return DDERR_INVALIDPARAMS; } - SDL_Log("Todo: Set %dbit Z-Buffer", lpDDSurfaceDesc->dwZBufferBitDepth); + SDL_Log("Todo: Set %" PRIu32 "bit Z-Buffer", lpDDSurfaceDesc->dwZBufferBitDepth); *lplpDDSurface = static_cast(new DummySurfaceImpl); return DD_OK; } @@ -206,14 +210,18 @@ HRESULT DirectDrawImpl::GetCaps(LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) return S_OK; } -void EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx, Direct3DRMRenderer* device, GUID deviceGuid) +void EnumDevice( + LPD3DENUMDEVICESCALLBACK cb, + void* ctx, + const char* name, + D3DDEVICEDESC* halDesc, + D3DDEVICEDESC* helDesc, + GUID deviceGuid +) { - D3DDEVICEDESC halDesc = {}; - D3DDEVICEDESC helDesc = {}; - device->GetDesc(&halDesc, &helDesc); - char* deviceNameDup = SDL_strdup(device->GetName()); + char* deviceNameDup = SDL_strdup(name); char* deviceDescDup = SDL_strdup("Miniwin driver"); - cb(&deviceGuid, deviceNameDup, deviceDescDup, &halDesc, &helDesc, ctx); + cb(&deviceGuid, deviceNameDup, deviceDescDup, halDesc, helDesc, ctx); SDL_free(deviceDescDup); SDL_free(deviceNameDup); } @@ -227,6 +235,9 @@ HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) #ifdef USE_OPENGL1 OpenGL1Renderer_EnumDevice(cb, ctx); #endif +#ifdef __3DS__ + Citro3DRenderer_EnumDevice(cb, ctx); +#endif #ifdef _WIN32 DirectX9Renderer_EnumDevice(cb, ctx); #endif @@ -341,6 +352,11 @@ HRESULT DirectDrawImpl::CreateDevice( DDRenderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif +#ifdef __3DS__ + else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) { + DDRenderer = new Citro3DRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); + } +#endif #ifdef _WIN32 else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) { DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); diff --git a/miniwin/src/ddraw/framebuffer.cpp b/miniwin/src/ddraw/framebuffer.cpp index eb21abef..9f4224fd 100644 --- a/miniwin/src/ddraw/framebuffer.cpp +++ b/miniwin/src/ddraw/framebuffer.cpp @@ -62,7 +62,7 @@ HRESULT FrameBufferImpl::Blt( if (!surface) { return DDERR_GENERIC; } - Uint32 textureId = DDRenderer->GetTextureId(surface->ToTexture()); + Uint32 textureId = DDRenderer->GetTextureId(surface->ToTexture(), true); SDL_Rect srcRect = lpSrcRect ? ConvertRect(lpSrcRect) : SDL_Rect{0, 0, surface->m_surface->w, surface->m_surface->h}; SDL_Rect dstRect = diff --git a/miniwin/src/internal/d3drmrenderer.h b/miniwin/src/internal/d3drmrenderer.h index e76f0759..259b20eb 100644 --- a/miniwin/src/internal/d3drmrenderer.h +++ b/miniwin/src/internal/d3drmrenderer.h @@ -31,14 +31,12 @@ class Direct3DRMRenderer : public IDirect3DDevice2 { virtual void PushLights(const SceneLight* vertices, size_t count) = 0; virtual void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) = 0; virtual void SetFrustumPlanes(const Plane* frustumPlanes) = 0; - virtual Uint32 GetTextureId(IDirect3DRMTexture* texture) = 0; + virtual Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi = false) = 0; virtual Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) = 0; int GetWidth() { return m_width; } int GetHeight() { return m_height; } int GetVirtualWidth() { return m_virtualWidth; } int GetVirtualHeight() { return m_virtualHeight; } - virtual void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) = 0; - virtual const char* GetName() = 0; virtual HRESULT BeginFrame() = 0; virtual void EnableTransparency() = 0; virtual void SubmitDraw( diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h new file mode 100644 index 00000000..426f75c8 --- /dev/null +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -0,0 +1,82 @@ +#pragma once + +#include "d3drmrenderer.h" +#include "ddraw_impl.h" + +#include +#include +#include + +DEFINE_GUID(Citro3D_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x53); + +struct C3DTextureCacheEntry { + IDirect3DRMTexture* texture; + Uint32 version; + C3D_Tex c3dTex; + uint16_t width; + uint16_t height; +}; + +struct C3DMeshCacheEntry { + const MeshGroup* meshGroup = nullptr; + int version = 0; + void* vbo = nullptr; + int vertexCount = 0; +}; + +class Citro3DRenderer : public Direct3DRMRenderer { +public: + Citro3DRenderer(DWORD width, DWORD height); + ~Citro3DRenderer() override; + + void PushLights(const SceneLight* lightsArray, size_t count) override; + void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; + void SetFrustumPlanes(const Plane* frustumPlanes) override; + Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi) override; + Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; + HRESULT BeginFrame() override; + void EnableTransparency() override; + void SubmitDraw( + DWORD meshId, + const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, + const Matrix3x3& normalMatrix, + const Appearance& appearance + ) override; + HRESULT FinalizeFrame() override; + void Resize(int width, int height, const ViewportTransform& viewportTransform) override; + void Clear(float r, float g, float b) override; + void Flip() override; + void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) override; + void Download(SDL_Surface* target) override; + +private: + void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); + void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); + void StartFrame(); + + D3DRMMATRIX4D m_projection; + SDL_Surface* m_renderedImage; + C3D_RenderTarget* m_renderTarget; + std::vector m_textures; + std::vector m_meshs; + ViewportTransform m_viewportTransform; + std::vector m_lights; +}; + +inline static void Citro3DRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) +{ + D3DDEVICEDESC halDesc = {}; + halDesc.dcmColorModel = D3DCOLOR_RGB; + halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; + halDesc.dwDeviceZBufferBitDepth = DDBD_24; + halDesc.dwDeviceRenderBitDepth = DDBD_32; + halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; + halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; + halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + + D3DDEVICEDESC helDesc = {}; + + EnumDevice(cb, ctx, "Citro3D", &halDesc, &helDesc, Citro3D_GUID); +} diff --git a/miniwin/src/internal/d3drmrenderer_directx9.h b/miniwin/src/internal/d3drmrenderer_directx9.h index b7badb03..900bbe03 100644 --- a/miniwin/src/internal/d3drmrenderer_directx9.h +++ b/miniwin/src/internal/d3drmrenderer_directx9.h @@ -18,10 +18,8 @@ class DirectX9Renderer : public Direct3DRMRenderer { void PushLights(const SceneLight* lightsArray, size_t count) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; void SetFrustumPlanes(const Plane* frustumPlanes) override; - Uint32 GetTextureId(IDirect3DRMTexture* texture) override; + Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi) override; Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; - void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; - const char* GetName() override; HRESULT BeginFrame() override; void EnableTransparency() override; void SubmitDraw( @@ -52,8 +50,21 @@ class DirectX9Renderer : public Direct3DRMRenderer { inline static void DirectX9Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { Direct3DRMRenderer* device = DirectX9Renderer::Create(640, 480); - if (device) { - EnumDevice(cb, ctx, device, DirectX9_GUID); - delete device; + if (!device) { + return; } + delete device; + + D3DDEVICEDESC halDesc = {}; + halDesc.dcmColorModel = D3DCOLOR_RGB; + halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; + halDesc.dwDeviceZBufferBitDepth = DDBD_24; + halDesc.dwDeviceRenderBitDepth = DDBD_32; + halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; + halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; + halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + + D3DDEVICEDESC helDesc = {}; + + EnumDevice(cb, ctx, "DirectX 9 HAL", &halDesc, &helDesc, DirectX9_GUID); } diff --git a/miniwin/src/internal/d3drmrenderer_opengl1.h b/miniwin/src/internal/d3drmrenderer_opengl1.h index 71c0ea82..6e320ebe 100644 --- a/miniwin/src/internal/d3drmrenderer_opengl1.h +++ b/miniwin/src/internal/d3drmrenderer_opengl1.h @@ -1,44 +1,14 @@ #pragma once - +#include "../d3drm/backends/opengl1/actual.h" #include "d3drmrenderer.h" #include "d3drmtexture_impl.h" #include "ddraw_impl.h" -#ifdef __APPLE__ -#include -#else -#include -#endif - #include #include DEFINE_GUID(OpenGL1_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03); -struct GLTextureCacheEntry { - IDirect3DRMTexture* texture; - Uint32 version; - GLuint glTextureId; -}; - -struct GLMeshCacheEntry { - const MeshGroup* meshGroup; - int version; - bool flat; - - // non-VBO cache - std::vector positions; - std::vector normals; - std::vector texcoords; - std::vector indices; - - // VBO cache - GLuint vboPositions; - GLuint vboNormals; - GLuint vboTexcoords; - GLuint ibo; -}; - class OpenGL1Renderer : public Direct3DRMRenderer { public: static Direct3DRMRenderer* Create(DWORD width, DWORD height); @@ -48,10 +18,8 @@ class OpenGL1Renderer : public Direct3DRMRenderer { void PushLights(const SceneLight* lightsArray, size_t count) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; void SetFrustumPlanes(const Plane* frustumPlanes) override; - Uint32 GetTextureId(IDirect3DRMTexture* texture) override; + Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi) override; Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; - void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; - const char* GetName() override; HRESULT BeginFrame() override; void EnableTransparency() override; void SubmitDraw( @@ -78,6 +46,7 @@ class OpenGL1Renderer : public Direct3DRMRenderer { D3DRMMATRIX4D m_projection; SDL_Surface* m_renderedImage; bool m_useVBOs; + bool m_useNPOT; bool m_dirty = false; std::vector m_lights; SDL_GLContext m_context; @@ -86,9 +55,24 @@ class OpenGL1Renderer : public Direct3DRMRenderer { inline static void OpenGL1Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { +#ifndef __PSP__ Direct3DRMRenderer* device = OpenGL1Renderer::Create(640, 480); - if (device) { - EnumDevice(cb, ctx, device, OpenGL1_GUID); - delete device; + if (!device) { + return; } + delete device; +#endif + + D3DDEVICEDESC halDesc = {}; + halDesc.dcmColorModel = D3DCOLORMODEL::RGB; + halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; + halDesc.dwDeviceZBufferBitDepth = DDBD_24; + halDesc.dwDeviceRenderBitDepth = DDBD_32; + halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; + halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; + halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + + D3DDEVICEDESC helDesc = {}; + + EnumDevice(cb, ctx, "OpenGL 1.1 HAL", &halDesc, &helDesc, OpenGL1_GUID); } diff --git a/miniwin/src/internal/d3drmrenderer_opengles2.h b/miniwin/src/internal/d3drmrenderer_opengles2.h index 7ea1ed9a..f5d62579 100644 --- a/miniwin/src/internal/d3drmrenderer_opengles2.h +++ b/miniwin/src/internal/d3drmrenderer_opengles2.h @@ -39,10 +39,8 @@ class OpenGLES2Renderer : public Direct3DRMRenderer { void PushLights(const SceneLight* lightsArray, size_t count) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; void SetFrustumPlanes(const Plane* frustumPlanes) override; - Uint32 GetTextureId(IDirect3DRMTexture* texture) override; + Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi) override; Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; - void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; - const char* GetName() override; HRESULT BeginFrame() override; void EnableTransparency() override; void SubmitDraw( @@ -64,22 +62,60 @@ class OpenGLES2Renderer : public Direct3DRMRenderer { void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); + MeshGroup m_uiMesh; + GLES2MeshCacheEntry m_uiMeshCache; std::vector m_textures; std::vector m_meshs; D3DRMMATRIX4D m_projection; - SDL_Surface* m_renderedImage; + SDL_Surface* m_renderedImage = nullptr; bool m_dirty = false; std::vector m_lights; SDL_GLContext m_context; GLuint m_shaderProgram; + GLint m_posLoc; + GLint m_normLoc; + GLint m_texLoc; + GLint m_colorLoc; + GLint m_shinLoc; + GLint m_lightCountLoc; + GLint m_useTextureLoc; + GLint m_textureLoc; + GLint u_lightLocs[3][3]; + GLint m_modelViewMatrixLoc; + GLint m_normalMatrixLoc; + GLint m_projectionMatrixLoc; ViewportTransform m_viewportTransform; }; inline static void OpenGLES2Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { Direct3DRMRenderer* device = OpenGLES2Renderer::Create(640, 480); - if (device) { - EnumDevice(cb, ctx, device, OpenGLES2_GUID); - delete device; + if (!device) { + return; } + + D3DDEVICEDESC halDesc = {}; + halDesc.dcmColorModel = D3DCOLOR_RGB; + halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; + halDesc.dwDeviceZBufferBitDepth = DDBD_16; + halDesc.dwDeviceRenderBitDepth = DDBD_32; + halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; + halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; + halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + + const char* extensions = (const char*) glGetString(GL_EXTENSIONS); + if (extensions) { + if (strstr(extensions, "GL_OES_depth24")) { + halDesc.dwDeviceZBufferBitDepth |= DDBD_24; + } + if (strstr(extensions, "GL_OES_depth32")) { + halDesc.dwDeviceZBufferBitDepth |= DDBD_32; + } + } + + delete device; + + D3DDEVICEDESC helDesc = {}; + + EnumDevice(cb, ctx, "OpenGL ES 2.0 HAL", &halDesc, &helDesc, OpenGLES2_GUID); } diff --git a/miniwin/src/internal/d3drmrenderer_sdl3gpu.h b/miniwin/src/internal/d3drmrenderer_sdl3gpu.h index 25485b1f..02ec05d0 100644 --- a/miniwin/src/internal/d3drmrenderer_sdl3gpu.h +++ b/miniwin/src/internal/d3drmrenderer_sdl3gpu.h @@ -47,12 +47,10 @@ class Direct3DRMSDL3GPURenderer : public Direct3DRMRenderer { static Direct3DRMRenderer* Create(DWORD width, DWORD height); ~Direct3DRMSDL3GPURenderer() override; void PushLights(const SceneLight* vertices, size_t count) override; - Uint32 GetTextureId(IDirect3DRMTexture* texture) override; + Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi) override; Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; void SetFrustumPlanes(const Plane* frustumPlanes) override; - void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; - const char* GetName() override; HRESULT BeginFrame() override; void EnableTransparency() override; void SubmitDraw( @@ -122,8 +120,21 @@ class Direct3DRMSDL3GPURenderer : public Direct3DRMRenderer { inline static void Direct3DRMSDL3GPU_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { Direct3DRMRenderer* device = Direct3DRMSDL3GPURenderer::Create(640, 480); - if (device) { - EnumDevice(cb, ctx, device, SDL3_GPU_GUID); - delete device; + if (!device) { + return; } + delete device; + + D3DDEVICEDESC halDesc = {}; + halDesc.dcmColorModel = D3DCOLOR_RGB; + halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; + halDesc.dwDeviceZBufferBitDepth = DDBD_32; + halDesc.dwDeviceRenderBitDepth = DDBD_32; + halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; + halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; + halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + + D3DDEVICEDESC helDesc = {}; + + EnumDevice(cb, ctx, "SDL3 GPU HAL", &halDesc, &helDesc, SDL3_GPU_GUID); } diff --git a/miniwin/src/internal/d3drmrenderer_software.h b/miniwin/src/internal/d3drmrenderer_software.h index 444a80a3..87715999 100644 --- a/miniwin/src/internal/d3drmrenderer_software.h +++ b/miniwin/src/internal/d3drmrenderer_software.h @@ -29,12 +29,10 @@ class Direct3DRMSoftwareRenderer : public Direct3DRMRenderer { Direct3DRMSoftwareRenderer(DWORD width, DWORD height); ~Direct3DRMSoftwareRenderer() override; void PushLights(const SceneLight* vertices, size_t count) override; - Uint32 GetTextureId(IDirect3DRMTexture* texture) override; + Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi) override; Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; void SetFrustumPlanes(const Plane* frustumPlanes) override; - void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; - const char* GetName() override; HRESULT BeginFrame() override; void EnableTransparency() override; void SubmitDraw( @@ -87,8 +85,16 @@ class Direct3DRMSoftwareRenderer : public Direct3DRMRenderer { inline static void Direct3DRMSoftware_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { - Direct3DRMRenderer* device = nullptr; - device = new Direct3DRMSoftwareRenderer(640, 480); - EnumDevice(cb, ctx, device, SOFTWARE_GUID); - delete device; + D3DDEVICEDESC halDesc = {}; + + D3DDEVICEDESC helDesc = {}; + helDesc.dcmColorModel = D3DCOLOR_RGB; + helDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; + helDesc.dwDeviceZBufferBitDepth = DDBD_32; + helDesc.dwDeviceRenderBitDepth = DDBD_32; + helDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; + helDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; + helDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + + EnumDevice(cb, ctx, "Miniwin Emulation", &halDesc, &helDesc, SOFTWARE_GUID); } diff --git a/miniwin/src/internal/ddraw_impl.h b/miniwin/src/internal/ddraw_impl.h index f2604946..4adfb4a5 100644 --- a/miniwin/src/internal/ddraw_impl.h +++ b/miniwin/src/internal/ddraw_impl.h @@ -56,4 +56,11 @@ HRESULT DirectDrawEnumerate(LPDDENUMCALLBACKA cb, void* context); HRESULT DirectDrawCreate(LPGUID lpGuid, LPDIRECTDRAW* lplpDD, IUnknown* pUnkOuter); -void EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx, Direct3DRMRenderer* device, GUID deviceGuid); +void EnumDevice( + LPD3DENUMDEVICESCALLBACK cb, + void* ctx, + const char* name, + D3DDEVICEDESC* halDesc, + D3DDEVICEDESC* helDesc, + GUID deviceGuid +); diff --git a/miniwin/src/internal/meshutils.cpp b/miniwin/src/internal/meshutils.cpp index 818ecd87..e0ce59e6 100644 --- a/miniwin/src/internal/meshutils.cpp +++ b/miniwin/src/internal/meshutils.cpp @@ -75,3 +75,31 @@ void FlattenSurfaces( } } } + +void Create2DTransformMatrix( + const SDL_Rect& dstRect, + float scale, + float offsetX, + float offsetY, + D3DRMMATRIX4D& outMatrix +) +{ + float x = static_cast(dstRect.x) * scale + offsetX; + float y = static_cast(dstRect.y) * scale + offsetY; + float w = static_cast(dstRect.w) * scale; + float h = static_cast(dstRect.h) * scale; + + D3DVALUE tmp[4][4] = {{w, 0, 0, 0}, {0, h, 0, 0}, {0, 0, 1, 0}, {x, y, 0, 1}}; + memcpy(outMatrix, tmp, sizeof(tmp)); +} + +void CreateOrthographicProjection(float width, float height, D3DRMMATRIX4D& outProj) +{ + D3DVALUE tmp[4][4] = { + {2.0f / width, 0.0f, 0.0f, 0.0f}, + {0.0f, -2.0f / height, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {-1.0f, 1.0f, 0.0f, 1.0f} + }; + memcpy(outProj, tmp, sizeof(tmp)); +} diff --git a/miniwin/src/internal/meshutils.h b/miniwin/src/internal/meshutils.h index fb12a145..12c509ad 100644 --- a/miniwin/src/internal/meshutils.h +++ b/miniwin/src/internal/meshutils.h @@ -13,3 +13,13 @@ void FlattenSurfaces( std::vector& dedupedVertices, std::vector& newIndices ); + +void Create2DTransformMatrix( + const SDL_Rect& dstRect, + float scale, + float offsetX, + float offsetY, + D3DRMMATRIX4D& outMatrix +); + +void CreateOrthographicProjection(float width, float height, D3DRMMATRIX4D& outProj); diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt new file mode 100644 index 00000000..d6fc19f0 --- /dev/null +++ b/packaging/CMakeLists.txt @@ -0,0 +1,22 @@ +set(APP_ID "org.legoisland.Isle") +set(APP_NAME "Isle Portable") +set(APP_SUMMARY "Portable version of the LEGO Island Decompilation Project") +set(APP_SPDX "LGPL-3.0-or-later") + +string(TIMESTAMP BUILD_DATE UTC) + +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/icons) +file(COPY_FILE icons/isle.svg ${CMAKE_BINARY_DIR}/icons/${APP_ID}.svg) + +# The following will need to be refined if we wish to post actual releases to a repo such as Flathub +if(DEFINED ENV{GITHUB_ACTIONS} AND ENV{GITHUB_ACTIONS} EQUAL TRUE) + # Use the sequential run# of the current pipeline when running in GH Actions + set(SEMANTIC_VERSION "${PROJECT_VERSION}~build$ENV{GITHUB_RUN_NUMBER}") +else() + # Don't worry about the build number for local builds + set(SEMANTIC_VERSION "${PROJECT_VERSION}") +endif() + +if(LINUX) + add_subdirectory(linux) +endif() diff --git a/packaging/icons/isle.svg b/packaging/icons/isle.svg new file mode 100644 index 00000000..d3f6dbf1 --- /dev/null +++ b/packaging/icons/isle.svg @@ -0,0 +1,161 @@ + + + +LEGO Island IconLEGO Island Icon2025-06-22 diff --git a/packaging/linux/CMakeLists.txt b/packaging/linux/CMakeLists.txt new file mode 100644 index 00000000..2ece9db8 --- /dev/null +++ b/packaging/linux/CMakeLists.txt @@ -0,0 +1,7 @@ +# Injects the required variables into the Desktop and MetaInfo files +configure_file(isledecomp.desktop.in "${APP_ID}.desktop" @ONLY) +configure_file(isledecomp.metainfo.xml.in "${APP_ID}.metainfo.xml" @ONLY) + +install(FILES "../icons/isle.svg" RENAME "${APP_ID}.svg" DESTINATION "share/icons/hicolor/scalable/apps") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${APP_ID}.desktop" DESTINATION "share/applications") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${APP_ID}.metainfo.xml" DESTINATION "share/metainfo") diff --git a/packaging/linux/appimage/AppRun b/packaging/linux/appimage/AppRun new file mode 100755 index 00000000..0da15db1 --- /dev/null +++ b/packaging/linux/appimage/AppRun @@ -0,0 +1,25 @@ +#!/bin/sh + +HERE="$(dirname "$(readlink -f "${0}")")" + +MAIN=$(grep -r "^Exec=.*" "$HERE"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1) + +# MAIN_BIN=$(find "$HERE/usr/bin" -name "$MAIN" | head -n 1) +MAIN_BIN="$HERE/usr/bin/isle-config" + +export PATH="${HERE}/usr/bin/":$PATH # Prefer bundled binaries + +export QT_QPA_PLATFORMTHEME=xdgdesktopportal # Use XDG filepicker for forward compatability +[ -z "$QT_PLUGIN_PATH" ] && export QT_PLUGIN_PATH=/usr/lib/qt6/plugins:/usr/lib64/qt6/plugins # Use system Qt theme, will fallback to the default one if unavailable + + +if [ ! -z $APPIMAGE ]; then + BINARY_NAME=$(basename "$ARGV0") + if [ -e "$HERE/usr/bin/$BINARY_NAME" ]; then + exec "$HERE/usr/bin/$BINARY_NAME" "$@" + else + exec "${MAIN_BIN}" "$@" + fi +else + exec "${MAIN_BIN}" "$@" +fi diff --git a/packaging/linux/appimage/Build b/packaging/linux/appimage/Build new file mode 100755 index 00000000..9cf0325c --- /dev/null +++ b/packaging/linux/appimage/Build @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +set -e + +export LD_LIBRARY_PATH="build/source/lib:$LD_LIBRARY_PATH" +[ -z "$QMAKE" ] && export QMAKE=/usr/lib/qt6/bin/qmake + +# Sets a directory that has to have a following structure: +# build +# ├── bin +# │   ├── isle +# │   └── isle-config +# └── lib +# ├── liblego1.so +# ├── libSDL3.so -> libSDL3.so.0 # Not important if available on the system +# ├── libSDL3.so.0 -> libSDL3.so.0.3.0 # Not important if available on the system +# └── libSDL3.so.0.3.0 # Not important if available on the system +# Can also be defined using --build=path +BUILD_SOURCE=source + +# Sets where AppRun for AppImage is, can also be defined using --apprun=path +APPRUN_SOURCE=AppRun + +# Sets where desktop file for AppImage is, can also be defined using --desktop-file=path +DESKTOP_FILE_SOURCE=isledecomp.desktop + +# You know the drill +ICON_SOURCE=../../icons/isle.svg + +cd $(dirname $0) + +clean(){ + echo "Deleting build directory" + rm -rf build +} + +download(){ + if [ ! -e "$1" ]; then + curl -Lo "$1" "$2" + fi +} + +prepare(){ + mkdir -p build/tools + mkdir -p build/assets + + download build/tools/appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage + chmod u+x build/tools/appimagetool.AppImage + + download build/tools/linuxdeploy.AppImage https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-$(uname -m).AppImage + chmod u+x build/tools/linuxdeploy.AppImage + + download build/tools/linuxdeploy-plugin-qt.AppImage https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/latest/download/linuxdeploy-plugin-qt-$(uname -m).AppImage + chmod u+x build/tools/linuxdeploy-plugin-qt.AppImage + + if [ ! -f "build/assets/isledecomp.desktop" ]; then + cp $DESKTOP_FILE_SOURCE build/assets/isledecomp.desktop + cp $APPRUN_SOURCE build/assets/AppRun + cp ../../icons/isle.svg build/assets/isle.svg + fi + + if [ ! -d "build/source" ]; then + cp -r $BUILD_SOURCE build/source + fi +} + +compile(){ + NO_STRIP=1 build/tools/linuxdeploy.AppImage \ + --plugin qt \ + -e build/source/bin/isle \ + -e build/source/bin/isle-config \ + -d build/assets/isledecomp.desktop \ + -i build/assets/isle.svg \ + --custom-apprun=AppRun \ + --appdir=build/AppDir +} + +package(){ + build/tools/appimagetool.AppImage build/AppDir build/"LEGO_Island-$(uname -m).AppImage" +} + +stop(){ # Can be used to do `Build clean stop` to just clean the directory + exit +} + +for arg in "$@"; do + case "$arg" in + --build=*) BUILD_SOURCE="${arg#--build=}";; + --apprun=*) APPRUN_SOURCE="${arg#--apprun=}";; + --desktop-file=*) DESKTOP_FILE_SOURCE="${arg#--desktop-file=}";; + --icon=*) ICON_SOURCE="${arg#--icon=}";; + *) "$arg" + esac +done + +prepare +compile +package +# Symlinks named as binaries in appimage can call these binaries specifically +# ln -s "LEGO_Island-$(uname -m).AppImage" isle-config +# ln -s "LEGO_Island-$(uname -m).AppImage" isle \ No newline at end of file diff --git a/packaging/linux/flatpak/org.legoisland.Isle.json b/packaging/linux/flatpak/org.legoisland.Isle.json new file mode 100644 index 00000000..e8cd3f8e --- /dev/null +++ b/packaging/linux/flatpak/org.legoisland.Isle.json @@ -0,0 +1,89 @@ +{ + "id": "org.legoisland.Isle", + + "runtime": "org.kde.Platform", + "sdk": "org.kde.Sdk", + "runtime-version": "6.8", + + "command": "isle", + + "finish-args": [ + "--share=ipc", + "--socket=wayland", + "--socket=fallback-x11", + "--socket=pulseaudio", + "--device=dri", + "--device=input", + "--filesystem=/run/media/:ro", + "--filesystem=/media/:ro", + "--filesystem=/mnt/:ro", + "--filesystem=home:ro" + ], + + "modules": [ + { + "name": "isle", + "buildsystem": "cmake-ninja", + "config-opts": [ + "-DCMAKE_BUILD_TYPE=RelWithDebInfo", + "-DISLE_DEBUG=OFF" + ], + "sources": [ + { + "type": "dir", + "path": "../../../3rdparty", + "dest": "3rdparty/" + }, + { + "type": "dir", + "path": "../../../cmake", + "dest": "cmake/" + }, + { + "type": "dir", + "path": "../../../CMake", + "dest": "CMake/" + }, + { + "type": "dir", + "path": "../../../CONFIG", + "dest": "CONFIG/" + }, + { + "type": "dir", + "path": "../../../ISLE", + "dest": "ISLE/" + }, + { + "type": "dir", + "path": "../../../LEGO1", + "dest": "LEGO1/" + }, + { + "type": "dir", + "path": "../../../miniwin", + "dest": "miniwin/" + }, + { + "type": "dir", + "path": "../../../packaging", + "dest": "packaging/" + }, + { + "type": "dir", + "path": "../../../util", + "dest": "util/" + }, + { + "type": "file", + "path": "../../../CMakeLists.txt" + } + ], + "build-options": { + "build-args": [ + "--share=network" + ] + } + } + ] +} diff --git a/packaging/linux/isledecomp.desktop.in b/packaging/linux/isledecomp.desktop.in new file mode 100644 index 00000000..28309205 --- /dev/null +++ b/packaging/linux/isledecomp.desktop.in @@ -0,0 +1,38 @@ +[Desktop Entry] +Version=1.5 + +Name=@APP_NAME@ +Comment=@APP_SUMMARY@ + +Icon=@APP_ID@ +Type=Application +Categories=Game;KidsGame;AdventureGame;Qt + +Keywords=LEGO;lego;LEGO Island +Keywords[da]=LEGO;lego;Panik på LEGO Øen +Keywords[de]=LEGO;lego;Abenteuer auf der LEGO Insel +Keywords[es]=LEGO;lego;La Isla LEGO +Keywords[fr]=LEGO;lego;Aventures sur L'île LEGO +Keywords[it]=LEGO;lego;Isola LEGO +Keywords[ja]=LEGO;lego;レゴアイランドの大冒険 +Keywords[ko]=LEGO;lego;레고 아일랜드 +Keywords[pt]=LEGO;lego;A Ilha LEGO +Keywords[ru]=LEGO;lego;Остров LEGO +Keywords[uk_UA]=LEGO;lego;LEGO острів + +SingleMainWindow=true + +TryExec=isle +Exec=isle + +Actions=play;configure + +[Desktop Action play] +Name=Play Game +Icon=currenttrack_play +Exec=isle + +[Desktop Action configure] +Name=Configure Settings +Icon=settings +Exec=isle-config diff --git a/packaging/linux/isledecomp.metainfo.xml.in b/packaging/linux/isledecomp.metainfo.xml.in new file mode 100644 index 00000000..b8faecc1 --- /dev/null +++ b/packaging/linux/isledecomp.metainfo.xml.in @@ -0,0 +1,91 @@ + + + + + @APP_ID@ + + @APP_NAME@ + @APP_SUMMARY@ + + @APP_ID@.desktop + + + #e3000b + + + + Isle Decomp Team + + + https://github.com/isledecomp/isle-portable + https://github.com/isledecomp/isle-portable/blob/master/CONTRIBUTING.md + https://github.com/isledecomp/isle-portable/tree/master + https://github.com/isledecomp/isle-portable/issues + + MIT + @APP_SPDX@ + + + 640 + offline-only + + + + 128 + + + + pointing + keyboard + gamepad + + + +

This initiative is a portable version of LEGO Island (Version 1.1, English) + based on the decompilation project. Our primary goal is to transform the codebase to achieve + platform independence, thereby enhancing compatibility across various systems while preserving + the original game's experience as faithfully as possible. +

+ +

+ Please note: this project is dedicated to achieving platform independence without altering the + core gameplay, adding new features, enhancing visual quality, or rewriting code for + improvement's sake. While those are worthwhile objectives, they are not within the scope + of this project. +

+ +
+ + + mild + mild + + + + Game + KidsGame + AdventureGame + Qt + + + + LEGO + lego + + LEGO Island + + Panik på LEGO Øen + Abenteuer auf der LEGO Insel + La Isla LEGO + Aventures sur L'île LEGO + Isola LEGO + レゴアイランドの大冒険 + 레고 아일랜드 + A Ilha LEGO + Остров LEGO + + + + + +
diff --git a/tools/ncc/skip.yml b/tools/ncc/skip.yml index 65de1ce7..c5ceef72 100644 --- a/tools/ncc/skip.yml +++ b/tools/ncc/skip.yml @@ -4,6 +4,7 @@ configureLegoModelPresenter(MxS32): 'DLL exported function' configureLegoPartPresenter(MxS32, MxS32): 'DLL exported function' configureLegoROI(int): 'DLL exported function' configureLegoWorldPresenter(MxS32): 'DLL exported function' +configureMxTransitionManager(TransitionType): 'DLL exported function' GetNoCD_SourceName(): 'DLL exported function' m_3dView: 'Allow this variable name' m_3dManager: 'Allow this variable name'