Merge pull request #5 from AJenbo/psp

Enable OpenGL 1.1 support and merge latest upstream
This commit is contained in:
VoxelTek 2025-07-02 10:42:12 +10:00 committed by GitHub
commit 30a1de22a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
106 changed files with 3303 additions and 1207 deletions

View File

@ -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
View File

@ -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

View File

@ -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/*

View File

@ -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
View File

@ -30,3 +30,9 @@ LEGO1.DLL
# Kate - Text
/.cache
# Flatpak build cache
**/.flatpak-builder/
# Flatpak build dir
**/flatpak-build/

View File

@ -13,6 +13,9 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include(CheckCXXSourceCompiles)
include(CMakeDependentOption)
include(CMakePushCheckState)
include(cmake/detectcpu.cmake)
DetectTargetCPUArchitectures(ISLE_CPUS)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -35,14 +38,14 @@ option(ISLE_WERROR "Treat warnings as errors" OFF)
option(ISLE_DEBUG "Enable imgui debug" ON)
cmake_dependent_option(ISLE_USE_DX5 "Build with internal DirectX 5 SDK" "${NOT_MINGW}" "WIN32;CMAKE_SIZEOF_VOID_P EQUAL 4" OFF)
cmake_dependent_option(ISLE_MINIWIN "Use miniwin" ON "NOT ISLE_USE_DX5" OFF)
cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN" OFF)
cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "(MSVC OR ISLE_MINIWIN) AND (NOT PSP) AND (NOT NINTENDO_3DS)" OFF)
cmake_dependent_option(ISLE_COMPILE_SHADERS "Compile shaders" ON "SDL_SHADERCROSS_BIN;TARGET Python3::Interpreter" OFF)
option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON)
cmake_dependent_option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON "NOT PSP" OFF)
option(ENABLE_CLANG_TIDY "Enable clang-tidy")
option(DOWNLOAD_DEPENDENCIES "Download dependencies" ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" CACHE PATH "Directory where to put executables and dll")
set(ISLE_EMSCRIPTEN_HOST "" CACHE STRING "Host URL for Emscripten streaming (e.g., https://test.com)")
cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN" OFF)
cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN AND NOT PSP" OFF)
message(STATUS "Isle app: ${ISLE_BUILD_APP}")
message(STATUS "Config app: ${ISLE_BUILD_CONFIG}")
@ -55,15 +58,19 @@ add_library(Isle::iniparser INTERFACE IMPORTED)
if (DOWNLOAD_DEPENDENCIES)
# FetchContent downloads and configures dependencies
message(STATUS "Fetching SDL3 and iniparser. This might take a while...")
include(FetchContent)
FetchContent_Declare(
SDL3
GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git"
GIT_TAG "main"
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(SDL3)
if (NOT PSP)
message(STATUS "Fetching SDL3 and iniparser. This might take a while...")
FetchContent_Declare(
SDL3
GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git"
GIT_TAG "main"
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(SDL3)
else()
find_package(SDL3 CONFIG REQUIRED)
endif()
FetchContent_Declare(
iniparser
@ -345,6 +352,11 @@ if (WIN32)
target_link_libraries(lego1 INTERFACE winmm)
endif()
target_link_libraries(lego1 PRIVATE libsmacker miniaudio)
if (PSP)
target_link_libraries(lego1 PRIVATE atomic)
endif()
target_include_directories(lego1 PUBLIC $<BUILD_INTERFACE:$<TARGET_PROPERTY:miniaudio,INTERFACE_INCLUDE_DIRECTORIES>>)
# lego1_impl sources
@ -502,6 +514,9 @@ if (ISLE_BUILD_APP)
)
target_compile_definitions(isle PRIVATE ISLE_DEBUG)
target_link_libraries(isle PRIVATE imgui)
if(PSP)
target_compile_definitions(imgui PRIVATE IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS)
endif()
find_path(valgrind_INCLUDE_PATH NAMES valgrind/callgrind.h)
if(valgrind_INCLUDE_PATH)
# Run isle under valgrind to create a profile. Use e.g. kcachegrind to view the profile.
@ -520,12 +535,18 @@ if (ISLE_BUILD_APP)
target_compile_definitions(isle PRIVATE "ISLE_EMSCRIPTEN_HOST=\"${ISLE_EMSCRIPTEN_HOST}\"")
set_property(TARGET isle PROPERTY SUFFIX ".html")
endif()
if(NINTENDO_3DS)
target_sources(isle PRIVATE
ISLE/3ds/apthooks.cpp
ISLE/3ds/config.cpp
)
endif()
endif()
if (ISLE_BUILD_CONFIG)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
qt_standard_project_setup()
qt_add_executable(config WIN32
qt_add_executable(isle-config WIN32
LEGO1/mxdirectx/mxdirectxinfo.cpp
LEGO1/mxdirectx/legodxinfo.cpp
CONFIG/config.cpp
@ -535,22 +556,22 @@ if (ISLE_BUILD_CONFIG)
CONFIG/res/config.rc
CONFIG/res/config.qrc
)
target_link_libraries(config PRIVATE Qt6::Core Qt6::Widgets)
set_property(TARGET config PROPERTY AUTOMOC ON)
set_property(TARGET config PROPERTY AUTORCC ON)
set_property(TARGET config PROPERTY AUTOUIC ON)
set_property(TARGET config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res")
list(APPEND isle_targets config)
target_compile_definitions(config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG)
target_include_directories(config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1")
target_include_directories(config PUBLIC "$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/util>")
target_link_libraries(isle-config PRIVATE Qt6::Core Qt6::Widgets)
set_property(TARGET isle-config PROPERTY AUTOMOC ON)
set_property(TARGET isle-config PROPERTY AUTORCC ON)
set_property(TARGET isle-config PROPERTY AUTOUIC ON)
set_property(TARGET isle-config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res")
list(APPEND isle_targets isle-config)
target_compile_definitions(isle-config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG)
target_include_directories(isle-config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1")
target_include_directories(isle-config PUBLIC "$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/util>")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14)
target_link_libraries(config PRIVATE DirectX5::DirectX5)
target_link_libraries(isle-config PRIVATE DirectX5::DirectX5)
endif()
target_compile_definitions(config PRIVATE DIRECT3D_VERSION=0x500)
target_link_libraries(config PRIVATE SDL3::SDL3 Isle::iniparser)
target_compile_definitions(isle-config PRIVATE DIRECT3D_VERSION=0x500)
target_link_libraries(isle-config PRIVATE SDL3::SDL3 Isle::iniparser)
if (NOT ISLE_MINIWIN)
target_link_libraries(config PRIVATE ddraw dxguid)
target_link_libraries(isle-config PRIVATE ddraw dxguid)
endif()
endif()
@ -564,8 +585,8 @@ if (MSVC)
if (TARGET isle)
target_compile_definitions(isle PRIVATE "_CRT_SECURE_NO_WARNINGS")
endif()
if (TARGET config)
target_compile_definitions(config PRIVATE "_CRT_SECURE_NO_WARNINGS")
if (TARGET isle-config)
target_compile_definitions(isle-config PRIVATE "_CRT_SECURE_NO_WARNINGS")
endif()
endif()
# Visual Studio 2017 version 15.7 needs "/Zc:__cplusplus" for __cplusplus
@ -574,8 +595,8 @@ if (MSVC)
if (TARGET isle)
target_compile_options(isle PRIVATE "-Zc:__cplusplus")
endif()
if (TARGET config)
target_compile_options(config PRIVATE "-Zc:__cplusplus")
if (TARGET isle-config)
target_compile_options(isle-config PRIVATE "-Zc:__cplusplus")
endif()
endif()
endif()
@ -624,7 +645,9 @@ else()
include(GNUInstallDirs)
endif()
set(ISLE_PACKAGE_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CACHE STRING "Platform name of the package")
string(REPLACE ";" "-" ISLE_CPUS_STRING "${ISLE_CPUS}")
string(TOLOWER "${ISLE_CPUS_STRING}" ISLE_CPUS_STRING)
set(ISLE_PACKAGE_NAME "${CMAKE_SYSTEM_NAME}-${ISLE_CPUS_STRING}" CACHE STRING "Platform name of the package")
if(BUILD_SHARED_LIBS)
list(APPEND install_extra_targets lego1)
endif()
@ -633,7 +656,7 @@ install(TARGETS isle ${install_extra_targets}
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
if (ISLE_BUILD_CONFIG)
install(TARGETS config
install(TARGETS isle-config
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
)
endif()
@ -643,11 +666,39 @@ if(EMSCRIPTEN)
)
endif()
add_subdirectory(packaging)
set(CPACK_PACKAGE_DIRECTORY "dist")
set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}")
if(NINTENDO_3DS)
ctr_generate_smdh(isle.smdh
NAME "LEGO Island"
TITLE "LEGO Island"
DESCRIPTION "LEGO Island for the Nintendo 3DS"
AUTHOR "isledecomp/isle-portable"
VERSION "${PROJECT_VERSION}"
ICON "ISLE/res/3ds/isle.png"
)
ctr_create_3dsx(isle SMDH isle.smdh)
install(FILES "$<TARGET_FILE_DIR:isle>/isle.3dsx" DESTINATION "${CMAKE_INSTALL_BINDIR}")
endif()
if(MSVC)
set(CPACK_GENERATOR ZIP)
elseif(PSP)
# Create an EBOOT.PBP file
create_pbp_file(
TARGET ${PROJECT_NAME}
ICON_PATH "${CMAKE_SOURCE_DIR}/XMB/Game/ICON0.png"
ANIM_PATH "${CMAKE_SOURCE_DIR}/XMB/Game/ICON1.pmf"
BACKGROUND_PATH "${CMAKE_SOURCE_DIR}/XMB/Game/PIC1.png"
PREVIEW_PATH NULL
MUSIC_PATH "${CMAKE_SOURCE_DIR}/XMB/Game/SND0.at3"
TITLE ${PROJECT_NAME}
VERSION 01.00
)
else()
set(CPACK_GENERATOR TGZ)
endif()
include(CPack)

View File

@ -11,6 +11,8 @@
#include "res/resource.h"
#include <QKeyEvent>
#include <QMessageBox>
#include <QProcess>
#include <mxdirectx/legodxinfo.h>
#include <ui_maindialog.h>
@ -59,6 +61,7 @@ CMainDialog::CMainDialog(QWidget* pParent) : QDialog(pParent)
connect(m_ui->fullscreenCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxFullscreen);
connect(m_ui->okButton, &QPushButton::clicked, this, &CMainDialog::accept);
connect(m_ui->cancelButton, &QPushButton::clicked, this, &CMainDialog::reject);
connect(m_ui->launchButton, &QPushButton::clicked, this, &CMainDialog::launch);
connect(m_ui->dataPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectDataPathDialog);
connect(m_ui->savePathOpen, &QPushButton::clicked, this, &CMainDialog::SelectSavePathDialog);
@ -155,6 +158,30 @@ void CMainDialog::accept()
QDialog::accept();
}
void CMainDialog::launch()
{
if (m_modified) {
currentConfigApp->WriteRegisterSettings();
}
QDir::setCurrent(QCoreApplication::applicationDirPath());
QMessageBox msgBox = QMessageBox(
QMessageBox::Warning,
QString("Error!"),
QString("Unable to locate isle executable!"),
QMessageBox::Close
);
if (!QProcess::startDetached("./isle")) { // Check in isle-config directory
if (!QProcess::startDetached("isle")) { // Check in $PATH
msgBox.exec();
}
}
QDialog::accept();
}
// FUNCTION: CONFIG 0x00404360
void CMainDialog::UpdateInterface()
{

View File

@ -45,6 +45,7 @@ private slots:
void OnCheckboxFullscreen(bool checked);
void accept() override;
void reject() override;
void launch();
void SelectDataPathDialog();
void SelectSavePathDialog();
void DataPathEdited();

View File

@ -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;

View File

@ -508,8 +508,11 @@
</property>
<item>
<widget class="QPushButton" name="okButton">
<property name="toolTip">
<string>Save configuration and close the config tool.</string>
</property>
<property name="text">
<string>OK</string>
<string>Save and Exit</string>
</property>
<property name="default">
<bool>true</bool>
@ -517,9 +520,22 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<widget class="QPushButton" name="launchButton">
<property name="toolTip">
<string>Save configuration and launch LEGO Island.</string>
</property>
<property name="text">
<string>Cancel</string>
<string>Save and Launch</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="toolTip">
<string>Discard changed settings and close the config tool.</string>
</property>
<property name="text">
<string>Exit without saving</string>
</property>
</widget>
</item>

30
ISLE/3ds/apthooks.cpp Normal file
View 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
View 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
View 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
View 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

View File

@ -1,5 +1,5 @@
diff --git a/src/lib/libwasmfs_fetch.js b/src/lib/libwasmfs_fetch.js
index e8c9f7e21..1c0eea957 100644
index e8c9f7e21..caf1971d2 100644
--- a/src/lib/libwasmfs_fetch.js
+++ b/src/lib/libwasmfs_fetch.js
@@ -38,36 +38,7 @@ addToLibrary({
@ -89,7 +89,21 @@ index e8c9f7e21..1c0eea957 100644
return Promise.resolve();
}
@@ -164,6 +156,21 @@ addToLibrary({
@@ -156,14 +148,31 @@ addToLibrary({
return readLength;
},
getSize: async (file) => {
- try {
- await getFileRange(file, 0, 0);
- } catch (failedResponse) {
- return 0;
+ if (!(file in wasmFS$JSMemoryRanges)) {
+ try {
+ await getFileRange(file, undefined, undefined);
+ } catch (failedResponse) {
+ return 0;
+ }
}
return wasmFS$JSMemoryRanges[file].size;
},
};

View File

@ -50,6 +50,11 @@
#include "emscripten/messagebox.h"
#endif
#ifdef __3DS__
#include "3ds/apthooks.h"
#include "3ds/config.h"
#endif
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
// GLOBAL: ISLE 0x410030
@ -96,11 +101,7 @@ IsleApp::IsleApp()
m_cdPath = NULL;
m_deviceId = NULL;
m_savePath = NULL;
#ifdef __EMSCRIPTEN__
m_fullScreen = FALSE;
#else
m_fullScreen = TRUE;
#endif
m_flipSurfaces = FALSE;
m_backBuffersInVram = TRUE;
m_using8bit = FALSE;
@ -140,6 +141,7 @@ IsleApp::IsleApp()
m_iniPath = NULL;
m_maxLod = RealtimeView::GetUserMaxLOD();
m_maxAllowedExtras = m_islandQuality <= 1 ? 10 : 20;
m_transitionType = MxTransitionManager::e_mosaic;
}
// FUNCTION: ISLE 0x4011a0
@ -316,6 +318,9 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
},
NULL
);
#endif
#ifdef __3DS__
N3DS_SetupAptHooks();
#endif
return SDL_APP_CONTINUE;
}
@ -657,9 +662,12 @@ MxResult IsleApp::SetupWindow()
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, g_targetHeight);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, m_fullScreen);
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE);
#ifdef MINIWIN
#if defined(MINIWIN) && !defined(__3DS__)
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#ifndef __PSP__
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
#endif
#endif
window = SDL_CreateWindowWithProperties(props);
@ -703,6 +711,7 @@ MxResult IsleApp::SetupWindow()
return FAILURE;
}
DetectGameVersion();
GameState()->SerializePlayersInfo(LegoStorage::c_read);
GameState()->SerializeScoreHistory(LegoStorage::c_read);
@ -725,6 +734,7 @@ MxResult IsleApp::SetupWindow()
LegoBuildingManager::configureLegoBuildingManager(m_islandQuality);
LegoROI::configureLegoROI(iVar10);
LegoAnimationManager::configureLegoAnimationManager(m_maxAllowedExtras);
MxTransitionManager::configureMxTransitionManager(m_transitionType);
RealtimeView::SetUserMaxLOD(m_maxLod);
if (LegoOmni::GetInstance()) {
if (LegoOmni::GetInstance()->GetInputManager()) {
@ -823,7 +833,11 @@ bool IsleApp::LoadConfig()
SDL_snprintf(buf, sizeof(buf), "%f", m_maxLod);
iniparser_set(dict, "isle:Max LOD", buf);
iniparser_set(dict, "isle:Max Allowed Extras", SDL_itoa(m_maxAllowedExtras, buf, 10));
iniparser_set(dict, "isle:Transition Type", SDL_itoa(m_transitionType, buf, 10));
#ifdef __3DS__
N3DS_SetupDefaultConfigOverrides(dict);
#endif
iniparser_dump_ini(dict, iniFP);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig);
fclose(iniFP);
@ -852,7 +866,13 @@ bool IsleApp::LoadConfig()
strcpy(m_mediaPath, mediaPath);
m_flipSurfaces = iniparser_getboolean(dict, "isle:Flip Surfaces", m_flipSurfaces);
#ifdef __EMSCRIPTEN__
m_fullScreen = FALSE;
#else
m_fullScreen = iniparser_getboolean(dict, "isle:Full Screen", m_fullScreen);
#endif
m_wideViewAngle = iniparser_getboolean(dict, "isle:Wide View Angle", m_wideViewAngle);
m_use3dSound = iniparser_getboolean(dict, "isle:3DSound", m_use3dSound);
m_useMusic = iniparser_getboolean(dict, "isle:Music", m_useMusic);
@ -879,6 +899,8 @@ bool IsleApp::LoadConfig()
m_islandTexture = iniparser_getint(dict, "isle:Island Texture", m_islandTexture);
m_maxLod = iniparser_getdouble(dict, "isle:Max LOD", m_maxLod);
m_maxAllowedExtras = iniparser_getint(dict, "isle:Max Allowed Extras", m_maxAllowedExtras);
m_transitionType =
(MxTransitionManager::TransitionType) iniparser_getint(dict, "isle:Transition Type", m_transitionType);
const char* deviceId = iniparser_getstring(dict, "isle:3D Device ID", NULL);
if (deviceId != NULL) {
@ -1098,6 +1120,34 @@ MxResult IsleApp::VerifyFilesystem()
return SUCCESS;
}
void IsleApp::DetectGameVersion()
{
const char* file = "/lego/scripts/infocntr/infomain.si";
SDL_PathInfo info;
bool success = false;
MxString path = MxString(m_hdPath) + file;
path.MapPathToFilesystem();
if (!(success = SDL_GetPathInfo(path.GetData(), &info))) {
path = MxString(m_cdPath) + file;
path.MapPathToFilesystem();
success = SDL_GetPathInfo(path.GetData(), &info);
}
assert(success);
// File sizes of INFOMAIN.SI in English 1.0 and Japanese 1.0
Lego()->SetVersion10(info.size == 58130432 || info.size == 57737216);
if (Lego()->IsVersion10()) {
SDL_Log("Detected game version 1.0");
SDL_SetWindowTitle(reinterpret_cast<SDL_Window*>(m_windowHandle), "Lego Island");
}
else {
SDL_Log("Detected game version 1.1");
}
}
IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice()
{
LegoVideoManager* videoManager = LegoOmni::GetInstance()->GetVideoManager();

View File

@ -3,6 +3,7 @@
#include "lego1_export.h"
#include "legoutils.h"
#include "mxtransitionmanager.h"
#include "mxtypes.h"
#include "mxvideoparam.h"
@ -56,6 +57,7 @@ class IsleApp {
MxResult ParseArguments(int argc, char** argv);
MxResult VerifyFilesystem();
void DetectGameVersion();
private:
char* m_hdPath; // 0x00
@ -90,9 +92,11 @@ class IsleApp {
char* m_iniPath;
MxFloat m_maxLod;
MxU32 m_maxAllowedExtras;
MxTransitionManager::TransitionType m_transitionType;
};
extern IsleApp* g_isle;
extern MxS32 g_closed;
extern IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice();

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

View File

@ -11,6 +11,12 @@ class MxEndActionNotificationParam;
// SIZE 0x24
class AmbulanceMissionState : public LegoState {
public:
enum {
e_ready = 0,
e_enteredAmbulance = 1,
e_prepareAmbulance = 2,
};
AmbulanceMissionState();
// FUNCTION: LEGO1 0x10037440
@ -125,18 +131,18 @@ class AmbulanceMissionState : public LegoState {
// SYNTHETIC: LEGO1 0x100376c0
// AmbulanceMissionState::`scalar deleting destructor'
undefined4 m_unk0x08; // 0x08
MxLong m_startTime; // 0x0c
MxS16 m_peScore; // 0x10
MxS16 m_maScore; // 0x12
MxS16 m_paScore; // 0x14
MxS16 m_niScore; // 0x16
MxS16 m_laScore; // 0x18
MxS16 m_peHighScore; // 0x1a
MxS16 m_maHighScore; // 0x1c
MxS16 m_paHighScore; // 0x1e
MxS16 m_niHighScore; // 0x20
MxS16 m_laHighScore; // 0x22
MxU32 m_state; // 0x08
MxLong m_startTime; // 0x0c
MxS16 m_peScore; // 0x10
MxS16 m_maScore; // 0x12
MxS16 m_paScore; // 0x14
MxS16 m_niScore; // 0x16
MxS16 m_laScore; // 0x18
MxS16 m_peHighScore; // 0x1a
MxS16 m_maHighScore; // 0x1c
MxS16 m_paHighScore; // 0x1e
MxS16 m_niHighScore; // 0x20
MxS16 m_laHighScore; // 0x22
};
// VTABLE: LEGO1 0x100d71a8
@ -177,15 +183,21 @@ class Ambulance : public IslePathActor {
virtual MxLong HandleEndAction(MxEndActionNotificationParam& p_param); // vtable+0xf4
void CreateState();
void FUN_10036e60();
void Init();
void ActivateSceneActions();
void StopActions();
void FUN_10037250();
void Reset();
// SYNTHETIC: LEGO1 0x10036130
// Ambulance::`scalar deleting destructor'
private:
enum {
e_none = 0,
e_waiting = 1,
e_finished = 3,
};
void PlayAnimation(IsleScript::Script p_objectId);
void PlayFinalAnimation(IsleScript::Script p_objectId);
void StopAction(IsleScript::Script p_objectId);
@ -196,9 +208,9 @@ class Ambulance : public IslePathActor {
AmbulanceMissionState* m_state; // 0x164
MxS16 m_unk0x168; // 0x168
MxS16 m_actorId; // 0x16a
MxS16 m_unk0x16c; // 0x16c
MxS16 m_unk0x16e; // 0x16e
MxS16 m_unk0x170; // 0x170
MxS16 m_atPoliceTask; // 0x16c
MxS16 m_atBeachTask; // 0x16e
MxS16 m_taskState; // 0x170
MxS16 m_unk0x172; // 0x172
IsleScript::Script m_lastAction; // 0x174
IsleScript::Script m_lastAnimation; // 0x178

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -200,6 +200,9 @@ class LegoOmni : public MxOmni {
SDL_PushEvent(&event);
}
void SetVersion10(MxBool p_version10) { m_version10 = p_version10; }
MxBool IsVersion10() { return m_version10; }
// SYNTHETIC: LEGO1 0x10058b30
// LegoOmni::`scalar deleting destructor'
@ -221,6 +224,7 @@ class LegoOmni : public MxOmni {
MxDSAction m_action; // 0xa0
MxBackgroundAudioManager* m_bkgAudioManager; // 0x134
MxTransitionManager* m_transitionManager; // 0x138
MxBool m_version10;
public:
MxBool m_unk0x13c; // 0x13c

View File

@ -13,7 +13,7 @@ struct LegoPathEdgeContainer;
struct LegoOrientedEdge;
class LegoWEEdge;
extern MxLong g_unk0x100f3308;
extern MxLong g_timeLastHitSoundPlayed;
extern const char* g_strHIT_WALL_SOUND;
// VTABLE: LEGO1 0x100d6e28

View File

@ -49,12 +49,12 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore {
MxBool SwitchMove(LegoEntity* p_entity);
MxBool SwitchMood(LegoEntity* p_entity);
MxU32 GetAnimationId(LegoEntity* p_entity);
MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_state);
MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood);
LegoPlantInfo* GetInfoArray(MxS32& p_length);
LegoEntity* CreatePlant(MxS32 p_index, LegoWorld* p_world, LegoOmni::World p_worldId);
MxBool DecrementCounter(LegoEntity* p_entity);
void ScheduleAnimation(LegoEntity* p_entity, MxLong p_length);
MxResult FUN_10026410();
MxResult DetermineBoundaries();
void ClearCounters();
void SetInitialCounters();
@ -77,11 +77,11 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore {
static MxS32 g_maxMove[4];
static MxU32 g_maxSound;
LegoOmni::World m_worldId; // 0x08
undefined m_unk0x0c; // 0x0c
AnimEntry* m_entries[5]; // 0x10
MxS8 m_numEntries; // 0x24
LegoWorld* m_world; // 0x28
LegoOmni::World m_worldId; // 0x08
MxBool m_boundariesDetermined; // 0x0c
AnimEntry* m_entries[5]; // 0x10
MxS8 m_numEntries; // 0x24
LegoWorld* m_world; // 0x28
friend class DebugViewer;
};

View File

@ -144,10 +144,10 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap {
MxResult VTable0x9c() override; // vtable+0x9c
virtual void SetMaxLinearVelocity(float p_maxLinearVelocity);
virtual void FUN_10012ff0(float p_param);
virtual void KickCamera(float p_param);
virtual MxU32 HandleSkeletonKicks(float p_param1);
static void FUN_10012de0();
static void InitYouCantStopSound();
static void InitSoundIndices();
// SYNTHETIC: LEGO1 0x10014240
@ -155,7 +155,7 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap {
private:
undefined m_userState; // 0x54
float m_unk0x58; // 0x58
float m_kickStart; // 0x58
Mx3DPointFloat m_unk0x5c; // 0x5c
// Names verified by BETA10 0x100cb4a9

View File

@ -44,8 +44,11 @@ class LegoCarRaceActor : public virtual LegoRaceActor {
Vector3& p_v3
) override; // vtable+0x6c
void Animate(float p_time) override; // vtable+0x70
void SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOrientedEdge*& p_edge, float& p_unk0xe4)
override; // vtable+0x98
void SwitchBoundary(
LegoPathBoundary*& p_boundary,
LegoOrientedEdge*& p_edge,
float& p_unk0xe4
) override; // vtable+0x98
MxResult VTable0x9c() override; // vtable+0x9c
// LegoCarRaceActor vtable
@ -83,7 +86,7 @@ class LegoCarRaceActor : public virtual LegoRaceActor {
protected:
MxFloat m_unk0x08; // 0x08
MxU8 m_unk0x0c; // 0x0c
MxU8 m_animState; // 0x0c
// Could be a multiplier for the maximum speed when going straight
MxFloat m_unk0x10; // 0x10

View File

@ -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'

View File

@ -40,9 +40,9 @@ Ambulance::Ambulance()
m_state = NULL;
m_unk0x168 = 0;
m_actorId = -1;
m_unk0x16c = 0;
m_unk0x16e = 0;
m_unk0x170 = 0;
m_atPoliceTask = 0;
m_atBeachTask = 0;
m_taskState = Ambulance::e_none;
m_lastAction = IsleScript::c_noneIsle;
m_unk0x172 = 0;
m_lastAnimation = IsleScript::c_noneIsle;
@ -73,7 +73,7 @@ MxResult Ambulance::Create(MxDSAction& p_dsAction)
m_state = (AmbulanceMissionState*) GameState()->GetState("AmbulanceMissionState");
if (!m_state) {
m_state = new AmbulanceMissionState();
m_state->m_unk0x08 = 0;
m_state->m_state = AmbulanceMissionState::e_ready;
GameState()->RegisterState(m_state);
}
}
@ -173,25 +173,25 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param)
m_lastAction = IsleScript::c_noneIsle;
}
else if (objectId == IsleScript::c_hho027en_RunAnim) {
m_state->m_unk0x08 = 1;
m_state->m_state = AmbulanceMissionState::e_enteredAmbulance;
CurrentWorld()->PlaceActor(UserActor());
HandleClick();
m_unk0x172 = 0;
TickleManager()->RegisterClient(this, 40000);
}
else if (objectId == IsleScript::c_hpz047pe_RunAnim || objectId == IsleScript::c_hpz048pe_RunAnim || objectId == IsleScript::c_hpz049bd_RunAnim || objectId == IsleScript::c_hpz053pa_RunAnim) {
if (m_unk0x170 == 3) {
if (m_taskState == Ambulance::e_finished) {
PlayAnimation(IsleScript::c_hpz055pa_RunAnim);
m_unk0x170 = 0;
m_taskState = Ambulance::e_none;
}
else {
PlayAnimation(IsleScript::c_hpz053pa_RunAnim);
}
}
else if (objectId == IsleScript::c_hpz050bd_RunAnim || objectId == IsleScript::c_hpz052ma_RunAnim) {
if (m_unk0x170 == 3) {
if (m_taskState == Ambulance::e_finished) {
PlayAnimation(IsleScript::c_hpz057ma_RunAnim);
m_unk0x170 = 0;
m_taskState = Ambulance::e_none;
}
else {
PlayAnimation(IsleScript::c_hpz052ma_RunAnim);
@ -204,18 +204,18 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param)
m_unk0x172 = 0;
TickleManager()->RegisterClient(this, 40000);
if (m_unk0x16c != 0) {
if (m_atPoliceTask != 0) {
StopActions();
}
}
else if (objectId == IsleScript::c_hps116bd_RunAnim || objectId == IsleScript::c_hps118re_RunAnim) {
if (objectId == IsleScript::c_hps116bd_RunAnim && m_unk0x170 != 3) {
if (objectId == IsleScript::c_hps116bd_RunAnim && m_taskState != Ambulance::e_finished) {
PlayAction(IsleScript::c_Avo923In_PlayWav);
}
if (m_unk0x170 == 3) {
if (m_taskState == Ambulance::e_finished) {
PlayAnimation(IsleScript::c_hps117bd_RunAnim);
m_unk0x170 = 0;
m_taskState = Ambulance::e_none;
}
else {
PlayAnimation(IsleScript::c_hps118re_RunAnim);
@ -228,12 +228,12 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param)
m_unk0x172 = 0;
TickleManager()->RegisterClient(this, 40000);
if (m_unk0x16e != 0) {
if (m_atBeachTask != 0) {
StopActions();
}
}
else if (objectId == IsleScript::c_hho142cl_RunAnim || objectId == IsleScript::c_hho143cl_RunAnim || objectId == IsleScript::c_hho144cl_RunAnim) {
FUN_10037250();
Reset();
}
}
@ -244,18 +244,18 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param)
// FUNCTION: BETA10 0x100230bf
MxLong Ambulance::HandleButtonDown(LegoControlManagerNotificationParam& p_param)
{
if (m_unk0x170 == 1) {
if (m_taskState == Ambulance::e_waiting) {
LegoROI* roi = PickROI(p_param.GetX(), p_param.GetY());
if (roi != NULL && !SDL_strcasecmp(roi->GetName(), "ps-gate")) {
m_unk0x170 = 3;
m_taskState = Ambulance::e_finished;
return 1;
}
roi = PickRootROI(p_param.GetX(), p_param.GetY());
if (roi != NULL && !SDL_strcasecmp(roi->GetName(), "gd")) {
m_unk0x170 = 3;
m_taskState = Ambulance::e_finished;
return 1;
}
}
@ -273,9 +273,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param)
}
if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && p_param.GetData() == 0x0b) {
if (m_unk0x16e != 0) {
if (m_unk0x16c != 0) {
m_state->m_unk0x08 = 2;
if (m_atBeachTask != 0) {
if (m_atPoliceTask != 0) {
m_state->m_state = AmbulanceMissionState::e_prepareAmbulance;
if (m_lastAction != IsleScript::c_noneIsle) {
InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL);
@ -300,7 +300,7 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param)
return 0;
}
if (m_unk0x16e != 0) {
if (m_atBeachTask != 0) {
if (m_lastAction != IsleScript::c_noneIsle) {
InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL);
}
@ -310,7 +310,7 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param)
}
}
if (m_unk0x16c != 0) {
if (m_atPoliceTask != 0) {
if (m_lastAction != IsleScript::c_noneIsle) {
InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL);
}
@ -318,9 +318,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param)
PlayAction(IsleScript::c_Avo915In_PlayWav);
}
}
else if (p_param.GetTrigger() == LegoPathStruct::c_s && p_param.GetData() == 0x131 && m_unk0x16e == 0) {
m_unk0x16e = 1;
m_unk0x170 = 1;
else if (p_param.GetTrigger() == LegoPathStruct::c_s && p_param.GetData() == 0x131 && m_atBeachTask == 0) {
m_atBeachTask = 1;
m_taskState = Ambulance::e_waiting;
if (m_lastAction != IsleScript::c_noneIsle) {
InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL);
@ -348,9 +348,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param)
break;
}
}
else if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && (p_param.GetData() == 0x22 || p_param.GetData() == 0x23 || p_param.GetData() == 0x24) && m_unk0x16c == 0) {
m_unk0x16c = 1;
m_unk0x170 = 1;
else if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && (p_param.GetData() == 0x22 || p_param.GetData() == 0x23 || p_param.GetData() == 0x24) && m_atPoliceTask == 0) {
m_atPoliceTask = 1;
m_taskState = Ambulance::e_waiting;
if (m_lastAction != IsleScript::c_noneIsle) {
InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL);
@ -371,7 +371,7 @@ MxLong Ambulance::HandleClick()
return 1;
}
if (m_state->m_unk0x08 == 2) {
if (m_state->m_state == AmbulanceMissionState::e_prepareAmbulance) {
return 1;
}
@ -390,7 +390,7 @@ MxLong Ambulance::HandleClick()
InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_AmbulanceDashboard, NULL);
ControlManager()->Register(this);
if (m_state->m_unk0x08 == 1) {
if (m_state->m_state == AmbulanceMissionState::e_enteredAmbulance) {
SpawnPlayer(LegoGameState::e_hospitalExited, TRUE, 0);
m_state->m_startTime = Timer()->GetTime();
InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_pns018rd_RunAnim, NULL);
@ -401,9 +401,9 @@ MxLong Ambulance::HandleClick()
// FUNCTION: LEGO1 0x10036e60
// FUNCTION: BETA10 0x100236bb
void Ambulance::FUN_10036e60()
void Ambulance::Init()
{
m_state->m_unk0x08 = 2;
m_state->m_state = AmbulanceMissionState::e_prepareAmbulance;
PlayAnimation(IsleScript::c_hho027en_RunAnim);
m_lastAction = IsleScript::c_noneIsle;
m_lastAnimation = IsleScript::c_noneIsle;
@ -414,7 +414,7 @@ void Ambulance::Exit()
{
GameState()->m_currentArea = LegoGameState::e_hospitalExterior;
StopActions();
FUN_10037250();
Reset();
Leave();
}
@ -470,11 +470,11 @@ void Ambulance::ActivateSceneActions()
{
PlayMusic(JukeboxScript::c_Hospital_Music);
if (m_state->m_unk0x08 == 1) {
m_state->m_unk0x08 = 0;
if (m_state->m_state == AmbulanceMissionState::e_enteredAmbulance) {
m_state->m_state = AmbulanceMissionState::e_ready;
PlayAction(IsleScript::c_ham033cl_PlayWav);
}
else if (m_unk0x16c != 0 && m_unk0x16e != 0) {
else if (m_atPoliceTask != 0 && m_atBeachTask != 0) {
IsleScript::Script objectId;
switch (SDL_rand(2)) {
@ -574,14 +574,14 @@ void Ambulance::StopActions()
}
// FUNCTION: LEGO1 0x10037250
void Ambulance::FUN_10037250()
void Ambulance::Reset()
{
StopAction(m_lastAction);
BackgroundAudioManager()->RaiseVolume();
((Act1State*) GameState()->GetState("Act1State"))->m_unk0x018 = 0;
m_state->m_unk0x08 = 0;
m_unk0x16e = 0;
m_unk0x16c = 0;
m_state->m_state = AmbulanceMissionState::e_ready;
m_atBeachTask = 0;
m_atPoliceTask = 0;
g_isleFlags |= Isle::c_playMusic;
AnimationManager()->EnableCamAnims(TRUE);
AnimationManager()->FUN_1005f6d0(TRUE);
@ -629,7 +629,7 @@ void Ambulance::PlayAction(IsleScript::Script p_objectId)
// FUNCTION: LEGO1 0x100373a0
AmbulanceMissionState::AmbulanceMissionState()
{
m_unk0x08 = 0;
m_state = AmbulanceMissionState::e_ready;
m_startTime = 0;
m_peScore = 0;
m_maScore = 0;

View File

@ -488,7 +488,7 @@ void LegoCarBuild::FUN_100236d0()
m_unk0x110 = NULL;
m_unk0x100 = 0;
if (m_animPresenter->AllPartsPlaced()) {
if (m_animPresenter->AllPartsPlaced() && !Lego()->IsVersion10()) {
// Note the code duplication with LEGO1 0x10025ee0
switch (m_carId) {
case 1:

View File

@ -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;

View File

@ -39,10 +39,10 @@ MxU32 g_characterAnimationId = 10;
char* LegoCharacterManager::g_customizeAnimFile = NULL;
// GLOBAL: LEGO1 0x100fc4d8
MxU32 g_soundIdOffset = 50;
MxU32 g_characterSoundIdOffset = 50;
// GLOBAL: LEGO1 0x100fc4dc
MxU32 g_soundIdMoodOffset = 66;
MxU32 g_characterSoundIdMoodOffset = 66;
// GLOBAL: LEGO1 0x100fc4e8
MxU32 g_headTextureCounter = 0;
@ -636,7 +636,7 @@ MxBool LegoCharacterManager::SetHeadTexture(LegoROI* p_roi, LegoTextureInfo* p_t
LegoLOD* clone = lod->Clone(renderer);
if (p_texture != NULL) {
clone->FUN_100aad70(p_texture);
clone->UpdateTextureInfo(p_texture);
}
dupLodList->PushBack(clone);
@ -933,16 +933,16 @@ MxU32 LegoCharacterManager::GetAnimationId(LegoROI* p_roi)
// FUNCTION: LEGO1 0x10085140
// FUNCTION: BETA10 0x10076855
MxU32 LegoCharacterManager::GetSoundId(LegoROI* p_roi, MxBool p_und)
MxU32 LegoCharacterManager::GetSoundId(LegoROI* p_roi, MxBool p_basedOnMood)
{
LegoActorInfo* info = GetActorInfo(p_roi);
if (p_und) {
return info->m_mood + g_soundIdMoodOffset;
if (p_basedOnMood) {
return info->m_mood + g_characterSoundIdMoodOffset;
}
if (info != NULL) {
return info->m_sound + g_soundIdOffset;
return info->m_sound + g_characterSoundIdOffset;
}
else {
return 0;

View File

@ -40,10 +40,10 @@ MxU8 g_counters[] = {1, 2, 2, 3};
MxU32 LegoPlantManager::g_maxSound = 8;
// GLOBAL: LEGO1 0x100f3160
MxU32 g_unk0x100f3160 = 56;
MxU32 g_plantSoundIdOffset = 56;
// GLOBAL: LEGO1 0x100f3164
MxU32 g_unk0x100f3164 = 66;
MxU32 g_plantSoundIdMoodOffset = 66;
// GLOBAL: LEGO1 0x100f3168
MxS32 LegoPlantManager::g_maxMove[4] = {3, 3, 3, 3};
@ -83,7 +83,7 @@ void LegoPlantManager::Init()
}
m_worldId = LegoOmni::e_undefined;
m_unk0x0c = 0;
m_boundariesDetermined = FALSE;
m_numEntries = 0;
}
@ -98,7 +98,7 @@ void LegoPlantManager::LoadWorldInfo(LegoOmni::World p_worldId)
CreatePlant(i, world, p_worldId);
}
m_unk0x0c = 0;
m_boundariesDetermined = FALSE;
}
// FUNCTION: LEGO1 0x100263a0
@ -119,12 +119,12 @@ void LegoPlantManager::Reset(LegoOmni::World p_worldId)
}
m_worldId = LegoOmni::e_undefined;
m_unk0x0c = 0;
m_boundariesDetermined = FALSE;
}
// FUNCTION: LEGO1 0x10026410
// FUNCTION: BETA10 0x100c50e9
MxResult LegoPlantManager::FUN_10026410()
MxResult LegoPlantManager::DetermineBoundaries()
{
// similar to LegoBuildingManager::FUN_10030630()
@ -192,7 +192,7 @@ MxResult LegoPlantManager::FUN_10026410()
}
}
m_unk0x0c = TRUE;
m_boundariesDetermined = TRUE;
return SUCCESS;
}
@ -200,8 +200,8 @@ MxResult LegoPlantManager::FUN_10026410()
// FUNCTION: BETA10 0x100c55e0
LegoPlantInfo* LegoPlantManager::GetInfoArray(MxS32& p_length)
{
if (!m_unk0x0c) {
FUN_10026410();
if (!m_boundariesDetermined) {
DetermineBoundaries();
}
p_length = sizeOfArray(g_plantInfo);
@ -514,16 +514,16 @@ MxU32 LegoPlantManager::GetAnimationId(LegoEntity* p_entity)
// FUNCTION: LEGO1 0x10026ba0
// FUNCTION: BETA10 0x100c61ba
MxU32 LegoPlantManager::GetSoundId(LegoEntity* p_entity, MxBool p_state)
MxU32 LegoPlantManager::GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood)
{
LegoPlantInfo* info = GetInfo(p_entity);
if (p_state) {
return (info->m_mood & 1) + g_unk0x100f3164;
if (p_basedOnMood) {
return (info->m_mood & 1) + g_plantSoundIdMoodOffset;
}
if (info != NULL) {
return info->m_sound + g_unk0x100f3160;
return info->m_sound + g_plantSoundIdOffset;
}
return 0;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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()) {

View File

@ -76,6 +76,7 @@ void LegoOmni::Init()
m_bkgAudioManager = NULL;
m_unk0x13c = TRUE;
m_transitionManager = NULL;
m_version10 = FALSE;
}
// FUNCTION: LEGO1 0x10058c30

View File

@ -37,7 +37,7 @@ const char* g_strHIT_WALL_SOUND = "HIT_WALL_SOUND";
// GLOBAL: LEGO1 0x100f3308
// GLOBAL: BETA10 0x101f1e1c
MxLong g_unk0x100f3308 = 0;
MxLong g_timeLastHitSoundPlayed = 0;
// FUNCTION: LEGO1 0x1002d700
// FUNCTION: BETA10 0x100ae6e0
@ -292,8 +292,8 @@ MxS32 LegoPathActor::VTable0x8c(float p_time, Matrix4& p_transform)
if (m_boundary == oldBoundary) {
MxLong time = Timer()->GetTime();
if (time - g_unk0x100f3308 > 1000) {
g_unk0x100f3308 = time;
if (time - g_timeLastHitSoundPlayed > 1000) {
g_timeLastHitSoundPlayed = time;
const char* var = VariableTable()->GetVariable(g_strHIT_WALL_SOUND);
if (var && var[0] != 0) {

View File

@ -246,7 +246,7 @@ MxLong CarRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
NavController()->SetDeadZone(NavController()->GetDefaultDeadZone());
NavController()->SetTrackDefault(1);
LegoRaceCar::FUN_10012de0();
LegoRaceCar::InitYouCantStopSound();
m_raceState->m_unk0x28 = 2;
RaceState::Entry* raceState = m_raceState->GetState(GameState()->GetActorId());
@ -348,7 +348,7 @@ MxLong CarRace::HandleClick(LegoEventNotificationParam& p_param)
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
NavController()->SetDeadZone(NavController()->GetDefaultDeadZone());
NavController()->SetTrackDefault(1);
LegoRaceCar::FUN_10012de0();
LegoRaceCar::InitYouCantStopSound();
m_destLocation = LegoGameState::e_infomain;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
GameState()->GetBackgroundColor()->SetValue("reset");
@ -360,7 +360,7 @@ MxLong CarRace::HandleClick(LegoEventNotificationParam& p_param)
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
NavController()->SetDeadZone(NavController()->GetDefaultDeadZone());
NavController()->SetTrackDefault(1);
LegoRaceCar::FUN_10012de0();
LegoRaceCar::InitYouCantStopSound();
m_destLocation = LegoGameState::e_carraceExterior;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
GameState()->GetBackgroundColor()->SetValue("reset");
@ -424,7 +424,7 @@ MxBool CarRace::Escape()
NavController()->SetDeadZone(NavController()->GetDefaultDeadZone());
NavController()->SetTrackDefault(1);
LegoRaceCar::FUN_10012de0();
LegoRaceCar::InitYouCantStopSound();
GameState()->GetBackgroundColor()->SetValue("reset");
m_destLocation = LegoGameState::e_infomain;

View File

@ -132,7 +132,7 @@ MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param)
m_act1State->m_unk0x018 = 0;
VariableTable()->SetVariable(g_raceState, "");
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
LegoRaceCar::FUN_10012de0();
LegoRaceCar::InitYouCantStopSound();
m_destLocation = LegoGameState::e_jetraceExterior;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
break;
@ -140,7 +140,7 @@ MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param)
m_act1State->m_unk0x018 = 0;
VariableTable()->SetVariable(g_raceState, "");
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
LegoRaceCar::FUN_10012de0();
LegoRaceCar::InitYouCantStopSound();
m_destLocation = LegoGameState::e_infomain;
result = 1;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
@ -193,7 +193,7 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
VariableTable()->SetVariable(g_raceState, "");
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
LegoRaceCar::FUN_10012de0();
LegoRaceCar::InitYouCantStopSound();
m_raceState->m_unk0x28 = 2;
RaceState::Entry* raceStateEntry = m_raceState->GetState(GameState()->GetActorId());
@ -294,6 +294,6 @@ MxBool JetskiRace::Escape()
VariableTable()->SetVariable(g_raceState, "");
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
m_destLocation = LegoGameState::e_infomain;
LegoRaceCar::FUN_10012de0();
LegoRaceCar::InitYouCantStopSound();
return TRUE;
}

View File

@ -31,8 +31,8 @@ MxS32 LegoRaceActor::VTable0x68(Vector3& p_v1, Vector3& p_v2, Vector3& p_v3)
if (m_userNavFlag && result) {
MxLong time = Timer()->GetTime();
if (time - g_unk0x100f3308 > 1000) {
g_unk0x100f3308 = time;
if (time - g_timeLastHitSoundPlayed > 1000) {
g_timeLastHitSoundPlayed = time;
const char* soundKey = VariableTable()->GetVariable(g_strHIT_ACTOR_SOUND);
if (soundKey && *soundKey) {

View File

@ -178,7 +178,7 @@ LegoRaceCar::LegoRaceCar()
m_skelKick1Anim = 0;
m_skelKick2Anim = 0;
m_unk0x5c.Clear();
m_unk0x58 = 0;
m_kickStart = 0;
m_kick1B = 0;
m_kick2B = 0;
NotificationManager()->Register(this);
@ -201,10 +201,10 @@ MxLong LegoRaceCar::Notify(MxParam& p_param)
// Initialized at LEGO1 0x10012db0
// GLOBAL: LEGO1 0x10102af0
// GLOBAL: BETA10 0x102114c0
Mx3DPointFloat g_unk0x10102af0 = Mx3DPointFloat(0.0f, 2.0f, 0.0f);
Mx3DPointFloat g_hitOffset = Mx3DPointFloat(0.0f, 2.0f, 0.0f);
// FUNCTION: LEGO1 0x10012de0
void LegoRaceCar::FUN_10012de0()
void LegoRaceCar::InitYouCantStopSound()
{
// Init to TRUE so we don't play "you can't stop in the middle of the race!" before the player ever moves
g_playedYouCantStopSound = TRUE;
@ -229,7 +229,7 @@ void LegoRaceCar::InitSoundIndices()
void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed)
{
if (!m_userNavFlag) {
if (!LegoCarRaceActor::m_unk0x0c) {
if (!LegoCarRaceActor::m_animState) {
m_maxLinearVel = p_worldSpeed;
}
LegoAnimActor::SetWorldSpeed(p_worldSpeed);
@ -244,7 +244,7 @@ void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed)
void LegoRaceCar::SetMaxLinearVelocity(float p_maxLinearVelocity)
{
if (p_maxLinearVelocity < 0) {
LegoCarRaceActor::m_unk0x0c = 2;
LegoCarRaceActor::m_animState = 2;
m_maxLinearVel = 0;
SetWorldSpeed(0);
}
@ -299,7 +299,7 @@ void LegoRaceCar::ParseAction(char* p_extra)
// FUNCTION: LEGO1 0x10012ff0
// FUNCTION: BETA10 0x100cb60e
void LegoRaceCar::FUN_10012ff0(float p_param)
void LegoRaceCar::KickCamera(float p_param)
{
LegoAnimActorStruct* a; // called `a` in BETA10
float deltaTime;
@ -315,7 +315,7 @@ void LegoRaceCar::FUN_10012ff0(float p_param)
assert(a && a->GetAnimTreePtr() && a->GetAnimTreePtr()->GetCamAnim());
if (a->GetAnimTreePtr()) {
deltaTime = p_param - m_unk0x58;
deltaTime = p_param - m_kickStart;
if (a->GetDuration() <= deltaTime || deltaTime < 0.0) {
if (m_userState == LEGORACECAR_KICK1) {
@ -390,7 +390,7 @@ MxU32 LegoRaceCar::HandleSkeletonKicks(float p_param1)
return FALSE;
}
m_unk0x58 = p_param1;
m_kickStart = p_param1;
SoundManager()->GetCacheSoundManager()->Play(g_soundSkel3, NULL, FALSE);
return TRUE;
@ -401,7 +401,7 @@ MxU32 LegoRaceCar::HandleSkeletonKicks(float p_param1)
void LegoRaceCar::Animate(float p_time)
{
if (m_userNavFlag && (m_userState == LEGORACECAR_KICK1 || m_userState == LEGORACECAR_KICK2)) {
FUN_10012ff0(p_time);
KickCamera(p_time);
return;
}
@ -413,7 +413,7 @@ void LegoRaceCar::Animate(float p_time)
}
}
if (LegoCarRaceActor::m_unk0x0c == 1) {
if (LegoCarRaceActor::m_animState == 1) {
FUN_1005d4b0();
if (!m_userNavFlag) {
@ -471,7 +471,7 @@ MxResult LegoRaceCar::HitActor(LegoPathActor* p_actor, MxBool p_bool)
assert(roi);
matr = roi->GetLocal2World();
Vector3(matr[3]) += g_unk0x10102af0;
Vector3(matr[3]) += g_hitOffset;
roi->SetLocal2World(matr);
p_actor->SetActorState(c_two);
@ -516,7 +516,7 @@ MxResult LegoRaceCar::HitActor(LegoPathActor* p_actor, MxBool p_bool)
if (soundKey) {
SoundManager()->GetCacheSoundManager()->Play(soundKey, NULL, FALSE);
g_timeLastRaceCarSoundPlayed = g_unk0x100f3308 = time;
g_timeLastRaceCarSoundPlayed = g_timeLastHitSoundPlayed = time;
}
}
@ -582,7 +582,7 @@ void LegoJetski::InitSoundIndices()
void LegoJetski::SetWorldSpeed(MxFloat p_worldSpeed)
{
if (!m_userNavFlag) {
if (!LegoCarRaceActor::m_unk0x0c) {
if (!LegoCarRaceActor::m_animState) {
m_maxLinearVel = p_worldSpeed;
}
LegoAnimActor::SetWorldSpeed(p_worldSpeed);
@ -597,7 +597,7 @@ void LegoJetski::SetWorldSpeed(MxFloat p_worldSpeed)
void LegoJetski::FUN_100136f0(float p_worldSpeed)
{
if (p_worldSpeed < 0) {
LegoCarRaceActor::m_unk0x0c = 2;
LegoCarRaceActor::m_animState = 2;
m_maxLinearVel = 0;
SetWorldSpeed(0);
}
@ -612,7 +612,7 @@ void LegoJetski::Animate(float p_time)
{
LegoJetskiRaceActor::Animate(p_time);
if (LegoCarRaceActor::m_unk0x0c == 1) {
if (LegoCarRaceActor::m_animState == 1) {
FUN_1005d4b0();
if (!m_userNavFlag) {
@ -685,7 +685,7 @@ MxResult LegoJetski::HitActor(LegoPathActor* p_actor, MxBool p_bool)
LegoROI* roi = p_actor->GetROI();
matr = roi->GetLocal2World();
Vector3(matr[3]) += g_unk0x10102af0;
Vector3(matr[3]) += g_hitOffset;
roi->SetLocal2World(matr);
p_actor->SetActorState(c_two);
@ -714,7 +714,7 @@ MxResult LegoJetski::HitActor(LegoPathActor* p_actor, MxBool p_bool)
if (soundKey) {
SoundManager()->GetCacheSoundManager()->Play(soundKey, NULL, FALSE);
g_timeLastJetskiSoundPlayed = g_unk0x100f3308 = time;
g_timeLastJetskiSoundPlayed = g_timeLastHitSoundPlayed = time;
}
}

View File

@ -45,7 +45,7 @@ LegoCarRaceActor::LegoCarRaceActor()
{
m_unk0x08 = 1.0f;
m_unk0x70 = 0.0f;
m_unk0x0c = 0;
m_animState = 0;
m_maxLinearVel = 0.0f;
m_frequencyFactor = 1.0f;
m_unk0x1c = 0;
@ -224,18 +224,18 @@ void LegoCarRaceActor::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOriente
// FUNCTION: BETA10 0x100cdbae
void LegoCarRaceActor::Animate(float p_time)
{
// m_unk0x0c is not an MxBool, there are places where it is set to 2 or higher
if (m_unk0x0c == 0) {
// m_animState is not an MxBool, there are places where it is set to 2 or higher
if (m_animState == 0) {
const char* value = VariableTable()->GetVariable(g_raceState);
if (SDL_strcasecmp(value, g_racing) == 0) {
m_unk0x0c = 1;
m_animState = 1;
m_lastTime = p_time - 1.0f;
m_unk0x1c = p_time;
}
}
if (m_unk0x0c == 1) {
if (m_animState == 1) {
LegoAnimActor::Animate(p_time);
}
}
@ -399,10 +399,10 @@ MxS32 LegoJetskiRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_
// FUNCTION: LEGO1 0x10081550
void LegoJetskiRaceActor::Animate(float p_time)
{
if (m_unk0x0c == 0) {
if (m_animState == 0) {
const LegoChar* raceState = VariableTable()->GetVariable(g_raceState);
if (!SDL_strcasecmp(raceState, g_racing)) {
m_unk0x0c = 1;
m_animState = 1;
m_lastTime = p_time - 1.0f;
m_unk0x1c = p_time;
}
@ -411,7 +411,7 @@ void LegoJetskiRaceActor::Animate(float p_time)
}
}
if (m_unk0x0c == 1) {
if (m_animState == 1) {
LegoAnimActor::Animate(p_time);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -306,29 +306,32 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param)
if (!m_unk0x1d4) {
PlayMusic(JukeboxScript::c_InformationCenter_Music);
GameState()->SetActor(m_selectedCharacter);
switch (m_selectedCharacter) {
case e_pepper:
PlayAction(InfomainScript::c_avo901in_RunAnim);
break;
case e_mama:
PlayAction(InfomainScript::c_avo902in_RunAnim);
break;
case e_papa:
PlayAction(InfomainScript::c_avo903in_RunAnim);
break;
case e_nick:
PlayAction(InfomainScript::c_avo904in_RunAnim);
break;
case e_laura:
PlayAction(InfomainScript::c_avo905in_RunAnim);
break;
default:
break;
if (!Lego()->IsVersion10()) {
GameState()->SetActor(m_selectedCharacter);
switch (m_selectedCharacter) {
case e_pepper:
PlayAction(InfomainScript::c_avo901in_RunAnim);
break;
case e_mama:
PlayAction(InfomainScript::c_avo902in_RunAnim);
break;
case e_papa:
PlayAction(InfomainScript::c_avo903in_RunAnim);
break;
case e_nick:
PlayAction(InfomainScript::c_avo904in_RunAnim);
break;
case e_laura:
PlayAction(InfomainScript::c_avo905in_RunAnim);
break;
default:
break;
}
UpdateFrameHot(TRUE);
}
UpdateFrameHot(TRUE);
}
}
@ -338,7 +341,7 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param)
return result;
}
if (action->GetObjectId() == InfomainScript::c_iicx26in_RunAnim) {
if (action->GetObjectId() == InfomainScript::c_iicx26in_RunAnim - Lego()->IsVersion10()) {
ControlManager()->FUN_100293c0(InfomainScript::c_BigInfo_Ctl, action->GetAtomId().GetInternal(), 0);
m_unk0x1d6 = 0;
}
@ -478,7 +481,7 @@ void Infocenter::ReadyWorld()
InfomainScript::Script script = m_infocenterState->GetNextReturnDialogue();
PlayAction(script);
if (script == InfomainScript::c_iicx26in_RunAnim) {
if (script == InfomainScript::c_iicx26in_RunAnim - Lego()->IsVersion10()) {
m_unk0x1d6 = 1;
}
@ -1186,13 +1189,13 @@ MxLong Infocenter::HandleNotification0(MxNotificationParam& p_param)
m_currentInfomainScript == InfomainScript::c_Pepper_All_Movie ||
m_currentInfomainScript == InfomainScript::c_Nick_All_Movie ||
m_currentInfomainScript == InfomainScript::c_Laura_All_Movie ||
m_currentInfomainScript == InfomainScript::c_iic007ra_PlayWav ||
m_currentInfomainScript == InfomainScript::c_ijs002ra_PlayWav ||
m_currentInfomainScript == InfomainScript::c_irt001ra_PlayWav ||
m_currentInfomainScript == InfomainScript::c_ipz006ra_PlayWav ||
m_currentInfomainScript == InfomainScript::c_igs004ra_PlayWav ||
m_currentInfomainScript == InfomainScript::c_iho003ra_PlayWav ||
m_currentInfomainScript == InfomainScript::c_ips005ra_PlayWav) {
m_currentInfomainScript == InfomainScript::c_iic007ra_PlayWav - Lego()->IsVersion10() ||
m_currentInfomainScript == InfomainScript::c_ijs002ra_PlayWav - Lego()->IsVersion10() ||
m_currentInfomainScript == InfomainScript::c_irt001ra_PlayWav - Lego()->IsVersion10() ||
m_currentInfomainScript == InfomainScript::c_ipz006ra_PlayWav - Lego()->IsVersion10() ||
m_currentInfomainScript == InfomainScript::c_igs004ra_PlayWav - Lego()->IsVersion10() ||
m_currentInfomainScript == InfomainScript::c_iho003ra_PlayWav - Lego()->IsVersion10() ||
m_currentInfomainScript == InfomainScript::c_ips005ra_PlayWav - Lego()->IsVersion10()) {
StopCurrentAction();
}
}
@ -1506,6 +1509,17 @@ void Infocenter::StopCredits()
// FUNCTION: BETA10 0x1002ee8c
void Infocenter::PlayAction(InfomainScript::Script p_script)
{
if (Lego()->IsVersion10()) {
if (p_script == InfomainScript::c_iicx18in_RunAnim) {
// Alternative dialogue after signing in (1.0 version)
p_script = InfomainScript::c_iic016in_RunAnim;
}
else if (p_script > InfomainScript::c_iicx18in_RunAnim) {
// Shift all other actions by 1
p_script = (InfomainScript::Script)((int) p_script - 1);
}
}
MxDSAction action;
action.SetObjectId(p_script);
action.SetAtomId(*g_infomainScript);

View File

@ -810,7 +810,7 @@ void Isle::Enable(MxBool p_enable)
AnimationManager()->EnableCamAnims(FALSE);
g_isleFlags &= ~c_playMusic;
m_ambulance->FUN_10036e60();
m_ambulance->Init();
break;
case 11:
m_act1state->m_unk0x018 = 0;
@ -1209,7 +1209,7 @@ MxBool Isle::Escape()
case 10:
if (UserActor() != NULL && !UserActor()->IsA("Ambulance")) {
m_ambulance->StopActions();
m_ambulance->FUN_10037250();
m_ambulance->Reset();
}
break;
}
@ -1250,7 +1250,7 @@ void Isle::FUN_10033350()
if (m_act1state->m_unk0x018 == 10) {
if (UserActor() != NULL && !UserActor()->IsA("Ambulance")) {
m_ambulance->StopActions();
m_ambulance->FUN_10037250();
m_ambulance->Reset();
}
}

View File

@ -353,7 +353,7 @@ LegoResult LegoLOD::SetTextureInfo(LegoTextureInfo* p_textureInfo)
}
// FUNCTION: LEGO1 0x100aad70
LegoResult LegoLOD::FUN_100aad70(LegoTextureInfo* p_textureInfo)
LegoResult LegoLOD::UpdateTextureInfo(LegoTextureInfo* p_textureInfo)
{
for (LegoU32 i = m_meshOffset; i < m_numMeshes; i++) {
if (m_melems[i].m_textured) {

View File

@ -31,7 +31,7 @@ class LegoLOD : public ViewLOD {
LegoLOD* Clone(Tgl::Renderer* p_renderer);
LegoResult SetColor(LegoFloat p_red, LegoFloat p_green, LegoFloat p_blue, LegoFloat p_alpha);
LegoResult SetTextureInfo(LegoTextureInfo* p_textureInfo);
LegoResult FUN_100aad70(LegoTextureInfo* p_textureInfo);
LegoResult UpdateTextureInfo(LegoTextureInfo* p_textureInfo);
void ClearMeshOffset();
LegoResult GetTextureInfo(LegoTextureInfo*& p_textureInfo);

View File

@ -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));

View File

@ -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

View File

@ -20,10 +20,10 @@ class MxString : public MxCore {
void ToLowerCase();
void MapPathToFilesystem() { MapPathToFilesystem(m_data); }
MxString& operator=(const MxString& p_str);
const MxString& operator=(const char* p_str);
MxString operator+(const MxString& p_str) const;
MxString operator+(const char* p_str) const;
LEGO1_EXPORT MxString& operator=(const MxString& p_str);
LEGO1_EXPORT const MxString& operator=(const char* p_str);
LEGO1_EXPORT MxString operator+(const MxString& p_str) const;
LEGO1_EXPORT MxString operator+(const char* p_str) const;
LEGO1_EXPORT MxString& operator+=(const char* p_str);
static void CharSwap(char* p_a, char* p_b);

View File

@ -5,7 +5,11 @@
#include "mxsemaphore.h"
#include "mxtypes.h"
#ifdef PSP
#include <pspkerneltypes.h>
#else
#include <SDL3/SDL_thread.h>
#endif
class MxCore;
@ -33,9 +37,15 @@ class MxThread {
virtual ~MxThread();
private:
#ifdef PSP
static int ThreadProc(SceSize args, void* argp);
int m_thread;
#else
static int SDLCALL ThreadProc(void* p_thread);
SDL_Thread* m_thread;
#endif
MxBool m_running; // 0x0c
MxSemaphore m_semaphore; // 0x10

View File

@ -3,13 +3,20 @@
#include "decomp.h"
#include <SDL3/SDL_timer.h>
#ifdef PSP
#include <pspkernel.h>
#endif
DECOMP_SIZE_ASSERT(MxThread, 0x1c)
// FUNCTION: LEGO1 0x100bf510
MxThread::MxThread()
{
#ifdef PSP
m_thread = 0;
#else
m_thread = NULL;
#endif
m_running = TRUE;
}
@ -17,7 +24,13 @@ MxThread::MxThread()
MxThread::~MxThread()
{
if (m_thread) {
#ifdef PSP
sceKernelWaitThreadEnd(m_thread, NULL);
sceKernelDeleteThread(m_thread);
m_thread = 0;
#else
SDL_WaitThread(m_thread, NULL);
#endif
}
}
@ -27,6 +40,25 @@ MxResult MxThread::Start(MxS32 p_stackSize, MxS32 p_flag)
MxResult result = FAILURE;
if (m_semaphore.Init(0, 1) == SUCCESS) {
#ifdef PSP
int thid = sceKernelCreateThread(
"MxThread",
ThreadProc,
0x18, // priority (0x18 is typical)
p_stackSize * 4,
0,
NULL
);
if (thid >= 0) {
MxThread* self = this;
int start = sceKernelStartThread(thid, sizeof(MxThread*), &self);
if (start >= 0) {
result = SUCCESS;
m_thread = thid; // store thread ID if needed
}
}
#else
const SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void*) MxThread::ThreadProc);
SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, this);
@ -37,6 +69,7 @@ MxResult MxThread::Start(MxS32 p_stackSize, MxS32 p_flag)
}
SDL_DestroyProperties(props);
#endif
}
return result;
@ -55,11 +88,19 @@ void MxThread::Terminate()
m_semaphore.Wait();
}
#ifdef PSP
int MxThread::ThreadProc(SceSize args, void* argp)
{
MxThread* self = *(MxThread**) argp;
return self->Run();
}
#else
// FUNCTION: LEGO1 0x100bf680
int MxThread::ThreadProc(void* p_thread)
{
return static_cast<MxThread*>(p_thread)->Run();
}
#endif
// FUNCTION: LEGO1 0x100bf690
MxResult MxThread::Run()

View File

@ -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);
}
}

View File

@ -16,6 +16,7 @@ Please note: this project is dedicated to achieving platform independence withou
| Linux | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
| macOS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
| [Web](https://isle.pizza) | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
| Nintendo 3DS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) |
We are actively working to support more platforms. If you have experience with a particular platform, we encourage you to contribute to `isle-portable`. You can find a [list of ongoing efforts](https://github.com/isledecomp/isle-portable/wiki/Work%E2%80%90in%E2%80%90progress-ports) in our Wiki.

BIN
XMB/Game/ICON0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
XMB/Game/ICON1.pmf Normal file

Binary file not shown.

BIN
XMB/Game/PIC1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
XMB/Game/SND0.at3 Normal file

Binary file not shown.

BIN
XMB/Save/EMPTY.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
XMB/Save/ICON0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
XMB/Save/ICON1.pmf Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
The model was sourced from https://3dwarehouse.sketchup.com/model/c300a598e63cb2f593dcf5e22ecc3f5e/Memory-Stick-Pro-Duo. It was further modified by Sonen10 to get the results seen in the animation.

BIN
XMB/Save/PIC1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
XMB/Save/SND0.AT3 Normal file

Binary file not shown.

10
XMB/explanation.txt Normal file
View File

@ -0,0 +1,10 @@
Explanation of where the assets in these folders are used
ICON0.PNG - Icon used in various scenarios depending on if it's used for a save or a game. This includes when:
> The game/save isn't selected in the XMB
> There is no ICON1.PMF present (XMB only)
> The "Group Content" mode is enabled in the XMB game selection menu
> The game is on a save management screen with UI elements shared with the XMB (note the font)
ICON1.PMF - Seen when the game/save is selected in the XMB (NOT IN-GAME)
PIC1.PNG - Seen as a background when the game/save is selected in the XMB
SND0.AT3 - Plays when the game/save is selected in the XMB (NOT IN-GAME)
EMPTY.PNG - Used as an icon for empty saves when selecting a save to write to.

156
cmake/detectcpu.cmake Normal file
View 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()

View File

@ -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

View 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();
}

View 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

View File

@ -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();

View File

@ -0,0 +1,369 @@
// This file cannot include any minwin headers.
#include "actual.h"
#include "structs.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_opengl.h>
#include <algorithm>
#include <cstring>
#include <vector>
// GL extension API functions.
bool g_useVBOs;
PFNGLGENBUFFERSPROC mwglGenBuffers;
PFNGLBINDBUFFERPROC mwglBindBuffer;
PFNGLBUFFERDATAPROC mwglBufferData;
PFNGLDELETEBUFFERSPROC mwglDeleteBuffers;
void GL11_InitState()
{
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);
}
void GL11_LoadExtensions()
{
g_useVBOs = SDL_GL_ExtensionSupported("GL_ARB_vertex_buffer_object");
if (g_useVBOs) {
// Load the required GL function pointers.
mwglGenBuffers = (PFNGLGENBUFFERSPROC) SDL_GL_GetProcAddress("glGenBuffersARB");
mwglBindBuffer = (PFNGLBINDBUFFERPROC) SDL_GL_GetProcAddress("glBindBufferARB");
mwglBufferData = (PFNGLBUFFERDATAPROC) SDL_GL_GetProcAddress("glBufferDataARB");
mwglDeleteBuffers = (PFNGLDELETEBUFFERSPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB");
}
}
void GL11_DestroyTexture(GLuint texId)
{
glDeleteTextures(1, &texId);
}
GLuint GL11_UploadTextureData(void* pixels, int width, int height, bool isUi)
{
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
if (isUi) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
return texId;
}
void GL11_UploadMesh(GLMeshCacheEntry& cache, bool hasTexture)
{
if (g_useVBOs) {
mwglGenBuffers(1, &cache.vboPositions);
mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboPositions);
mwglBufferData(
GL_ARRAY_BUFFER_ARB,
cache.positions.size() * sizeof(GL11_BridgeVector),
cache.positions.data(),
GL_STATIC_DRAW_ARB
);
mwglGenBuffers(1, &cache.vboNormals);
mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboNormals);
mwglBufferData(
GL_ARRAY_BUFFER_ARB,
cache.normals.size() * sizeof(GL11_BridgeVector),
cache.normals.data(),
GL_STATIC_DRAW_ARB
);
if (hasTexture) {
mwglGenBuffers(1, &cache.vboTexcoords);
mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboTexcoords);
mwglBufferData(
GL_ARRAY_BUFFER_ARB,
cache.texcoords.size() * sizeof(GL11_BridgeTexCoord),
cache.texcoords.data(),
GL_STATIC_DRAW_ARB
);
}
mwglGenBuffers(1, &cache.ibo);
mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, cache.ibo);
mwglBufferData(
GL_ELEMENT_ARRAY_BUFFER_ARB,
cache.indices.size() * sizeof(cache.indices[0]),
cache.indices.data(),
GL_STATIC_DRAW_ARB
);
}
}
void GL11_DestroyMesh(GLMeshCacheEntry& cache)
{
if (g_useVBOs) {
mwglDeleteBuffers(1, &cache.vboPositions);
mwglDeleteBuffers(1, &cache.vboNormals);
mwglDeleteBuffers(1, &cache.vboTexcoords);
mwglDeleteBuffers(1, &cache.ibo);
}
}
void GL11_BeginFrame(const Matrix4x4* projection)
{
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
// Disable all lights and reset global ambient
for (int i = 0; i < 8; ++i) {
glDisable(GL_LIGHT0 + i);
}
const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroAmbient);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
// Projection and view
glMatrixMode(GL_PROJECTION);
glLoadMatrixf((const GLfloat*) projection);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void GL11_UploadLight(int lightIdx, GL11_BridgeSceneLight* l)
{
// Setup light
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
GLenum lightId = GL_LIGHT0 + lightIdx++;
const FColor& c = l->color;
GLfloat col[4] = {c.r, c.g, c.b, c.a};
const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f};
if (l->positional == 0.f && l->directional == 0.f) {
// Ambient light only
glLightfv(lightId, GL_AMBIENT, col);
const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f};
glLightfv(lightId, GL_DIFFUSE, black);
glLightfv(lightId, GL_SPECULAR, black);
const GLfloat dummyPos[4] = {0.f, 0.f, 1.f, 0.f};
glLightfv(lightId, GL_POSITION, dummyPos);
}
else {
glLightfv(lightId, GL_AMBIENT, zeroAmbient);
glLightfv(lightId, GL_DIFFUSE, col);
if (l->directional == 1.0f) {
glLightfv(lightId, GL_SPECULAR, col);
}
else {
const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f};
glLightfv(lightId, GL_SPECULAR, black);
}
GLfloat pos[4];
if (l->directional == 1.f) {
pos[0] = -l->direction.x;
pos[1] = -l->direction.y;
pos[2] = -l->direction.z;
pos[3] = 0.f;
}
else {
pos[0] = l->position.x;
pos[1] = l->position.y;
pos[2] = l->position.z;
pos[3] = 1.f;
}
glLightfv(lightId, GL_POSITION, pos);
}
glEnable(lightId);
glPopMatrix();
}
void GL11_EnableTransparency()
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
}
#define NO_TEXTURE_ID 0xffffffff
void GL11_SubmitDraw(
GLMeshCacheEntry& mesh,
const Matrix4x4& modelViewMatrix,
const Appearance& appearance,
GLuint texId
)
{
glLoadMatrixf(&modelViewMatrix[0][0]);
glEnable(GL_NORMALIZE);
#ifndef __PSP__
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glColor4ub(appearance.color.r, appearance.color.g, appearance.color.b, appearance.color.a);
#else
GLfloat color[4] = {
appearance.color.r / 255.0f,
appearance.color.g / 255.0f,
appearance.color.b / 255.0f,
appearance.color.a / 255.0f
};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
#endif
if (appearance.shininess != 0.0f) {
GLfloat whiteSpec[] = {1.f, 1.f, 1.f, 1.f};
glMaterialfv(GL_FRONT, GL_SPECULAR, whiteSpec);
glMaterialf(GL_FRONT, GL_SHININESS, appearance.shininess);
}
else {
GLfloat noSpec[] = {0.0f, 0.0f, 0.0f, 0.0f};
glMaterialfv(GL_FRONT, GL_SPECULAR, noSpec);
glMaterialf(GL_FRONT, GL_SHININESS, 0.0f);
}
if (mesh.flat) {
glShadeModel(GL_FLAT);
}
else {
glShadeModel(GL_SMOOTH);
}
// Bind texture if present
if (appearance.textureId != NO_TEXTURE_ID) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texId);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
else {
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
if (g_useVBOs) {
mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboPositions);
glVertexPointer(3, GL_FLOAT, 0, nullptr);
mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboNormals);
glNormalPointer(GL_FLOAT, 0, nullptr);
if (appearance.textureId != NO_TEXTURE_ID) {
mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboTexcoords);
glTexCoordPointer(2, GL_FLOAT, 0, nullptr);
}
mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, mesh.ibo);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr);
mwglBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
}
else {
glVertexPointer(3, GL_FLOAT, 0, mesh.positions.data());
glNormalPointer(GL_FLOAT, 0, mesh.normals.data());
if (appearance.textureId != NO_TEXTURE_ID) {
glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords.data());
}
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_SHORT, mesh.indices.data());
}
glPopMatrix();
}
void GL11_Resize(int width, int height)
{
glViewport(0, 0, width, height);
}
void GL11_Clear(float r, float g, float b)
{
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glClearColor(r, g, b, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void GL11_Draw2DImage(
GLTextureCacheEntry& cache,
const SDL_Rect& srcRect,
const SDL_Rect& dstRect,
float left,
float right,
float bottom,
float top
)
{
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(left, right, bottom, top, -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glDisable(GL_LIGHTING);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, cache.glTextureId);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLint boundTexture = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
float u1 = srcRect.x / cache.width;
float v1 = srcRect.y / cache.height;
float u2 = (srcRect.x + srcRect.w) / cache.width;
float v2 = (srcRect.y + srcRect.h) / cache.height;
float x1 = (float) dstRect.x;
float y1 = (float) dstRect.y;
float x2 = x1 + dstRect.w;
float y2 = y1 + dstRect.h;
glBegin(GL_QUADS);
glTexCoord2f(u1, v1);
glVertex2f(x1, y1);
glTexCoord2f(u2, v1);
glVertex2f(x2, y1);
glTexCoord2f(u2, v2);
glVertex2f(x2, y2);
glTexCoord2f(u1, v2);
glVertex2f(x1, y2);
glEnd();
// Restore state
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
}
void GL11_Download(SDL_Surface* target)
{
glFinish();
glReadPixels(0, 0, target->w, target->h, GL_RGBA, GL_UNSIGNED_BYTE, target->pixels);
}

View 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);

View File

@ -1,5 +1,4 @@
#include <GL/glew.h>
// must come after GLEW
#include "actual.h"
#include "d3drmrenderer_opengl1.h"
#include "ddraw_impl.h"
#include "ddsurface_impl.h"
@ -11,58 +10,44 @@
#include <cstring>
#include <vector>
static_assert(sizeof(Matrix4x4) == sizeof(D3DRMMATRIX4D), "Matrix4x4 is wrong size");
static_assert(sizeof(GL11_BridgeVector) == sizeof(D3DVECTOR), "GL11_BridgeVector is wrong size");
static_assert(sizeof(GL11_BridgeTexCoord) == sizeof(TexCoord), "GL11_BridgeTexCoord is wrong size");
static_assert(sizeof(GL11_BridgeSceneLight) == sizeof(SceneLight), "GL11_BridgeSceneLight is wrong size");
static_assert(sizeof(GL11_BridgeSceneVertex) == sizeof(D3DRMVERTEX), "GL11_BridgeSceneVertex is wrong size");
Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height)
{
// We have to reset the attributes here after having enumerated the
// OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE
// call below when on an EGL-based backend, and crashes with EGL_BAD_MATCH.
SDL_GL_ResetAttributes();
// But ResetAttributes resets it to 16.
#ifndef __PSP__
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
#endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
SDL_Window* window = DDWindow;
bool testWindow = false;
if (!window) {
window = SDL_CreateWindow("OpenGL 1.1 test", width, height, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
if (!window) {
SDL_Log("SDL_CreateWindow: %s", SDL_GetError());
return nullptr;
}
testWindow = true;
if (!DDWindow) {
SDL_Log("No window handler");
return nullptr;
}
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GLContext context = SDL_GL_CreateContext(window);
SDL_GLContext context = SDL_GL_CreateContext(DDWindow);
if (!context) {
SDL_Log("SDL_GL_CreateContext: %s", SDL_GetError());
if (testWindow) {
SDL_DestroyWindow(window);
}
return nullptr;
}
if (!SDL_GL_MakeCurrent(window, context)) {
if (!SDL_GL_MakeCurrent(DDWindow, context)) {
SDL_GL_DestroyContext(context);
SDL_Log("SDL_GL_MakeCurrent: %s", SDL_GetError());
if (testWindow) {
SDL_DestroyWindow(window);
}
return nullptr;
}
GLenum err = glewInit();
if (err != GLEW_OK) {
SDL_Log("glewInit: %s", glewGetErrorString(err));
if (testWindow) {
SDL_DestroyWindow(window);
}
return nullptr;
}
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);
if (testWindow) {
SDL_DestroyWindow(window);
}
GL11_InitState();
return new OpenGL1Renderer(width, height, context);
}
@ -74,12 +59,15 @@ OpenGL1Renderer::OpenGL1Renderer(DWORD width, DWORD height, SDL_GLContext contex
m_virtualWidth = width;
m_virtualHeight = height;
m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32);
m_useVBOs = GLEW_ARB_vertex_buffer_object;
GL11_LoadExtensions();
m_useVBOs = SDL_GL_ExtensionSupported("GL_ARB_vertex_buffer_object");
m_useNPOT = SDL_GL_ExtensionSupported("GL_OES_texture_npot");
}
OpenGL1Renderer::~OpenGL1Renderer()
{
SDL_DestroySurface(m_renderedImage);
SDL_GL_DestroyContext(m_context);
}
void OpenGL1Renderer::PushLights(const SceneLight* lightsArray, size_t count)
@ -114,7 +102,7 @@ void OpenGL1Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* t
auto* ctx = static_cast<TextureDestroyContextGL*>(arg);
auto& cache = ctx->renderer->m_textures[ctx->textureId];
if (cache.glTextureId != 0) {
glDeleteTextures(1, &cache.glTextureId);
GL11_DestroyTexture(cache.glTextureId);
cache.glTextureId = 0;
cache.texture = nullptr;
}
@ -124,7 +112,60 @@ void OpenGL1Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* t
);
}
Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture)
static int NextPowerOfTwo(int v)
{
int power = 1;
while (power < v) {
power <<= 1;
}
return power;
}
static Uint32 UploadTextureData(SDL_Surface* src, bool useNPOT, bool isUi)
{
SDL_Surface* working = src;
if (src->format != SDL_PIXELFORMAT_RGBA32) {
working = SDL_ConvertSurface(src, SDL_PIXELFORMAT_RGBA32);
if (!working) {
SDL_Log("SDL_ConvertSurface failed: %s", SDL_GetError());
return NO_TEXTURE_ID;
}
}
SDL_Surface* finalSurface = working;
// Resize to next power-of-two if needed and NPOT isn't supported
int newW = NextPowerOfTwo(working->w);
int newH = NextPowerOfTwo(working->h);
if (!useNPOT && (newW != working->w || newH != working->h)) {
SDL_Surface* resized = SDL_CreateSurface(newW, newH, working->format);
if (!resized) {
SDL_Log("SDL_CreateSurface (resize) failed: %s", SDL_GetError());
if (working != src) {
SDL_DestroySurface(working);
}
return NO_TEXTURE_ID;
}
SDL_Rect srcRect = {0, 0, working->w, working->h};
SDL_Rect dstRect = {0, 0, newW, newH};
SDL_BlitSurfaceScaled(working, &srcRect, resized, &dstRect, SDL_SCALEMODE_NEAREST);
if (working != src) {
SDL_DestroySurface(working);
}
finalSurface = resized;
}
Uint32 texId = GL11_UploadTextureData(finalSurface->pixels, finalSurface->w, finalSurface->h, isUi);
if (finalSurface != src) {
SDL_DestroySurface(finalSurface);
}
return texId;
}
Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi)
{
auto texture = static_cast<Direct3DRMTextureImpl*>(iTexture);
auto surface = static_cast<DirectDrawSurfaceImpl*>(texture->m_surface);
@ -133,33 +174,17 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture)
auto& tex = m_textures[i];
if (tex.texture == texture) {
if (tex.version != texture->m_version) {
glDeleteTextures(1, &tex.glTextureId);
glGenTextures(1, &tex.glTextureId);
glBindTexture(GL_TEXTURE_2D, tex.glTextureId);
SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32);
if (!surf) {
return NO_TEXTURE_ID;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels);
SDL_DestroySurface(surf);
GL11_DestroyTexture(tex.glTextureId);
tex.glTextureId = UploadTextureData(surface->m_surface, m_useNPOT, isUi);
tex.version = texture->m_version;
tex.width = surface->m_surface->w;
tex.height = surface->m_surface->h;
}
return i;
}
}
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32);
if (!surf) {
return NO_TEXTURE_ID;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels);
SDL_DestroySurface(surf);
GLuint texId = UploadTextureData(surface->m_surface, m_useNPOT, isUi);
for (Uint32 i = 0; i < m_textures.size(); ++i) {
auto& tex = m_textures[i];
@ -167,12 +192,14 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture)
tex.texture = texture;
tex.version = texture->m_version;
tex.glTextureId = texId;
tex.width = surface->m_surface->w;
tex.height = surface->m_surface->h;
AddTextureDestroyCallback(i, texture);
return i;
}
}
m_textures.push_back({texture, texture->m_version, texId});
m_textures.push_back({texture, texture->m_version, texId, surface->m_surface->w, surface->m_surface->h});
AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture);
return (Uint32) (m_textures.size() - 1);
}
@ -206,52 +233,19 @@ GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup, bool useVBOs)
if (meshGroup.texture) {
cache.texcoords.resize(vertices.size());
std::transform(vertices.begin(), vertices.end(), cache.texcoords.begin(), [](const D3DRMVERTEX& v) {
return v.texCoord;
return GL11_BridgeTexCoord{v.texCoord.u, v.texCoord.v};
});
}
cache.positions.resize(vertices.size());
std::transform(vertices.begin(), vertices.end(), cache.positions.begin(), [](const D3DRMVERTEX& v) {
return v.position;
return GL11_BridgeVector{v.position.x, v.position.y, v.position.z};
});
cache.normals.resize(vertices.size());
std::transform(vertices.begin(), vertices.end(), cache.normals.begin(), [](const D3DRMVERTEX& v) {
return v.normal;
return GL11_BridgeVector{v.normal.x, v.normal.y, v.normal.z};
});
if (useVBOs) {
glGenBuffers(1, &cache.vboPositions);
glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions);
glBufferData(
GL_ARRAY_BUFFER,
cache.positions.size() * sizeof(D3DVECTOR),
cache.positions.data(),
GL_STATIC_DRAW
);
glGenBuffers(1, &cache.vboNormals);
glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals);
glBufferData(GL_ARRAY_BUFFER, cache.normals.size() * sizeof(D3DVECTOR), cache.normals.data(), GL_STATIC_DRAW);
if (meshGroup.texture) {
glGenBuffers(1, &cache.vboTexcoords);
glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords);
glBufferData(
GL_ARRAY_BUFFER,
cache.texcoords.size() * sizeof(TexCoord),
cache.texcoords.data(),
GL_STATIC_DRAW
);
}
glGenBuffers(1, &cache.ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache.ibo);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
cache.indices.size() * sizeof(cache.indices[0]),
cache.indices.data(),
GL_STATIC_DRAW
);
}
GL11_UploadMesh(cache, meshGroup.texture != nullptr);
return cache;
}
@ -269,12 +263,7 @@ void OpenGL1Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh)
auto* ctx = static_cast<GLMeshDestroyContext*>(arg);
auto& cache = ctx->renderer->m_meshs[ctx->id];
cache.meshGroup = nullptr;
if (ctx->renderer->m_useVBOs) {
glDeleteBuffers(1, &cache.vboPositions);
glDeleteBuffers(1, &cache.vboNormals);
glDeleteBuffers(1, &cache.vboTexcoords);
glDeleteBuffers(1, &cache.ibo);
}
GL11_DestroyMesh(cache);
delete ctx;
},
ctx
@ -309,110 +298,25 @@ Uint32 OpenGL1Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGr
return (Uint32) (m_meshs.size() - 1);
}
void OpenGL1Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc)
{
halDesc->dcmColorModel = D3DCOLORMODEL::RGB;
halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH;
halDesc->dwDeviceZBufferBitDepth = DDBD_24;
helDesc->dwDeviceRenderBitDepth = DDBD_32;
halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE;
halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND;
halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR;
memset(helDesc, 0, sizeof(D3DDEVICEDESC));
}
const char* OpenGL1Renderer::GetName()
{
return "OpenGL 1.1 HAL";
}
HRESULT OpenGL1Renderer::BeginFrame()
{
m_dirty = true;
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// Disable all lights and reset global ambient
for (int i = 0; i < 8; ++i) {
glDisable(GL_LIGHT0 + i);
}
const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroAmbient);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
// Setup lights
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
GL11_BeginFrame((Matrix4x4*) &m_projection[0][0]);
int lightIdx = 0;
for (const auto& l : m_lights) {
if (lightIdx > 7) {
break;
}
GLenum lightId = GL_LIGHT0 + lightIdx++;
const FColor& c = l.color;
GLfloat col[4] = {c.r, c.g, c.b, c.a};
GL11_UploadLight(lightIdx, (GL11_BridgeSceneLight*) &l);
if (l.positional == 0.f && l.directional == 0.f) {
// Ambient light only
glLightfv(lightId, GL_AMBIENT, col);
const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f};
glLightfv(lightId, GL_DIFFUSE, black);
glLightfv(lightId, GL_SPECULAR, black);
const GLfloat dummyPos[4] = {0.f, 0.f, 1.f, 0.f};
glLightfv(lightId, GL_POSITION, dummyPos);
}
else {
glLightfv(lightId, GL_AMBIENT, zeroAmbient);
glLightfv(lightId, GL_DIFFUSE, col);
if (l.directional == 1.0f) {
glLightfv(lightId, GL_SPECULAR, col);
}
else {
const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f};
glLightfv(lightId, GL_SPECULAR, black);
}
GLfloat pos[4];
if (l.directional == 1.f) {
pos[0] = -l.direction.x;
pos[1] = -l.direction.y;
pos[2] = -l.direction.z;
pos[3] = 0.f;
}
else {
pos[0] = l.position.x;
pos[1] = l.position.y;
pos[2] = l.position.z;
pos[3] = 1.f;
}
glLightfv(lightId, GL_POSITION, pos);
}
glEnable(lightId);
lightIdx++;
}
glPopMatrix();
// Projection and view
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&m_projection[0][0]);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return DD_OK;
}
void OpenGL1Renderer::EnableTransparency()
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
GL11_EnableTransparency();
}
void OpenGL1Renderer::SubmitDraw(
@ -426,75 +330,14 @@ void OpenGL1Renderer::SubmitDraw(
{
auto& mesh = m_meshs[meshId];
glLoadMatrixf(&modelViewMatrix[0][0]);
glEnable(GL_NORMALIZE);
glColor4ub(appearance.color.r, appearance.color.g, appearance.color.b, appearance.color.a);
if (appearance.shininess != 0.0f) {
GLfloat whiteSpec[] = {1.f, 1.f, 1.f, 1.f};
glMaterialfv(GL_FRONT, GL_SPECULAR, whiteSpec);
glMaterialf(GL_FRONT, GL_SHININESS, appearance.shininess);
}
else {
GLfloat noSpec[] = {0.0f, 0.0f, 0.0f, 0.0f};
glMaterialfv(GL_FRONT, GL_SPECULAR, noSpec);
glMaterialf(GL_FRONT, GL_SHININESS, 0.0f);
}
if (mesh.flat) {
glShadeModel(GL_FLAT);
}
else {
glShadeModel(GL_SMOOTH);
}
// Bind texture if present
if (appearance.textureId != NO_TEXTURE_ID) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
auto& tex = m_textures[appearance.textureId];
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex.glTextureId);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
GL11_SubmitDraw(mesh, modelViewMatrix, appearance, tex.glTextureId);
}
else {
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
GL11_SubmitDraw(mesh, modelViewMatrix, appearance, 0);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
if (m_useVBOs) {
glBindBuffer(GL_ARRAY_BUFFER, mesh.vboPositions);
glVertexPointer(3, GL_FLOAT, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, mesh.vboNormals);
glNormalPointer(GL_FLOAT, 0, nullptr);
if (appearance.textureId != NO_TEXTURE_ID) {
glBindBuffer(GL_ARRAY_BUFFER, mesh.vboTexcoords);
glTexCoordPointer(2, GL_FLOAT, 0, nullptr);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.ibo);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
else {
glVertexPointer(3, GL_FLOAT, 0, mesh.positions.data());
glNormalPointer(GL_FLOAT, 0, mesh.normals.data());
if (appearance.textureId != NO_TEXTURE_ID) {
glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords.data());
}
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_SHORT, mesh.indices.data());
}
glPopMatrix();
}
HRESULT OpenGL1Renderer::FinalizeFrame()
@ -509,16 +352,13 @@ void OpenGL1Renderer::Resize(int width, int height, const ViewportTransform& vie
m_viewportTransform = viewportTransform;
SDL_DestroySurface(m_renderedImage);
m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32);
glViewport(0, 0, m_width, m_height);
GL11_Resize(width, height);
}
void OpenGL1Renderer::Clear(float r, float g, float b)
{
m_dirty = true;
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glClearColor(r, g, b, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GL11_Clear(r, g, b);
}
void OpenGL1Renderer::Flip()
@ -532,73 +372,18 @@ void OpenGL1Renderer::Flip()
void OpenGL1Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect)
{
m_dirty = true;
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
float left = -m_viewportTransform.offsetX / m_viewportTransform.scale;
float right = (m_width - m_viewportTransform.offsetX) / m_viewportTransform.scale;
float top = -m_viewportTransform.offsetY / m_viewportTransform.scale;
float bottom = (m_height - m_viewportTransform.offsetY) / m_viewportTransform.scale;
glOrtho(left, right, bottom, top, -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glDisable(GL_LIGHTING);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_textures[textureId].glTextureId);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLint boundTexture = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
GLfloat texW, texH;
glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texW);
glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texH);
float u1 = srcRect.x / texW;
float v1 = srcRect.y / texH;
float u2 = (srcRect.x + srcRect.w) / texW;
float v2 = (srcRect.y + srcRect.h) / texH;
float x1 = (float) dstRect.x;
float y1 = (float) dstRect.y;
float x2 = x1 + dstRect.w;
float y2 = y1 + dstRect.h;
glBegin(GL_QUADS);
glTexCoord2f(u1, v1);
glVertex2f(x1, y1);
glTexCoord2f(u2, v1);
glVertex2f(x2, y1);
glTexCoord2f(u2, v2);
glVertex2f(x2, y2);
glTexCoord2f(u1, v2);
glVertex2f(x1, y2);
glEnd();
// Restore state
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
GL11_Draw2DImage(m_textures[textureId], srcRect, dstRect, left, right, bottom, top);
}
void OpenGL1Renderer::Download(SDL_Surface* target)
{
glFinish();
glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels);
GL11_Download(m_renderedImage);
SDL_Rect srcRect = {
static_cast<int>(m_viewportTransform.offsetX),

View File

@ -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)

