Merge pull request #5 from AJenbo/psp
Enable OpenGL 1.1 support and merge latest upstream
@ -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
|
||||
|
||||
4
.gitattributes
vendored
@ -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
|
||||
|
||||
121
.github/workflows/ci.yml
vendored
@ -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/*
|
||||
|
||||
116
.github/workflows/release.yml
vendored
@ -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/*
|
||||
6
.gitignore
vendored
@ -30,3 +30,9 @@ LEGO1.DLL
|
||||
|
||||
# Kate - Text
|
||||
/.cache
|
||||
|
||||
# Flatpak build cache
|
||||
**/.flatpak-builder/
|
||||
|
||||
# Flatpak build dir
|
||||
**/flatpak-build/
|
||||
|
||||
115
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 $<BUILD_INTERFACE:$<TARGET_PROPERTY:miniaudio,INTERFACE_INCLUDE_DIRECTORIES>>)
|
||||
|
||||
# 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 "$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/util>")
|
||||
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 "$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/util>")
|
||||
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 "$<TARGET_FILE_DIR:isle>/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)
|
||||
|
||||
@ -11,6 +11,8 @@
|
||||
#include "res/resource.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <mxdirectx/legodxinfo.h>
|
||||
#include <ui_maindialog.h>
|
||||
|
||||
@ -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()
|
||||
{
|
||||
|
||||
@ -45,6 +45,7 @@ private slots:
|
||||
void OnCheckboxFullscreen(bool checked);
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
void launch();
|
||||
void SelectDataPathDialog();
|
||||
void SelectSavePathDialog();
|
||||
void DataPathEdited();
|
||||
|
||||
@ -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<HWND>(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;
|
||||
|
||||
@ -508,8 +508,11 @@
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="okButton">
|
||||
<property name="toolTip">
|
||||
<string>Save configuration and close the config tool.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
<string>Save and Exit</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
@ -517,9 +520,22 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<widget class="QPushButton" name="launchButton">
|
||||
<property name="toolTip">
|
||||
<string>Save configuration and launch LEGO Island.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
<string>Save and Launch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="toolTip">
|
||||
<string>Discard changed settings and close the config tool.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Exit without saving</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
30
ISLE/3ds/apthooks.cpp
Normal file
@ -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);
|
||||
}
|
||||
9
ISLE/3ds/apthooks.h
Normal file
@ -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
|
||||
22
ISLE/3ds/config.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <iniparser.h>
|
||||
|
||||
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");
|
||||
}
|
||||
8
ISLE/3ds/config.h
Normal file
@ -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
|
||||
@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
@ -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<SDL_Window*>(m_windowHandle), "Lego Island");
|
||||
}
|
||||
else {
|
||||
SDL_Log("Detected game version 1.1");
|
||||
}
|
||||
}
|
||||
|
||||
IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice()
|
||||
{
|
||||
LegoVideoManager* videoManager = LegoOmni::GetInstance()->GetVideoManager();
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
BIN
ISLE/res/3ds/isle.png
Normal file
|
After Width: | Height: | Size: 438 B |
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -76,6 +76,7 @@ void LegoOmni::Init()
|
||||
m_bkgAudioManager = NULL;
|
||||
m_unk0x13c = TRUE;
|
||||
m_transitionManager = NULL;
|
||||
m_version10 = FALSE;
|
||||
}
|
||||
|
||||
// FUNCTION: LEGO1 0x10058c30
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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<MxDriver> m_list; // 0x04
|
||||
unsigned char m_initialized; // 0x10
|
||||
HWND m_hWnd;
|
||||
};
|
||||
|
||||
// TEMPLATE: BETA10 0x1011c1b0
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -5,7 +5,11 @@
|
||||
#include "mxsemaphore.h"
|
||||
#include "mxtypes.h"
|
||||
|
||||
#ifdef PSP
|
||||
#include <pspkerneltypes.h>
|
||||
#else
|
||||
#include <SDL3/SDL_thread.h>
|
||||
#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
|
||||
|
||||
|
||||
@ -3,13 +3,20 @@
|
||||
#include "decomp.h"
|
||||
|
||||
#include <SDL3/SDL_timer.h>
|
||||
#ifdef PSP
|
||||
#include <pspkernel.h>
|
||||
#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<MxThread*>(p_thread)->Run();
|
||||
}
|
||||
#endif
|
||||
|
||||
// FUNCTION: LEGO1 0x100bf690
|
||||
MxResult MxThread::Run()
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ Please note: this project is dedicated to achieving platform independence withou
|
||||
| Linux | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
||||
| macOS | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
||||
| [Web](https://isle.pizza) | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
||||
| Nintendo 3DS | [](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.
|
||||
|
||||
|
||||
BIN
XMB/Game/ICON0.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
XMB/Game/ICON1.pmf
Normal file
BIN
XMB/Game/PIC1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
XMB/Game/SND0.at3
Normal file
BIN
XMB/Save/EMPTY.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
XMB/Save/ICON0.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
XMB/Save/ICON1.pmf
Normal file
1
XMB/Save/Memory Stick Model Source.txt
Normal file
@ -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.
|
||||
BIN
XMB/Save/PIC1.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
XMB/Save/SND0.AT3
Normal file
10
XMB/explanation.txt
Normal file
@ -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.
|
||||
156
cmake/detectcpu.cmake
Normal file
@ -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} - <ERROR>")
|
||||
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()
|
||||
@ -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
|
||||
|
||||
606
miniwin/src/d3drm/backends/citro3d/renderer.cpp
Normal file
@ -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 <cstring>
|
||||
|
||||
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<Citro3DCacheDestroyContext*>(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<int>(converted->w * scale);
|
||||
int scaledH = static_cast<int>(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<Direct3DRMTextureImpl*>(iTexture);
|
||||
auto surface = static_cast<DirectDrawSurfaceImpl*>(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<D3DRMVERTEX> vertexBuffer;
|
||||
std::vector<uint16_t> 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<D3DRMVERTEX> 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<Citro3DCacheDestroyContext*>(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<u32>(r * 255) << 24) | (static_cast<u32>(g * 255) << 16) | (static_cast<u32>(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<float>(dstRect.x);
|
||||
float y1 = static_cast<float>(dstRect.y);
|
||||
float x2 = x1 + static_cast<float>(dstRect.w);
|
||||
float y2 = y1 + static_cast<float>(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();
|
||||
}
|
||||
130
miniwin/src/d3drm/backends/citro3d/vshader.v.pica
Normal file
@ -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
|
||||
@ -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<Direct3DRMTextureImpl*>(iTexture);
|
||||
auto surface = static_cast<DirectDrawSurfaceImpl*>(texture->m_surface);
|
||||
@ -216,24 +216,6 @@ Uint32 DirectX9Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshG
|
||||
return static_cast<Uint32>(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();
|
||||
|
||||
369
miniwin/src/d3drm/backends/opengl1/actual.cpp
Normal file
@ -0,0 +1,369 @@
|
||||
// This file cannot include any minwin headers.
|
||||
|
||||
#include "actual.h"
|
||||
|
||||
#include "structs.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
// 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<GLsizei>(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<GLsizei>(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);
|
||||
}
|
||||
90
miniwin/src/d3drm/backends/opengl1/actual.h
Normal file
@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include "structs.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
// 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<GL11_BridgeVector> positions;
|
||||
std::vector<GL11_BridgeVector> normals;
|
||||
std::vector<GL11_BridgeTexCoord> texcoords;
|
||||
std::vector<uint16_t> 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);
|
||||
@ -1,5 +1,4 @@
|
||||
#include <GL/glew.h>
|
||||
// must come after GLEW
|
||||
#include "actual.h"
|
||||
#include "d3drmrenderer_opengl1.h"
|
||||
#include "ddraw_impl.h"
|
||||
#include "ddsurface_impl.h"
|
||||
@ -11,58 +10,44 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
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<TextureDestroyContextGL*>(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<Direct3DRMTextureImpl*>(iTexture);
|
||||
auto surface = static_cast<DirectDrawSurfaceImpl*>(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<GLMeshDestroyContext*>(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<GLsizei>(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<GLsizei>(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<int>(m_viewportTransform.offsetX),
|
||||
|
||||
@ -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<D3DRMVERTEX> 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<uint16_t>(index);
|
||||
});
|
||||
}
|
||||
|
||||
return new OpenGLES2Renderer(width, height, context, shaderProgram);
|
||||
std::vector<TexCoord> 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<D3DVECTOR> positions(vertices.size());
|
||||
std::transform(vertices.begin(), vertices.end(), positions.begin(), [](const D3DRMVERTEX& v) {
|
||||
return v.position;
|
||||
});
|
||||
std::vector<D3DVECTOR> 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<Direct3DRMTextureImpl*>(iTexture);
|
||||
auto surface = static_cast<DirectDrawSurfaceImpl*>(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<D3DRMVERTEX> 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<uint16_t>(index);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<TexCoord> texcoords;
|
||||
if (meshGroup.texture) {
|
||||
texcoords.resize(vertices.size());
|
||||
std::transform(vertices.begin(), vertices.end(), texcoords.begin(), [](const D3DRMVERTEX& v) {
|
||||
return v.texCoord;
|
||||
});
|
||||
}
|
||||
std::vector<D3DVECTOR> positions(vertices.size());
|
||||
std::transform(vertices.begin(), vertices.end(), positions.begin(), [](const D3DRMVERTEX& v) {
|
||||
return v.position;
|
||||
});
|
||||
std::vector<D3DVECTOR> 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<GLsizei>(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<float>(dstRect.w) / srcRect.w;
|
||||
float scaleY = static_cast<float>(dstRect.h) / srcRect.h;
|
||||
SDL_Rect expandedDstRect = {
|
||||
static_cast<int>(std::round(dstRect.x - srcRect.x * scaleX)),
|
||||
static_cast<int>(std::round(dstRect.y - srcRect.y * scaleY)),
|
||||
static_cast<int>(std::round(texture.width * scaleX)),
|
||||
static_cast<int>(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<int>(std::round(dstRect.x * m_viewportTransform.scale + m_viewportTransform.offsetX)),
|
||||
m_height - static_cast<int>(
|
||||
std::round((dstRect.y + dstRect.h) * m_viewportTransform.scale + m_viewportTransform.offsetY)
|
||||
),
|
||||
static_cast<int>(std::round(dstRect.w * m_viewportTransform.scale)),
|
||||
static_cast<int>(std::round(dstRect.h * m_viewportTransform.scale))
|
||||
);
|
||||
|
||||
float x1 = static_cast<float>(dstRect.x);
|
||||
float y1 = static_cast<float>(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<GLsizei>(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)
|
||||
|
||||
@ -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<Direct3DRMTextureImpl*>(iTexture);
|
||||
auto surface = static_cast<DirectDrawSurfaceImpl*>(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<float>(dstRect.x) * scale + offsetX;
|
||||
float y = static_cast<float>(dstRect.y) * scale + offsetY;
|
||||
float w = static_cast<float>(dstRect.w) * scale;
|
||||
float h = static_cast<float>(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) {
|
||||
|
||||
@ -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<Direct3DRMTextureImpl*>(iTexture);
|
||||
auto surface = static_cast<DirectDrawSurfaceImpl*>(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)) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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++) {
|
||||
|
||||
@ -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 <SDL3/SDL.h>
|
||||
#include <assert.h>
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@ -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<IDirectDrawSurface*>(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);
|
||||
|
||||
@ -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 =
|
||||
|
||||
@ -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(
|
||||
|
||||
82
miniwin/src/internal/d3drmrenderer_citro3d.h
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "d3drmrenderer.h"
|
||||
#include "ddraw_impl.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <citro3d.h>
|
||||
#include <vector>
|
||||
|
||||
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<C3DTextureCacheEntry> m_textures;
|
||||
std::vector<C3DMeshCacheEntry> m_meshs;
|
||||
ViewportTransform m_viewportTransform;
|
||||
std::vector<SceneLight> 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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 <OpenGL/gl.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <vector>
|
||||
|
||||
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<D3DVECTOR> positions;
|
||||
std::vector<D3DVECTOR> normals;
|
||||
std::vector<TexCoord> texcoords;
|
||||
std::vector<uint16_t> 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<SceneLight> 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);
|
||||
}
|
||||
|
||||
@ -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<GLES2TextureCacheEntry> m_textures;
|
||||
std::vector<GLES2MeshCacheEntry> m_meshs;
|
||||
D3DRMMATRIX4D m_projection;
|
||||
SDL_Surface* m_renderedImage;
|
||||
SDL_Surface* m_renderedImage = nullptr;
|
||||
bool m_dirty = false;
|
||||
std::vector<SceneLight> 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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
|
||||
@ -75,3 +75,31 @@ void FlattenSurfaces(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Create2DTransformMatrix(
|
||||
const SDL_Rect& dstRect,
|
||||
float scale,
|
||||
float offsetX,
|
||||
float offsetY,
|
||||
D3DRMMATRIX4D& outMatrix
|
||||
)
|
||||
{
|
||||
float x = static_cast<float>(dstRect.x) * scale + offsetX;
|
||||
float y = static_cast<float>(dstRect.y) * scale + offsetY;
|
||||
float w = static_cast<float>(dstRect.w) * scale;
|
||||
float h = static_cast<float>(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));
|
||||
}
|
||||
|
||||
@ -13,3 +13,13 @@ void FlattenSurfaces(
|
||||
std::vector<D3DRMVERTEX>& dedupedVertices,
|
||||
std::vector<uint16_t>& newIndices
|
||||
);
|
||||
|
||||
void Create2DTransformMatrix(
|
||||
const SDL_Rect& dstRect,
|
||||
float scale,
|
||||
float offsetX,
|
||||
float offsetY,
|
||||
D3DRMMATRIX4D& outMatrix
|
||||
);
|
||||
|
||||
void CreateOrthographicProjection(float width, float height, D3DRMMATRIX4D& outProj);
|
||||
|
||||
22
packaging/CMakeLists.txt
Normal file
@ -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()
|
||||
161
packaging/icons/isle.svg
Normal file
@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="180"
|
||||
height="180"
|
||||
viewBox="0 0 180 180"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
sodipodi:docname="isle_icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"><title
|
||||
id="title19">LEGO Island Icon</title><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="3.2222222"
|
||||
inkscape:cx="99.931034"
|
||||
inkscape:cy="94.034483"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1371"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer2" /><defs
|
||||
id="defs1" /><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Sun"
|
||||
style="display:inline"><circle
|
||||
style="fill:#ffff00;fill-opacity:1;stroke-width:6.09858"
|
||||
id="path1"
|
||||
cx="16.087624"
|
||||
cy="16.087624"
|
||||
r="16.087624" /><g
|
||||
id="g7"
|
||||
inkscape:label="Rays"
|
||||
transform="scale(6.0985759)"><path
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 1.512931,6.2456897 V 10.74569"
|
||||
id="path2" /><path
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 10.922387,1.4939611 h -4.5"
|
||||
id="path3" /><path
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 3.5838432,6.4674267 4.802595,10.158437"
|
||||
id="path4" /><path
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 5.4215806,5.3718411 3.20707,2.1962729"
|
||||
id="path6" /><path
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 6.3519773,3.3961872 3.6866627,1.23184"
|
||||
id="path7" /></g></g><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="House"
|
||||
style="display:inline"><g
|
||||
id="g11"
|
||||
inkscape:label="Wall"
|
||||
style="display:inline"
|
||||
transform="scale(6.0985759)"><rect
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:1.31354;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect7"
|
||||
width="21.557148"
|
||||
height="2.960166"
|
||||
x="5.2214251"
|
||||
y="25.898151" /><rect
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:1.31354;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect9"
|
||||
width="21.557148"
|
||||
height="2.960166"
|
||||
x="5.2214251"
|
||||
y="13.077032" /><rect
|
||||
style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:1.31354;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect10"
|
||||
width="21.557148"
|
||||
height="2.960166"
|
||||
x="5.2214251"
|
||||
y="21.624445" /><rect
|
||||
style="fill:#0000ff;fill-opacity:1;stroke:#0000ff;stroke-width:1.31354;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect11"
|
||||
width="21.557148"
|
||||
height="2.960166"
|
||||
x="5.2214251"
|
||||
y="17.350739" /></g><g
|
||||
id="g16"
|
||||
inkscape:label="Window"
|
||||
transform="scale(6.0985759)"><rect
|
||||
style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.46119;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect12"
|
||||
width="9.0243168"
|
||||
height="11.312326"
|
||||
x="11.487842"
|
||||
y="13.257705" /><rect
|
||||
style="fill:#00ffff;fill-opacity:1;stroke:#00ffff;stroke-width:0.498507;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect13"
|
||||
width="3.3376997"
|
||||
height="4.0214062"
|
||||
x="11.928134"
|
||||
y="13.989297" /><rect
|
||||
style="fill:#00ffff;fill-opacity:1;stroke:#00ffff;stroke-width:0.498507;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect14"
|
||||
width="3.3376997"
|
||||
height="4.0214062"
|
||||
x="16.719082"
|
||||
y="13.989297" /><rect
|
||||
style="fill:#00ffff;fill-opacity:1;stroke:#00ffff;stroke-width:0.547494;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect15"
|
||||
width="3.288712"
|
||||
height="4.9228497"
|
||||
x="11.952628"
|
||||
y="19.056892" /><rect
|
||||
style="fill:#00ffff;fill-opacity:1;stroke:#00ffff;stroke-width:0.547494;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect16"
|
||||
width="3.288712"
|
||||
height="4.9228497"
|
||||
x="16.743576"
|
||||
y="19.056892" /></g></g><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="Roof"><g
|
||||
id="g19"
|
||||
inkscape:label="Antenna"
|
||||
transform="scale(6.0985759)"><path
|
||||
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 21.510776,1.2025862 V 9.1163793"
|
||||
id="path17" /><path
|
||||
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 18.363745,0.526459 6.294062,4.7971783"
|
||||
id="path18" /><path
|
||||
style="fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 18.99097,3.2200622 4.951565,3.7739605"
|
||||
id="path19" /></g><path
|
||||
style="display:inline;fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:3.04929;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 19.991237,75.943041 H 173.06023 L 150.40272,51.317332 h -7.63224 l -5.01561,6.189336 h -7.47182 l -4.27253,-8.057106 h -7.58654 l -4.56632,8.057106 h -8.00228 l -4.6922,-8.057106 h -7.441784 l -4.178171,8.057106 h -8.447839 l -3.904314,-8.057106 h -7.225947 l -4.362281,8.057106 h -8.610195 l -5.453345,-6.189336 h -6.082013 z"
|
||||
id="path16"
|
||||
sodipodi:nodetypes="ccccccccccccccccccccc"
|
||||
inkscape:label="Roof" /></g><metadata
|
||||
id="metadata19"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:title>LEGO Island Icon</dc:title><dc:date>2025-06-22</dc:date><cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc/4.0/" /></cc:Work><cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc/4.0/"><cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" /><cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" /><cc:prohibits
|
||||
rdf:resource="http://creativecommons.org/ns#CommercialUse" /><cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /></cc:License></rdf:RDF></metadata></svg>
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
7
packaging/linux/CMakeLists.txt
Normal file
@ -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")
|
||||