This commit is contained in:
Helloyunho 2025-06-30 17:06:06 +09:00
commit f375e7954e
36 changed files with 1372 additions and 500 deletions

View File

@ -14,3 +14,8 @@ trim_trailing_whitespace = true
[{CMakeLists.txt,*.cmake}] [{CMakeLists.txt,*.cmake}]
indent_size = 2 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 *.html text eol=lf diff=html
*.mdp binary *.mdp binary
*.mak text eol=crlf *.mak text eol=crlf
**/*.ico binary
**/*.png binary
**/*.svg text eol=lf
**/*.desktop text eol=lf

View File

@ -33,14 +33,14 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- { name: 'Linux', os: 'ubuntu-latest', dx5: false, config: true, build-type: 'Debug', linux: true, werror: true, clang-tidy: true } - { 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, build-type: 'Debug', msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64_x86' } - { 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, build-type: 'Debug', msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64' } - { 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, build-type: 'Debug', msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64_arm64' } - { 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, build-type: 'Debug', mingw: true, werror: true, clang-tidy: true, msystem: 'mingw32', msys-env: 'mingw-w64-i686', shell: 'msys2 {0}' } - { 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, build-type: 'Debug', mingw: true, werror: true, clang-tidy: true, msystem: 'mingw64', msys-env: 'mingw-w64-x86_64', 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, build-type: 'Debug', brew: true, werror: true, clang-tidy: false } - { 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, build-type: 'Debug', emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' } - { name: 'Emscripten', os: 'ubuntu-latest', dx5: false, config: false, emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' }
steps: steps:
- name: Setup vcvars - name: Setup vcvars
if: ${{ !!matrix.msvc }} if: ${{ !!matrix.msvc }}
@ -66,8 +66,8 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y \ sudo apt-get install -y \
libx11-dev libxext-dev libxrandr-dev libxrender-dev libxfixes-dev libxi-dev libxinerama-dev \ 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 \ libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev qt6-base-dev \
libasound2-dev libasound2-dev qt6-xdgdesktopportal-platformtheme
- name: Install macOS dependencies (brew) - name: Install macOS dependencies (brew)
if: ${{ matrix.brew }} if: ${{ matrix.brew }}
@ -89,11 +89,12 @@ jobs:
- name: Configure (CMake) - name: Configure (CMake)
run: | run: |
${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \ ${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \
-DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ -DCMAKE_BUILD_TYPE=Release \
-DISLE_USE_DX5=${{ !!matrix.dx5 }} \ -DISLE_USE_DX5=${{ !!matrix.dx5 }} \
-DISLE_BUILD_CONFIG=${{ matrix.config }} \ -DISLE_BUILD_CONFIG=${{ !!matrix.config }} \
-DENABLE_CLANG_TIDY=${{ !!matrix.clang-tidy }} \ -DENABLE_CLANG_TIDY=${{ !!matrix.clang-tidy }} \
-DISLE_WERROR=${{ !!matrix.werror }} \ -DISLE_WERROR=${{ !!matrix.werror }} \
-DISLE_DEBUG=OFF \
-Werror=dev -Werror=dev
- name: Build (CMake) - name: Build (CMake)
@ -104,11 +105,64 @@ jobs:
cd build cd build
cpack . 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: Upload Build Artifacts - name: Upload Build Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: '${{ matrix.name }} ${{ matrix.build-type }}' name: '${{ matrix.name }}'
path: build/dist/isle-* path: |
build/dist/isle-*
build/dist/*.AppImage
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: ncc:
name: 'C++' name: 'C++'
@ -148,3 +202,30 @@ jobs:
LEGO1/omni/src/video/flic.cpp \ LEGO1/omni/src/video/flic.cpp \
$action_headers \ $action_headers \
--path LEGO1/omni LEGO1/lego/legoomni --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 # Kate - Text
/.cache /.cache
# Flatpak build cache
**/.flatpak-builder/
# Flatpak build dir
**/flatpak-build/

View File

@ -16,6 +16,9 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include(CheckCXXSourceCompiles) include(CheckCXXSourceCompiles)
include(CMakeDependentOption) include(CMakeDependentOption)
include(CMakePushCheckState) include(CMakePushCheckState)
include(cmake/detectcpu.cmake)
DetectTargetCPUArchitectures(ISLE_CPUS)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -627,8 +630,9 @@ else()
include(GNUInstallDirs) include(GNUInstallDirs)
endif() 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) if(BUILD_SHARED_LIBS)
list(APPEND install_extra_targets lego1) list(APPEND install_extra_targets lego1)
endif() endif()
@ -644,9 +648,10 @@ if(EMSCRIPTEN)
) )
endif() endif()
set(CPACK_PACKAGE_DIRECTORY "dist") add_subdirectory(packaging)
set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_PACKAGE_DIRECTORY "dist")
set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}")
if(MSVC) if(MSVC)
set(CPACK_GENERATOR ZIP) set(CPACK_GENERATOR ZIP)
else() else()

View File

@ -654,6 +654,11 @@ MxResult IsleApp::SetupWindow()
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, g_targetHeight); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, g_targetHeight);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, m_fullScreen); SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, m_fullScreen);
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE); SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE);
#ifdef MINIWIN
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
#endif
window = SDL_CreateWindowWithProperties(props); window = SDL_CreateWindowWithProperties(props);
#ifdef MINIWIN #ifdef MINIWIN

View File

@ -123,7 +123,7 @@ class Hospital : public LegoWorld {
MxLong m_copLedAnimTimer; // 0x11c MxLong m_copLedAnimTimer; // 0x11c
MxLong m_pizzaLedAnimTimer; // 0x120 MxLong m_pizzaLedAnimTimer; // 0x120
MxLong m_time; // 0x124 MxLong m_time; // 0x124
undefined m_unk0x128; // 0x128 MxBool m_exited; // 0x128
}; };
#endif // HOSPITAL_H #endif // HOSPITAL_H

View File