View File

@ -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) {

View File

@ -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)) {

View File

@ -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);

View File

@ -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++) {

View File

@ -4,6 +4,9 @@
#ifdef USE_OPENGLES2
#include "d3drmrenderer_opengles2.h"
#endif
#ifdef __3DS__
#include "d3drmrenderer_citro3d.h"
#endif
#ifdef _WIN32
#include "d3drmrenderer_directx9.h"
#endif
@ -18,6 +21,7 @@
#include <SDL3/SDL.h>
#include <assert.h>
#include <cinttypes>
#include <cstdint>
#include <cstdlib>
#include <cstring>
@ -75,7 +79,7 @@ HRESULT DirectDrawImpl::CreateSurface(
if ((lpDDSurfaceDesc->dwFlags & DDSD_ZBUFFERBITDEPTH) != DDSD_ZBUFFERBITDEPTH) {
return DDERR_INVALIDPARAMS;
}
SDL_Log("Todo: Set %dbit Z-Buffer", lpDDSurfaceDesc->dwZBufferBitDepth);
SDL_Log("Todo: Set %" PRIu32 "bit Z-Buffer", lpDDSurfaceDesc->dwZBufferBitDepth);
*lplpDDSurface = static_cast<IDirectDrawSurface*>(new DummySurfaceImpl);
return DD_OK;
}
@ -206,14 +210,18 @@ HRESULT DirectDrawImpl::GetCaps(LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps)
return S_OK;
}
void EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx, Direct3DRMRenderer* device, GUID deviceGuid)
void EnumDevice(
LPD3DENUMDEVICESCALLBACK cb,
void* ctx,
const char* name,
D3DDEVICEDESC* halDesc,
D3DDEVICEDESC* helDesc,
GUID deviceGuid
)
{
D3DDEVICEDESC halDesc = {};
D3DDEVICEDESC helDesc = {};
device->GetDesc(&halDesc, &helDesc);
char* deviceNameDup = SDL_strdup(device->GetName());
char* deviceNameDup = SDL_strdup(name);
char* deviceDescDup = SDL_strdup("Miniwin driver");
cb(&deviceGuid, deviceNameDup, deviceDescDup, &halDesc, &helDesc, ctx);
cb(&deviceGuid, deviceNameDup, deviceDescDup, halDesc, helDesc, ctx);
SDL_free(deviceDescDup);
SDL_free(deviceNameDup);
}
@ -227,6 +235,9 @@ HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
#ifdef USE_OPENGL1
OpenGL1Renderer_EnumDevice(cb, ctx);
#endif
#ifdef __3DS__
Citro3DRenderer_EnumDevice(cb, ctx);
#endif
#ifdef _WIN32
DirectX9Renderer_EnumDevice(cb, ctx);
#endif
@ -341,6 +352,11 @@ HRESULT DirectDrawImpl::CreateDevice(
DDRenderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
}
#endif
#ifdef __3DS__
else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) {
DDRenderer = new Citro3DRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight);
}
#endif
#ifdef _WIN32
else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) {
DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);

