mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-02-03 12:31:15 +00:00
Merge branch 'master' of github.com:isledecomp/isle-portable into psp
This commit is contained in:
commit
4533e4deb3
@ -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
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
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/*
|
||||
|
||||
55
.github/workflows/docker.yml
vendored
Normal file
55
.github/workflows/docker.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}-emscripten
|
||||
|
||||
jobs:
|
||||
publish-emscripten:
|
||||
name: Publish web port
|
||||
env:
|
||||
IMAGE_NAME: ${{ github.repository }}-emscripten
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
file: docker/emscripten/Dockerfile
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
116
.github/workflows/release.yml
vendored
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
6
.gitignore
vendored
@ -30,3 +30,9 @@ LEGO1.DLL
|
||||
|
||||
# Kate - Text
|
||||
/.cache
|
||||
|
||||
# Flatpak build cache
|
||||
**/.flatpak-builder/
|
||||
|
||||
# Flatpak build dir
|
||||
**/flatpak-build/
|
||||
|
||||
156
CMake/detectcpu.cmake
Normal file
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()
|
||||
@ -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,7 +38,7 @@ 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) AND (NOT PSP)" 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)
|
||||
cmake_dependent_option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON "NOT PSP" OFF)
|
||||
option(ENABLE_CLANG_TIDY "Enable clang-tidy")
|
||||
@ -532,6 +535,12 @@ 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)
|
||||
@ -636,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()
|
||||
@ -655,8 +666,23 @@ 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)
|
||||
@ -674,4 +700,5 @@ elseif(PSP)
|
||||
else()
|
||||
set(CPACK_GENERATOR TGZ)
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
|
||||
@ -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;
|
||||
|
||||
30
ISLE/3ds/apthooks.cpp
Normal file
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
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
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
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
|
||||
@ -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,10 @@ 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);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
#endif
|
||||
|
||||
window = SDL_CreateWindowWithProperties(props);
|
||||
@ -726,6 +732,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()) {
|
||||
@ -824,7 +831,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);
|
||||
@ -853,7 +864,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);
|
||||
@ -880,6 +897,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) {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include "lego1_export.h"
|
||||
#include "legoutils.h"
|
||||
#include "mxtransitionmanager.h"
|
||||
#include "mxtypes.h"
|
||||
#include "mxvideoparam.h"
|
||||
|
||||
@ -91,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
BIN
ISLE/res/3ds/isle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 438 B |
@ -129,7 +129,7 @@ class Act3 : public LegoWorld {
|
||||
void RemoveDonut(Act3Ammo& p_p);
|
||||
MxResult ShootPizza(LegoPathController* p_controller, Vector3& p_location, Vector3& p_direction, Vector3& p_up);
|
||||
MxResult ShootDonut(LegoPathController* p_controller, Vector3& p_location, Vector3& p_direction, Vector3& p_up);
|
||||
void FUN_10072ad0(undefined4 p_param1);
|
||||
void TriggerHitSound(undefined4 p_param1);
|
||||
MxResult FUN_10073360(Act3Ammo& p_ammo, const Vector3& p_param2);
|
||||
MxResult FUN_10073390(Act3Ammo& p_ammo, const Vector3& p_param2);
|
||||
void SetBrickster(Act3Brickster* p_brickster);
|
||||
@ -168,12 +168,12 @@ class Act3 : public LegoWorld {
|
||||
Helicopter* m_copter; // 0x420c
|
||||
Act3Shark* m_shark; // 0x4210
|
||||
MxFloat m_time; // 0x4214
|
||||
MxU8 m_unk0x4218; // 0x4218
|
||||
MxU8 m_unk0x4219; // 0x4219
|
||||
MxU8 m_unk0x421a; // 0x421a
|
||||
MxU8 m_unk0x421b; // 0x421b
|
||||
MxU8 m_unk0x421c; // 0x421c
|
||||
MxU8 m_unk0x421d; // 0x421d
|
||||
MxU8 m_pizzaHitSound; // 0x4218
|
||||
MxU8 m_pizzaMissSound; // 0x4219
|
||||
MxU8 m_copDonutSound; // 0x421a
|
||||
MxU8 m_donutMissSound; // 0x421b
|
||||
MxU8 m_islanderSound; // 0x421c
|
||||
MxU8 m_bricksterDonutSound; // 0x421d
|
||||
undefined m_unk0x421e; // 0x421e
|
||||
Act3List m_unk0x4220; // 0x4220
|
||||
MxPresenter* m_helicopterDots[15]; // 0x4230
|
||||
|
||||
@ -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
|
||||
|
||||
@ -49,7 +49,7 @@ 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);
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
@ -580,7 +580,7 @@ void Act3Brickster::Animate(float p_time)
|
||||
}
|
||||
|
||||
if (m_unk0x54 < p_time) {
|
||||
((Act3*) m_world)->FUN_10072ad0(5);
|
||||
((Act3*) m_world)->TriggerHitSound(5);
|
||||
m_unk0x54 = p_time + 15000.0f;
|
||||
}
|
||||
|
||||
@ -596,7 +596,7 @@ void Act3Brickster::Animate(float p_time)
|
||||
assert(SoundManager()->GetCacheSoundManager());
|
||||
|
||||
if (m_unk0x58 >= 8) {
|
||||
((Act3*) m_world)->FUN_10072ad0(6);
|
||||
((Act3*) m_world)->TriggerHitSound(6);
|
||||
}
|
||||
else {
|
||||
SoundManager()->GetCacheSoundManager()->Play("eatpz", NULL, FALSE);
|
||||
|
||||
@ -378,11 +378,11 @@ void Act3Ammo::Animate(float p_time)
|
||||
if (IsBit4()) {
|
||||
if (IsPizza()) {
|
||||
m_world->RemovePizza(*this);
|
||||
m_world->FUN_10072ad0(2);
|
||||
m_world->TriggerHitSound(2);
|
||||
}
|
||||
else {
|
||||
m_world->RemoveDonut(*this);
|
||||
m_world->FUN_10072ad0(4);
|
||||
m_world->TriggerHitSound(4);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@ -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;
|
||||
@ -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};
|
||||
@ -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;
|
||||
|
||||
@ -511,7 +511,7 @@ MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id)
|
||||
}
|
||||
else {
|
||||
if (((MxPresenter*) object)->GetAction()) {
|
||||
FUN_100b7220(((MxPresenter*) object)->GetAction(), MxDSAction::c_world, FALSE);
|
||||
ApplyMask(((MxPresenter*) object)->GetAction(), MxDSAction::c_world, FALSE);
|
||||
}
|
||||
|
||||
((MxPresenter*) object)->EndAction();
|
||||
@ -540,7 +540,7 @@ MxBool RemoveFromWorld(MxAtomId& p_entityAtom, MxS32 p_entityId, MxAtomId& p_wor
|
||||
}
|
||||
else {
|
||||
if (((MxPresenter*) object)->GetAction()) {
|
||||
FUN_100b7220(((MxPresenter*) object)->GetAction(), MxDSAction::c_world, FALSE);
|
||||
ApplyMask(((MxPresenter*) object)->GetAction(), MxDSAction::c_world, FALSE);
|
||||
}
|
||||
|
||||
((MxPresenter*) object)->EndAction();
|
||||
|
||||
@ -46,7 +46,7 @@ MxResult MxControlPresenter::StartAction(MxStreamController* p_controller, MxDSA
|
||||
{
|
||||
MxResult result = MxCompositePresenter::StartAction(p_controller, p_action);
|
||||
|
||||
FUN_100b7220(m_action, MxDSAction::c_world | MxDSAction::c_looping, TRUE);
|
||||
ApplyMask(m_action, MxDSAction::c_world | MxDSAction::c_looping, TRUE);
|
||||
ParseExtra();
|
||||
|
||||
MxS16 i = 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()) {
|
||||
|
||||
@ -122,12 +122,12 @@ void LegoWorld::Destroy(MxBool p_fromDestructor)
|
||||
|
||||
animPresenter->DecrementUnknown0xd4();
|
||||
if (animPresenter->GetUnknown0xd4() == 0) {
|
||||
FUN_100b7220(action, MxDSAction::c_world, FALSE);
|
||||
ApplyMask(action, MxDSAction::c_world, FALSE);
|
||||
presenter->EndAction();
|
||||
}
|
||||
}
|
||||
else {
|
||||
FUN_100b7220(action, MxDSAction::c_world, FALSE);
|
||||
ApplyMask(action, MxDSAction::c_world, FALSE);
|
||||
presenter->EndAction();
|
||||
}
|
||||
}
|
||||
@ -143,7 +143,7 @@ void LegoWorld::Destroy(MxBool p_fromDestructor)
|
||||
MxDSAction* action = presenter->GetAction();
|
||||
|
||||
if (action) {
|
||||
FUN_100b7220(action, MxDSAction::c_world, FALSE);
|
||||
ApplyMask(action, MxDSAction::c_world, FALSE);
|
||||
presenter->EndAction();
|
||||
}
|
||||
}
|
||||
@ -159,7 +159,7 @@ void LegoWorld::Destroy(MxBool p_fromDestructor)
|
||||
|
||||
MxDSAction* action = presenter->GetAction();
|
||||
if (action) {
|
||||
FUN_100b7220(action, MxDSAction::c_world, FALSE);
|
||||
ApplyMask(action, MxDSAction::c_world, FALSE);
|
||||
presenter->EndAction();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ MxResult LegoPartPresenter::Read(MxDSChunk& p_chunk)
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
if (surplusLODs != 0 && lod->GetUnknown0x08Test8()) {
|
||||
if (surplusLODs != 0 && lod->IsExtraLOD()) {
|
||||
numLODs++;
|
||||
surplusLODs--;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ DECOMP_SIZE_ASSERT(Act3ListElement, 0x0c)
|
||||
DECOMP_SIZE_ASSERT(Act3List, 0x10)
|
||||
|
||||
// GLOBAL: LEGO1 0x100d94f8
|
||||
Act3Script::Script g_unk0x100d94f8[] = {
|
||||
Act3Script::Script g_pizzaHitSounds[] = {
|
||||
Act3Script::c_sns02xni_PlayWav,
|
||||
Act3Script::c_sns03xni_PlayWav,
|
||||
Act3Script::c_sns04xni_PlayWav,
|
||||
@ -52,7 +52,7 @@ Act3Script::Script g_unk0x100d94f8[] = {
|
||||
};
|
||||
|
||||
// GLOBAL: LEGO1 0x100d9538
|
||||
Act3Script::Script g_unk0x100d9538[] = {
|
||||
Act3Script::Script g_pizzaMissSounds[] = {
|
||||
Act3Script::c_sns19xni_PlayWav,
|
||||
Act3Script::c_sns20xni_PlayWav,
|
||||
Act3Script::c_sns22xni_PlayWav,
|
||||
@ -62,7 +62,7 @@ Act3Script::Script g_unk0x100d9538[] = {
|
||||
};
|
||||
|
||||
// GLOBAL: LEGO1 0x100d9550
|
||||
Act3Script::Script g_unk0x100d9550[] = {
|
||||
Act3Script::Script g_copDonutSounds[] = {
|
||||
Act3Script::c_sns25xni_PlayWav,
|
||||
Act3Script::c_sns26xni_PlayWav,
|
||||
Act3Script::c_sns27xni_PlayWav,
|
||||
@ -74,7 +74,7 @@ Act3Script::Script g_unk0x100d9550[] = {
|
||||
};
|
||||
|
||||
// GLOBAL: LEGO1 0x100d9570
|
||||
Act3Script::Script g_unk0x100d9570[] = {
|
||||
Act3Script::Script g_donutMissSounds[] = {
|
||||
Act3Script::c_sns30xni_PlayWav,
|
||||
Act3Script::c_sns31xni_PlayWav,
|
||||
Act3Script::c_sns32xni_PlayWav,
|
||||
@ -84,7 +84,7 @@ Act3Script::Script g_unk0x100d9570[] = {
|
||||
};
|
||||
|
||||
// GLOBAL: LEGO1 0x100d9588
|
||||
Act3Script::Script g_unk0x100d9588[] = {
|
||||
Act3Script::Script g_islanderSounds[] = {
|
||||
Act3Script::c_sns43xma_PlayWav, Act3Script::c_sns46xin_PlayWav, Act3Script::c_sns60xna_PlayWav,
|
||||
Act3Script::c_sns52xro_PlayWav, Act3Script::c_sns58xna_PlayWav, Act3Script::c_sns68xbu_PlayWav,
|
||||
Act3Script::c_sns59xna_PlayWav, Act3Script::c_sns51xin_PlayWav, Act3Script::c_sns61xva_PlayWav,
|
||||
@ -95,7 +95,7 @@ Act3Script::Script g_unk0x100d9588[] = {
|
||||
};
|
||||
|
||||
// GLOBAL: LEGO1 0x100d95d8
|
||||
Act3Script::Script g_unk0x100d95d8[] = {
|
||||
Act3Script::Script g_bricksterDonutSounds[] = {
|
||||
Act3Script::c_tns080br_PlayWav,
|
||||
Act3Script::c_tnsx07br_PlayWav,
|
||||
Act3Script::c_snsxx2br_PlayWav,
|
||||
@ -404,58 +404,58 @@ MxResult Act3::ShootDonut(LegoPathController* p_controller, Vector3& p_location,
|
||||
|
||||
// FUNCTION: LEGO1 0x10072ad0
|
||||
// FUNCTION: BETA10 0x10015eec
|
||||
void Act3::FUN_10072ad0(undefined4 p_param1)
|
||||
void Act3::TriggerHitSound(undefined4 p_param1)
|
||||
{
|
||||
float time = Timer()->GetTime();
|
||||
Act3Script::Script objectId;
|
||||
|
||||
switch (p_param1) {
|
||||
case 1: {
|
||||
if (m_unk0x4218 >= sizeOfArray(g_unk0x100d94f8)) {
|
||||
m_unk0x4218 = 0;
|
||||
if (m_pizzaHitSound >= sizeOfArray(g_pizzaHitSounds)) {
|
||||
m_pizzaHitSound = 0;
|
||||
}
|
||||
|
||||
objectId = g_unk0x100d94f8[m_unk0x4218++];
|
||||
objectId = g_pizzaHitSounds[m_pizzaHitSound++];
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (m_unk0x4219 >= sizeOfArray(g_unk0x100d9538) - 1) {
|
||||
m_unk0x4219 = 0;
|
||||
if (m_pizzaMissSound >= sizeOfArray(g_pizzaMissSounds) - 1) {
|
||||
m_pizzaMissSound = 0;
|
||||
}
|
||||
|
||||
objectId = g_unk0x100d9538[m_unk0x4219++];
|
||||
objectId = g_pizzaMissSounds[m_pizzaMissSound++];
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (m_unk0x421a >= sizeOfArray(g_unk0x100d9550)) {
|
||||
m_unk0x421a = 0;
|
||||
if (m_copDonutSound >= sizeOfArray(g_copDonutSounds)) {
|
||||
m_copDonutSound = 0;
|
||||
}
|
||||
|
||||
objectId = g_unk0x100d9550[m_unk0x421a++];
|
||||
objectId = g_copDonutSounds[m_copDonutSound++];
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (m_unk0x421b >= sizeOfArray(g_unk0x100d9570)) {
|
||||
m_unk0x421b = 0;
|
||||
if (m_donutMissSound >= sizeOfArray(g_donutMissSounds)) {
|
||||
m_donutMissSound = 0;
|
||||
}
|
||||
|
||||
objectId = g_unk0x100d9570[m_unk0x421b++];
|
||||
objectId = g_donutMissSounds[m_donutMissSound++];
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
if (m_unk0x421c >= sizeOfArray(g_unk0x100d9588)) {
|
||||
m_unk0x421c = 0;
|
||||
if (m_islanderSound >= sizeOfArray(g_islanderSounds)) {
|
||||
m_islanderSound = 0;
|
||||
}
|
||||
|
||||
objectId = g_unk0x100d9588[m_unk0x421c++];
|
||||
objectId = g_islanderSounds[m_islanderSound++];
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (m_unk0x421d >= sizeOfArray(g_unk0x100d95d8)) {
|
||||
m_unk0x421d = 0;
|
||||
if (m_bricksterDonutSound >= sizeOfArray(g_bricksterDonutSounds)) {
|
||||
m_bricksterDonutSound = 0;
|
||||
}
|
||||
|
||||
m_unk0x4220.Insert(g_unk0x100d95d8[m_unk0x421d++], 1);
|
||||
m_unk0x4220.Insert(g_bricksterDonutSounds[m_bricksterDonutSound++], 1);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
@ -576,13 +576,12 @@ MxLong Act3::Notify(MxParam& p_param)
|
||||
m_cop2->VTable0xa8();
|
||||
|
||||
m_brickster->VTable0xa8();
|
||||
|
||||
m_unk0x4218 = 0;
|
||||
m_unk0x4219 = 0;
|
||||
m_unk0x421a = 0;
|
||||
m_unk0x421b = 0;
|
||||
m_unk0x421c = 0;
|
||||
m_unk0x421d = 0;
|
||||
m_pizzaHitSound = 0;
|
||||
m_pizzaMissSound = 0;
|
||||
m_copDonutSound = 0;
|
||||
m_donutMissSound = 0;
|
||||
m_islanderSound = 0;
|
||||
m_bricksterDonutSound = 0;
|
||||
|
||||
MxS32 length;
|
||||
LegoBuildingInfo* info = BuildingManager()->GetInfoArray(length);
|
||||
@ -702,7 +701,7 @@ MxResult Act3::FUN_10073360(Act3Ammo& p_ammo, const Vector3& p_param2)
|
||||
{
|
||||
assert(m_brickster);
|
||||
m_brickster->FUN_100417a0(p_ammo, p_param2);
|
||||
FUN_10072ad0(1);
|
||||
TriggerHitSound(1);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@ -719,7 +718,7 @@ MxResult Act3::FUN_10073390(Act3Ammo& p_ammo, const Vector3& p_param2)
|
||||
m_cop2->FUN_10040350(p_ammo, p_param2);
|
||||
}
|
||||
|
||||
FUN_10072ad0(3);
|
||||
TriggerHitSound(3);
|
||||
g_unk0x100f7814++;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -1457,7 +1457,7 @@ void Infocenter::StartCredits()
|
||||
MxDSAction* action = presenter->GetAction();
|
||||
|
||||
if (action) {
|
||||
FUN_100b7220(action, MxDSAction::c_world, FALSE);
|
||||
ApplyMask(action, MxDSAction::c_world, FALSE);
|
||||
presenter->EndAction();
|
||||
}
|
||||
}
|
||||
@ -1473,7 +1473,7 @@ void Infocenter::StartCredits()
|
||||
|
||||
MxDSAction* action = presenter->GetAction();
|
||||
if (action) {
|
||||
FUN_100b7220(action, MxDSAction::c_world, FALSE);
|
||||
ApplyMask(action, MxDSAction::c_world, FALSE);
|
||||
presenter->EndAction();
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,11 +71,11 @@ LegoResult LegoLOD::Read(Tgl::Renderer* p_renderer, LegoTextureContainer* p_text
|
||||
LegoU32 i, indexBackwards, indexForwards, tempNumVertsAndNormals;
|
||||
unsigned char paletteEntries[256];
|
||||
|
||||
if (p_storage->Read(&m_unk0x08, sizeof(undefined4)) != SUCCESS) {
|
||||
if (p_storage->Read(&m_flags, sizeof(LegoU32)) != SUCCESS) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (GetUnknown0x08Test4()) {
|
||||
if (SkipReadingData()) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@ -86,11 +86,11 @@ LegoResult LegoLOD::Read(Tgl::Renderer* p_renderer, LegoTextureContainer* p_text
|
||||
}
|
||||
|
||||
if (m_numMeshes == 0) {
|
||||
ClearFlag(c_bit4);
|
||||
ClearFlag(c_hasMesh);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
SetFlag(c_bit4);
|
||||
SetFlag(c_hasMesh);
|
||||
|
||||
m_melems = new Mesh[m_numMeshes];
|
||||
memset(m_melems, 0, sizeof(*m_melems) * m_numMeshes);
|
||||
@ -317,7 +317,7 @@ LegoLOD* LegoLOD::Clone(Tgl::Renderer* p_renderer)
|
||||
dupLod->m_melems[i].m_textured = m_melems[i].m_textured;
|
||||
}
|
||||
|
||||
dupLod->m_unk0x08 = m_unk0x08;
|
||||
dupLod->m_flags = m_flags;
|
||||
dupLod->m_numMeshes = m_numMeshes;
|
||||
dupLod->m_numVertices = m_numVertices;
|
||||
dupLod->m_numPolys = m_numPolys;
|
||||
|
||||
@ -260,7 +260,7 @@ LegoResult LegoROI::Read(
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
if (surplusLODs != 0 && lod->GetUnknown0x08Test8()) {
|
||||
if (surplusLODs != 0 && lod->IsExtraLOD()) {
|
||||
numLODs++;
|
||||
}
|
||||
}
|
||||
@ -277,7 +277,7 @@ LegoResult LegoROI::Read(
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
if (surplusLODs != 0 && lod->GetUnknown0x08Test8()) {
|
||||
if (surplusLODs != 0 && lod->IsExtraLOD()) {
|
||||
numLODs++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -92,7 +92,7 @@ void MakeSourceName(char*, const char*);
|
||||
void OmniError(const char* p_message, MxS32 p_status);
|
||||
void SetOmniUserMessage(void (*p_omniUserMessage)(const char*, MxS32));
|
||||
MxBool ContainsPresenter(MxCompositePresenterList& p_presenterList, MxPresenter* p_presenter);
|
||||
void FUN_100b7220(MxDSAction* p_action, MxU32 p_newFlags, MxBool p_setFlags);
|
||||
void ApplyMask(MxDSAction* p_action, MxU32 p_mask, MxBool p_setFlags);
|
||||
MxBool KeyValueStringParse(char*, const char*, const char*);
|
||||
|
||||
// TEMPLATE: BETA10 0x1012dfd0
|
||||
|
||||
@ -153,16 +153,16 @@ void SetOmniUserMessage(void (*p_omniUserMessage)(const char*, MxS32))
|
||||
|
||||
// FUNCTION: LEGO1 0x100b7220
|
||||
// FUNCTION: BETA10 0x10136f37
|
||||
void FUN_100b7220(MxDSAction* p_action, MxU32 p_newFlags, MxBool p_setFlags)
|
||||
void ApplyMask(MxDSAction* p_action, MxU32 p_mask, MxBool p_setFlags)
|
||||
{
|
||||
MxU32 oldFlags = p_action->GetFlags();
|
||||
MxU32 newFlags;
|
||||
|
||||
if (p_setFlags) {
|
||||
newFlags = oldFlags | p_newFlags;
|
||||
newFlags = oldFlags | p_mask;
|
||||
}
|
||||
else {
|
||||
newFlags = oldFlags & ~p_newFlags;
|
||||
newFlags = oldFlags & ~p_mask;
|
||||
}
|
||||
|
||||
p_action->SetFlags(newFlags);
|
||||
@ -172,7 +172,7 @@ void FUN_100b7220(MxDSAction* p_action, MxU32 p_newFlags, MxBool p_setFlags)
|
||||
MxDSAction* action;
|
||||
|
||||
while (cursor.Next(action)) {
|
||||
FUN_100b7220(action, p_newFlags, p_setFlags);
|
||||
ApplyMask(action, p_mask, p_setFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
class ViewLOD : public LODObject {
|
||||
public:
|
||||
enum {
|
||||
c_bit4 = 0x10
|
||||
c_hasMesh = 0x10
|
||||
};
|
||||
|
||||
ViewLOD(Tgl::Renderer* pRenderer) : m_meshBuilder(NULL), m_unk0x08(3) {}
|
||||
ViewLOD(Tgl::Renderer* pRenderer) : m_meshBuilder(NULL), m_flags(3) {}
|
||||
~ViewLOD() override;
|
||||
|
||||
// FUNCTION: LEGO1 0x100a6f30
|
||||
@ -28,19 +28,19 @@ class ViewLOD : public LODObject {
|
||||
|
||||
Tgl::MeshBuilder* GetMeshBuilder() { return m_meshBuilder; }
|
||||
const Tgl::MeshBuilder* GetMeshBuilder() const { return m_meshBuilder; }
|
||||
undefined4 GetUnknown0x08() { return m_unk0x08; }
|
||||
unsigned char GetUnknown0x08Test4() { return m_unk0x08 & 0xffffff04; }
|
||||
unsigned char GetUnknown0x08Test8() { return m_unk0x08 & 0xffffff08; }
|
||||
unsigned int GetFlags() { return m_flags; }
|
||||
unsigned char SkipReadingData() { return m_flags & 0xffffff04; }
|
||||
unsigned char IsExtraLOD() { return m_flags & 0xffffff08; }
|
||||
|
||||
void SetFlag(unsigned char p_flag) { m_unk0x08 |= p_flag; }
|
||||
void ClearFlag(unsigned char p_flag) { m_unk0x08 &= ~p_flag; }
|
||||
void SetFlag(unsigned char p_flag) { m_flags |= p_flag; }
|
||||
void ClearFlag(unsigned char p_flag) { m_flags &= ~p_flag; }
|
||||
|
||||
// SYNTHETIC: LEGO1 0x100a6f60
|
||||
// ViewLOD::`scalar deleting destructor'
|
||||
|
||||
protected:
|
||||
Tgl::MeshBuilder* m_meshBuilder; // 0x04
|
||||
undefined4 m_unk0x08; // 0x08
|
||||
unsigned int m_flags; // 0x08
|
||||
};
|
||||
|
||||
#endif // VIEWLOD_H
|
||||
|
||||
@ -165,7 +165,7 @@ void ViewManager::UpdateROIDetailBasedOnLOD(ViewROI* p_roi, int p_lodLevel)
|
||||
if (lodLevel < 0) {
|
||||
lod = (ViewLOD*) p_roi->GetLOD(p_lodLevel);
|
||||
|
||||
if (lod->GetUnknown0x08() & ViewLOD::c_bit4) {
|
||||
if (lod->GetFlags() & ViewLOD::c_hasMesh) {
|
||||
scene->Add(group);
|
||||
SetAppData(p_roi, reinterpret_cast<LPD3DRM_APPDATA>(p_roi));
|
||||
}
|
||||
@ -184,7 +184,7 @@ void ViewManager::UpdateROIDetailBasedOnLOD(ViewROI* p_roi, int p_lodLevel)
|
||||
lod = (ViewLOD*) p_roi->GetLOD(p_lodLevel);
|
||||
}
|
||||
|
||||
if (lod->GetUnknown0x08() & ViewLOD::c_bit4) {
|
||||
if (lod->GetFlags() & ViewLOD::c_hasMesh) {
|
||||
meshBuilder = lod->GetMeshBuilder();
|
||||
|
||||
if (meshBuilder != NULL) {
|
||||
@ -389,7 +389,7 @@ inline int ViewManager::GetFirstLODIndex(ViewROI* p_roi)
|
||||
const LODListBase* lods = p_roi->GetLODs();
|
||||
|
||||
if (lods != NULL && lods->Size() > 0) {
|
||||
if (((ViewLOD*) p_roi->GetLOD(0))->GetUnknown0x08Test8()) {
|
||||
if (((ViewLOD*) p_roi->GetLOD(0))->IsExtraLOD()) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
@ -404,7 +404,7 @@ inline int ViewManager::GetFirstLODIndex(ViewROI* p_roi)
|
||||
const LODListBase* lods = ((ViewROI*) *it)->GetLODs();
|
||||
|
||||
if (lods != NULL && lods->Size() > 0) {
|
||||
if (((ViewLOD*) ((ViewROI*) *it)->GetLOD(0))->GetUnknown0x08Test8()) {
|
||||
if (((ViewLOD*) ((ViewROI*) *it)->GetLOD(0))->IsExtraLOD()) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
|
||||
20
README.md
20
README.md
@ -8,18 +8,26 @@ Please note: this project is dedicated to achieving platform independence withou
|
||||
|
||||
## Status
|
||||
|
||||
### Supported platforms
|
||||
|
||||
| Platform | Status |
|
||||
| - | - |
|
||||
| Windows | [](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
|
||||
| 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.
|
||||
|
||||
### Library substitutions
|
||||
|
||||
## Usage
|
||||
|
||||
**An existing copy of LEGO Island is required to use this project.**
|
||||
|
||||
As it stands, builds provided in the [Releases tab](https://github.com/isledecomp/isle-portable/releases/tag/continuous) are mainly for developers; as such, they may not work properly for all end-users. Work is currently ongoing to create workable release builds ready for gameplay and general use by end-users. If you are technically inclined, you may find it easiest to compile the project yourself to get it running at this current point in time.
|
||||
|
||||
[Installation instructions](https://github.com/isledecomp/isle-portable/wiki/Installation) for some ports can be found in our Wiki.
|
||||
|
||||
## Library substitutions
|
||||
|
||||
To achieve our goal of platform independence, we need to replace any Windows-only libraries with platform-independent alternatives. This ensures that our codebase remains versatile and compatible across various systems. The following table serves as an overview of major libraries / subsystems and their chosen replacements. For any significant changes or additions, it's recommended to discuss them with the team on the Matrix chat first to ensure consistency and alignment with our project's objectives.
|
||||
|
||||
@ -42,12 +50,6 @@ To achieve our goal of platform independence, we need to replace any Windows-onl
|
||||
|
||||
This project uses the [CMake](https://cmake.org/) build system, which allows for a high degree of versatility regarding compilers and development environments. Please refer to the [GitHub action](/.github/workflows//ci.yml) for guidance.
|
||||
|
||||
## Usage
|
||||
|
||||
**An existing copy of LEGO Island is required to use this project.**
|
||||
|
||||
As it stands, the builds provided in the Releases tab are for developers; as such, they may not work properly for end-users. Work is currently ongoing to create workable release builds ready for gameplay and general use by end-users. If you are technically inclined, you may find it easiest to compile the project yourself to get it running at this current point in time.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you're interested in helping or contributing to this project, check out the [CONTRIBUTING](/CONTRIBUTING.md) page.
|
||||
|
||||
37
docker/emscripten/Dockerfile
Normal file
37
docker/emscripten/Dockerfile
Normal file
@ -0,0 +1,37 @@
|
||||
FROM emscripten/emsdk:latest AS builder
|
||||
|
||||
ARG CMAKE_VERSION=3.29.3
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
USER root
|
||||
|
||||
RUN apt-get update && apt-get install -y git wget && rm -rf /var/lib/apt/lists/*
|
||||
RUN wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-Linux-x86_64.sh -O /tmp/cmake.sh && \
|
||||
chmod +x /tmp/cmake.sh && \
|
||||
/tmp/cmake.sh --skip-license --prefix=/usr/local && \
|
||||
rm /tmp/cmake.sh
|
||||
|
||||
RUN chown -R emscripten:emscripten /src
|
||||
|
||||
USER emscripten
|
||||
|
||||
COPY ISLE/emscripten/libwasmfs_fetch.js.patch /tmp/
|
||||
RUN cd /emsdk/upstream/emscripten && \
|
||||
git apply --check /tmp/libwasmfs_fetch.js.patch && \
|
||||
git apply /tmp/libwasmfs_fetch.js.patch
|
||||
|
||||
COPY --chown=emscripten:emscripten . .
|
||||
|
||||
RUN emcmake cmake -S . -B build -DISLE_BUILD_CONFIG=OFF -DISLE_DEBUG=OFF -DCMAKE_BUILD_TYPE=Release -DISLE_EMSCRIPTEN_HOST=/assets && \
|
||||
emmake cmake --build build -j 32
|
||||
|
||||
RUN echo "Fetching isle.pizza frontend..."; \
|
||||
git clone --depth 1 https://github.com/isledecomp/isle.pizza /tmp/isle.pizza;
|
||||
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY docker/emscripten/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY --from=builder /tmp/isle.pizza /usr/share/nginx/html
|
||||
COPY --from=builder /src/build/isle.* /usr/share/nginx/html
|
||||
EXPOSE 6931
|
||||
27
docker/emscripten/nginx.conf
Normal file
27
docker/emscripten/nginx.conf
Normal file
@ -0,0 +1,27 @@
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
server {
|
||||
listen 6931;
|
||||
server_name localhost;
|
||||
|
||||
add_header 'Cross-Origin-Embedder-Policy' 'require-corp';
|
||||
add_header 'Cross-Origin-Opener-Policy' 'same-origin';
|
||||
add_header 'Cross-Origin-Resource-Policy' 'cross-origin';
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html isle.html;
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location ~* ^/assets/(.*)$ {
|
||||
alias /assets/;
|
||||
try_files /$1 /DATA/disk/$1 =404;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
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
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();
|
||||
|
||||
359
miniwin/src/d3drm/backends/opengl1/actual.cpp
Normal file
359
miniwin/src/d3drm/backends/opengl1/actual.cpp
Normal file
@ -0,0 +1,359 @@
|
||||
// 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);
|
||||
|
||||
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
|
||||
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) {
|
||||
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
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,42 @@
|
||||
#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.
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
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 +57,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 +100,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 +110,59 @@ 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;
|
||||
|
||||
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 +171,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 +189,20 @@ 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,
|
||||
static_cast<float>(surface->m_surface->w),
|
||||
static_cast<float>(surface->m_surface->h)}
|
||||
);
|
||||
AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture);
|
||||
return (Uint32) (m_textures.size() - 1);
|
||||
}
|
||||
@ -206,52 +236,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 +266,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 +301,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 +333,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 +355,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 +375,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
|
||||
@ -207,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);
|
||||
}
|
||||
@ -228,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
|
||||
@ -342,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
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;
|
||||
@ -87,8 +56,21 @@ class OpenGL1Renderer : public Direct3DRMRenderer {
|
||||
inline static void OpenGL1Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
|
||||
{
|
||||
Direct3DRMRenderer* device = OpenGL1Renderer::Create(640, 480);
|
||||
if (device) {
|
||||
EnumDevice(cb, ctx, device, OpenGL1_GUID);
|
||||
delete device;
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
delete device;
|
||||
|
||||
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
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
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
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")
|
||||
25
packaging/linux/appimage/AppRun
Executable file
25
packaging/linux/appimage/AppRun
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
HERE="$(dirname "$(readlink -f "${0}")")"
|
||||
|
||||
MAIN=$(grep -r "^Exec=.*" "$HERE"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1)
|
||||
|
||||
# MAIN_BIN=$(find "$HERE/usr/bin" -name "$MAIN" | head -n 1)
|
||||
MAIN_BIN="$HERE/usr/bin/isle-config"
|
||||
|
||||
export PATH="${HERE}/usr/bin/":$PATH # Prefer bundled binaries
|
||||
|
||||
export QT_QPA_PLATFORMTHEME=xdgdesktopportal # Use XDG filepicker for forward compatability
|
||||
[ -z "$QT_PLUGIN_PATH" ] && export QT_PLUGIN_PATH=/usr/lib/qt6/plugins:/usr/lib64/qt6/plugins # Use system Qt theme, will fallback to the default one if unavailable
|
||||
|
||||
|
||||
if [ ! -z $APPIMAGE ]; then
|
||||
BINARY_NAME=$(basename "$ARGV0")
|
||||
if [ -e "$HERE/usr/bin/$BINARY_NAME" ]; then
|
||||
exec "$HERE/usr/bin/$BINARY_NAME" "$@"
|
||||
else
|
||||
exec "${MAIN_BIN}" "$@"
|
||||
fi
|
||||
else
|
||||
exec "${MAIN_BIN}" "$@"
|
||||
fi
|
||||
100
packaging/linux/appimage/Build
Executable file
100
packaging/linux/appimage/Build
Executable file
@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
export LD_LIBRARY_PATH="build/source/lib:$LD_LIBRARY_PATH"
|
||||
[ -z "$QMAKE" ] && export QMAKE=/usr/lib/qt6/bin/qmake
|
||||
|
||||
# Sets a directory that has to have a following structure:
|
||||
# build
|
||||
# ├── bin
|
||||
# │ ├── isle
|
||||
# │ └── isle-config
|
||||
# └── lib
|
||||
# ├── liblego1.so
|
||||
# ├── libSDL3.so -> libSDL3.so.0 # Not important if available on the system
|
||||
# ├── libSDL3.so.0 -> libSDL3.so.0.3.0 # Not important if available on the system
|
||||
# └── libSDL3.so.0.3.0 # Not important if available on the system
|
||||
# Can also be defined using --build=path
|
||||
BUILD_SOURCE=source
|
||||
|
||||
# Sets where AppRun for AppImage is, can also be defined using --apprun=path
|
||||
APPRUN_SOURCE=AppRun
|
||||
|
||||
# Sets where desktop file for AppImage is, can also be defined using --desktop-file=path
|
||||
DESKTOP_FILE_SOURCE=isledecomp.desktop
|
||||
|
||||
# You know the drill
|
||||
ICON_SOURCE=../../icons/isle.svg
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
clean(){
|
||||
echo "Deleting build directory"
|
||||
rm -rf build
|
||||
}
|
||||
|
||||
download(){
|
||||
if [ ! -e "$1" ]; then
|
||||
curl -Lo "$1" "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
prepare(){
|
||||
mkdir -p build/tools
|
||||
mkdir -p build/assets
|
||||
|
||||
download build/tools/appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage
|
||||
chmod u+x build/tools/appimagetool.AppImage
|
||||
|
||||
download build/tools/linuxdeploy.AppImage https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-$(uname -m).AppImage
|
||||
chmod u+x build/tools/linuxdeploy.AppImage
|
||||
|
||||
download build/tools/linuxdeploy-plugin-qt.AppImage https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/latest/download/linuxdeploy-plugin-qt-$(uname -m).AppImage
|
||||
chmod u+x build/tools/linuxdeploy-plugin-qt.AppImage
|
||||
|
||||
if [ ! -f "build/assets/isledecomp.desktop" ]; then
|
||||
cp $DESKTOP_FILE_SOURCE build/assets/isledecomp.desktop
|
||||
cp $APPRUN_SOURCE build/assets/AppRun
|
||||
cp ../../icons/isle.svg build/assets/isle.svg
|
||||
fi
|
||||
|
||||
if [ ! -d "build/source" ]; then
|
||||
cp -r $BUILD_SOURCE build/source
|
||||
fi
|
||||
}
|
||||
|
||||
compile(){
|
||||
NO_STRIP=1 build/tools/linuxdeploy.AppImage \
|
||||
--plugin qt \
|
||||
-e build/source/bin/isle \
|
||||
-e build/source/bin/isle-config \
|
||||
-d build/assets/isledecomp.desktop \
|
||||
-i build/assets/isle.svg \
|
||||
--custom-apprun=AppRun \
|
||||
--appdir=build/AppDir
|
||||
}
|
||||
|
||||
package(){
|
||||
build/tools/appimagetool.AppImage build/AppDir build/"LEGO_Island-$(uname -m).AppImage"
|
||||
}
|
||||
|
||||
stop(){ # Can be used to do `Build clean stop` to just clean the directory
|
||||
exit
|
||||
}
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--build=*) BUILD_SOURCE="${arg#--build=}";;
|
||||
--apprun=*) APPRUN_SOURCE="${arg#--apprun=}";;
|
||||
--desktop-file=*) DESKTOP_FILE_SOURCE="${arg#--desktop-file=}";;
|
||||
--icon=*) ICON_SOURCE="${arg#--icon=}";;
|
||||
*) "$arg"
|
||||
esac
|
||||
done
|
||||
|
||||
prepare
|
||||
compile
|
||||
package
|
||||
# Symlinks named as binaries in appimage can call these binaries specifically
|
||||
# ln -s "LEGO_Island-$(uname -m).AppImage" isle-config
|
||||
# ln -s "LEGO_Island-$(uname -m).AppImage" isle
|
||||
80
packaging/linux/flatpak/org.legoisland.Isle.json
Normal file
80
packaging/linux/flatpak/org.legoisland.Isle.json
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
"id": "org.legoisland.Isle",
|
||||
"runtime": "org.kde.Platform",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"runtime-version": "6.8",
|
||||
"command": "isle",
|
||||
"finish-args": [
|
||||
"--share=ipc",
|
||||
"--socket=wayland",
|
||||
"--socket=fallback-x11",
|
||||
"--socket=pulseaudio",
|
||||
"--device=dri",
|
||||
"--device=input",
|
||||
"--filesystem=/run/media/:ro",
|
||||
"--filesystem=/media/:ro",
|
||||
"--filesystem=/mnt/:ro",
|
||||
"--filesystem=home:ro"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "isle",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [
|
||||
"-DCMAKE_BUILD_TYPE=RelWithDebInfo",
|
||||
"-DISLE_DEBUG=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../../3rdparty",
|
||||
"dest": "3rdparty/"
|
||||
},
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../../CMake",
|
||||
"dest": "CMake/"
|
||||
},
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../../CONFIG",
|
||||
"dest": "CONFIG/"
|
||||
},
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../../ISLE",
|
||||
"dest": "ISLE/"
|
||||
},
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../../LEGO1",
|
||||
"dest": "LEGO1/"
|
||||
},
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../../miniwin",
|
||||
"dest": "miniwin/"
|
||||
},
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../../packaging",
|
||||
"dest": "packaging/"
|
||||
},
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../../util",
|
||||
"dest": "util/"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "../../../CMakeLists.txt"
|
||||
}
|
||||
],
|
||||
"build-options": {
|
||||
"build-args": [
|
||||
"--share=network"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
38
packaging/linux/isledecomp.desktop.in
Normal file
38
packaging/linux/isledecomp.desktop.in
Normal file
@ -0,0 +1,38 @@
|
||||
[Desktop Entry]
|
||||
Version=1.5
|
||||
|
||||
Name=@APP_NAME@
|
||||
Comment=@APP_SUMMARY@
|
||||
|
||||
Icon=@APP_ID@
|
||||
Type=Application
|
||||
Categories=Game;KidsGame;AdventureGame;Qt
|
||||
|
||||
Keywords=LEGO;lego;LEGO Island
|
||||
Keywords[da]=LEGO;lego;Panik på LEGO Øen
|
||||
Keywords[de]=LEGO;lego;Abenteuer auf der LEGO Insel
|
||||
Keywords[es]=LEGO;lego;La Isla LEGO
|
||||
Keywords[fr]=LEGO;lego;Aventures sur L'île LEGO
|
||||
Keywords[it]=LEGO;lego;Isola LEGO
|
||||
Keywords[ja]=LEGO;lego;レゴアイランドの大冒険
|
||||
Keywords[ko]=LEGO;lego;레고 아일랜드
|
||||
Keywords[pt]=LEGO;lego;A Ilha LEGO
|
||||
Keywords[ru]=LEGO;lego;Остров LEGO
|
||||
Keywords[uk_UA]=LEGO;lego;LEGO острів
|
||||
|
||||
SingleMainWindow=true
|
||||
|
||||
TryExec=isle
|
||||
Exec=isle
|
||||
|
||||
Actions=play;configure
|
||||
|
||||
[Desktop Action play]
|
||||
Name=Play Game
|
||||
Icon=currenttrack_play
|
||||
Exec=isle
|
||||
|
||||
[Desktop Action configure]
|
||||
Name=Configure Settings
|
||||
Icon=settings
|
||||
Exec=isle-config
|
||||
91
packaging/linux/isledecomp.metainfo.xml.in
Normal file
91
packaging/linux/isledecomp.metainfo.xml.in
Normal file
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Freedesktop AppStream metadata file (https://www.freedesktop.org/software/appstream/docs) -->
|
||||
|
||||
<component type="desktop-application">
|
||||
<id>@APP_ID@</id>
|
||||
|
||||
<name>@APP_NAME@</name>
|
||||
<summary>@APP_SUMMARY@</summary>
|
||||
|
||||
<launchable type="desktop-id">@APP_ID@.desktop</launchable>
|
||||
|
||||
<branding>
|
||||
<color type="primary">#e3000b</color>
|
||||
</branding>
|
||||
|
||||
<developer id="io.github.isledecomp">
|
||||
<name>Isle Decomp Team</name>
|
||||
</developer>
|
||||
|
||||
<url type="homepage">https://github.com/isledecomp/isle-portable</url>
|
||||
<url type="contribute">https://github.com/isledecomp/isle-portable/blob/master/CONTRIBUTING.md</url>
|
||||
<url type="vcs-browser">https://github.com/isledecomp/isle-portable/tree/master</url>
|
||||
<url type="bugtracker">https://github.com/isledecomp/isle-portable/issues</url>
|
||||
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>@APP_SPDX@</project_license>
|
||||
|
||||
<requires>
|
||||
<display_length side="longest" compare="ge">640</display_length>
|
||||
<internet>offline-only</internet>
|
||||
</requires>
|
||||
|
||||
<recommends>
|
||||
<memory>128</memory>
|
||||
</recommends>
|
||||
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>gamepad</control>
|
||||
</supports>
|
||||
|
||||
<description>
|
||||
<p> This initiative is a portable version of LEGO Island (Version 1.1, English)
|
||||
based on the decompilation project. Our primary goal is to transform the codebase to achieve
|
||||
platform independence, thereby enhancing compatibility across various systems while preserving
|
||||
the original game's experience as faithfully as possible.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Please note: this project is dedicated to achieving platform independence without altering the
|
||||
core gameplay, adding new features, enhancing visual quality, or rewriting code for
|
||||
improvement's sake. While those are worthwhile objectives, they are not within the scope
|
||||
of this project.
|
||||
</p>
|
||||
|
||||
</description>
|
||||
|
||||
<content_rating type="oars-1.1">
|
||||
<content_attribute id="violence-cartoon">mild</content_attribute>
|
||||
<content_attribute id="language-humor">mild</content_attribute>
|
||||
</content_rating>
|
||||
|
||||
<categories>
|
||||
<category>Game</category>
|
||||
<category>KidsGame</category>
|
||||
<category>AdventureGame</category>
|
||||
<category>Qt</category>
|
||||
</categories>
|
||||
|
||||
<keywords>
|
||||
<keyword translate="no">LEGO</keyword>
|
||||
<keyword translate="no">lego</keyword>
|
||||
|
||||
<keyword>LEGO Island</keyword>
|
||||
<!-- Translations sourced from https://www.pcgamingwiki.com/wiki/Lego_Island#Localizations -->
|
||||
<keyword xml:lang="da">Panik på LEGO Øen</keyword>
|
||||
<keyword xml:lang="de">Abenteuer auf der LEGO Insel</keyword>
|
||||
<keyword xml:lang="es">La Isla LEGO</keyword>
|
||||
<keyword xml:lang="fr">Aventures sur L'île LEGO</keyword>
|
||||
<keyword xml:lang="it">Isola LEGO</keyword>
|
||||
<keyword xml:lang="ja">レゴアイランドの大冒険</keyword>
|
||||
<keyword xml:lang="ko">레고 아일랜드</keyword>
|
||||
<keyword xml:lang="pt">A Ilha LEGO</keyword>
|
||||
<keyword xml:lang="ru">Остров LEGO</keyword>
|
||||
</keywords>
|
||||
|
||||
<releases>
|
||||
<release version="@SEMANTIC_VERSION@" type="development" date="@BUILD_DATE@" />
|
||||
</releases>
|
||||
</component>
|
||||
@ -4,6 +4,7 @@ configureLegoModelPresenter(MxS32): 'DLL exported function'
|
||||
configureLegoPartPresenter(MxS32, MxS32): 'DLL exported function'
|
||||
configureLegoROI(int): 'DLL exported function'
|
||||
configureLegoWorldPresenter(MxS32): 'DLL exported function'
|
||||
configureMxTransitionManager(TransitionType): 'DLL exported function'
|
||||
GetNoCD_SourceName(): 'DLL exported function'
|
||||
m_3dView: 'Allow this variable name'
|
||||
m_3dManager: 'Allow this variable name'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user