@ -121,7 +121,7 @@ class LegoAnimPresenter : public MxVideoPresenter {
void SubstituteVariables(); void SubstituteVariables();
void FUN_1006b900(LegoAnim* p_anim, MxLong p_time, Matrix4* p_matrix); 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_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 LegoAnim* m_anim; // 0x64
LegoROI** m_roiMap; // 0x68 LegoROI** m_roiMap; // 0x68

View File

@ -79,7 +79,7 @@ class LegoBuildingManager : public MxCore {
MxBool SwitchMove(LegoEntity* p_entity); MxBool SwitchMove(LegoEntity* p_entity);
MxBool SwitchMood(LegoEntity* p_entity); MxBool SwitchMood(LegoEntity* p_entity);
MxU32 GetAnimationId(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(LegoEntity* p_entity);
MxBool DecrementCounter(MxS32 p_index); MxBool DecrementCounter(MxS32 p_index);
MxBool DecrementCounter(LegoBuildingInfo* p_data); MxBool DecrementCounter(LegoBuildingInfo* p_data);

View File

@ -88,7 +88,7 @@ class LegoCharacterManager {
MxBool SwitchMove(LegoROI* p_roi); MxBool SwitchMove(LegoROI* p_roi);
MxBool SwitchMood(LegoROI* p_roi); MxBool SwitchMood(LegoROI* p_roi);
MxU32 GetAnimationId(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); MxU8 GetMood(LegoROI* p_roi);
LegoROI* CreateAutoROI(const char* p_name, const char* p_lodName, MxBool p_createEntity); LegoROI* CreateAutoROI(const char* p_name, const char* p_lodName, MxBool p_createEntity);
MxResult UpdateBoundingSphereAndBox(LegoROI* p_roi); MxResult UpdateBoundingSphereAndBox(LegoROI* p_roi);

View File

@ -28,7 +28,7 @@ class LegoEntity : public MxEntity {
}; };
enum { enum {
c_altBit1 = 0x01 c_disabled = 0x01
}; };
LegoEntity() { Init(); } LegoEntity() { Init(); }
@ -68,13 +68,13 @@ class LegoEntity : public MxEntity {
// FUNCTION: BETA10 0x10013260 // FUNCTION: BETA10 0x10013260
virtual void SetWorldSpeed(MxFloat p_worldSpeed) { m_worldSpeed = p_worldSpeed; } // vtable+0x30 virtual void SetWorldSpeed(MxFloat p_worldSpeed) { m_worldSpeed = p_worldSpeed; } // vtable+0x30
virtual void ClickSound(MxBool p_und); // vtable+0x34 virtual void ClickSound(MxBool p_basedOnMood); // vtable+0x34
virtual void ClickAnimation(); // vtable+0x38 virtual void ClickAnimation(); // vtable+0x38
virtual void SwitchVariant(); // vtable+0x3c virtual void SwitchVariant(); // vtable+0x3c
virtual void SwitchSound(); // vtable+0x40 virtual void SwitchSound(); // vtable+0x40
virtual void SwitchMove(); // vtable+0x44 virtual void SwitchMove(); // vtable+0x44
virtual void SwitchColor(LegoROI* p_roi); // vtable+0x48 virtual void SwitchColor(LegoROI* p_roi); // vtable+0x48
virtual void SwitchMood(); // vtable+0x4c virtual void SwitchMood(); // vtable+0x4c
void FUN_10010c30(); void FUN_10010c30();
void SetType(MxU8 p_type); void SetType(MxU8 p_type);
@ -83,7 +83,7 @@ class LegoEntity : public MxEntity {
Mx3DPointFloat GetWorldUp(); Mx3DPointFloat GetWorldUp();
Mx3DPointFloat GetWorldPosition(); 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; } MxBool GetFlagsIsSet(MxU8 p_flag) { return m_flags & p_flag; }
MxU8 GetFlags() { return m_flags; } MxU8 GetFlags() { return m_flags; }
@ -101,14 +101,14 @@ class LegoEntity : public MxEntity {
void SetFlags(MxU8 p_flags) { m_flags = p_flags; } void SetFlags(MxU8 p_flags) { m_flags = p_flags; }
void SetFlag(MxU8 p_flag) { m_flags |= p_flag; } void SetFlag(MxU8 p_flag) { m_flags |= p_flag; }
void ClearFlag(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 SetInteractionFlag(MxU8 p_flag) { m_interaction |= p_flag; }
void ClearUnknown0x10Flag(MxU8 p_flag) { m_unk0x10 &= ~p_flag; } void ClearInteractionFlag(MxU8 p_flag) { m_interaction &= ~p_flag; }
protected: protected:
void Init(); void Init();
void SetWorld(); void SetWorld();
MxU8 m_unk0x10; // 0x10 MxU8 m_interaction; // 0x10
MxU8 m_flags; // 0x11 MxU8 m_flags; // 0x11
Mx3DPointFloat m_worldLocation; // 0x14 Mx3DPointFloat m_worldLocation; // 0x14
Mx3DPointFloat m_worldDirection; // 0x28 Mx3DPointFloat m_worldDirection; // 0x28

View File

@ -49,7 +49,7 @@ class LEGO1_EXPORT LegoPlantManager : public MxCore {
MxBool SwitchMove(LegoEntity* p_entity); MxBool SwitchMove(LegoEntity* p_entity);
MxBool SwitchMood(LegoEntity* p_entity); MxBool SwitchMood(LegoEntity* p_entity);
MxU32 GetAnimationId(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); LegoPlantInfo* GetInfoArray(MxS32& p_length);
LegoEntity* CreatePlant(MxS32 p_index, LegoWorld* p_world, LegoOmni::World p_worldId); LegoEntity* CreatePlant(MxS32 p_index, LegoWorld* p_world, LegoOmni::World p_worldId);
MxBool DecrementCounter(LegoEntity* p_entity); MxBool DecrementCounter(LegoEntity* p_entity);

View File

@ -199,10 +199,10 @@ LegoBuildingInfo g_buildingInfoInit[16] = {
MxU32 LegoBuildingManager::g_maxSound = 6; MxU32 LegoBuildingManager::g_maxSound = 6;
// GLOBAL: LEGO1 0x100f373c // GLOBAL: LEGO1 0x100f373c
MxU32 g_unk0x100f373c = 0x3c; MxU32 g_buildingSoundIdOffset = 0x3c;
// GLOBAL: LEGO1 0x100f3740 // GLOBAL: LEGO1 0x100f3740
MxU32 g_unk0x100f3740 = 0x42; MxU32 g_buildingSoundIdMoodOffset = 0x42;
// clang-format off // clang-format off
// GLOBAL: LEGO1 0x100f3788 // GLOBAL: LEGO1 0x100f3788
@ -227,6 +227,8 @@ LegoBuildingInfo g_buildingInfo[16];
// GLOBAL: LEGO1 0x100f3748 // GLOBAL: LEGO1 0x100f3748
MxS32 LegoBuildingManager::g_maxMove[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0}; 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 // FUNCTION: LEGO1 0x1002f8b0
void LegoBuildingManager::configureLegoBuildingManager(MxS32 p_buildingManagerConfig) void LegoBuildingManager::configureLegoBuildingManager(MxS32 p_buildingManagerConfig)
{ {
@ -392,6 +394,9 @@ MxResult LegoBuildingManager::Read(LegoStorage* p_storage)
m_nextVariant = 0; 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; result = SUCCESS;
done: done:
@ -461,7 +466,7 @@ MxBool LegoBuildingManager::SwitchVariant(LegoEntity* p_entity)
roi->SetVisibility(FALSE); roi->SetVisibility(FALSE);
info->m_variant = g_buildingInfoVariants[m_nextVariant]; info->m_variant = g_buildingInfoVariants[m_nextVariant];
CreateBuilding(12, CurrentWorld()); CreateBuilding(HAUS1_INDEX, CurrentWorld());
if (info->m_entity != NULL) { if (info->m_entity != NULL) {
info->m_entity->GetROI()->SetVisibility(TRUE); info->m_entity->GetROI()->SetVisibility(TRUE);
@ -548,7 +553,7 @@ MxU32 LegoBuildingManager::GetAnimationId(LegoEntity* p_entity)
// FUNCTION: LEGO1 0x1002ff40 // FUNCTION: LEGO1 0x1002ff40
// FUNCTION: BETA10 0x10064398 // 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); LegoBuildingInfo* info = GetInfo(p_entity);
@ -556,12 +561,12 @@ MxU32 LegoBuildingManager::GetSoundId(LegoEntity* p_entity, MxBool p_state)
return 0; return 0;
} }
if (p_state) { if (p_basedOnMood) {
return info->m_mood + g_unk0x100f3740; return info->m_mood + g_buildingSoundIdMoodOffset;
} }
if (info != NULL) { if (info != NULL) {
return info->m_sound + g_unk0x100f373c; return info->m_sound + g_buildingSoundIdOffset;
} }
return 0; return 0;

View File

@ -39,10 +39,10 @@ MxU32 g_characterAnimationId = 10;
char* LegoCharacterManager::g_customizeAnimFile = NULL; char* LegoCharacterManager::g_customizeAnimFile = NULL;
// GLOBAL: LEGO1 0x100fc4d8 // GLOBAL: LEGO1 0x100fc4d8
MxU32 g_soundIdOffset = 50; MxU32 g_characterSoundIdOffset = 50;
// GLOBAL: LEGO1 0x100fc4dc // GLOBAL: LEGO1 0x100fc4dc
MxU32 g_soundIdMoodOffset = 66; MxU32 g_characterSoundIdMoodOffset = 66;
// GLOBAL: LEGO1 0x100fc4e8 // GLOBAL: LEGO1 0x100fc4e8
MxU32 g_headTextureCounter = 0; MxU32 g_headTextureCounter = 0;
@ -933,16 +933,16 @@ MxU32 LegoCharacterManager::GetAnimationId(LegoROI* p_roi)
// FUNCTION: LEGO1 0x10085140 // FUNCTION: LEGO1 0x10085140
// FUNCTION: BETA10 0x10076855 // 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); LegoActorInfo* info = GetActorInfo(p_roi);
if (p_und) { if (p_basedOnMood) {
return info->m_mood + g_soundIdMoodOffset; return info->m_mood + g_characterSoundIdMoodOffset;
} }
if (info != NULL) { if (info != NULL) {
return info->m_sound + g_soundIdOffset; return info->m_sound + g_characterSoundIdOffset;
} }
else { else {
return 0; return 0;

View File

@ -40,10 +40,10 @@ MxU8 g_counters[] = {1, 2, 2, 3};
MxU32 LegoPlantManager::g_maxSound = 8; MxU32 LegoPlantManager::g_maxSound = 8;
// GLOBAL: LEGO1 0x100f3160 // GLOBAL: LEGO1 0x100f3160
MxU32 g_unk0x100f3160 = 56; MxU32 g_plantSoundIdOffset = 56;
// GLOBAL: LEGO1 0x100f3164 // GLOBAL: LEGO1 0x100f3164
MxU32 g_unk0x100f3164 = 66; MxU32 g_plantSoundIdMoodOffset = 66;
// GLOBAL: LEGO1 0x100f3168 // GLOBAL: LEGO1 0x100f3168
MxS32 LegoPlantManager::g_maxMove[4] = {3, 3, 3, 3}; MxS32 LegoPlantManager::g_maxMove[4] = {3, 3, 3, 3};
@ -514,16 +514,16 @@ MxU32 LegoPlantManager::GetAnimationId(LegoEntity* p_entity)
// FUNCTION: LEGO1 0x10026ba0 // FUNCTION: LEGO1 0x10026ba0
// FUNCTION: BETA10 0x100c61ba // 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); LegoPlantInfo* info = GetInfo(p_entity);
if (p_state) { if (p_basedOnMood) {
return (info->m_mood & 1) + g_unk0x100f3164; return (info->m_mood & 1) + g_plantSoundIdMoodOffset;
} }
if (info != NULL) { if (info != NULL) {
return info->m_sound + g_unk0x100f3160; return info->m_sound + g_plantSoundIdOffset;
} }
return 0; return 0;

View File

@ -20,7 +20,7 @@ LegoActor::LegoActor()
m_frequencyFactor = 0.0f; m_frequencyFactor = 0.0f;
m_sound = NULL; m_sound = NULL;
m_unk0x70 = 0.0f; m_unk0x70 = 0.0f;
m_unk0x10 = 0; m_interaction = 0;
m_actorId = 0; m_actorId = 0;
} }

View File

@ -31,7 +31,7 @@ void LegoEntity::Init()
m_roi = NULL; m_roi = NULL;
m_cameraFlag = FALSE; m_cameraFlag = FALSE;
m_siFile = NULL; m_siFile = NULL;
m_unk0x10 = 0; m_interaction = 0;
m_flags = 0; m_flags = 0;
m_actionType = Extra::ActionType::e_unknown; m_actionType = Extra::ActionType::e_unknown;
m_targetEntityId = -1; m_targetEntityId = -1;
@ -266,23 +266,23 @@ void LegoEntity::ParseAction(char* p_extra)
// FUNCTION: LEGO1 0x10010f10 // FUNCTION: LEGO1 0x10010f10
// FUNCTION: BETA10 0x1007ee87 // 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; MxU32 objectId = 0;
const char* name = m_roi->GetName(); const char* name = m_roi->GetName();
switch (m_type) { switch (m_type) {
case e_actor: case e_actor:
objectId = CharacterManager()->GetSoundId(m_roi, p_und); objectId = CharacterManager()->GetSoundId(m_roi, p_basedOnMood);
break; break;
case e_unk1: case e_unk1:
break; break;
case e_plant: case e_plant:
objectId = PlantManager()->GetSoundId(this, p_und); objectId = PlantManager()->GetSoundId(this, p_basedOnMood);
break; break;
case e_building: case e_building:
objectId = BuildingManager()->GetSoundId(this, p_und); objectId = BuildingManager()->GetSoundId(this, p_basedOnMood);
break; break;
} }
@ -300,7 +300,7 @@ void LegoEntity::ClickSound(MxBool p_und)
// FUNCTION: BETA10 0x1007f062 // FUNCTION: BETA10 0x1007f062
void LegoEntity::ClickAnimation() void LegoEntity::ClickAnimation()
{ {
if (!GetUnknown0x10IsSet(c_altBit1)) { if (!IsInteraction(c_disabled)) {
MxU32 objectId = 0; MxU32 objectId = 0;
MxDSAction action; MxDSAction action;
const char* name = m_roi->GetName(); const char* name = m_roi->GetName();
@ -332,7 +332,7 @@ void LegoEntity::ClickAnimation()
action.SetObjectId(objectId); action.SetObjectId(objectId);
action.AppendExtra(strlen(extra) + 1, extra); action.AppendExtra(strlen(extra) + 1, extra);
LegoOmni::GetInstance()->GetAnimationManager()->StartEntityAction(action, this); 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++) { for (MxS32 i = 0; i < numPlants; i++) {
LegoEntity* entity = plantMgr->CreatePlant(i, NULL, LegoOmni::e_act1); 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(); LegoROI* roi = entity->GetROI();
if (roi != NULL && roi->GetVisibility()) { if (roi != NULL && roi->GetVisibility()) {

View File

@ -793,7 +793,7 @@ void LegoAnimPresenter::StartingTickle()
} }
FUN_10069b10(); FUN_10069b10();
FUN_1006c8a0(TRUE); SetDisabled(TRUE);
if (m_unk0x78 == NULL) { if (m_unk0x78 == NULL) {
if (fabs(m_action->GetDirection()[0]) >= 0.00000047683716F || if (fabs(m_action->GetDirection()[0]) >= 0.00000047683716F ||
@ -1093,7 +1093,7 @@ void LegoAnimPresenter::EndAction()
} }
} }
FUN_1006c8a0(FALSE); SetDisabled(FALSE);
FUN_1006ab70(); FUN_1006ab70();
VTable0x90(); VTable0x90();
@ -1154,18 +1154,18 @@ void LegoAnimPresenter::VTable0x90()
} }
// FUNCTION: LEGO1 0x1006c8a0 // FUNCTION: LEGO1 0x1006c8a0
void LegoAnimPresenter::FUN_1006c8a0(MxBool p_bool) void LegoAnimPresenter::SetDisabled(MxBool p_disabled)
{ {
if (m_roiMapSize != 0 && m_roiMap != NULL) { if (m_roiMapSize != 0 && m_roiMap != NULL) {
for (MxU32 i = 1; i <= m_roiMapSize; i++) { for (MxU32 i = 1; i <= m_roiMapSize; i++) {
LegoEntity* entity = m_roiMap[i]->GetEntity(); LegoEntity* entity = m_roiMap[i]->GetEntity();
if (entity != NULL) { if (entity != NULL) {
if (p_bool) { if (p_disabled) {
entity->SetUnknown0x10Flag(LegoEntity::c_altBit1); entity->SetInteractionFlag(LegoEntity::c_disabled);
} }
else { else {
entity->ClearUnknown0x10Flag(LegoEntity::c_altBit1); entity->ClearInteractionFlag(LegoEntity::c_disabled);
} }
} }
} }

View File

@ -48,7 +48,7 @@ Hospital::Hospital()
m_flashingLeds = 0; m_flashingLeds = 0;
m_copLedAnimTimer = 0; m_copLedAnimTimer = 0;
m_pizzaLedAnimTimer = 0; m_pizzaLedAnimTimer = 0;
m_unk0x128 = 0; m_exited = FALSE;
NotificationManager()->Register(this); NotificationManager()->Register(this);
} }
@ -369,8 +369,8 @@ MxLong Hospital::HandleEndAction(MxEndActionNotificationParam& p_param)
act1State = (Act1State*) GameState()->GetState("Act1State"); act1State = (Act1State*) GameState()->GetState("Act1State");
act1State->SetUnknown18(9); act1State->SetUnknown18(9);
case HospitalState::e_exitToFront: case HospitalState::e_exitToFront:
if (m_unk0x128 == 0) { if (m_exited == FALSE) {
m_unk0x128 = 1; m_exited = TRUE;
m_destLocation = LegoGameState::e_hospitalExited; m_destLocation = LegoGameState::e_hospitalExited;
DeleteObjects(&m_atomId, HospitalScript::c_hho002cl_RunAnim, HospitalScript::c_hho006cl_RunAnim); DeleteObjects(&m_atomId, HospitalScript::c_hho002cl_RunAnim, HospitalScript::c_hho006cl_RunAnim);
@ -378,8 +378,8 @@ MxLong Hospital::HandleEndAction(MxEndActionNotificationParam& p_param)
} }
break; break;
case HospitalState::e_exitToInfocenter: case HospitalState::e_exitToInfocenter:
if (m_unk0x128 == 0) { if (m_exited == FALSE) {
m_unk0x128 = 1; m_exited = TRUE;
m_destLocation = LegoGameState::e_infomain; m_destLocation = LegoGameState::e_infomain;
DeleteObjects(&m_atomId, HospitalScript::c_hho002cl_RunAnim, HospitalScript::c_hho006cl_RunAnim); DeleteObjects(&m_atomId, HospitalScript::c_hho002cl_RunAnim, HospitalScript::c_hho006cl_RunAnim);
@ -412,8 +412,8 @@ MxLong Hospital::HandleButtonDown(LegoControlManagerNotificationParam& p_param)
m_interactionMode = 3; m_interactionMode = 3;
if (m_hospitalState->m_state == HospitalState::e_explainQuestShort) { if (m_hospitalState->m_state == HospitalState::e_explainQuestShort) {
if (m_unk0x128 == 0) { if (m_exited == FALSE) {
m_unk0x128 = 1; m_exited = TRUE;
TickleManager()->UnregisterClient(this); TickleManager()->UnregisterClient(this);
@ -568,8 +568,8 @@ MxBool Hospital::HandleControl(LegoControlManagerNotificationParam& p_param)
m_currentAction = HospitalScript::c_hho016cl_RunAnim; m_currentAction = HospitalScript::c_hho016cl_RunAnim;
m_setWithCurrentAction = 1; m_setWithCurrentAction = 1;
} }
else if (m_unk0x128 == 0) { else if (m_exited == FALSE) {
m_unk0x128 = 1; m_exited = TRUE;
m_hospitalState->m_state = HospitalState::e_exitImmediately; m_hospitalState->m_state = HospitalState::e_exitImmediately;
m_destLocation = LegoGameState::e_infomain; m_destLocation = LegoGameState::e_infomain;
@ -589,8 +589,8 @@ MxBool Hospital::HandleControl(LegoControlManagerNotificationParam& p_param)
m_currentAction = HospitalScript::c_hho016cl_RunAnim; m_currentAction = HospitalScript::c_hho016cl_RunAnim;
m_setWithCurrentAction = 1; m_setWithCurrentAction = 1;
} }
else if (m_unk0x128 == 0) { else if (m_exited == FALSE) {
m_unk0x128 = 1; m_exited = TRUE;
m_hospitalState->m_state = HospitalState::e_exitImmediately; m_hospitalState->m_state = HospitalState::e_exitImmediately;
m_destLocation = LegoGameState::e_hospitalExited; m_destLocation = LegoGameState::e_hospitalExited;

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(OpenGL)
find_package(GLEW) if(OpenGL_FOUND)
if(OpenGL_FOUND AND GLEW_FOUND) message(STATUS "Found OpenGL: enabling OpenGL 1.x renderer")
message(STATUS "Found OpenGL and GLEW: enabling OpenGL 1.x renderer") target_sources(miniwin PRIVATE
target_sources(miniwin PRIVATE src/d3drm/backends/opengl1/renderer.cpp) src/d3drm/backends/opengl1/actual.cpp
src/d3drm/backends/opengl1/renderer.cpp
)
target_compile_definitions(miniwin PRIVATE USE_OPENGL1) target_compile_definitions(miniwin PRIVATE USE_OPENGL1)
target_link_libraries(miniwin PRIVATE OpenGL::GL GLEW::GLEW) target_link_libraries(miniwin PRIVATE OpenGL::GL)
else() else()
message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL and GLEW") message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL")
endif() endif()
find_library(OPENGL_ES2_LIBRARY NAMES GLESv2) find_library(OPENGL_ES2_LIBRARY NAMES GLESv2)

View File

@ -0,0 +1,354 @@
// 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)
{
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
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);
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);
// 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);
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);
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(
GLuint texId,
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, texId);
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();
}
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,88 @@
#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;
};
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);
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(
GLuint texId,
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> #include "actual.h"
// must come after GLEW
#include "d3drmrenderer_opengl1.h" #include "d3drmrenderer_opengl1.h"
#include "ddraw_impl.h" #include "ddraw_impl.h"
#include "ddsurface_impl.h" #include "ddsurface_impl.h"
@ -11,8 +10,20 @@
#include <cstring> #include <cstring>
#include <vector> #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) Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height)
{ {
// We have to reset the attributes here after having enumerated the
// OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE
// call below when on an EGL-based backend, and crashes with EGL_BAD_MATCH.
SDL_GL_ResetAttributes();
// But ResetAttributes resets it to 16.
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
@ -28,8 +39,6 @@ Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height)
testWindow = true; testWindow = true;
} }
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GLContext context = SDL_GL_CreateContext(window); SDL_GLContext context = SDL_GL_CreateContext(window);
if (!context) { if (!context) {
SDL_Log("SDL_GL_CreateContext: %s", SDL_GetError()); SDL_Log("SDL_GL_CreateContext: %s", SDL_GetError());
@ -47,18 +56,7 @@ Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height)
return nullptr; return nullptr;
} }
GLenum err = glewInit(); GL11_InitState();
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) { if (testWindow) {
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
@ -74,7 +72,7 @@ OpenGL1Renderer::OpenGL1Renderer(DWORD width, DWORD height, SDL_GLContext contex
m_virtualWidth = width; m_virtualWidth = width;
m_virtualHeight = height; m_virtualHeight = height;
m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32);
m_useVBOs = GLEW_ARB_vertex_buffer_object; GL11_LoadExtensions();
} }
OpenGL1Renderer::~OpenGL1Renderer() OpenGL1Renderer::~OpenGL1Renderer()
@ -114,7 +112,7 @@ void OpenGL1Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* t
auto* ctx = static_cast<TextureDestroyContextGL*>(arg); auto* ctx = static_cast<TextureDestroyContextGL*>(arg);
auto& cache = ctx->renderer->m_textures[ctx->textureId]; auto& cache = ctx->renderer->m_textures[ctx->textureId];
if (cache.glTextureId != 0) { if (cache.glTextureId != 0) {
glDeleteTextures(1, &cache.glTextureId); GL11_DestroyTexture(cache.glTextureId);
cache.glTextureId = 0; cache.glTextureId = 0;
cache.texture = nullptr; cache.texture = nullptr;
} }
@ -133,15 +131,13 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture)
auto& tex = m_textures[i]; auto& tex = m_textures[i];
if (tex.texture == texture) { if (tex.texture == texture) {
if (tex.version != texture->m_version) { if (tex.version != texture->m_version) {
glDeleteTextures(1, &tex.glTextureId); GL11_DestroyTexture(tex.glTextureId);
glGenTextures(1, &tex.glTextureId);
glBindTexture(GL_TEXTURE_2D, tex.glTextureId);
SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32);
if (!surf) { if (!surf) {
return NO_TEXTURE_ID; return NO_TEXTURE_ID;
} }
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); tex.glTextureId = GL11_UploadTextureData(surf->pixels, surf->w, surf->h);
SDL_DestroySurface(surf); SDL_DestroySurface(surf);
tex.version = texture->m_version; tex.version = texture->m_version;
@ -151,14 +147,12 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture)
} }
GLuint texId; GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32);
if (!surf) { if (!surf) {
return NO_TEXTURE_ID; return NO_TEXTURE_ID;
} }
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); texId = GL11_UploadTextureData(surf->pixels, surf->w, surf->h);
SDL_DestroySurface(surf); SDL_DestroySurface(surf);
for (Uint32 i = 0; i < m_textures.size(); ++i) { for (Uint32 i = 0; i < m_textures.size(); ++i) {
@ -206,52 +200,19 @@ GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup, bool useVBOs)
if (meshGroup.texture) { if (meshGroup.texture) {
cache.texcoords.resize(vertices.size()); cache.texcoords.resize(vertices.size());
std::transform(vertices.begin(), vertices.end(), cache.texcoords.begin(), [](const D3DRMVERTEX& v) { 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()); cache.positions.resize(vertices.size());
std::transform(vertices.begin(), vertices.end(), cache.positions.begin(), [](const D3DRMVERTEX& v) { 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()); cache.normals.resize(vertices.size());
std::transform(vertices.begin(), vertices.end(), cache.normals.begin(), [](const D3DRMVERTEX& v) { 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) { GL11_UploadMesh(cache, meshGroup.texture != nullptr);
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
);
}
return cache; return cache;
} }
@ -269,12 +230,7 @@ void OpenGL1Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh)
auto* ctx = static_cast<GLMeshDestroyContext*>(arg); auto* ctx = static_cast<GLMeshDestroyContext*>(arg);
auto& cache = ctx->renderer->m_meshs[ctx->id]; auto& cache = ctx->renderer->m_meshs[ctx->id];
cache.meshGroup = nullptr; cache.meshGroup = nullptr;
if (ctx->renderer->m_useVBOs) { GL11_DestroyMesh(cache);
glDeleteBuffers(1, &cache.vboPositions);
glDeleteBuffers(1, &cache.vboNormals);
glDeleteBuffers(1, &cache.vboTexcoords);
glDeleteBuffers(1, &cache.ibo);
}
delete ctx; delete ctx;
}, },
ctx ctx
@ -329,90 +285,23 @@ const char* OpenGL1Renderer::GetName()
HRESULT OpenGL1Renderer::BeginFrame() HRESULT OpenGL1Renderer::BeginFrame()
{ {
m_dirty = true; GL11_BeginFrame((Matrix4x4*) &m_projection[0][0]);
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();
int lightIdx = 0; int lightIdx = 0;
for (const auto& l : m_lights) { for (const auto& l : m_lights) {
if (lightIdx > 7) { if (lightIdx > 7) {
break; break;
} }
GLenum lightId = GL_LIGHT0 + lightIdx++; GL11_UploadLight(lightIdx, (GL11_BridgeSceneLight*) &l);
const FColor& c = l.color;
GLfloat col[4] = {c.r, c.g, c.b, c.a};
if (l.positional == 0.f && l.directional == 0.f) { lightIdx++;
// 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();
// Projection and view
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&m_projection[0][0]);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return DD_OK; return DD_OK;
} }
void OpenGL1Renderer::EnableTransparency() void OpenGL1Renderer::EnableTransparency()
{ {
glEnable(GL_BLEND); GL11_EnableTransparency();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
} }
void OpenGL1Renderer::SubmitDraw( void OpenGL1Renderer::SubmitDraw(
@ -426,75 +315,14 @@ void OpenGL1Renderer::SubmitDraw(
{ {
auto& mesh = m_meshs[meshId]; 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 // Bind texture if present
if (appearance.textureId != NO_TEXTURE_ID) { 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]; auto& tex = m_textures[appearance.textureId];
glEnable(GL_TEXTURE_2D); GL11_SubmitDraw(mesh, modelViewMatrix, appearance, tex.glTextureId);
glBindTexture(GL_TEXTURE_2D, tex.glTextureId);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
} }
else { else {
glDisable(GL_TEXTURE_2D); GL11_SubmitDraw(mesh, modelViewMatrix, appearance, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
} }
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() HRESULT OpenGL1Renderer::FinalizeFrame()
@ -509,16 +337,13 @@ void OpenGL1Renderer::Resize(int width, int height, const ViewportTransform& vie
m_viewportTransform = viewportTransform; m_viewportTransform = viewportTransform;
SDL_DestroySurface(m_renderedImage); SDL_DestroySurface(m_renderedImage);
m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); 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) void OpenGL1Renderer::Clear(float r, float g, float b)
{ {
m_dirty = true; m_dirty = true;
glEnable(GL_DEPTH_TEST); GL11_Clear(r, g, b);
glDepthMask(GL_TRUE);
glClearColor(r, g, b, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
} }
void OpenGL1Renderer::Flip() void OpenGL1Renderer::Flip()
@ -532,73 +357,18 @@ void OpenGL1Renderer::Flip()
void OpenGL1Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) void OpenGL1Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect)
{ {
m_dirty = true; m_dirty = true;
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
float left = -m_viewportTransform.offsetX / m_viewportTransform.scale; float left = -m_viewportTransform.offsetX / m_viewportTransform.scale;
float right = (m_width - 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 top = -m_viewportTransform.offsetY / m_viewportTransform.scale;
float bottom = (m_height - 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); GL11_Draw2DImage(m_textures[textureId].glTextureId, srcRect, dstRect, left, right, bottom, top);
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();
} }
void OpenGL1Renderer::Download(SDL_Surface* target) void OpenGL1Renderer::Download(SDL_Surface* target)
{ {
glFinish(); GL11_Download(m_renderedImage);
glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels);
SDL_Rect srcRect = { SDL_Rect srcRect = {
static_cast<int>(m_viewportTransform.offsetX), static_cast<int>(m_viewportTransform.offsetX),

View File

@ -30,6 +30,12 @@ struct SceneLightGLES2 {
Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) 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_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
@ -41,8 +47,6 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height)
testWindow = true; testWindow = true;
} }
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GLContext context = SDL_GL_CreateContext(window); SDL_GLContext context = SDL_GL_CreateContext(window);
if (!context) { if (!context) {
if (testWindow) { if (testWindow) {

View File

@ -1,44 +1,14 @@
#pragma once #pragma once
#include "../d3drm/backends/opengl1/actual.h"
#include "d3drmrenderer.h" #include "d3drmrenderer.h"
#include "d3drmtexture_impl.h" #include "d3drmtexture_impl.h"
#include "ddraw_impl.h" #include "ddraw_impl.h"
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <vector> #include <vector>
DEFINE_GUID(OpenGL1_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03); 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 { class OpenGL1Renderer : public Direct3DRMRenderer {
public: public:
static Direct3DRMRenderer* Create(DWORD width, DWORD height); static Direct3DRMRenderer* Create(DWORD width, DWORD height);

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

25
packaging/linux/appimage/AppRun Executable file
View File

@ -0,0 +1,25 @@
#!/bin/sh
HERE="$(dirname "$(readlink -f "${0}")")"
MAIN=$(grep -r "^Exec=.*" "$HERE"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1)
# MAIN_BIN=$(find "$HERE/usr/bin" -name "$MAIN" | head -n 1)
MAIN_BIN="$HERE/usr/bin/isle-config"
export PATH="${HERE}/usr/bin/":$PATH # Prefer bundled binaries
export QT_QPA_PLATFORMTHEME=xdgdesktopportal # Use XDG filepicker for forward compatability
[ -z "$QT_PLUGIN_PATH" ] && export QT_PLUGIN_PATH=/usr/lib/qt6/plugins:/usr/lib64/qt6/plugins # Use system Qt theme, will fallback to the default one if unavailable
if [ ! -z $APPIMAGE ]; then
BINARY_NAME=$(basename "$ARGV0")
if [ -e "$HERE/usr/bin/$BINARY_NAME" ]; then
exec "$HERE/usr/bin/$BINARY_NAME" "$@"
else
exec "${MAIN_BIN}" "$@"
fi
else
exec "${MAIN_BIN}" "$@"
fi

100
packaging/linux/appimage/Build Executable file
View File

@ -0,0 +1,100 @@
#!/usr/bin/env bash
set -e
export LD_LIBRARY_PATH="build/source/lib:$LD_LIBRARY_PATH"
[ -z "$QMAKE" ] && export QMAKE=/usr/lib/qt6/bin/qmake
# Sets a directory that has to have a following structure:
# build
# ├── bin
# │   ├── isle
# │   └── isle-config
# └── lib
# ├── liblego1.so
# ├── libSDL3.so -> libSDL3.so.0 # Not important if available on the system
# ├── libSDL3.so.0 -> libSDL3.so.0.3.0 # Not important if available on the system
# └── libSDL3.so.0.3.0 # Not important if available on the system
# Can also be defined using --build=path
BUILD_SOURCE=source
# Sets where AppRun for AppImage is, can also be defined using --apprun=path
APPRUN_SOURCE=AppRun
# Sets where desktop file for AppImage is, can also be defined using --desktop-file=path
DESKTOP_FILE_SOURCE=isledecomp.desktop
# You know the drill
ICON_SOURCE=../../icons/isle.svg
cd $(dirname $0)
clean(){
echo "Deleting build directory"
rm -rf build
}
download(){
if [ ! -e "$1" ]; then
curl -Lo "$1" "$2"
fi
}
prepare(){
mkdir -p build/tools
mkdir -p build/assets
download build/tools/appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage
chmod u+x build/tools/appimagetool.AppImage
download build/tools/linuxdeploy.AppImage https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-$(uname -m).AppImage
chmod u+x build/tools/linuxdeploy.AppImage
download build/tools/linuxdeploy-plugin-qt.AppImage https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/latest/download/linuxdeploy-plugin-qt-$(uname -m).AppImage
chmod u+x build/tools/linuxdeploy-plugin-qt.AppImage
if [ ! -f "build/assets/isledecomp.desktop" ]; then
cp $DESKTOP_FILE_SOURCE build/assets/isledecomp.desktop
cp $APPRUN_SOURCE build/assets/AppRun
cp ../../icons/isle.svg build/assets/isle.svg
fi
if [ ! -d "build/source" ]; then
cp -r $BUILD_SOURCE build/source
fi
}
compile(){
NO_STRIP=1 build/tools/linuxdeploy.AppImage \
--plugin qt \
-e build/source/bin/isle \
-e build/source/bin/isle-config \
-d build/assets/isledecomp.desktop \
-i build/assets/isle.svg \
--custom-apprun=AppRun \
--appdir=build/AppDir
}
package(){
build/tools/appimagetool.AppImage build/AppDir build/"LEGO_Island-$(uname -m).AppImage"
}
stop(){ # Can be used to do `Build clean stop` to just clean the directory
exit
}
for arg in "$@"; do
case "$arg" in
--build=*) BUILD_SOURCE="${arg#--build=}";;
--apprun=*) APPRUN_SOURCE="${arg#--apprun=}";;
--desktop-file=*) DESKTOP_FILE_SOURCE="${arg#--desktop-file=}";;
--icon=*) ICON_SOURCE="${arg#--icon=}";;
*) "$arg"
esac
done
prepare
compile
package
# Symlinks named as binaries in appimage can call these binaries specifically
# ln -s "LEGO_Island-$(uname -m).AppImage" isle-config
# ln -s "LEGO_Island-$(uname -m).AppImage" isle

View File

@ -0,0 +1,89 @@
{
"id": "org.legoisland.Isle",
"runtime": "org.kde.Platform",
"sdk": "org.kde.Sdk",
"runtime-version": "6.8",
"command": "isle",
"finish-args": [
"--share=ipc",
"--socket=wayland",
"--socket=fallback-x11",
"--socket=pulseaudio",
"--device=dri",
"--device=input",
"--filesystem=/run/media/:ro",
"--filesystem=/media/:ro",
"--filesystem=/mnt/:ro",
"--filesystem=home:ro"
],
"modules": [
{
"name": "isle",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DCMAKE_BUILD_TYPE=RelWithDebInfo",
"-DISLE_DEBUG=OFF"
],
"sources": [
{
"type": "dir",
"path": "../../../3rdparty",
"dest": "3rdparty/"
},
{
"type": "dir",
"path": "../../../cmake",
"dest": "cmake/"
},
{
"type": "dir",
"path": "../../../CMake",
"dest": "CMake/"
},
{
"type": "dir",
"path": "../../../CONFIG",
"dest": "CONFIG/"
},
{
"type": "dir",
"path": "../../../ISLE",
"dest": "ISLE/"
},
{
"type": "dir",
"path": "../../../LEGO1",
"dest": "LEGO1/"
},
{
"type": "dir",
"path": "../../../miniwin",
"dest": "miniwin/"
},
{
"type": "dir",
"path": "../../../packaging",
"dest": "packaging/"
},
{
"type": "dir",
"path": "../../../util",
"dest": "util/"
},
{
"type": "file",
"path": "../../../CMakeLists.txt"
}
],
"build-options": {
"build-args": [
"--share=network"
]
}
}
]
}

View File

@ -0,0 +1,38 @@
[Desktop Entry]
Version=1.5
Name=@APP_NAME@
Comment=@APP_SUMMARY@
Icon=@APP_ID@
Type=Application
Categories=Game;KidsGame;AdventureGame;Qt
Keywords=LEGO;lego;LEGO Island
Keywords[da]=LEGO;lego;Panik på LEGO Øen
Keywords[de]=LEGO;lego;Abenteuer auf der LEGO Insel
Keywords[es]=LEGO;lego;La Isla LEGO
Keywords[fr]=LEGO;lego;Aventures sur L'île LEGO
Keywords[it]=LEGO;lego;Isola LEGO
Keywords[ja]=LEGO;lego;
Keywords[ko]=LEGO;lego;
Keywords[pt]=LEGO;lego;A Ilha LEGO
Keywords[ru]=LEGO;lego;Остров LEGO
Keywords[uk_UA]=LEGO;lego;LEGO острів
SingleMainWindow=true
TryExec=isle
Exec=isle
Actions=play;configure
[Desktop Action play]
Name=Play Game
Icon=currenttrack_play
Exec=isle
[Desktop Action configure]
Name=Configure Settings
Icon=settings
Exec=isle-config

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Freedesktop AppStream metadata file (https://www.freedesktop.org/software/appstream/docs) -->
<component type="desktop-application">
<id>@APP_ID@</id>
<name>@APP_NAME@</name>
<summary>@APP_SUMMARY@</summary>
<launchable type="desktop-id">@APP_ID@.desktop</launchable>
<branding>
<color type="primary">#e3000b</color>
</branding>
<developer id="io.github.isledecomp">
<name>Isle Decomp Team</name>
</developer>
<url type="homepage">https://github.com/isledecomp/isle-portable</url>
<url type="contribute">https://github.com/isledecomp/isle-portable/blob/master/CONTRIBUTING.md</url>
<url type="vcs-browser">https://github.com/isledecomp/isle-portable/tree/master</url>
<url type="bugtracker">https://github.com/isledecomp/isle-portable/issues</url>
<metadata_license>MIT</metadata_license>
<project_license>@APP_SPDX@</project_license>
<requires>
<display_length side="longest" compare="ge">640</display_length>
<internet>offline-only</internet>
</requires>
<recommends>
<memory>128</memory>
</recommends>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>gamepad</control>
</supports>
<description>
<p> This initiative is a portable version of LEGO Island (Version 1.1, English)
based on the decompilation project. Our primary goal is to transform the codebase to achieve
platform independence, thereby enhancing compatibility across various systems while preserving
the original game's experience as faithfully as possible.
</p>
<p>
Please note: this project is dedicated to achieving platform independence without altering the
core gameplay, adding new features, enhancing visual quality, or rewriting code for
improvement's sake. While those are worthwhile objectives, they are not within the scope
of this project.
</p>
</description>
<content_rating type="oars-1.1">
<content_attribute id="violence-cartoon">mild</content_attribute>
<content_attribute id="language-humor">mild</content_attribute>
</content_rating>
<categories>
<category>Game</category>
<category>KidsGame</category>
<category>AdventureGame</category>
<category>Qt</category>
</categories>
<keywords>
<keyword translate="no">LEGO</keyword>
<keyword translate="no">lego</keyword>
<keyword>LEGO Island</keyword>
<!-- Translations sourced from https://www.pcgamingwiki.com/wiki/Lego_Island#Localizations -->
<keyword xml:lang="da">Panik på LEGO Øen</keyword>
<keyword xml:lang="de">Abenteuer auf der LEGO Insel</keyword>
<keyword xml:lang="es">La Isla LEGO</keyword>
<keyword xml:lang="fr">Aventures sur L'île LEGO</keyword>
<keyword xml:lang="it">Isola LEGO</keyword>
<keyword xml:lang="ja">レゴアイランドの大冒険</keyword>
<keyword xml:lang="ko">레고 아일랜드</keyword>
<keyword xml:lang="pt">A Ilha LEGO</keyword>
<keyword xml:lang="ru">Остров LEGO</keyword>
</keywords>
<releases>
<release version="@SEMANTIC_VERSION@" type="development" date="@BUILD_DATE@" />
</releases>
</component>