View File

@ -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 =

View File

@ -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(

View 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);
}

View File

@ -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);
}

View File

@ -1,44 +1,14 @@
#pragma once
#include "../d3drm/backends/opengl1/actual.h"
#include "d3drmrenderer.h"
#include "d3drmtexture_impl.h"
#include "ddraw_impl.h"
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include <SDL3/SDL.h>
#include <vector>
DEFINE_GUID(OpenGL1_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03);
struct GLTextureCacheEntry {
IDirect3DRMTexture* texture;
Uint32 version;
GLuint glTextureId;
};
struct GLMeshCacheEntry {
const MeshGroup* meshGroup;
int version;
bool flat;
// non-VBO cache
std::vector<D3DVECTOR> positions;
std::vector<D3DVECTOR> normals;
std::vector<TexCoord> texcoords;
std::vector<uint16_t> indices;
// VBO cache
GLuint vboPositions;
GLuint vboNormals;
GLuint vboTexcoords;
GLuint ibo;
};
class OpenGL1Renderer : public Direct3DRMRenderer {
public:
static Direct3DRMRenderer* Create(DWORD width, DWORD height);
@ -48,10 +18,8 @@ class OpenGL1Renderer : public Direct3DRMRenderer {
void PushLights(const SceneLight* lightsArray, size_t count) override;
void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override;
void SetFrustumPlanes(const Plane* frustumPlanes) override;
Uint32 GetTextureId(IDirect3DRMTexture* texture) override;
Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi) override;
Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override;
void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override;
const char* GetName() override;
HRESULT BeginFrame() override;
void EnableTransparency() override;
void SubmitDraw(
@ -78,6 +46,7 @@ class OpenGL1Renderer : public Direct3DRMRenderer {
D3DRMMATRIX4D m_projection;
SDL_Surface* m_renderedImage;
bool m_useVBOs;
bool m_useNPOT;
bool m_dirty = false;
std::vector<SceneLight> m_lights;
SDL_GLContext m_context;
@ -86,9 +55,24 @@ class OpenGL1Renderer : public Direct3DRMRenderer {
inline static void OpenGL1Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
{
#ifndef __PSP__
Direct3DRMRenderer* device = OpenGL1Renderer::Create(640, 480);
if (device) {
EnumDevice(cb, ctx, device, OpenGL1_GUID);
delete device;
if (!device) {
return;
}
delete device;
#endif
D3DDEVICEDESC halDesc = {};
halDesc.dcmColorModel = D3DCOLORMODEL::RGB;
halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH;
halDesc.dwDeviceZBufferBitDepth = DDBD_24;
halDesc.dwDeviceRenderBitDepth = DDBD_32;
halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE;
halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND;
halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR;
D3DDEVICEDESC helDesc = {};
EnumDevice(cb, ctx, "OpenGL 1.1 HAL", &halDesc, &helDesc, OpenGL1_GUID);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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
);

View File

@ -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));
}

View File

@ -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
View 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
View 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

View 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")

Some files were not shown because too many files have changed in this diff Show More