From 8498c69af1b7428828ef5a03f120df851f87dde3 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Tue, 24 Jun 2025 22:38:08 +0200 Subject: [PATCH 01/22] Clear unknowns in `LegoPlantManager` (#1587) --- LEGO1/lego/legoomni/include/legoplantmanager.h | 12 ++++++------ .../lego/legoomni/src/common/legoplantmanager.cpp | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legoplantmanager.h b/LEGO1/lego/legoomni/include/legoplantmanager.h index 8a8c7902..91fe49c8 100644 --- a/LEGO1/lego/legoomni/include/legoplantmanager.h +++ b/LEGO1/lego/legoomni/include/legoplantmanager.h @@ -54,7 +54,7 @@ class LegoPlantManager : public MxCore { 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 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 }; #endif // LEGOPLANTMANAGER_H diff --git a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp index 734806fc..766edf79 100644 --- a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp @@ -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); From dcacdcc71120fc91ec41cdaed85b6b510d861d29 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Thu, 26 Jun 2025 02:46:10 +0200 Subject: [PATCH 02/22] Fix d3drmmesh type error (#433) --- miniwin/src/d3drm/d3drmmesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miniwin/src/d3drm/d3drmmesh.cpp b/miniwin/src/d3drm/d3drmmesh.cpp index 5d96ac86..4545036b 100644 --- a/miniwin/src/d3drm/d3drmmesh.cpp +++ b/miniwin/src/d3drm/d3drmmesh.cpp @@ -59,7 +59,7 @@ HRESULT Direct3DRMMeshImpl::AddGroup( MeshGroup group; group.vertexPerFace = vertexPerFace; - DWORD* src = faceBuffer; + unsigned int* src = faceBuffer; group.indices.assign(src, src + faceCount * vertexPerFace); m_groups.push_back(std::move(group)); From 7a92f532129d88d09c2c8c8fa90e794dfa8ce197 Mon Sep 17 00:00:00 2001 From: VoxelTek <53562267+VoxelTek@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:58:06 +1000 Subject: [PATCH 03/22] Revamp config tool, add tooltips and consolidate options. (#427) * Add tooltips, remove obsolete directory stuff In the config tool, the "Media Path" entry was removed, as it doesn't do anything in isle-portable as far as I know. The "Disk Path" and "CD Path" options were merged into one directory editing thing, where CD Path is specified and Disk Path is then derived from that. A checkbox to enable or disable full-screen was added. Tooltips were added to the config app, so now users can more easily tell what a setting in the config tool does. * Make clang-format happy * oops that's an extra semicolon I don't need * Remove obsolete full screen forcing Co-authored-by: Anders Jenbo * Update CONFIG/config.cpp Co-authored-by: Anders Jenbo --------- Co-authored-by: Anders Jenbo --- CONFIG/MainDlg.cpp | 110 ++++++++++---------------- CONFIG/MainDlg.h | 11 +-- CONFIG/config.cpp | 10 --- CONFIG/config.h | 1 - CONFIG/res/maindialog.ui | 165 +++++++++++++-------------------------- 5 files changed, 103 insertions(+), 194 deletions(-) diff --git a/CONFIG/MainDlg.cpp b/CONFIG/MainDlg.cpp index 46a2efe8..b90f1f66 100644 --- a/CONFIG/MainDlg.cpp +++ b/CONFIG/MainDlg.cpp @@ -56,18 +56,15 @@ CMainDialog::CMainDialog(QWidget* pParent) : QDialog(pParent) connect(m_ui->musicCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxMusic); connect(m_ui->sound3DCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckbox3DSound); connect(m_ui->joystickCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxJoystick); + 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->diskPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectDiskPathDialog); - connect(m_ui->cdPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectCDPathDialog); - connect(m_ui->mediaPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectMediaPathDialog); + connect(m_ui->dataPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectDataPathDialog); connect(m_ui->savePathOpen, &QPushButton::clicked, this, &CMainDialog::SelectSavePathDialog); - connect(m_ui->diskPath, &QLineEdit::textEdited, this, &CMainDialog::DiskPathEdited); - connect(m_ui->cdPath, &QLineEdit::textEdited, this, &CMainDialog::CDPathEdited); - connect(m_ui->mediaPath, &QLineEdit::textEdited, this, &CMainDialog::MediaPathEdited); - connect(m_ui->savePath, &QLineEdit::textEdited, this, &CMainDialog::SavePathEdited); + connect(m_ui->dataPath, &QLineEdit::editingFinished, this, &CMainDialog::DataPathEdited); + connect(m_ui->savePath, &QLineEdit::editingFinished, this, &CMainDialog::SavePathEdited); connect(m_ui->maxLoDSlider, &QSlider::valueChanged, this, &CMainDialog::MaxLoDChanged); connect(m_ui->maxActorsSlider, &QSlider::valueChanged, this, &CMainDialog::MaxActorsChanged); @@ -187,9 +184,8 @@ void CMainDialog::UpdateInterface() } m_ui->joystickCheckBox->setChecked(currentConfigApp->m_use_joystick); m_ui->musicCheckBox->setChecked(currentConfigApp->m_music); - m_ui->diskPath->setText(QString::fromStdString(currentConfigApp->m_base_path)); - m_ui->cdPath->setText(QString::fromStdString(currentConfigApp->m_cd_path)); - m_ui->mediaPath->setText(QString::fromStdString(currentConfigApp->m_media_path)); + m_ui->fullscreenCheckBox->setChecked(currentConfigApp->m_full_screen); + m_ui->dataPath->setText(QString::fromStdString(currentConfigApp->m_cd_path)); m_ui->savePath->setText(QString::fromStdString(currentConfigApp->m_save_path)); } @@ -266,54 +262,33 @@ void CMainDialog::OnCheckboxMusic(bool checked) UpdateInterface(); } -void CMainDialog::SelectDiskPathDialog() +void CMainDialog::OnCheckboxFullscreen(bool checked) { - QString disk_path = QString::fromStdString(currentConfigApp->m_base_path); - disk_path = QFileDialog::getExistingDirectory( - this, - tr("Open Directory"), - disk_path, - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks - ); - - if (disk_path.toStdString() != "") { - currentConfigApp->m_base_path = disk_path.toStdString(); - m_modified = true; - UpdateInterface(); - } + currentConfigApp->m_full_screen = checked; + m_modified = true; + UpdateInterface(); } -void CMainDialog::SelectCDPathDialog() +void CMainDialog::SelectDataPathDialog() { - QString cd_path = QString::fromStdString(currentConfigApp->m_cd_path); - cd_path = QFileDialog::getExistingDirectory( + QString data_path = QString::fromStdString(currentConfigApp->m_cd_path); + data_path = QFileDialog::getExistingDirectory( this, tr("Open Directory"), - cd_path, + data_path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks ); - if (cd_path.toStdString() != "") { - currentConfigApp->m_cd_path = cd_path.toStdString(); - m_modified = true; - UpdateInterface(); - } -} + QDir data_dir = QDir(data_path); -void CMainDialog::SelectMediaPathDialog() -{ - QString media_path = QString::fromStdString(currentConfigApp->m_media_path); - media_path = QFileDialog::getExistingDirectory( - this, - tr("Open Directory"), - media_path, - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks - ); - if (media_path.toStdString() != "") { - currentConfigApp->m_media_path = media_path.toStdString(); + if (data_dir.exists()) { + currentConfigApp->m_cd_path = data_dir.absolutePath().toStdString(); + data_dir.cd(QString("DATA")); + data_dir.cd(QString("disk")); + currentConfigApp->m_base_path = data_dir.absolutePath().toStdString(); m_modified = true; - UpdateInterface(); } + UpdateInterface(); } void CMainDialog::SelectSavePathDialog() @@ -326,38 +301,39 @@ void CMainDialog::SelectSavePathDialog() QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks ); - if (save_path.toStdString() != "") { - currentConfigApp->m_save_path = save_path.toStdString(); + QDir save_dir = QDir(save_path); + + if (save_dir.exists()) { + currentConfigApp->m_save_path = save_dir.absolutePath().toStdString(); m_modified = true; - UpdateInterface(); } -} - -void CMainDialog::DiskPathEdited(const QString& text) -{ - currentConfigApp->m_base_path = text.toStdString(); - m_modified = true; UpdateInterface(); } -void CMainDialog::CDPathEdited(const QString& text) +void CMainDialog::DataPathEdited() { - currentConfigApp->m_cd_path = text.toStdString(); - m_modified = true; + QDir data_dir = QDir(m_ui->dataPath->text()); + + if (data_dir.exists()) { + currentConfigApp->m_cd_path = data_dir.absolutePath().toStdString(); + data_dir.cd(QString("DATA")); + data_dir.cd(QString("disk")); + currentConfigApp->m_base_path = data_dir.absolutePath().toStdString(); + m_modified = true; + } + UpdateInterface(); } -void CMainDialog::MediaPathEdited(const QString& text) +void CMainDialog::SavePathEdited() { - currentConfigApp->m_media_path = text.toStdString(); - m_modified = true; - UpdateInterface(); -} -void CMainDialog::SavePathEdited(const QString& text) -{ - currentConfigApp->m_save_path = text.toStdString(); - m_modified = true; + QDir save_dir = QDir(m_ui->savePath->text()); + + if (save_dir.exists()) { + currentConfigApp->m_save_path = save_dir.absolutePath().toStdString(); + m_modified = true; + } UpdateInterface(); } diff --git a/CONFIG/MainDlg.h b/CONFIG/MainDlg.h index b441248d..65782037 100644 --- a/CONFIG/MainDlg.h +++ b/CONFIG/MainDlg.h @@ -42,16 +42,13 @@ private slots: void OnRadiobuttonTextureHighQuality(bool checked); void OnCheckboxJoystick(bool checked); void OnCheckboxMusic(bool checked); + void OnCheckboxFullscreen(bool checked); void accept() override; void reject() override; - void SelectDiskPathDialog(); - void SelectCDPathDialog(); - void SelectMediaPathDialog(); + void SelectDataPathDialog(); void SelectSavePathDialog(); - void DiskPathEdited(const QString& text); - void CDPathEdited(const QString& text); - void MediaPathEdited(const QString& text); - void SavePathEdited(const QString& text); + void DataPathEdited(); + void SavePathEdited(); void MaxLoDChanged(int value); void MaxActorsChanged(int value); }; diff --git a/CONFIG/config.cpp b/CONFIG/config.cpp index 31e7032b..bd38e669 100644 --- a/CONFIG/config.cpp +++ b/CONFIG/config.cpp @@ -141,7 +141,6 @@ bool CConfigApp::ReadRegisterSettings() } m_base_path = iniparser_getstring(dict, "isle:diskpath", m_base_path.c_str()); m_cd_path = iniparser_getstring(dict, "isle:cdpath", m_cd_path.c_str()); - m_media_path = iniparser_getstring(dict, "isle:mediapath", m_media_path.c_str()); m_save_path = iniparser_getstring(dict, "isle:savepath", m_save_path.c_str()); m_display_bit_depth = iniparser_getint(dict, "isle:Display Bit Depth", -1); m_flip_surfaces = iniparser_getboolean(dict, "isle:Flip Surfaces", m_flip_surfaces); @@ -166,10 +165,6 @@ bool CConfigApp::ValidateSettings() { BOOL is_modified = FALSE; - if (!IsPrimaryDriver() && !m_full_screen) { - m_full_screen = TRUE; - is_modified = TRUE; - } if (IsDeviceInBasicRGBMode()) { if (m_3d_video_ram) { m_3d_video_ram = FALSE; @@ -203,10 +198,6 @@ bool CConfigApp::ValidateSettings() m_3d_video_ram = TRUE; is_modified = TRUE; } - if (!m_full_screen) { - m_full_screen = TRUE; - is_modified = TRUE; - } } if ((m_display_bit_depth != 8 && m_display_bit_depth != 16) && (m_display_bit_depth != 0 || m_full_screen)) { m_display_bit_depth = 16; @@ -302,7 +293,6 @@ void CConfigApp::WriteRegisterSettings() const } iniparser_set(dict, "isle:diskpath", m_base_path.c_str()); iniparser_set(dict, "isle:cdpath", m_cd_path.c_str()); - iniparser_set(dict, "isle:mediapath", m_media_path.c_str()); iniparser_set(dict, "isle:savepath", m_save_path.c_str()); SetIniInt(dict, "isle:Display Bit Depth", m_display_bit_depth); diff --git a/CONFIG/config.h b/CONFIG/config.h index 276dc755..a621655a 100644 --- a/CONFIG/config.h +++ b/CONFIG/config.h @@ -77,7 +77,6 @@ class CConfigApp { std::string m_iniPath; std::string m_base_path; std::string m_cd_path; - std::string m_media_path; std::string m_save_path; float m_max_lod; int m_max_actors; diff --git a/CONFIG/res/maindialog.ui b/CONFIG/res/maindialog.ui index ac69f782..78c5a174 100644 --- a/CONFIG/res/maindialog.ui +++ b/CONFIG/res/maindialog.ui @@ -7,7 +7,7 @@ 0 0 575 - 650 + 600 @@ -44,6 +44,9 @@ 16777215 + + Jaws. + @@ -76,13 +79,7 @@ 0 - - - - - - - + @@ -101,68 +98,8 @@ - - - - - 0 - 0 - - - - - 55 - 16777215 - - - - Open - - - - - - - - 0 - 0 - - - - - 55 - 16777215 - - - - Open - - - - - - - - 0 - 0 - - - - CD Path: - - - Qt::PlainText - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - + 0 @@ -180,26 +117,7 @@ - - - - - 0 - 0 - - - - Disk Path: - - - Qt::PlainText - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + @@ -218,8 +136,22 @@ - - + + + + Path to the game data files. Set this to the CD image root. + + + + + + + Folder where save files are kept. + + + + + 0 @@ -227,7 +159,7 @@ - Media Path: + Data Path: Qt::PlainText @@ -237,9 +169,6 @@ - - - @@ -266,6 +195,9 @@ 120 + + Set 3D model detail level. + Island Model Quality @@ -345,6 +277,9 @@ + + Set texture detail level. + Island Texture Quality @@ -422,16 +357,13 @@ - - - - 0 - - - 0 - + + + + Enable 3D positional audio effects. + 3D Sound @@ -439,6 +371,9 @@ + + Enable in-game background music. + Music @@ -446,11 +381,24 @@ + + Enable joystick and gamepad support for LEGO Island. + Use Joystick + + + + Toggle fullscreen display mode. + + + Fullscreen + + + @@ -462,6 +410,9 @@ 225 + + 3D graphics device used to render the game. + @@ -581,12 +532,8 @@ - diskPath - diskPathOpen - cdPath - cdPathOpen - mediaPath - mediaPathOpen + dataPath + dataPathOpen savePath savePathOpen textureQualityFastRadioButton From 7797729a3ff986de57186445e68ebb1600c6690a Mon Sep 17 00:00:00 2001 From: Carson <57198646+carson-coder@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:27:53 -0400 Subject: [PATCH 04/22] Add option to remove startup delay (#395) * Add startup delay option * Remove build option * Add comment back --- ISLE/isleapp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index b097eb3b..2ca33577 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -911,7 +911,7 @@ inline bool IsleApp::Tick() static MxLong g_lastFrameTime = 0; // GLOBAL: ISLE 0x4101bc - static MxS32 g_startupDelay = 200; + static MxS32 g_startupDelay = 1; if (IsleDebug_Paused() && IsleDebug_StepModeEnabled()) { IsleDebug_SetPaused(false); From 73bab247218fee0f6e5e555a08c6a88a33f105e5 Mon Sep 17 00:00:00 2001 From: VoxelTek <53562267+VoxelTek@users.noreply.github.com> Date: Fri, 27 Jun 2025 00:21:11 +1000 Subject: [PATCH 05/22] Add Launch Game button to config tool, rename executable to isle-config (#435) * Add Launch Game option to config tool * Rename executable from "config" to "isle-config" * Add error popup if unable to find game executable * Use one QMessageBox for both Win and *nix Create one QMessageBox object to use for both Windows and non-Windows platforms. Additionally, set all relevant text during creation of QMessageBox, and show Warning icon as part of message box. * Add tooltips to save, launch, and exit buttons * Change "Launch Game" to "Save and Launch" * Remove unnecessary Windows-specific code --- CMakeLists.txt | 38 +++++++++++++++++++------------------- CONFIG/MainDlg.cpp | 27 +++++++++++++++++++++++++++ CONFIG/MainDlg.h | 1 + CONFIG/res/maindialog.ui | 22 +++++++++++++++++++--- 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 334580c0..1f7e0595 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -525,7 +525,7 @@ 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 +535,22 @@ if (ISLE_BUILD_CONFIG) CONFIG/res/config.rc CONFIG/res/config.qrc ) - target_link_libraries(config PRIVATE Qt6::Core Qt6::Widgets) - set_property(TARGET config PROPERTY AUTOMOC ON) - set_property(TARGET config PROPERTY AUTORCC ON) - set_property(TARGET config PROPERTY AUTOUIC ON) - set_property(TARGET config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res") - list(APPEND isle_targets config) - target_compile_definitions(config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG) - target_include_directories(config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1") - target_include_directories(config PUBLIC "$") + target_link_libraries(isle-config PRIVATE Qt6::Core Qt6::Widgets) + set_property(TARGET isle-config PROPERTY AUTOMOC ON) + set_property(TARGET isle-config PROPERTY AUTORCC ON) + set_property(TARGET isle-config PROPERTY AUTOUIC ON) + set_property(TARGET isle-config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res") + list(APPEND isle_targets isle-config) + target_compile_definitions(isle-config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG) + target_include_directories(isle-config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1") + target_include_directories(isle-config PUBLIC "$") if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14) - target_link_libraries(config PRIVATE DirectX5::DirectX5) + target_link_libraries(isle-config PRIVATE DirectX5::DirectX5) endif() - target_compile_definitions(config PRIVATE DIRECT3D_VERSION=0x500) - target_link_libraries(config PRIVATE SDL3::SDL3 Isle::iniparser) + target_compile_definitions(isle-config PRIVATE DIRECT3D_VERSION=0x500) + target_link_libraries(isle-config PRIVATE SDL3::SDL3 Isle::iniparser) if (NOT ISLE_MINIWIN) - target_link_libraries(config PRIVATE ddraw dxguid) + target_link_libraries(isle-config PRIVATE ddraw dxguid) endif() endif() @@ -564,8 +564,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 +574,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() @@ -633,7 +633,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() diff --git a/CONFIG/MainDlg.cpp b/CONFIG/MainDlg.cpp index b90f1f66..e2c526f6 100644 --- a/CONFIG/MainDlg.cpp +++ b/CONFIG/MainDlg.cpp @@ -11,6 +11,8 @@ #include "res/resource.h" #include +#include +#include #include #include @@ -59,6 +61,7 @@ CMainDialog::CMainDialog(QWidget* pParent) : QDialog(pParent) connect(m_ui->fullscreenCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxFullscreen); connect(m_ui->okButton, &QPushButton::clicked, this, &CMainDialog::accept); connect(m_ui->cancelButton, &QPushButton::clicked, this, &CMainDialog::reject); + connect(m_ui->launchButton, &QPushButton::clicked, this, &CMainDialog::launch); connect(m_ui->dataPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectDataPathDialog); connect(m_ui->savePathOpen, &QPushButton::clicked, this, &CMainDialog::SelectSavePathDialog); @@ -155,6 +158,30 @@ void CMainDialog::accept() QDialog::accept(); } +void CMainDialog::launch() +{ + if (m_modified) { + currentConfigApp->WriteRegisterSettings(); + } + + QDir::setCurrent(QCoreApplication::applicationDirPath()); + + QMessageBox msgBox = QMessageBox( + QMessageBox::Warning, + QString("Error!"), + QString("Unable to locate isle executable!"), + QMessageBox::Close + ); + + if (!QProcess::startDetached("./isle")) { // Check in isle-config directory + if (!QProcess::startDetached("isle")) { // Check in $PATH + msgBox.exec(); + } + } + + QDialog::accept(); +} + // FUNCTION: CONFIG 0x00404360 void CMainDialog::UpdateInterface() { diff --git a/CONFIG/MainDlg.h b/CONFIG/MainDlg.h index 65782037..372a1156 100644 --- a/CONFIG/MainDlg.h +++ b/CONFIG/MainDlg.h @@ -45,6 +45,7 @@ private slots: void OnCheckboxFullscreen(bool checked); void accept() override; void reject() override; + void launch(); void SelectDataPathDialog(); void SelectSavePathDialog(); void DataPathEdited(); diff --git a/CONFIG/res/maindialog.ui b/CONFIG/res/maindialog.ui index 78c5a174..c2760e7c 100644 --- a/CONFIG/res/maindialog.ui +++ b/CONFIG/res/maindialog.ui @@ -508,8 +508,11 @@ + + Save configuration and close the config tool. + - OK + Save and Exit true @@ -517,9 +520,22 @@ - + + + Save configuration and launch LEGO Island. + - Cancel + Save and Launch + + + + + + + Discard changed settings and close the config tool. + + + Exit without saving From a782c489809f7eeae99d8c4fd3509f3497dacdee Mon Sep 17 00:00:00 2001 From: Danct12 Date: Fri, 27 Jun 2025 00:41:46 +0700 Subject: [PATCH 06/22] miniwin: Use PRIu32 for printing uint32_t (#436) Some architecture uses different type for uint32_t. For example: x86_64 | aarch64 = unsigned int xtensa | riscv32 = long unsigned int arm = long unsigned int --- miniwin/src/ddraw/ddraw.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index 2765a2a1..acf695eb 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -75,7 +76,7 @@ HRESULT DirectDrawImpl::CreateSurface( if ((lpDDSurfaceDesc->dwFlags & DDSD_ZBUFFERBITDEPTH) != DDSD_ZBUFFERBITDEPTH) { return DDERR_INVALIDPARAMS; } - SDL_Log("Todo: Set %dbit Z-Buffer", lpDDSurfaceDesc->dwZBufferBitDepth); + SDL_Log("Todo: Set %" PRIu32 "bit Z-Buffer", lpDDSurfaceDesc->dwZBufferBitDepth); *lplpDDSurface = static_cast(new DummySurfaceImpl); return DD_OK; } From b66d1e2f64547d29854f36275ffa9a0fb584f23d Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Thu, 26 Jun 2025 20:31:30 +0200 Subject: [PATCH 07/22] Clear unknowns in `Ambulance` and `AmbulanceMissionState` (#1588) --- LEGO1/lego/legoomni/include/ambulance.h | 46 +++++++---- LEGO1/lego/legoomni/src/actors/ambulance.cpp | 84 ++++++++++---------- LEGO1/lego/legoomni/src/worlds/isle.cpp | 6 +- 3 files changed, 74 insertions(+), 62 deletions(-) diff --git a/LEGO1/lego/legoomni/include/ambulance.h b/LEGO1/lego/legoomni/include/ambulance.h index 97e6ba38..cb6a396a 100644 --- a/LEGO1/lego/legoomni/include/ambulance.h +++ b/LEGO1/lego/legoomni/include/ambulance.h @@ -11,6 +11,12 @@ class MxEndActionNotificationParam; // SIZE 0x24 class AmbulanceMissionState : public LegoState { public: + enum { + e_ready = 0, + e_enteredAmbulance = 1, + e_prepareAmbulance = 2, + }; + AmbulanceMissionState(); // FUNCTION: LEGO1 0x10037440 @@ -125,18 +131,18 @@ class AmbulanceMissionState : public LegoState { // SYNTHETIC: LEGO1 0x100376c0 // AmbulanceMissionState::`scalar deleting destructor' - undefined4 m_unk0x08; // 0x08 - MxLong m_startTime; // 0x0c - MxS16 m_peScore; // 0x10 - MxS16 m_maScore; // 0x12 - MxS16 m_paScore; // 0x14 - MxS16 m_niScore; // 0x16 - MxS16 m_laScore; // 0x18 - MxS16 m_peHighScore; // 0x1a - MxS16 m_maHighScore; // 0x1c - MxS16 m_paHighScore; // 0x1e - MxS16 m_niHighScore; // 0x20 - MxS16 m_laHighScore; // 0x22 + MxU32 m_state; // 0x08 + MxLong m_startTime; // 0x0c + MxS16 m_peScore; // 0x10 + MxS16 m_maScore; // 0x12 + MxS16 m_paScore; // 0x14 + MxS16 m_niScore; // 0x16 + MxS16 m_laScore; // 0x18 + MxS16 m_peHighScore; // 0x1a + MxS16 m_maHighScore; // 0x1c + MxS16 m_paHighScore; // 0x1e + MxS16 m_niHighScore; // 0x20 + MxS16 m_laHighScore; // 0x22 }; // VTABLE: LEGO1 0x100d71a8 @@ -177,15 +183,21 @@ class Ambulance : public IslePathActor { virtual MxLong HandleEndAction(MxEndActionNotificationParam& p_param); // vtable+0xf4 void CreateState(); - void FUN_10036e60(); + void Init(); void ActivateSceneActions(); void StopActions(); - void FUN_10037250(); + void Reset(); // SYNTHETIC: LEGO1 0x10036130 // Ambulance::`scalar deleting destructor' private: + enum { + e_none = 0, + e_waiting = 1, + e_finished = 3, + }; + void PlayAnimation(IsleScript::Script p_objectId); void PlayFinalAnimation(IsleScript::Script p_objectId); void StopAction(IsleScript::Script p_objectId); @@ -196,9 +208,9 @@ class Ambulance : public IslePathActor { AmbulanceMissionState* m_state; // 0x164 MxS16 m_unk0x168; // 0x168 MxS16 m_actorId; // 0x16a - MxS16 m_unk0x16c; // 0x16c - MxS16 m_unk0x16e; // 0x16e - MxS16 m_unk0x170; // 0x170 + MxS16 m_atPoliceTask; // 0x16c + MxS16 m_atBeachTask; // 0x16e + MxS16 m_taskState; // 0x170 MxS16 m_unk0x172; // 0x172 IsleScript::Script m_lastAction; // 0x174 IsleScript::Script m_lastAnimation; // 0x178 diff --git a/LEGO1/lego/legoomni/src/actors/ambulance.cpp b/LEGO1/lego/legoomni/src/actors/ambulance.cpp index 22c1e701..95ca6975 100644 --- a/LEGO1/lego/legoomni/src/actors/ambulance.cpp +++ b/LEGO1/lego/legoomni/src/actors/ambulance.cpp @@ -37,9 +37,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; @@ -70,7 +70,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); } } @@ -170,25 +170,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); @@ -201,18 +201,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); @@ -225,12 +225,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(); } } @@ -241,18 +241,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 && !strcmpi(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 && !strcmpi(roi->GetName(), "gd")) { - m_unk0x170 = 3; + m_taskState = Ambulance::e_finished; return 1; } } @@ -270,9 +270,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); @@ -297,7 +297,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); } @@ -307,7 +307,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); } @@ -315,9 +315,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); @@ -345,9 +345,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); @@ -368,7 +368,7 @@ MxLong Ambulance::HandleClick() return 1; } - if (m_state->m_unk0x08 == 2) { + if (m_state->m_state == AmbulanceMissionState::e_prepareAmbulance) { return 1; } @@ -387,7 +387,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); @@ -398,9 +398,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; @@ -411,7 +411,7 @@ void Ambulance::Exit() { GameState()->m_currentArea = LegoGameState::e_hospitalExterior; StopActions(); - FUN_10037250(); + Reset(); Leave(); } @@ -467,11 +467,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 (rand() % 2) { @@ -571,14 +571,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); @@ -626,7 +626,7 @@ void Ambulance::PlayAction(IsleScript::Script p_objectId) // FUNCTION: LEGO1 0x100373a0 AmbulanceMissionState::AmbulanceMissionState() { - m_unk0x08 = 0; + m_state = AmbulanceMissionState::e_ready; m_startTime = 0; m_peScore = 0; m_maScore = 0; diff --git a/LEGO1/lego/legoomni/src/worlds/isle.cpp b/LEGO1/lego/legoomni/src/worlds/isle.cpp index 050aae97..2eb47874 100644 --- a/LEGO1/lego/legoomni/src/worlds/isle.cpp +++ b/LEGO1/lego/legoomni/src/worlds/isle.cpp @@ -810,7 +810,7 @@ void Isle::Enable(MxBool p_enable) AnimationManager()->EnableCamAnims(FALSE); g_isleFlags &= ~c_playMusic; - m_ambulance->FUN_10036e60(); + m_ambulance->Init(); break; case 11: m_act1state->m_unk0x018 = 0; @@ -1209,7 +1209,7 @@ MxBool Isle::Escape() case 10: if (UserActor() != NULL && !UserActor()->IsA("Ambulance")) { m_ambulance->StopActions(); - m_ambulance->FUN_10037250(); + m_ambulance->Reset(); } break; } @@ -1250,7 +1250,7 @@ void Isle::FUN_10033350() if (m_act1state->m_unk0x018 == 10) { if (UserActor() != NULL && !UserActor()->IsA("Ambulance")) { m_ambulance->StopActions(); - m_ambulance->FUN_10037250(); + m_ambulance->Reset(); } } From 056064f9d4f4d9c69faae65012ff6de0c22f56e7 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Thu, 26 Jun 2025 22:35:04 +0200 Subject: [PATCH 08/22] Clear unknown in `LegoLOD` (#1590) The function does mostly the same as `SetTextureInfo` but does not explicitly set the color as well. --- LEGO1/lego/legoomni/src/common/legocharactermanager.cpp | 2 +- LEGO1/lego/sources/roi/legolod.cpp | 2 +- LEGO1/lego/sources/roi/legolod.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp b/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp index 485d3511..80b69efc 100644 --- a/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp @@ -634,7 +634,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); diff --git a/LEGO1/lego/sources/roi/legolod.cpp b/LEGO1/lego/sources/roi/legolod.cpp index 2291a3e7..bf157b56 100644 --- a/LEGO1/lego/sources/roi/legolod.cpp +++ b/LEGO1/lego/sources/roi/legolod.cpp @@ -351,7 +351,7 @@ LegoResult LegoLOD::SetTextureInfo(LegoTextureInfo* p_textureInfo) } // FUNCTION: LEGO1 0x100aad70 -LegoResult LegoLOD::FUN_100aad70(LegoTextureInfo* p_textureInfo) +LegoResult LegoLOD::UpdateTextureInfo(LegoTextureInfo* p_textureInfo) { for (LegoU32 i = m_meshOffset; i < m_numMeshes; i++) { if (m_melems[i].m_textured) { diff --git a/LEGO1/lego/sources/roi/legolod.h b/LEGO1/lego/sources/roi/legolod.h index 114e377b..e787d377 100644 --- a/LEGO1/lego/sources/roi/legolod.h +++ b/LEGO1/lego/sources/roi/legolod.h @@ -31,7 +31,7 @@ class LegoLOD : public ViewLOD { LegoLOD* Clone(Tgl::Renderer* p_renderer); LegoResult SetColor(LegoFloat p_red, LegoFloat p_green, LegoFloat p_blue, LegoFloat p_alpha); LegoResult SetTextureInfo(LegoTextureInfo* p_textureInfo); - LegoResult FUN_100aad70(LegoTextureInfo* p_textureInfo); + LegoResult UpdateTextureInfo(LegoTextureInfo* p_textureInfo); void ClearMeshOffset(); LegoResult GetTextureInfo(LegoTextureInfo*& p_textureInfo); From a987595e1eea009ffd1b078a2108d60fdf35f295 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Thu, 26 Jun 2025 17:30:24 -0700 Subject: [PATCH 09/22] 1.0 compatibility (#434) * Make Infocenter compatible with 1.0 versions * Fix * Emscripten patch * Fix * Fix Emscripten patch * Window title * Naming * Don't exit car build automatically in 1.0 * Disable character selection by clicking icon in 1.0 * Remove obsolete includes --- ISLE/emscripten/libwasmfs_fetch.js.patch | 18 ++++- ISLE/isleapp.cpp | 29 ++++++++ ISLE/isleapp.h | 1 + LEGO1/lego/legoomni/include/legomain.h | 4 + .../lego/legoomni/src/build/legocarbuild.cpp | 2 +- LEGO1/lego/legoomni/src/main/legomain.cpp | 1 + LEGO1/lego/legoomni/src/worlds/infocenter.cpp | 74 +++++++++++-------- LEGO1/omni/include/mxstring.h | 8 +- 8 files changed, 100 insertions(+), 37 deletions(-) diff --git a/ISLE/emscripten/libwasmfs_fetch.js.patch b/ISLE/emscripten/libwasmfs_fetch.js.patch index eb16caf0..de8fab60 100644 --- a/ISLE/emscripten/libwasmfs_fetch.js.patch +++ b/ISLE/emscripten/libwasmfs_fetch.js.patch @@ -1,5 +1,5 @@ diff --git a/src/lib/libwasmfs_fetch.js b/src/lib/libwasmfs_fetch.js -index e8c9f7e21..1c0eea957 100644 +index e8c9f7e21..caf1971d2 100644 --- a/src/lib/libwasmfs_fetch.js +++ b/src/lib/libwasmfs_fetch.js @@ -38,36 +38,7 @@ addToLibrary({ @@ -89,7 +89,21 @@ index e8c9f7e21..1c0eea957 100644 return Promise.resolve(); } -@@ -164,6 +156,21 @@ addToLibrary({ +@@ -156,14 +148,31 @@ addToLibrary({ + return readLength; + }, + getSize: async (file) => { +- try { +- await getFileRange(file, 0, 0); +- } catch (failedResponse) { +- return 0; ++ if (!(file in wasmFS$JSMemoryRanges)) { ++ try { ++ await getFileRange(file, undefined, undefined); ++ } catch (failedResponse) { ++ return 0; ++ } + } return wasmFS$JSMemoryRanges[file].size; }, }; diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 2ca33577..a5091ece 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -703,6 +703,7 @@ MxResult IsleApp::SetupWindow() return FAILURE; } + DetectGameVersion(); GameState()->SerializePlayersInfo(LegoStorage::c_read); GameState()->SerializeScoreHistory(LegoStorage::c_read); @@ -1098,6 +1099,34 @@ MxResult IsleApp::VerifyFilesystem() return SUCCESS; } +void IsleApp::DetectGameVersion() +{ + const char* file = "/lego/scripts/infocntr/infomain.si"; + SDL_PathInfo info; + bool success = false; + + MxString path = MxString(m_hdPath) + file; + path.MapPathToFilesystem(); + if (!(success = SDL_GetPathInfo(path.GetData(), &info))) { + path = MxString(m_cdPath) + file; + path.MapPathToFilesystem(); + success = SDL_GetPathInfo(path.GetData(), &info); + } + + assert(success); + + // File sizes of INFOMAIN.SI in English 1.0 and Japanese 1.0 + Lego()->SetVersion10(info.size == 58130432 || info.size == 57737216); + + if (Lego()->IsVersion10()) { + SDL_Log("Detected game version 1.0"); + SDL_SetWindowTitle(reinterpret_cast(m_windowHandle), "Lego Island"); + } + else { + SDL_Log("Detected game version 1.1"); + } +} + IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice() { LegoVideoManager* videoManager = LegoOmni::GetInstance()->GetVideoManager(); diff --git a/ISLE/isleapp.h b/ISLE/isleapp.h index d76a291b..2766607f 100644 --- a/ISLE/isleapp.h +++ b/ISLE/isleapp.h @@ -56,6 +56,7 @@ class IsleApp { MxResult ParseArguments(int argc, char** argv); MxResult VerifyFilesystem(); + void DetectGameVersion(); private: char* m_hdPath; // 0x00 diff --git a/LEGO1/lego/legoomni/include/legomain.h b/LEGO1/lego/legoomni/include/legomain.h index fa9a91c5..c27be280 100644 --- a/LEGO1/lego/legoomni/include/legomain.h +++ b/LEGO1/lego/legoomni/include/legomain.h @@ -200,6 +200,9 @@ class LegoOmni : public MxOmni { SDL_PushEvent(&event); } + void SetVersion10(MxBool p_version10) { m_version10 = p_version10; } + MxBool IsVersion10() { return m_version10; } + // SYNTHETIC: LEGO1 0x10058b30 // LegoOmni::`scalar deleting destructor' @@ -221,6 +224,7 @@ class LegoOmni : public MxOmni { MxDSAction m_action; // 0xa0 MxBackgroundAudioManager* m_bkgAudioManager; // 0x134 MxTransitionManager* m_transitionManager; // 0x138 + MxBool m_version10; public: MxBool m_unk0x13c; // 0x13c diff --git a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp index e69415f6..fa0be996 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp @@ -488,7 +488,7 @@ void LegoCarBuild::FUN_100236d0() m_unk0x110 = NULL; m_unk0x100 = 0; - if (m_animPresenter->AllPartsPlaced()) { + if (m_animPresenter->AllPartsPlaced() && !Lego()->IsVersion10()) { // Note the code duplication with LEGO1 0x10025ee0 switch (m_carId) { case 1: diff --git a/LEGO1/lego/legoomni/src/main/legomain.cpp b/LEGO1/lego/legoomni/src/main/legomain.cpp index 3b99f0b5..1695e1c3 100644 --- a/LEGO1/lego/legoomni/src/main/legomain.cpp +++ b/LEGO1/lego/legoomni/src/main/legomain.cpp @@ -76,6 +76,7 @@ void LegoOmni::Init() m_bkgAudioManager = NULL; m_unk0x13c = TRUE; m_transitionManager = NULL; + m_version10 = FALSE; } // FUNCTION: LEGO1 0x10058c30 diff --git a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp index f9bfb4eb..c007dc96 100644 --- a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp +++ b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp @@ -306,29 +306,32 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) if (!m_unk0x1d4) { PlayMusic(JukeboxScript::c_InformationCenter_Music); - GameState()->SetActor(m_selectedCharacter); - switch (m_selectedCharacter) { - case e_pepper: - PlayAction(InfomainScript::c_avo901in_RunAnim); - break; - case e_mama: - PlayAction(InfomainScript::c_avo902in_RunAnim); - break; - case e_papa: - PlayAction(InfomainScript::c_avo903in_RunAnim); - break; - case e_nick: - PlayAction(InfomainScript::c_avo904in_RunAnim); - break; - case e_laura: - PlayAction(InfomainScript::c_avo905in_RunAnim); - break; - default: - break; + if (!Lego()->IsVersion10()) { + GameState()->SetActor(m_selectedCharacter); + + switch (m_selectedCharacter) { + case e_pepper: + PlayAction(InfomainScript::c_avo901in_RunAnim); + break; + case e_mama: + PlayAction(InfomainScript::c_avo902in_RunAnim); + break; + case e_papa: + PlayAction(InfomainScript::c_avo903in_RunAnim); + break; + case e_nick: + PlayAction(InfomainScript::c_avo904in_RunAnim); + break; + case e_laura: + PlayAction(InfomainScript::c_avo905in_RunAnim); + break; + default: + break; + } + + UpdateFrameHot(TRUE); } - - UpdateFrameHot(TRUE); } } @@ -338,7 +341,7 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) return result; } - if (action->GetObjectId() == InfomainScript::c_iicx26in_RunAnim) { + if (action->GetObjectId() == InfomainScript::c_iicx26in_RunAnim - Lego()->IsVersion10()) { ControlManager()->FUN_100293c0(InfomainScript::c_BigInfo_Ctl, action->GetAtomId().GetInternal(), 0); m_unk0x1d6 = 0; } @@ -478,7 +481,7 @@ void Infocenter::ReadyWorld() InfomainScript::Script script = m_infocenterState->GetNextReturnDialogue(); PlayAction(script); - if (script == InfomainScript::c_iicx26in_RunAnim) { + if (script == InfomainScript::c_iicx26in_RunAnim - Lego()->IsVersion10()) { m_unk0x1d6 = 1; } @@ -1186,13 +1189,13 @@ MxLong Infocenter::HandleNotification0(MxNotificationParam& p_param) m_currentInfomainScript == InfomainScript::c_Pepper_All_Movie || m_currentInfomainScript == InfomainScript::c_Nick_All_Movie || m_currentInfomainScript == InfomainScript::c_Laura_All_Movie || - m_currentInfomainScript == InfomainScript::c_iic007ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ijs002ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_irt001ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ipz006ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_igs004ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_iho003ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ips005ra_PlayWav) { + m_currentInfomainScript == InfomainScript::c_iic007ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ijs002ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_irt001ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ipz006ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_igs004ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_iho003ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ips005ra_PlayWav - Lego()->IsVersion10()) { StopCurrentAction(); } } @@ -1506,6 +1509,17 @@ void Infocenter::StopCredits() // FUNCTION: BETA10 0x1002ee8c void Infocenter::PlayAction(InfomainScript::Script p_script) { + if (Lego()->IsVersion10()) { + if (p_script == InfomainScript::c_iicx18in_RunAnim) { + // Alternative dialogue after signing in (1.0 version) + p_script = InfomainScript::c_iic016in_RunAnim; + } + else if (p_script > InfomainScript::c_iicx18in_RunAnim) { + // Shift all other actions by 1 + p_script = (InfomainScript::Script)((int) p_script - 1); + } + } + MxDSAction action; action.SetObjectId(p_script); action.SetAtomId(*g_infomainScript); diff --git a/LEGO1/omni/include/mxstring.h b/LEGO1/omni/include/mxstring.h index eaa58181..b4b25262 100644 --- a/LEGO1/omni/include/mxstring.h +++ b/LEGO1/omni/include/mxstring.h @@ -20,10 +20,10 @@ class MxString : public MxCore { void ToLowerCase(); void MapPathToFilesystem() { MapPathToFilesystem(m_data); } - MxString& operator=(const MxString& p_str); - const MxString& operator=(const char* p_str); - MxString operator+(const MxString& p_str) const; - MxString operator+(const char* p_str) const; + LEGO1_EXPORT MxString& operator=(const MxString& p_str); + LEGO1_EXPORT const MxString& operator=(const char* p_str); + LEGO1_EXPORT MxString operator+(const MxString& p_str) const; + LEGO1_EXPORT MxString operator+(const char* p_str) const; LEGO1_EXPORT MxString& operator+=(const char* p_str); static void CharSwap(char* p_a, char* p_b); From f80a55d4fbd58f0b5ba955156008fc4b6dd77be3 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Thu, 26 Jun 2025 21:00:38 +0200 Subject: [PATCH 10/22] Implement 2D rendering via Citro3D --- ISLE/isledebug.cpp | 2 +- .../src/d3drm/backends/citro3d/renderer.cpp | 517 +++++++++--------- .../src/d3drm/backends/citro3d/vshader.v.pica | 51 +- .../src/d3drm/backends/directx9/renderer.cpp | 2 +- .../src/d3drm/backends/opengl1/renderer.cpp | 2 +- .../src/d3drm/backends/opengles2/renderer.cpp | 2 +- miniwin/src/internal/d3drmrenderer_citro3d.h | 26 +- 7 files changed, 301 insertions(+), 301 deletions(-) diff --git a/ISLE/isledebug.cpp b/ISLE/isledebug.cpp index a8950fa3..ec27004b 100644 --- a/ISLE/isledebug.cpp +++ b/ISLE/isledebug.cpp @@ -309,7 +309,7 @@ void IsleDebug_Render() if (ImGui::TreeNode("Sound Manager")) { LegoSoundManager* soundManager = lego->GetSoundManager(); Sint32 oldVolume = soundManager->GetVolume(); - Sint32 volume = oldVolume; + int volume = oldVolume; ImGui::SliderInt("volume", &volume, 0, 100); if (volume != oldVolume) { soundManager->SetVolume(volume); diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 5db989cd..8ee2f01e 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -4,6 +4,7 @@ #include "miniwin/d3d.h" #include "miniwin/d3drm.h" #include "miniwin/windows.h" +#include "vshader_shbin.h" #include <3ds/console.h> #include <3ds/gfx.h> @@ -13,220 +14,120 @@ #include #include #include -#include <3ds.h> #include +#include #include -#include "vshader_shbin.h" +bool g_rendering = false; -int projectionShaderUniformLocation, modelViewUniformLocation; +#define DISPLAY_TRANSFER_FLAGS \ + (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)) -typedef struct { - float positions[3]; - float texcoords[2]; - float normals[3]; -} Vertex; +static DVLB_s* vshader_dvlb; +static shaderProgram_s program; +static int uLoc_projection; +static int uLoc_modelView; +static int uLoc_meshColor; +C3D_RenderTarget* target; -// from this wiki: https://github.com/tommai78101/homebrew/wiki/Version-002:-Core-Engine -static const Vertex vertexList[] = +static void sceneInit(void) { - // First face (PZ) - // First triangle - { {-0.5f, -0.5f, +0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, +1.0f} }, - { {+0.5f, -0.5f, +0.5f}, {1.0f, 0.0f}, {0.0f, 0.0f, +1.0f} }, - { {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, +1.0f} }, - // Second triangle - { {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, +1.0f} }, - { {-0.5f, +0.5f, +0.5f}, {0.0f, 1.0f}, {0.0f, 0.0f, +1.0f} }, - { {-0.5f, -0.5f, +0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, +1.0f} }, + // Load the vertex shader, create a shader program and bind it + vshader_dvlb = DVLB_ParseFile((u32*) vshader_shbin, vshader_shbin_size); + shaderProgramInit(&program); + shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]); + C3D_BindProgram(&program); - // Second face (MZ) - // First triangle - { {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, -1.0f} }, - { {-0.5f, +0.5f, -0.5f}, {1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} }, - { {+0.5f, +0.5f, -0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, -1.0f} }, - // Second triangle - { {+0.5f, +0.5f, -0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, -1.0f} }, - { {+0.5f, -0.5f, -0.5f}, {0.0f, 1.0f}, {0.0f, 0.0f, -1.0f} }, - { {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, -1.0f} }, + // Get the location of the uniforms + uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection"); + uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView"); + uLoc_meshColor = shaderInstanceGetUniformLocation(program.vertexShader, "meshColor"); - // Third face (PX) - // First triangle - { {+0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} }, - { {+0.5f, +0.5f, -0.5f}, {1.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} }, - { {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} }, - // Second triangle - { {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} }, - { {+0.5f, -0.5f, +0.5f}, {0.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} }, - { {+0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} }, - - // Fourth face (MX) - // First triangle - { {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} }, - { {-0.5f, -0.5f, +0.5f}, {1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} }, - { {-0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} }, - // Second triangle - { {-0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} }, - { {-0.5f, +0.5f, -0.5f}, {0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} }, - { {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} }, - - // Fifth face (PY) - // First triangle - { {-0.5f, +0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, +1.0f, 0.0f} }, - { {-0.5f, +0.5f, +0.5f}, {1.0f, 0.0f}, {0.0f, +1.0f, 0.0f} }, - { {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, +1.0f, 0.0f} }, - // Second triangle - { {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, +1.0f, 0.0f} }, - { {+0.5f, +0.5f, -0.5f}, {0.0f, 1.0f}, {0.0f, +1.0f, 0.0f} }, - { {-0.5f, +0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, +1.0f, 0.0f} }, - - // Sixth face (MY) - // First triangle - { {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} }, - { {+0.5f, -0.5f, -0.5f}, {1.0f, 0.0f}, {0.0f, -1.0f, 0.0f} }, - { {+0.5f, -0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, -1.0f, 0.0f} }, - // Second triangle - { {+0.5f, -0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, -1.0f, 0.0f} }, - { {-0.5f, -0.5f, +0.5f}, {0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} }, - { {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} }, -}; - -void *vbo_data; -void sceneInit(shaderProgram_s* prog) { - MINIWIN_TRACE("set uniform loc"); - projectionShaderUniformLocation = shaderInstanceGetUniformLocation(prog->vertexShader, "projection"); - modelViewUniformLocation = shaderInstanceGetUniformLocation(prog->vertexShader, "modelView"); - - // src: https://github.com/devkitPro/citro3d/blob/9f21cf7b380ce6f9e01a0420f19f0763e5443ca7/test/3ds/source/main.cpp#L122C3-L126C62 - MINIWIN_TRACE("pre attr info"); + // Configure attributes for use with the vertex shader + // Attribute format and element count are ignored in immediate mode C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); - AttrInfo_Init(attrInfo); + AttrInfo_Init(attrInfo); AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // v2=normal - MINIWIN_TRACE("pre alloc"); - vbo_data = linearAlloc(sizeof(vertexList)); - memcpy(vbo_data, vertexList, sizeof(vertexList)); - - //Initialize and configure buffers. - MINIWIN_TRACE("pre buf"); - C3D_BufInfo* bufferInfo = C3D_GetBufInfo(); - BufInfo_Init(bufferInfo); - BufInfo_Add(bufferInfo, vbo_data, sizeof(Vertex), 3, 0x210); - - // this is probably wrong - MINIWIN_TRACE("pre tex"); + // Configure the first fragment shading substage to just pass through the vertex color + // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight 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); } +static void sceneExit(void) +{ + // Free the shader program + shaderProgramFree(&program); + DVLB_Free(vshader_dvlb); +} + Direct3DRMRenderer* Citro3DRenderer::Create(DWORD width, DWORD height) { - // TODO: Doesn't SDL call this function? - // Actually it's in ctrulib -max - gfxInitDefault(); - gfxSetWide(true); - gfxSet3D(false); - consoleInit(GFX_BOTTOM, nullptr); + gfxInitDefault(); + consoleInit(GFX_BOTTOM, nullptr); C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + // Initialize the render target + target = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + + // Initialize the scene + sceneInit(); + return new Citro3DRenderer(width, height); } -// constructor parameters not finalized Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) { - static shaderProgram_s program; - DVLB_s *vsh_dvlb; - + SDL_Log("Citro3DRenderer %dx%d", width, height); m_width = width; - m_height = height / 2; + m_height = height; m_virtualWidth = width; - m_virtualHeight = height / 2; - - // FIXME: is this the right pixel format? - - shaderProgramInit(&program); - vsh_dvlb = DVLB_ParseFile((u32*)vshader_shbin, vshader_shbin_size); - shaderProgramSetVsh(&program, &vsh_dvlb->DVLE[0]); - - // WARNING: This might crash, not sure - SDL_Log("pre bind"); - C3D_BindProgram(&program); - - // todo: move to scene init next - SDL_Log("setting uniform loc"); - sceneInit(&program); - - // TODO: is GPU_RB_RGBA8 correct? - // TODO: is GPU_RB_DEPTH24_STENCIL8 correct? - m_renderTarget = C3D_RenderTargetCreate(width, height, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - - // TODO: what color should be used, if we shouldn't use 0x777777FF - C3D_RenderTargetClear(m_renderTarget, C3D_CLEAR_ALL, 0x777777FF, 0); - - // TODO: Cleanup as we see what is needed - m_flipVertFlag = 0; - m_outTiledFlag = 0; - m_rawCopyFlag = 0; - - // TODO: correct values? - m_transferInputFormatFlag = GX_TRANSFER_FMT_RGBA8; - m_transferOutputFormatFlag = GX_TRANSFER_FMT_RGB8; - - m_transferScaleFlag = GX_TRANSFER_SCALE_NO; - - m_transferFlags = (GX_TRANSFER_FLIP_VERT(m_flipVertFlag) | GX_TRANSFER_OUT_TILED(m_outTiledFlag) | \ - GX_TRANSFER_RAW_COPY(m_rawCopyFlag) | GX_TRANSFER_IN_FORMAT(m_transferInputFormatFlag) | \ - GX_TRANSFER_OUT_FORMAT(m_transferOutputFormatFlag) | GX_TRANSFER_SCALING(m_transferScaleFlag)); - - C3D_RenderTargetSetOutput(m_renderTarget, GFX_TOP, GFX_LEFT, m_transferFlags); - - m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); - MINIWIN_NOT_IMPLEMENTED(); + m_virtualHeight = height; } Citro3DRenderer::~Citro3DRenderer() { - SDL_DestroySurface(m_renderedImage); - C3D_RenderTargetDelete(m_renderTarget); + sceneExit(); + C3D_Fini(); + gfxExit(); } void Citro3DRenderer::PushLights(const SceneLight* lightsArray, size_t count) { - MINIWIN_NOT_IMPLEMENTED(); } void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) { - MINIWIN_TRACE("Set projection"); - memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); } void Citro3DRenderer::SetFrustumPlanes(const Plane* frustumPlanes) { - MINIWIN_NOT_IMPLEMENTED(); } -struct TextureDestroyContextC3D { +struct TextureDestroyContextCitro { Citro3DRenderer* renderer; Uint32 textureId; }; void Citro3DRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) { - auto* ctx = new TextureDestroyContextC3D{this, id}; + auto* ctx = new TextureDestroyContextCitro{this, id}; texture->AddDestroyCallback( [](IDirect3DRMObject* obj, void* arg) { - auto* ctx = static_cast(arg); - auto& cache = ctx->renderer->m_textures[ctx->textureId]; - if (cache.c3dTex != nullptr) { - C3D_TexDelete(cache.c3dTex); - cache.c3dTex = nullptr; - cache.texture = nullptr; + auto* ctx = static_cast(arg); + auto& entry = ctx->renderer->m_textures[ctx->textureId]; + if (entry.texture) { + C3D_TexDelete(&entry.c3dTex); + entry.texture = nullptr; } delete ctx; }, @@ -234,89 +135,180 @@ void Citro3DRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* t ); } +static int NearestPowerOfTwoClamp(int val) +{ + static const int sizes[] = {512, 256, 128, 64, 32, 16, 8}; + for (int size : sizes) { + if (val >= size) { + return size; + } + } + return 8; +} + +static SDL_Surface* ConvertAndResizeSurface(SDL_Surface* original) +{ + SDL_Surface* converted = SDL_ConvertSurface(original, SDL_PIXELFORMAT_RGBA8888); + if (!converted) { + return nullptr; + } + + int newW = NearestPowerOfTwoClamp(converted->w); + int newH = NearestPowerOfTwoClamp(converted->h); + + if (converted->w == newW && converted->h == newH) { + return converted; + } + + SDL_Surface* resized = SDL_CreateSurface(newW, newH, SDL_PIXELFORMAT_RGBA8888); + if (!resized) { + SDL_DestroySurface(converted); + return nullptr; + } + + SDL_BlitSurfaceScaled(converted, nullptr, resized, nullptr, SDL_SCALEMODE_NEAREST); + + SDL_DestroySurface(converted); + return resized; +} + +inline int mortonInterleave(int x, int y) +{ + int answer = 0; + for (int i = 0; i < 3; ++i) { + answer |= ((y >> i) & 1) << (2 * i + 1); + answer |= ((x >> i) & 1) << (2 * i); + } + return answer; +} + +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; + + 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 mortonIndex = mortonInterleave(x, y); + int dstIndex = (tileIndex + mortonIndex) * bytesPerPixel; + int srcIndex = (srcY * width + srcX) * bytesPerPixel; + + std::memcpy(&dst[dstIndex], &src[srcIndex], bytesPerPixel); + } + } + } + } +} + +static bool ConvertAndUploadTexture(C3D_Tex* tex, SDL_Surface* originalSurface) +{ + SDL_Surface* resized = ConvertAndResizeSurface(originalSurface); + if (!resized) { + return false; + } + + int width = resized->w; + int height = resized->h; + + if (!C3D_TexInit(tex, width, height, GPU_RGBA8)) { + SDL_DestroySurface(resized); + return false; + } + + // Allocate buffer for tiled texture + uint8_t* tiledData = (uint8_t*) malloc(width * height * 4); + if (!tiledData) { + SDL_DestroySurface(resized); + return false; + } + + EncodeTextureLayout((const u8*) resized->pixels, tiledData, width, height); + + C3D_TexUpload(tex, tiledData); + C3D_TexSetFilter(tex, GPU_LINEAR, GPU_NEAREST); + + free(tiledData); + SDL_DestroySurface(resized); + return true; +} + Uint32 Citro3DRenderer::GetTextureId(IDirect3DRMTexture* iTexture) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); + SDL_Surface* originalSurface = surface->m_surface; + + int originalW = originalSurface->w; + int originalH = originalSurface->h; for (Uint32 i = 0; i < m_textures.size(); ++i) { auto& tex = m_textures[i]; if (tex.texture == texture) { if (tex.version != texture->m_version) { - // This, for some reason, causes the app to close - // instead of crashing the cpu, useful for - // debugging :) - for(i = 0; i < 10000000; i++) - printf("HI IM DAISY"); - - C3D_TexDelete(tex.c3dTex); - - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); - if (!surf) { + C3D_TexDelete(&tex.c3dTex); + if (!ConvertAndUploadTexture(&tex.c3dTex, originalSurface)) { return NO_TEXTURE_ID; } - // TODO: C3D_TexGenerateMipmap or C3D_TexInit? - // glGenTextures(1, &tex.glTextureId); - // FIXME: GPU_RGBA8 may be wrong - C3D_TexInit(tex.c3dTex, surf->w, surf->h, GPU_RGBA8); - - C3D_TexBind(0, tex.c3dTex); - C3D_TexUpload(tex.c3dTex, surf->pixels); - SDL_DestroySurface(surf); tex.version = texture->m_version; + tex.width = originalW; + tex.height = originalH; } return i; } } - C3D_Tex newTex; + C3DTextureCacheEntry entry; + entry.texture = texture; + entry.version = texture->m_version; + entry.width = originalW; + entry.height = originalH; - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); - if (!surf) { + if (!ConvertAndUploadTexture(&entry.c3dTex, originalSurface)) { return NO_TEXTURE_ID; } - C3D_TexInit(&newTex, surf->w, surf->h, GPU_RGBA8); - C3D_TexBind(0, &newTex); - C3D_TexUpload(&newTex, surf->pixels); - SDL_DestroySurface(surf); for (Uint32 i = 0; i < m_textures.size(); ++i) { - auto& tex = m_textures[i]; - if (!tex.texture) { - tex.texture = texture; - tex.version = texture->m_version; - tex.c3dTex = &newTex; + if (!m_textures[i].texture) { + m_textures[i] = std::move(entry); AddTextureDestroyCallback(i, texture); return i; } } - m_textures.push_back({texture, texture->m_version, &newTex}); + m_textures.push_back(std::move(entry)); AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture); return (Uint32) (m_textures.size() - 1); } Uint32 Citro3DRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) { - MINIWIN_NOT_IMPLEMENTED(); return 0; } void Citro3DRenderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) { - // not sure if this is correct? - MINIWIN_NOT_IMPLEMENTED(); - halDesc->dcmColorModel = D3DCOLORMODEL::RGB; - helDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; - helDesc->dwDeviceZBufferBitDepth = DDBD_24; - helDesc->dwDeviceRenderBitDepth = DDBD_24; - helDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; - helDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; - - // TODO: shouldn't this be bilinear - helDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + 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; memset(helDesc, 0, sizeof(D3DDEVICEDESC)); } @@ -326,20 +318,23 @@ const char* Citro3DRenderer::GetName() return "Citro3D"; } +void StartFrame() +{ + if (g_rendering) { + return; + } + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C3D_FrameDrawOn(target); +} + HRESULT Citro3DRenderer::BeginFrame() { - MINIWIN_NOT_IMPLEMENTED(); - gfxFlushBuffers(); - gfxSwapBuffers(); - gspWaitForVBlank(); // FIXME: is this the right place to call, if we should at all? - C3D_FrameBegin(C3D_FRAME_SYNCDRAW); - C3D_FrameDrawOn(m_renderTarget); + StartFrame(); return S_OK; } void Citro3DRenderer::EnableTransparency() { - MINIWIN_NOT_IMPLEMENTED(); } void Citro3DRenderer::SubmitDraw( @@ -351,80 +346,104 @@ void Citro3DRenderer::SubmitDraw( const Appearance& appearance ) { - MINIWIN_NOT_IMPLEMENTED(); } HRESULT Citro3DRenderer::FinalizeFrame() { - MINIWIN_NOT_IMPLEMENTED(); - Mtx_PerspStereoTilt(&this->m_projectionMatrix, 40.0f * (acos(-1) / 180.0f), 400.0f / 240.0f, 0.01f, 1000.0f, 1, 2.0f, false); - Mtx_Translate(&this->m_projectionMatrix, 0.0, 0.0, -10.0, 0); - - //Calculate model view matrix. - C3D_Mtx modelView; - Mtx_Identity(&modelView); - // Mtx_Translate(&modelView, 0.0, 0.0, -2.0 + sinf(this->angleX)); - // Mtx_RotateX(&modelView, this->angleX, true); - // Mtx_RotateY(&modelView, this->angleY, true); - - // if (interOcularDistance >= 0.0f){ - // this->angleX += radian; - // this->angleY += radian; - // } - - //Update uniforms - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, m_projectionShaderUniformLocation, &this->m_projectionMatrix); - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, modelViewUniformLocation, &modelView); - - //Draw the vertex buffer objects. - C3D_DrawArrays(GPU_TRIANGLES, 0, sizeof(vertexList)); - C3D_FrameEnd(0); return S_OK; } void Citro3DRenderer::Resize(int width, int height, const ViewportTransform& viewportTransform) { - MINIWIN_NOT_IMPLEMENTED(); m_width = width; m_height = height; m_viewportTransform = viewportTransform; - - SDL_DestroySurface(m_renderedImage); - // FIXME: is this the right pixel format? - m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); } void Citro3DRenderer::Clear(float r, float g, float b) { - // FIXME: check colors - C3D_RenderTargetClear(m_renderTarget, C3D_CLEAR_ALL, RGB(static_cast(r * 255), static_cast(g * 255), static_cast(b * 255)), 0); + u32 color = + (static_cast(r * 255) << 24) | (static_cast(g * 255) << 16) | (static_cast(b * 255) << 8) | 255; + C3D_RenderTargetClear(target, C3D_CLEAR_ALL, color, 0); } void Citro3DRenderer::Flip() { - MINIWIN_NOT_IMPLEMENTED(); + C3D_FrameEnd(0); } - void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) { - MINIWIN_NOT_IMPLEMENTED(); - MINIWIN_TRACE("on draw 2d image"); + 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 mtx; + 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); - // TODO: isLeftHanded set to false. Should it be true? - MINIWIN_TRACE("pre orthotilt"); - Mtx_OrthoTilt(&mtx, left, right, bottom, top, -1, 1, false); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_meshColor, 1.0f, 1.0f, 1.0f, 1.0f); - MINIWIN_TRACE("pre fvunifmtx4x4"); - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, projectionShaderUniformLocation, &mtx); + 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); + + // Use dstRect size directly for quad size + float quadW = static_cast(dstRect.w); + float quadH = static_cast(dstRect.h); + + float x1 = static_cast(dstRect.x); + float y1 = static_cast(dstRect.y); + float x2 = x1 + quadW; + float y2 = y1 + quadH; + + float u0 = static_cast(srcRect.x) / texture.width; + float u1 = static_cast(srcRect.x + srcRect.w) / texture.width; + float v1 = 1.0f - static_cast(srcRect.y + srcRect.h) / texture.height; + float v0 = 1.0f - static_cast(srcRect.y) / texture.height; + + C3D_ImmDrawBegin(GPU_TRIANGLES); + + // Triangle 1 + C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f); + C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + + C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f); + C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + + C3D_ImmSendAttrib(x2, y1, 0.5f, 0.0f); + C3D_ImmSendAttrib(u1, v0, 0.0f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + + // Triangle 2 + C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f); + C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + + C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f); + C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + + C3D_ImmSendAttrib(x1, y2, 0.5f, 0.0f); + C3D_ImmSendAttrib(u0, v1, 0.0f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + + C3D_ImmDrawEnd(); } void Citro3DRenderer::Download(SDL_Surface* target) { - MINIWIN_NOT_IMPLEMENTED(); } diff --git a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica index 60a43fc1..e1d39e8d 100644 --- a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica +++ b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica @@ -5,12 +5,7 @@ ; Example PICA200 vertex shader ; Uniforms -.fvec projection[4], modelView[4], texView[2] -.fvec lightVec, lightHalfVec, lightClr, material[4] -.alias mat_amb material[0] -.alias mat_dif material[1] -.alias mat_spe material[2] -.alias mat_emi material[3] +.fvec projection[4], modelView[4], normView[2], meshColor ; Constants .constf myconst(0.0, 1.0, -1.0, -0.5) @@ -45,48 +40,10 @@ dp4 outpos.w, projection[3], r1 ; outtex = intex - dp4 outtc0.x, texView[0], intex - dp4 outtc0.y, texView[1], intex + mov outtc0, intex mov outtc0.zw, myconst.xy - - ; Transform the normal vector with the modelView matrix - ; r1 = normalize(modelView * innrm) - mov r0.xyz, innrm - mov r0.w, zeros - dp4 r1.x, modelView[0], r0 - dp4 r1.y, modelView[1], r0 - dp4 r1.z, modelView[2], r0 - mov r1.w, zeros - dp3 r2, r1, r1 ; r2 = x^2+y^2+z^2 for each component - rsq r2, r2 ; r2 = 1/sqrt(r2) '' - mul r1, r2, r1 ; r1 = r1*r2 - - ; Calculate the diffuse level (r0.x) and the shininess level (r0.y) - ; r0.x = max(0, -(lightVec * r1)) - ; r0.y = max(0, (-lightHalfVec[i]) * r1) ^ 2 - dp3 r0.x, lightVec, r1 - add r0.x, zeros, -r0 - dp3 r0.y, -lightHalfVec, r1 - max r0, zeros, r0 - mul r0.y, r0, r0 - - ; Accumulate the vertex color in r1, initializing it to the emission color - mov r1, mat_emi - - ; r1 += specularColor * lightClr * shininessLevel - mul r2, lightClr, r0.yyyy - mad r1, r2, mat_spe, r1 - - ; r1 += diffuseColor * lightClr * diffuseLevel - mul r2, lightClr, r0.xxxx - mad r1, r2, mat_dif, r1 - - ; r1 += ambientColor * lightClr - mov r2, lightClr - mad r1, r2, mat_amb, r1 - - ; outclr = clamp r1 to [0,1] - min outclr, ones, r1 + ; outclr = ones + mov outclr, meshColor ; We're finished end diff --git a/miniwin/src/d3drm/backends/directx9/renderer.cpp b/miniwin/src/d3drm/backends/directx9/renderer.cpp index 574939f5..15d13594 100644 --- a/miniwin/src/d3drm/backends/directx9/renderer.cpp +++ b/miniwin/src/d3drm/backends/directx9/renderer.cpp @@ -221,7 +221,7 @@ void DirectX9Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) halDesc->dcmColorModel = D3DCOLORMODEL::RGB; halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; halDesc->dwDeviceZBufferBitDepth = DDBD_24; - helDesc->dwDeviceRenderBitDepth = DDBD_32; + halDesc->dwDeviceRenderBitDepth = DDBD_32; halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; diff --git a/miniwin/src/d3drm/backends/opengl1/renderer.cpp b/miniwin/src/d3drm/backends/opengl1/renderer.cpp index cc415de8..dc4f5849 100644 --- a/miniwin/src/d3drm/backends/opengl1/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengl1/renderer.cpp @@ -314,7 +314,7 @@ void OpenGL1Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) halDesc->dcmColorModel = D3DCOLORMODEL::RGB; halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; halDesc->dwDeviceZBufferBitDepth = DDBD_24; - helDesc->dwDeviceRenderBitDepth = DDBD_32; + halDesc->dwDeviceRenderBitDepth = DDBD_32; halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; diff --git a/miniwin/src/d3drm/backends/opengles2/renderer.cpp b/miniwin/src/d3drm/backends/opengles2/renderer.cpp index 3dbabda2..34fcda90 100644 --- a/miniwin/src/d3drm/backends/opengles2/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengles2/renderer.cpp @@ -421,7 +421,7 @@ void OpenGLES2Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) halDesc->dwDeviceZBufferBitDepth |= DDBD_32; } } - helDesc->dwDeviceRenderBitDepth = DDBD_32; + halDesc->dwDeviceRenderBitDepth = DDBD_32; halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index bad5d18d..254303d7 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -15,9 +15,28 @@ DEFINE_GUID(Citro3D_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x struct C3DTextureCacheEntry { IDirect3DRMTexture* texture; Uint32 version; - C3D_Tex* c3dTex; + C3D_Tex c3dTex; + uint16_t width; + uint16_t height; }; +struct C3DMeshCacheEntry { + const MeshGroup* meshGroup = nullptr; + Uint32 version = 0; + bool flat = false; + + C3D_AttrInfo attrInfo; + C3D_BufInfo bufInfo; + + // CPU-side vertex data + std::vector vertices; + std::vector positions; + std::vector normals; + std::vector texcoords; // Only if you have textures + std::vector indices; // Indices for indexed drawing +}; + + class Citro3DRenderer : public Direct3DRMRenderer { public: static Direct3DRMRenderer* Create(DWORD width, DWORD height); @@ -49,14 +68,19 @@ class Citro3DRenderer : public Direct3DRMRenderer { void Flip() override; void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) override; void Download(SDL_Surface* target) override; + private: + C3DMeshCacheEntry UploadMesh(const MeshGroup& meshGroup); void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); + void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); D3DRMMATRIX4D m_projection; C3D_Mtx m_projectionMatrix; SDL_Surface* m_renderedImage; C3D_RenderTarget* m_renderTarget; int m_projectionShaderUniformLocation; std::vector m_textures; + std::vector m_meshs; + ViewportTransform m_viewportTransform; // TODO: All these flags can likely be cleaned up bool m_flipVertFlag; From cf2435134d66cd4cc73d15f91ac63a6658a281e3 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Fri, 27 Jun 2025 19:18:25 +0200 Subject: [PATCH 11/22] Render world content, sort of --- .../src/d3drm/backends/citro3d/renderer.cpp | 79 ++++++++++++++----- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 8ee2f01e..5624ccf1 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -32,6 +32,22 @@ static int uLoc_modelView; static int uLoc_meshColor; C3D_RenderTarget* target; +typedef struct { + float position[3]; + float texcoord[2]; + float normal[3]; +} vertex; + +static void* vbo_data_pos; + +static const vertex position_list[] = { + {{200.0f, 200.0f, 0.5f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, + {{100.0f, 40.0f, 0.5f}, {0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}}, + {{300.0f, 40.0f, 0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}, +}; + +_Static_assert(sizeof(vbo_data_pos) % 4 == 0, "vertex size not 4-byte aligned"); + static void sceneInit(void) { // Load the vertex shader, create a shader program and bind it @@ -45,32 +61,21 @@ static void sceneInit(void) uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView"); uLoc_meshColor = shaderInstanceGetUniformLocation(program.vertexShader, "meshColor"); - // Configure attributes for use with the vertex shader - // Attribute format and element count are ignored in immediate mode C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); AttrInfo_Init(attrInfo); - AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position - AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord - AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // v2=normal + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord + AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // v2=normal - // Configure the first fragment shading substage to just pass through the vertex color - // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight - 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_Mtx projection; + Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0, true); -static void sceneExit(void) -{ - // Free the shader program - shaderProgramFree(&program); - DVLB_Free(vshader_dvlb); + vbo_data_pos = linearAlloc(sizeof(position_list)); + memcpy(vbo_data_pos, position_list, sizeof(position_list)); } Direct3DRMRenderer* Citro3DRenderer::Create(DWORD width, DWORD height) { - gfxInitDefault(); consoleInit(GFX_BOTTOM, nullptr); C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); @@ -79,7 +84,6 @@ Direct3DRMRenderer* Citro3DRenderer::Create(DWORD width, DWORD height) target = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); - // Initialize the scene sceneInit(); return new Citro3DRenderer(width, height); @@ -96,7 +100,8 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) Citro3DRenderer::~Citro3DRenderer() { - sceneExit(); + shaderProgramFree(&program); + DVLB_Free(vshader_dvlb); C3D_Fini(); gfxExit(); } @@ -330,6 +335,7 @@ void StartFrame() HRESULT Citro3DRenderer::BeginFrame() { StartFrame(); + // C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_COLOR); return S_OK; } @@ -346,6 +352,37 @@ void Citro3DRenderer::SubmitDraw( const Appearance& appearance ) { + C3D_Mtx projection, modelView; + Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0, true); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + Mtx_Identity(&modelView); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, vbo_data_pos, sizeof(vertex), 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 + ); + + 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_DrawArrays(GPU_TRIANGLES, 0, sizeof(position_list) / sizeof(position_list[0])); } HRESULT Citro3DRenderer::FinalizeFrame() @@ -371,6 +408,7 @@ void Citro3DRenderer::Flip() { C3D_FrameEnd(0); } + 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); @@ -393,7 +431,6 @@ void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, con 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); From 9dd3feac8eb4fc9ba47da504e033e0bcbb9b6f2d Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Fri, 27 Jun 2025 20:54:50 +0200 Subject: [PATCH 12/22] Push mesh dynamically --- ISLE/isleapp.cpp | 2 +- .../src/d3drm/backends/citro3d/renderer.cpp | 44 ++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 630ba1ad..046adda7 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -912,7 +912,7 @@ inline bool IsleApp::Tick() static MxLong g_lastFrameTime = 0; // GLOBAL: ISLE 0x4101bc - static MxS32 g_startupDelay = 200; + static MxS32 g_startupDelay = 1; if (IsleDebug_Paused() && IsleDebug_StepModeEnabled()) { IsleDebug_SetPaused(false); diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 5624ccf1..ce8b9ae6 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -38,14 +38,14 @@ typedef struct { float normal[3]; } vertex; -static void* vbo_data_pos; +static void* vbo_data_pos = nullptr; static const vertex position_list[] = { {{200.0f, 200.0f, 0.5f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, {{100.0f, 40.0f, 0.5f}, {0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}}, {{300.0f, 40.0f, 0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}, }; - +int g_vertexCount = 0; _Static_assert(sizeof(vbo_data_pos) % 4 == 0, "vertex size not 4-byte aligned"); static void sceneInit(void) @@ -69,9 +69,6 @@ static void sceneInit(void) C3D_Mtx projection; Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0, true); - - vbo_data_pos = linearAlloc(sizeof(position_list)); - memcpy(vbo_data_pos, position_list, sizeof(position_list)); } Direct3DRMRenderer* Citro3DRenderer::Create(DWORD width, DWORD height) @@ -100,6 +97,7 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) Citro3DRenderer::~Citro3DRenderer() { + linearFree(vbo_data_pos); shaderProgramFree(&program); DVLB_Free(vshader_dvlb); C3D_Fini(); @@ -112,6 +110,7 @@ void Citro3DRenderer::PushLights(const SceneLight* lightsArray, size_t count) void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) { + memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); } void Citro3DRenderer::SetFrustumPlanes(const Plane* frustumPlanes) @@ -302,6 +301,39 @@ Uint32 Citro3DRenderer::GetTextureId(IDirect3DRMTexture* iTexture) Uint32 Citro3DRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) { + if (vbo_data_pos) { + linearFree(vbo_data_pos); + } + + const auto& verts = meshGroup->vertices; + const auto& indices = meshGroup->indices; + + std::vector vertices; + vertices.reserve(indices.size()); + + for (size_t i = 0; i < indices.size(); ++i) { + const D3DRMVERTEX& src = verts[indices[i]]; + vertex dst; + + dst.position[0] = src.position.x; + dst.position[1] = src.position.y; + dst.position[2] = src.position.z; + + dst.normal[0] = src.normal.x; + dst.normal[1] = src.normal.y; + dst.normal[2] = src.normal.z; + + dst.texcoord[0] = src.tu; + dst.texcoord[1] = src.tv; + + vertices.push_back(dst); + } + g_vertexCount = indices.size(); + + vbo_data_pos = linearAlloc(sizeof(position_list)); + memcpy(vbo_data_pos, position_list, sizeof(position_list)); + g_vertexCount = 3; + return 0; } @@ -382,7 +414,7 @@ void Citro3DRenderer::SubmitDraw( C3D_TexBind(0, nullptr); } - C3D_DrawArrays(GPU_TRIANGLES, 0, sizeof(position_list) / sizeof(position_list[0])); + C3D_DrawArrays(GPU_TRIANGLES, 0, g_vertexCount); } HRESULT Citro3DRenderer::FinalizeFrame() From 8a039a81f64b50fb995ec35a5ebad24876a6b09a Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Fri, 27 Jun 2025 21:27:59 +0200 Subject: [PATCH 13/22] Remove Citro3D init hacks --- .../src/d3drm/backends/citro3d/renderer.cpp | 52 +++++-------------- miniwin/src/d3drm/d3drm.cpp | 2 +- miniwin/src/ddraw/ddraw.cpp | 2 +- miniwin/src/internal/d3drmrenderer_citro3d.h | 52 +++++++++++-------- 4 files changed, 44 insertions(+), 64 deletions(-) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index ce8b9ae6..4620a85b 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -48,15 +48,25 @@ static const vertex position_list[] = { int g_vertexCount = 0; _Static_assert(sizeof(vbo_data_pos) % 4 == 0, "vertex size not 4-byte aligned"); -static void sceneInit(void) +Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) { - // Load the vertex shader, create a shader program and bind it + m_width = 400; + m_height = 240; + m_virtualWidth = width; + m_virtualHeight = height; + + gfxInitDefault(); + consoleInit(GFX_BOTTOM, nullptr); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + + target = C3D_RenderTargetCreate(m_height, m_width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + vshader_dvlb = DVLB_ParseFile((u32*) vshader_shbin, vshader_shbin_size); shaderProgramInit(&program); shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]); C3D_BindProgram(&program); - // Get the location of the uniforms uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection"); uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView"); uLoc_meshColor = shaderInstanceGetUniformLocation(program.vertexShader, "meshColor"); @@ -66,33 +76,6 @@ static void sceneInit(void) AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // v2=normal - - C3D_Mtx projection; - Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0, true); -} - -Direct3DRMRenderer* Citro3DRenderer::Create(DWORD width, DWORD height) -{ - gfxInitDefault(); - consoleInit(GFX_BOTTOM, nullptr); - C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); - - // Initialize the render target - target = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); - - sceneInit(); - - return new Citro3DRenderer(width, height); -} - -Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) -{ - SDL_Log("Citro3DRenderer %dx%d", width, height); - m_width = width; - m_height = height; - m_virtualWidth = width; - m_virtualHeight = height; } Citro3DRenderer::~Citro3DRenderer() @@ -339,15 +322,6 @@ Uint32 Citro3DRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGr void Citro3DRenderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) { - 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; - - memset(helDesc, 0, sizeof(D3DDEVICEDESC)); } const char* Citro3DRenderer::GetName() diff --git a/miniwin/src/d3drm/d3drm.cpp b/miniwin/src/d3drm/d3drm.cpp index 3b3a4b27..0ae75caa 100644 --- a/miniwin/src/d3drm/d3drm.cpp +++ b/miniwin/src/d3drm/d3drm.cpp @@ -164,7 +164,7 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface( #endif #ifdef USE_CITRO3D else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) { - DDRenderer = Citro3DRenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = new Citro3DRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif #ifdef _WIN32 diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index af095058..ddd2e431 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -349,7 +349,7 @@ HRESULT DirectDrawImpl::CreateDevice( #endif #ifdef USE_CITRO3D else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) { - DDRenderer = Citro3DRenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = new Citro3DRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif #ifdef _WIN32 diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index 254303d7..4320ee0e 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -5,12 +5,11 @@ #include "d3drmtexture_impl.h" #include "ddraw_impl.h" -#include #include -#include +#include #include -DEFINE_GUID(Citro3D_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07); +DEFINE_GUID(Citro3D_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x53); struct C3DTextureCacheEntry { IDirect3DRMTexture* texture; @@ -21,27 +20,23 @@ struct C3DTextureCacheEntry { }; struct C3DMeshCacheEntry { - const MeshGroup* meshGroup = nullptr; - Uint32 version = 0; - bool flat = false; + const MeshGroup* meshGroup = nullptr; + Uint32 version = 0; + bool flat = false; - C3D_AttrInfo attrInfo; - C3D_BufInfo bufInfo; + C3D_AttrInfo attrInfo; + C3D_BufInfo bufInfo; - // CPU-side vertex data + // CPU-side vertex data std::vector vertices; - std::vector positions; - std::vector normals; - std::vector texcoords; // Only if you have textures - std::vector indices; // Indices for indexed drawing + std::vector positions; + std::vector normals; + std::vector texcoords; // Only if you have textures + std::vector indices; // Indices for indexed drawing }; - class Citro3DRenderer : public Direct3DRMRenderer { public: - static Direct3DRMRenderer* Create(DWORD width, DWORD height); - - // constructor parameters not finalized Citro3DRenderer(DWORD width, DWORD height); ~Citro3DRenderer() override; @@ -94,10 +89,21 @@ class Citro3DRenderer : public Direct3DRMRenderer { inline static void Citro3DRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { - SDL_Log("Hello, enuming device"); - Direct3DRMRenderer* device = Citro3DRenderer::Create(400, 240); - if (device) { - EnumDevice(cb, ctx, device, Citro3D_GUID); - delete device; - } + GUID guid = Citro3D_GUID; + char* deviceNameDup = SDL_strdup("Citro3D"); + char* deviceDescDup = SDL_strdup("Miniwin driver"); + 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 = {}; + + cb(&guid, deviceNameDup, deviceDescDup, &halDesc, &helDesc, ctx); + + SDL_free(deviceDescDup); + SDL_free(deviceNameDup); } From 4aceebaf64d2a9dd1a994145f30efd820e93eee3 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Fri, 27 Jun 2025 21:37:09 +0200 Subject: [PATCH 14/22] Clean up Citro3D implementation --- .../src/d3drm/backends/citro3d/renderer.cpp | 11 ++++---- miniwin/src/internal/d3drmrenderer_citro3d.h | 26 +++---------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 4620a85b..708e8bc2 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -30,7 +30,6 @@ static shaderProgram_s program; static int uLoc_projection; static int uLoc_modelView; static int uLoc_meshColor; -C3D_RenderTarget* target; typedef struct { float position[3]; @@ -59,8 +58,8 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) consoleInit(GFX_BOTTOM, nullptr); C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); - target = C3D_RenderTargetCreate(m_height, m_width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetSetOutput(target, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + m_renderTarget = C3D_RenderTargetCreate(m_height, m_width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetOutput(m_renderTarget, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); vshader_dvlb = DVLB_ParseFile((u32*) vshader_shbin, vshader_shbin_size); shaderProgramInit(&program); @@ -329,13 +328,13 @@ const char* Citro3DRenderer::GetName() return "Citro3D"; } -void StartFrame() +void Citro3DRenderer::StartFrame() { if (g_rendering) { return; } C3D_FrameBegin(C3D_FRAME_SYNCDRAW); - C3D_FrameDrawOn(target); + C3D_FrameDrawOn(m_renderTarget); } HRESULT Citro3DRenderer::BeginFrame() @@ -407,7 +406,7 @@ void Citro3DRenderer::Clear(float r, float g, float b) { u32 color = (static_cast(r * 255) << 24) | (static_cast(g * 255) << 16) | (static_cast(b * 255) << 8) | 255; - C3D_RenderTargetClear(target, C3D_CLEAR_ALL, color, 0); + C3D_RenderTargetClear(m_renderTarget, C3D_CLEAR_ALL, color, 0); } void Citro3DRenderer::Flip() diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index 4320ee0e..406e2fcb 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -23,16 +23,7 @@ struct C3DMeshCacheEntry { const MeshGroup* meshGroup = nullptr; Uint32 version = 0; bool flat = false; - - C3D_AttrInfo attrInfo; - C3D_BufInfo bufInfo; - - // CPU-side vertex data - std::vector vertices; - std::vector positions; - std::vector normals; - std::vector texcoords; // Only if you have textures - std::vector indices; // Indices for indexed drawing + void* vbo = nullptr; }; class Citro3DRenderer : public Direct3DRMRenderer { @@ -65,26 +56,15 @@ class Citro3DRenderer : public Direct3DRMRenderer { void Download(SDL_Surface* target) override; private: - C3DMeshCacheEntry UploadMesh(const MeshGroup& meshGroup); void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); - void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); + void StartFrame(); + D3DRMMATRIX4D m_projection; - C3D_Mtx m_projectionMatrix; SDL_Surface* m_renderedImage; C3D_RenderTarget* m_renderTarget; - int m_projectionShaderUniformLocation; std::vector m_textures; std::vector m_meshs; ViewportTransform m_viewportTransform; - - // TODO: All these flags can likely be cleaned up - bool m_flipVertFlag; - bool m_outTiledFlag; - bool m_rawCopyFlag; - GX_TRANSFER_FORMAT m_transferInputFormatFlag; - GX_TRANSFER_FORMAT m_transferOutputFormatFlag; - GX_TRANSFER_SCALE m_transferScaleFlag; - u32 m_transferFlags; }; inline static void Citro3DRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) From bd9b52866047fc58aed2528c3ec60102f83e59d8 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Fri, 27 Jun 2025 22:17:24 +0200 Subject: [PATCH 15/22] Try to upload meshes and convert matricies --- .../src/d3drm/backends/citro3d/renderer.cpp | 170 ++++++++++++------ miniwin/src/internal/d3drmrenderer_citro3d.h | 14 +- 2 files changed, 124 insertions(+), 60 deletions(-) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 708e8bc2..f3170c28 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -1,22 +1,11 @@ -#include "SDL3/SDL_surface.h" #include "d3drmrenderer_citro3d.h" +#include "d3drmtexture_impl.h" +#include "ddraw_impl.h" +#include "meshutils.h" #include "miniwin.h" -#include "miniwin/d3d.h" -#include "miniwin/d3drm.h" -#include "miniwin/windows.h" #include "vshader_shbin.h" -#include <3ds/console.h> -#include <3ds/gfx.h> -#include <3ds/gpu/enums.h> -#include <3ds/gpu/gx.h> -#include <3ds/gpu/shaderProgram.h> -#include -#include -#include -#include #include -#include bool g_rendering = false; @@ -31,22 +20,6 @@ static int uLoc_projection; static int uLoc_modelView; static int uLoc_meshColor; -typedef struct { - float position[3]; - float texcoord[2]; - float normal[3]; -} vertex; - -static void* vbo_data_pos = nullptr; - -static const vertex position_list[] = { - {{200.0f, 200.0f, 0.5f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, - {{100.0f, 40.0f, 0.5f}, {0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}}, - {{300.0f, 40.0f, 0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}, -}; -int g_vertexCount = 0; -_Static_assert(sizeof(vbo_data_pos) % 4 == 0, "vertex size not 4-byte aligned"); - Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) { m_width = 400; @@ -79,7 +52,6 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) Citro3DRenderer::~Citro3DRenderer() { - linearFree(vbo_data_pos); shaderProgramFree(&program); DVLB_Free(vshader_dvlb); C3D_Fini(); @@ -88,11 +60,20 @@ Citro3DRenderer::~Citro3DRenderer() void Citro3DRenderer::PushLights(const SceneLight* lightsArray, size_t count) { + MINIWIN_NOT_IMPLEMENTED(); } void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) { memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); + + MINIWIN_NOT_IMPLEMENTED(); + // FIXME is this correct? + float depth = back - front; + m_projection[2][2] = 1.0f / depth; + m_projection[2][3] = 1.0f; + m_projection[3][2] = -front / depth; + m_projection[3][3] = 0.0f; } void Citro3DRenderer::SetFrustumPlanes(const Plane* frustumPlanes) @@ -281,42 +262,100 @@ Uint32 Citro3DRenderer::GetTextureId(IDirect3DRMTexture* iTexture) return (Uint32) (m_textures.size() - 1); } -Uint32 Citro3DRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) +C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup) { - if (vbo_data_pos) { - linearFree(vbo_data_pos); + C3DMeshCacheEntry cache{&meshGroup, meshGroup.version}; + + std::vector vertexBuffer; + std::vector indexBuffer; + + if (meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT) { + FlattenSurfaces( + meshGroup.vertices.data(), + meshGroup.vertices.size(), + meshGroup.indices.data(), + meshGroup.indices.size(), + meshGroup.texture != nullptr, + vertexBuffer, + indexBuffer + ); + } + else { + vertexBuffer.assign(meshGroup.vertices.begin(), meshGroup.vertices.end()); + indexBuffer.assign(meshGroup.indices.begin(), meshGroup.indices.end()); } - const auto& verts = meshGroup->vertices; - const auto& indices = meshGroup->indices; + std::vector vertexUploadBuffer; + vertexUploadBuffer.reserve(indexBuffer.size()); - std::vector vertices; - vertices.reserve(indices.size()); - - for (size_t i = 0; i < indices.size(); ++i) { - const D3DRMVERTEX& src = verts[indices[i]]; + for (size_t i = 0; i < indexBuffer.size(); ++i) { + const D3DRMVERTEX& src = vertexBuffer[indexBuffer[i]]; vertex dst; dst.position[0] = src.position.x; dst.position[1] = src.position.y; dst.position[2] = src.position.z; + dst.texcoord[0] = src.tu; + dst.texcoord[1] = src.tv; + dst.normal[0] = src.normal.x; dst.normal[1] = src.normal.y; dst.normal[2] = src.normal.z; - dst.texcoord[0] = src.tu; - dst.texcoord[1] = src.tv; - - vertices.push_back(dst); + vertexUploadBuffer.emplace_back(dst); } - g_vertexCount = indices.size(); - vbo_data_pos = linearAlloc(sizeof(position_list)); - memcpy(vbo_data_pos, position_list, sizeof(position_list)); - g_vertexCount = 3; + MINIWIN_NOT_IMPLEMENTED(); + // FIXME Render debug triangle untill meshes work + static const vertex position_list[] = { + {{200.0f, 200.0f, 0.5f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, + {{100.0f, 40.0f, 0.5f}, {0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}}, + {{300.0f, 40.0f, 0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}, + }; + cache.vbo = linearAlloc(sizeof(position_list)); + memcpy(cache.vbo, position_list, sizeof(position_list)); + cache.vertexCount = 3; - return 0; + // size_t bufferSize = vertexUploadBuffer.size() * sizeof(vertex); + // cache.vbo = linearAlloc(bufferSize); + // memcpy(cache.vbo, vertexUploadBuffer.data(), bufferSize); + // cache.vertexCount = indexBuffer.size(); + + return cache; +} + +void Citro3DRenderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) +{ + MINIWIN_NOT_IMPLEMENTED(); +} + +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::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) @@ -340,7 +379,9 @@ void Citro3DRenderer::StartFrame() HRESULT Citro3DRenderer::BeginFrame() { StartFrame(); - // C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_COLOR); + MINIWIN_NOT_IMPLEMENTED(); + // TODO DepthTest should be true, but off while testing + C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_COLOR); return S_OK; } @@ -348,6 +389,16 @@ void Citro3DRenderer::EnableTransparency() { } +void ConvertMatrix(const D3DRMMATRIX4D in, C3D_Mtx* out) +{ + for (int i = 0; i < 4; i++) { + out->r[i].x = in[i][0]; + out->r[i].y = in[i][1]; + out->r[i].z = in[i][2]; + out->r[i].w = in[i][3]; + } +} + void Citro3DRenderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, @@ -358,14 +409,22 @@ void Citro3DRenderer::SubmitDraw( ) { C3D_Mtx projection, modelView; - Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0, true); - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + + MINIWIN_NOT_IMPLEMENTED(); + // FIXME are we converting it right? + // ConvertMatrix(m_projection, &projection); + // ConvertMatrix(modelViewMatrix, &modelView); + Mtx_OrthoTilt(&projection, 0, 400, 0, 272, 0.0f, 1.0f, true); Mtx_Identity(&modelView); + + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + auto& mesh = m_meshs[meshId]; + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); BufInfo_Init(bufInfo); - BufInfo_Add(bufInfo, vbo_data_pos, sizeof(vertex), 3, 0x210); + BufInfo_Add(bufInfo, mesh.vbo, sizeof(vertex), 3, 0x210); C3D_FVUnifSet( GPU_VERTEX_SHADER, @@ -387,7 +446,7 @@ void Citro3DRenderer::SubmitDraw( C3D_TexBind(0, nullptr); } - C3D_DrawArrays(GPU_TRIANGLES, 0, g_vertexCount); + C3D_DrawArrays(GPU_TRIANGLES, 0, mesh.vertexCount); } HRESULT Citro3DRenderer::FinalizeFrame() @@ -488,4 +547,5 @@ void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, con void Citro3DRenderer::Download(SDL_Surface* target) { + MINIWIN_NOT_IMPLEMENTED(); } diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index 406e2fcb..704faa4d 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -1,9 +1,6 @@ #pragma once -#include "SDL3/SDL_log.h" #include "d3drmrenderer.h" -#include "d3drmtexture_impl.h" -#include "ddraw_impl.h" #include #include @@ -21,11 +18,17 @@ struct C3DTextureCacheEntry { struct C3DMeshCacheEntry { const MeshGroup* meshGroup = nullptr; - Uint32 version = 0; - bool flat = false; + int version = 0; void* vbo = nullptr; + int vertexCount = 0; }; +typedef struct { + float position[3]; + float texcoord[2]; + float normal[3]; +} vertex; + class Citro3DRenderer : public Direct3DRMRenderer { public: Citro3DRenderer(DWORD width, DWORD height); @@ -57,6 +60,7 @@ class Citro3DRenderer : public Direct3DRMRenderer { private: void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); + void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); void StartFrame(); D3DRMMATRIX4D m_projection; From 16a94c725caf3fc1d9aca50c685390ab78fb2b9a Mon Sep 17 00:00:00 2001 From: Korbo Date: Fri, 27 Jun 2025 18:08:45 -0500 Subject: [PATCH 16/22] Names for race related or adjacent functions and variables (#1592) * Names for race related or adjacent functions and variables * fix formatting * fix formatting --- LEGO1/lego/legoomni/include/legopathactor.h | 2 +- LEGO1/lego/legoomni/include/legoracers.h | 6 ++-- LEGO1/lego/legoomni/include/legoracespecial.h | 9 +++-- .../lego/legoomni/src/paths/legopathactor.cpp | 6 ++-- LEGO1/lego/legoomni/src/race/carrace.cpp | 8 ++--- LEGO1/lego/legoomni/src/race/jetskirace.cpp | 8 ++--- .../lego/legoomni/src/race/legoraceactor.cpp | 4 +-- LEGO1/lego/legoomni/src/race/legoracers.cpp | 34 +++++++++---------- .../legoomni/src/race/legoracespecial.cpp | 16 ++++----- 9 files changed, 48 insertions(+), 45 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legopathactor.h b/LEGO1/lego/legoomni/include/legopathactor.h index 6566c6fb..8df0e9db 100644 --- a/LEGO1/lego/legoomni/include/legopathactor.h +++ b/LEGO1/lego/legoomni/include/legopathactor.h @@ -13,7 +13,7 @@ struct LegoPathEdgeContainer; struct LegoOrientedEdge; class LegoWEEdge; -extern MxLong g_unk0x100f3308; +extern MxLong g_timeLastHitSoundPlayed; extern const char* g_strHIT_WALL_SOUND; // VTABLE: LEGO1 0x100d6e28 diff --git a/LEGO1/lego/legoomni/include/legoracers.h b/LEGO1/lego/legoomni/include/legoracers.h index 76d1071d..3c8ffe07 100644 --- a/LEGO1/lego/legoomni/include/legoracers.h +++ b/LEGO1/lego/legoomni/include/legoracers.h @@ -144,10 +144,10 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { MxResult VTable0x9c() override; // vtable+0x9c virtual void SetMaxLinearVelocity(float p_maxLinearVelocity); - virtual void FUN_10012ff0(float p_param); + virtual void KickCamera(float p_param); virtual MxU32 HandleSkeletonKicks(float p_param1); - static void FUN_10012de0(); + static void InitYouCantStopSound(); static void InitSoundIndices(); // SYNTHETIC: LEGO1 0x10014240 @@ -155,7 +155,7 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { private: undefined m_userState; // 0x54 - float m_unk0x58; // 0x58 + float m_kickStart; // 0x58 Mx3DPointFloat m_unk0x5c; // 0x5c // Names verified by BETA10 0x100cb4a9 diff --git a/LEGO1/lego/legoomni/include/legoracespecial.h b/LEGO1/lego/legoomni/include/legoracespecial.h index 97fa7d9f..bfa38632 100644 --- a/LEGO1/lego/legoomni/include/legoracespecial.h +++ b/LEGO1/lego/legoomni/include/legoracespecial.h @@ -44,8 +44,11 @@ class LegoCarRaceActor : public virtual LegoRaceActor { Vector3& p_v3 ) override; // vtable+0x6c void Animate(float p_time) override; // vtable+0x70 - void SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOrientedEdge*& p_edge, float& p_unk0xe4) - override; // vtable+0x98 + void SwitchBoundary( + LegoPathBoundary*& p_boundary, + LegoOrientedEdge*& p_edge, + float& p_unk0xe4 + ) override; // vtable+0x98 MxResult VTable0x9c() override; // vtable+0x9c // LegoCarRaceActor vtable @@ -83,7 +86,7 @@ class LegoCarRaceActor : public virtual LegoRaceActor { protected: MxFloat m_unk0x08; // 0x08 - MxU8 m_unk0x0c; // 0x0c + MxU8 m_animState; // 0x0c // Could be a multiplier for the maximum speed when going straight MxFloat m_unk0x10; // 0x10 diff --git a/LEGO1/lego/legoomni/src/paths/legopathactor.cpp b/LEGO1/lego/legoomni/src/paths/legopathactor.cpp index 292d8214..0a8f6878 100644 --- a/LEGO1/lego/legoomni/src/paths/legopathactor.cpp +++ b/LEGO1/lego/legoomni/src/paths/legopathactor.cpp @@ -36,7 +36,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 @@ -291,8 +291,8 @@ MxS32 LegoPathActor::VTable0x8c(float p_time, Matrix4& p_transform) if (m_boundary == oldBoundary) { MxLong time = Timer()->GetTime(); - if (time - g_unk0x100f3308 > 1000) { - g_unk0x100f3308 = time; + if (time - g_timeLastHitSoundPlayed > 1000) { + g_timeLastHitSoundPlayed = time; const char* var = VariableTable()->GetVariable(g_strHIT_WALL_SOUND); if (var && var[0] != 0) { diff --git a/LEGO1/lego/legoomni/src/race/carrace.cpp b/LEGO1/lego/legoomni/src/race/carrace.cpp index e775a3be..86c50b4a 100644 --- a/LEGO1/lego/legoomni/src/race/carrace.cpp +++ b/LEGO1/lego/legoomni/src/race/carrace.cpp @@ -244,7 +244,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()); @@ -346,7 +346,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"); @@ -358,7 +358,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"); @@ -422,7 +422,7 @@ MxBool CarRace::Escape() NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); GameState()->GetBackgroundColor()->SetValue("reset"); m_destLocation = LegoGameState::e_infomain; diff --git a/LEGO1/lego/legoomni/src/race/jetskirace.cpp b/LEGO1/lego/legoomni/src/race/jetskirace.cpp index d1de13e9..54ad806c 100644 --- a/LEGO1/lego/legoomni/src/race/jetskirace.cpp +++ b/LEGO1/lego/legoomni/src/race/jetskirace.cpp @@ -130,7 +130,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; @@ -138,7 +138,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); @@ -191,7 +191,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()); @@ -292,6 +292,6 @@ MxBool JetskiRace::Escape() VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); m_destLocation = LegoGameState::e_infomain; - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); return TRUE; } diff --git a/LEGO1/lego/legoomni/src/race/legoraceactor.cpp b/LEGO1/lego/legoomni/src/race/legoraceactor.cpp index 965ba326..f005d996 100644 --- a/LEGO1/lego/legoomni/src/race/legoraceactor.cpp +++ b/LEGO1/lego/legoomni/src/race/legoraceactor.cpp @@ -31,8 +31,8 @@ MxS32 LegoRaceActor::VTable0x68(Vector3& p_v1, Vector3& p_v2, Vector3& p_v3) if (m_userNavFlag && result) { MxLong time = Timer()->GetTime(); - if (time - g_unk0x100f3308 > 1000) { - g_unk0x100f3308 = time; + if (time - g_timeLastHitSoundPlayed > 1000) { + g_timeLastHitSoundPlayed = time; const char* soundKey = VariableTable()->GetVariable(g_strHIT_ACTOR_SOUND); if (soundKey && *soundKey) { diff --git a/LEGO1/lego/legoomni/src/race/legoracers.cpp b/LEGO1/lego/legoomni/src/race/legoracers.cpp index 39c0c0e6..0a9fa1e2 100644 --- a/LEGO1/lego/legoomni/src/race/legoracers.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracers.cpp @@ -175,7 +175,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); @@ -198,10 +198,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; @@ -226,7 +226,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); @@ -241,7 +241,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); } @@ -296,7 +296,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; @@ -312,7 +312,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) { @@ -387,7 +387,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; @@ -398,7 +398,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; } @@ -410,7 +410,7 @@ void LegoRaceCar::Animate(float p_time) } } - if (LegoCarRaceActor::m_unk0x0c == 1) { + if (LegoCarRaceActor::m_animState == 1) { FUN_1005d4b0(); if (!m_userNavFlag) { @@ -468,7 +468,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); @@ -513,7 +513,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; } } @@ -579,7 +579,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); @@ -594,7 +594,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); } @@ -609,7 +609,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) { @@ -682,7 +682,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); @@ -711,7 +711,7 @@ MxResult LegoJetski::HitActor(LegoPathActor* p_actor, MxBool p_bool) if (soundKey) { SoundManager()->GetCacheSoundManager()->Play(soundKey, NULL, FALSE); - g_timeLastJetskiSoundPlayed = g_unk0x100f3308 = time; + g_timeLastJetskiSoundPlayed = g_timeLastHitSoundPlayed = time; } } diff --git a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp index 81f9255f..e599c633 100644 --- a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp @@ -44,7 +44,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; @@ -223,18 +223,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 (strcmpi(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); } } @@ -398,10 +398,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 (!stricmp(raceState, g_racing)) { - m_unk0x0c = 1; + m_animState = 1; m_lastTime = p_time - 1.0f; m_unk0x1c = p_time; } @@ -410,7 +410,7 @@ void LegoJetskiRaceActor::Animate(float p_time) } } - if (m_unk0x0c == 1) { + if (m_animState == 1) { LegoAnimActor::Animate(p_time); } } From 02dd261ca91467688a6e0ed1fbffa50e99300338 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Fri, 27 Jun 2025 16:15:08 -0700 Subject: [PATCH 17/22] Fix isle/master merge --- LEGO1/lego/legoomni/src/race/legoracespecial.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp index 6fa64058..7fe58c71 100644 --- a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp @@ -401,13 +401,8 @@ void LegoJetskiRaceActor::Animate(float p_time) { if (m_animState == 0) { const LegoChar* raceState = VariableTable()->GetVariable(g_raceState); -<<<<<<< HEAD if (!SDL_strcasecmp(raceState, g_racing)) { - m_unk0x0c = 1; -======= - if (!stricmp(raceState, g_racing)) { m_animState = 1; ->>>>>>> isle/master m_lastTime = p_time - 1.0f; m_unk0x1c = p_time; } From 8891cbc71499dd4cc7b37852a8339c52d6fd0042 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sat, 28 Jun 2025 02:47:34 +0200 Subject: [PATCH 18/22] Fix 3D rendering --- .../src/d3drm/backends/citro3d/renderer.cpp | 98 ++++++++++--------- miniwin/src/internal/d3drmrenderer_citro3d.h | 6 -- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index f3170c28..2be3fb82 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -39,6 +39,8 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) 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"); @@ -66,14 +68,6 @@ void Citro3DRenderer::PushLights(const SceneLight* lightsArray, size_t count) void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) { memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); - - MINIWIN_NOT_IMPLEMENTED(); - // FIXME is this correct? - float depth = back - front; - m_projection[2][2] = 1.0f / depth; - m_projection[2][3] = 1.0f; - m_projection[3][2] = -front / depth; - m_projection[3][3] = 0.0f; } void Citro3DRenderer::SetFrustumPlanes(const Plane* frustumPlanes) @@ -172,7 +166,7 @@ static void EncodeTextureLayout(const u8* src, u8* dst, int width, int height) int mortonIndex = mortonInterleave(x, y); int dstIndex = (tileIndex + mortonIndex) * bytesPerPixel; - int srcIndex = (srcY * width + srcX) * bytesPerPixel; + int srcIndex = ((height - 1 - srcY) * width + srcX) * bytesPerPixel; std::memcpy(&dst[dstIndex], &src[srcIndex], bytesPerPixel); } @@ -262,6 +256,12 @@ Uint32 Citro3DRenderer::GetTextureId(IDirect3DRMTexture* iTexture) return (Uint32) (m_textures.size() - 1); } +struct vertex { + float position[3]; + float texcoord[2]; + float normal[3]; +}; + C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup) { C3DMeshCacheEntry cache{&meshGroup, meshGroup.version}; @@ -285,6 +285,10 @@ C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup) indexBuffer.assign(meshGroup.indices.begin(), meshGroup.indices.end()); } + MINIWIN_NOT_IMPLEMENTED(); + + // TODO use ibo instead of flattening verticies, see + // https://github.com/devkitPro/3ds-examples/blob/44faa81d79d5781c0e149e4a7005f2e005edb736/graphics/gpu/loop_subdivision/source/main.c#L104 std::vector vertexUploadBuffer; vertexUploadBuffer.reserve(indexBuffer.size()); @@ -306,21 +310,10 @@ C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup) vertexUploadBuffer.emplace_back(dst); } - MINIWIN_NOT_IMPLEMENTED(); - // FIXME Render debug triangle untill meshes work - static const vertex position_list[] = { - {{200.0f, 200.0f, 0.5f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, - {{100.0f, 40.0f, 0.5f}, {0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}}, - {{300.0f, 40.0f, 0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}, - }; - cache.vbo = linearAlloc(sizeof(position_list)); - memcpy(cache.vbo, position_list, sizeof(position_list)); - cache.vertexCount = 3; - - // size_t bufferSize = vertexUploadBuffer.size() * sizeof(vertex); - // cache.vbo = linearAlloc(bufferSize); - // memcpy(cache.vbo, vertexUploadBuffer.data(), bufferSize); - // cache.vertexCount = indexBuffer.size(); + size_t bufferSize = vertexUploadBuffer.size() * sizeof(vertex); + cache.vbo = linearAlloc(bufferSize); + memcpy(cache.vbo, vertexUploadBuffer.data(), bufferSize); + cache.vertexCount = vertexUploadBuffer.size(); return cache; } @@ -379,26 +372,39 @@ void Citro3DRenderer::StartFrame() HRESULT Citro3DRenderer::BeginFrame() { StartFrame(); - MINIWIN_NOT_IMPLEMENTED(); - // TODO DepthTest should be true, but off while testing - C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_COLOR); + C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL); 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[i][0]; - out->r[i].y = in[i][1]; - out->r[i].z = in[i][2]; - out->r[i].w = in[i][3]; + 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 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); +} + void Citro3DRenderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, @@ -412,10 +418,8 @@ void Citro3DRenderer::SubmitDraw( MINIWIN_NOT_IMPLEMENTED(); // FIXME are we converting it right? - // ConvertMatrix(m_projection, &projection); - // ConvertMatrix(modelViewMatrix, &modelView); - Mtx_OrthoTilt(&projection, 0, 400, 0, 272, 0.0f, 1.0f, true); - Mtx_Identity(&modelView); + ConvertPerspective(m_projection, &projection); + ConvertMatrix(modelViewMatrix, &modelView); C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); @@ -444,6 +448,10 @@ void Citro3DRenderer::SubmitDraw( } 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); @@ -511,8 +519,8 @@ void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, con float u0 = static_cast(srcRect.x) / texture.width; float u1 = static_cast(srcRect.x + srcRect.w) / texture.width; - float v1 = 1.0f - static_cast(srcRect.y + srcRect.h) / texture.height; - float v0 = 1.0f - static_cast(srcRect.y) / texture.height; + float v0 = static_cast(srcRect.y) / texture.height; + float v1 = static_cast(srcRect.y + srcRect.h) / texture.height; C3D_ImmDrawBegin(GPU_TRIANGLES); @@ -521,27 +529,27 @@ void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, con C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f); C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); - C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f); - C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f); - C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); - C3D_ImmSendAttrib(x2, y1, 0.5f, 0.0f); C3D_ImmSendAttrib(u1, v0, 0.0f, 0.0f); C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f); + C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + // Triangle 2 C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f); C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f); C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); - C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f); - C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f); - C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); - C3D_ImmSendAttrib(x1, y2, 0.5f, 0.0f); C3D_ImmSendAttrib(u0, v1, 0.0f, 0.0f); C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f); + C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f); + C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmDrawEnd(); } diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index 704faa4d..53058aed 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -23,12 +23,6 @@ struct C3DMeshCacheEntry { int vertexCount = 0; }; -typedef struct { - float position[3]; - float texcoord[2]; - float normal[3]; -} vertex; - class Citro3DRenderer : public Direct3DRMRenderer { public: Citro3DRenderer(DWORD width, DWORD height); From 6660082fefb2cebbeeec8428abd8508099f93ad4 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sat, 28 Jun 2025 03:16:55 +0200 Subject: [PATCH 19/22] Apply optimizations --- .../src/d3drm/backends/citro3d/renderer.cpp | 81 +++++++++---------- .../src/d3drm/backends/citro3d/vshader.v.pica | 4 +- miniwin/src/internal/d3drmrenderer_citro3d.h | 3 +- 3 files changed, 40 insertions(+), 48 deletions(-) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 2be3fb82..2837d089 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -48,8 +48,8 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); AttrInfo_Init(attrInfo); AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position - AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord - AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // v2=normal + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 3); // v2=normal + AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 2); // v1=texcoord } Citro3DRenderer::~Citro3DRenderer() @@ -74,18 +74,18 @@ void Citro3DRenderer::SetFrustumPlanes(const Plane* frustumPlanes) { } -struct TextureDestroyContextCitro { +struct Citro3DCacheDestroyContext { Citro3DRenderer* renderer; - Uint32 textureId; + Uint32 id; }; void Citro3DRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) { - auto* ctx = new TextureDestroyContextCitro{this, id}; + auto* ctx = new Citro3DCacheDestroyContext{this, id}; texture->AddDestroyCallback( [](IDirect3DRMObject* obj, void* arg) { - auto* ctx = static_cast(arg); - auto& entry = ctx->renderer->m_textures[ctx->textureId]; + auto* ctx = static_cast(arg); + auto& entry = ctx->renderer->m_textures[ctx->id]; if (entry.texture) { C3D_TexDelete(&entry.c3dTex); entry.texture = nullptr; @@ -115,7 +115,7 @@ static SDL_Surface* ConvertAndResizeSurface(SDL_Surface* original) } int newW = NearestPowerOfTwoClamp(converted->w); - int newH = NearestPowerOfTwoClamp(converted->h); + int newH = converted->h == 480 ? 256 : NearestPowerOfTwoClamp(converted->h); if (converted->w == newW && converted->h == newH) { return converted; @@ -285,42 +285,35 @@ C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup) indexBuffer.assign(meshGroup.indices.begin(), meshGroup.indices.end()); } - MINIWIN_NOT_IMPLEMENTED(); + size_t vertexBufferSize = vertexBuffer.size() * sizeof(vertex); + size_t indexBufferSize = indexBuffer.size() * sizeof(uint16_t); - // TODO use ibo instead of flattening verticies, see - // https://github.com/devkitPro/3ds-examples/blob/44faa81d79d5781c0e149e4a7005f2e005edb736/graphics/gpu/loop_subdivision/source/main.c#L104 - std::vector vertexUploadBuffer; - vertexUploadBuffer.reserve(indexBuffer.size()); + cache.vbo = linearAlloc(vertexBufferSize); + memcpy(cache.vbo, vertexBuffer.data(), vertexBufferSize); + cache.ibo = linearAlloc(indexBufferSize); + memcpy(cache.ibo, indexBuffer.data(), indexBufferSize); - for (size_t i = 0; i < indexBuffer.size(); ++i) { - const D3DRMVERTEX& src = vertexBuffer[indexBuffer[i]]; - vertex dst; - - dst.position[0] = src.position.x; - dst.position[1] = src.position.y; - dst.position[2] = src.position.z; - - dst.texcoord[0] = src.tu; - dst.texcoord[1] = src.tv; - - dst.normal[0] = src.normal.x; - dst.normal[1] = src.normal.y; - dst.normal[2] = src.normal.z; - - vertexUploadBuffer.emplace_back(dst); - } - - size_t bufferSize = vertexUploadBuffer.size() * sizeof(vertex); - cache.vbo = linearAlloc(bufferSize); - memcpy(cache.vbo, vertexUploadBuffer.data(), bufferSize); - cache.vertexCount = vertexUploadBuffer.size(); + cache.indexCount = indexBuffer.size(); return cache; } void Citro3DRenderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) { - MINIWIN_NOT_IMPLEMENTED(); + auto* ctx = new Citro3DCacheDestroyContext{this, id}; + mesh->AddDestroyCallback( + [](IDirect3DRMObject* obj, void* arg) { + auto* ctx = static_cast(arg); + auto& cacheEntry = ctx->renderer->m_meshs[ctx->id]; + if (cacheEntry.meshGroup) { + cacheEntry.meshGroup = nullptr; + linearFree(cacheEntry.vbo); + linearFree(cacheEntry.ibo); + } + delete ctx; + }, + ctx + ); } Uint32 Citro3DRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) @@ -416,8 +409,6 @@ void Citro3DRenderer::SubmitDraw( { C3D_Mtx projection, modelView; - MINIWIN_NOT_IMPLEMENTED(); - // FIXME are we converting it right? ConvertPerspective(m_projection, &projection); ConvertMatrix(modelViewMatrix, &modelView); @@ -454,7 +445,7 @@ void Citro3DRenderer::SubmitDraw( C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); } - C3D_DrawArrays(GPU_TRIANGLES, 0, mesh.vertexCount); + C3D_DrawElements(GPU_GEOMETRY_PRIM, mesh.indexCount, C3D_UNSIGNED_SHORT, mesh.ibo); } HRESULT Citro3DRenderer::FinalizeFrame() @@ -526,29 +517,29 @@ void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, con // Triangle 1 C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f); - C3D_ImmSendAttrib(u0, v0, 0.0f, 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(u1, v0, 0.0f, 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(u1, v1, 0.0f, 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(u1, v1, 0.0f, 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(u0, v1, 0.0f, 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(u0, v0, 0.0f, 0.0f); C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f); + C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f); C3D_ImmDrawEnd(); } diff --git a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica index e1d39e8d..fa1c9870 100644 --- a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica +++ b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica @@ -19,8 +19,8 @@ ; Inputs (defined as aliases for convenience) .alias inpos v0 -.alias intex v1 -.alias innrm v2 +.alias innrm v1 +.alias intex v2 .proc main ; Force the w component of inpos to be 1.0 diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index 53058aed..09c0001d 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -20,7 +20,8 @@ struct C3DMeshCacheEntry { const MeshGroup* meshGroup = nullptr; int version = 0; void* vbo = nullptr; - int vertexCount = 0; + void* ibo = nullptr; + int indexCount = 0; }; class Citro3DRenderer : public Direct3DRMRenderer { From 87f4c83ff2b06165ce7fb3f36bae6adfe60b2179 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sat, 28 Jun 2025 06:26:53 +0200 Subject: [PATCH 20/22] Implement lighting --- .../src/d3drm/backends/citro3d/renderer.cpp | 111 +++++++++++++----- .../src/d3drm/backends/citro3d/vshader.v.pica | 108 ++++++++++++++--- miniwin/src/d3drm/d3drmdevice.cpp | 4 + miniwin/src/internal/d3drmrenderer_citro3d.h | 1 + 4 files changed, 181 insertions(+), 43 deletions(-) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 2837d089..5ebf6b75 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -9,30 +9,35 @@ bool g_rendering = false; -#define DISPLAY_TRANSFER_FLAGS \ - (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)) - 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 = 400; + m_width = 320; m_height = 240; m_virtualWidth = width; m_virtualHeight = height; gfxInitDefault(); - consoleInit(GFX_BOTTOM, nullptr); + 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_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + 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); @@ -44,6 +49,9 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) 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); @@ -60,9 +68,9 @@ Citro3DRenderer::~Citro3DRenderer() gfxExit(); } -void Citro3DRenderer::PushLights(const SceneLight* lightsArray, size_t count) +void Citro3DRenderer::PushLights(const SceneLight* lights, size_t count) { - MINIWIN_NOT_IMPLEMENTED(); + m_lights.assign(lights, lights + count); } void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) @@ -362,10 +370,59 @@ void Citro3DRenderer::StartFrame() C3D_FrameDrawOn(m_renderTarget); } +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; } @@ -384,20 +441,6 @@ void ConvertMatrix(const D3DRMMATRIX4D in, C3D_Mtx* out) } } -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); -} - void Citro3DRenderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, @@ -407,12 +450,8 @@ void Citro3DRenderer::SubmitDraw( const Appearance& appearance ) { - C3D_Mtx projection, modelView; - - ConvertPerspective(m_projection, &projection); + C3D_Mtx modelView; ConvertMatrix(modelViewMatrix, &modelView); - - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); auto& mesh = m_meshs[meshId]; @@ -430,6 +469,8 @@ void Citro3DRenderer::SubmitDraw( 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); @@ -470,6 +511,8 @@ void Citro3DRenderer::Clear(float r, float g, float b) void Citro3DRenderer::Flip() { C3D_FrameEnd(0); + gfxFlushBuffers(); + gspWaitForVBlank(); } void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) @@ -489,6 +532,16 @@ void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, con 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]; diff --git a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica index fa1c9870..cd7f91c4 100644 --- a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica +++ b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica @@ -1,31 +1,29 @@ -; From https://github.com/devkitPro/citro3d/blob/master/test/3ds/source/vshader.v.pica -; zlib license -; ---------- - -; Example PICA200 vertex shader - ; Uniforms -.fvec projection[4], modelView[4], normView[2], meshColor +.fvec projection[4], modelView[4], meshColor +.fvec lightVec[2], lightClr[3], shininess ; Constants .constf myconst(0.0, 1.0, -1.0, -0.5) -.alias zeros myconst.xxxx ; Vector full of zeros -.alias ones myconst.yyyy ; Vector full of ones ; Outputs .out outpos position .out outtc0 texcoord0 .out outclr color -; Inputs (defined as aliases for convenience) +; 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, ones + mov r0.w, r15.y ; r1 = modelView * inpos dp4 r1.x, modelView[0], r0 @@ -42,9 +40,91 @@ ; outtex = intex mov outtc0, intex mov outtc0.zw, myconst.xy - ; outclr = ones - mov outclr, meshColor - ; We're finished + ; Transform normal + mov r2.xyz, innrm + mov r2.w, r15.x + dp4 r3.x, modelView[0], r2 + dp4 r3.y, modelView[1], r2 + dp4 r3.z, modelView[2], r2 + mov r3.w, r15.x + dp3 r4.x, r3, r3 + rsq r4.x, r4.x + mul r3, r4.xxxx, r3 ; r3 = normalized normal + + ; Normalize lightVec[0] + mov r5, lightVec[0] + dp3 r6.x, r5, r5 + rsq r6.x, r6.x + mul r5, r6.xxxx, r5 + + ; dot(normal, lightVec[0]) + dp3 r6.x, r3, r5 + max r6.x, r6.x, r15.xxxx + + ; Normalize lightVec[1] + mov r7, lightVec[1] + dp3 r8.x, r7, r7 + rsq r8.x, r8.x + mul r7, r8.xxxx, r7 + + ; dot(normal, lightVec[1]) + dp3 r6.y, r3, r7 + max r6.y, r6.y, r15.xxxx + + ; Load lightClr + mov r8, lightClr[2] ; ambient + mov r9, lightClr[0] ; point + mov r10, lightClr[1] ; directional + + ; diffuse = ambient + (lightClr[0] * dot0) + (lightClr[1] * dot1) + mul r11, r9, r6.xxxx + add r8, r8, r11 + mul r11, r10, r6.yyyy + add r8, r8, r11 ; r8 = diffuse + + ; Check if shininess > 0 + mov r12, shininess + slt r13.x, r15.x, r12.x + + ; viewVec = normalize(-position.xyz) + mov r14.xyz, r1.xyz + mul r14.xyz, r14.xyz, r15.zzz + dp3 r4.x, r14, r14 + rsq r4.x, r4.x + mul r14, r4.xxxx, r14 + + ; H = normalize(view + lightVec[1]) + add r11, r14, r7 + dp3 r4.x, r11, r11 + rsq r4.x, r4.x + mul r11, r4.xxxx, r11 + + ; dot(normal, H) + dp3 r4.x, r3, r11 + max r4.x, r4.x, r15.x + + ; Approximate pow(dotNH, 10) by repeated multiplication + mul r5.x, r4.x, r4.x ; dotNH^2 + mul r5.x, r5.x, r5.x ; dotNH^4 + mul r5.x, r5.x, r5.x ; dotNH^8 + mul r4.x, r5.x, r4.x ; dotNH^9 + mul r4.x, r4.x, r4.x ; dotNH^10 + + ; Multiply by shininess > 0 flag + mul r4.x, r4.x, r13.x + + ; specular = lightClr[1] * spec + mul r5, r10, r4.xxxx + + ; final = diffuse * meshColor + specular * lightClr[1] + mov r9, meshColor + mul r6, r8, r9 ; diffuse * meshColor + add r7.xyz, r6.xyz, r5.xyz ; add specular (already multiplied by lightClr) + min r7.xyz, r7.xyz, r15.yyyy + + mov outclr.xyz, r7.xyz + mov outclr.w, meshColor.w + end .end diff --git a/miniwin/src/d3drm/d3drmdevice.cpp b/miniwin/src/d3drm/d3drmdevice.cpp index f6b2f9f7..b1ae249b 100644 --- a/miniwin/src/d3drm/d3drmdevice.cpp +++ b/miniwin/src/d3drm/d3drmdevice.cpp @@ -155,6 +155,10 @@ void Direct3DRMDevice2Impl::Resize() { int width, height; SDL_GetWindowSizeInPixels(DDWindow, &width, &height); +#ifdef USE_CITRO3D + width = 320; // We are on the lower screen + height = 240; +#endif m_viewportTransform = CalculateViewportTransform(m_virtualWidth, m_virtualHeight, width, height); m_renderer->Resize(width, height, m_viewportTransform); for (int i = 0; i < m_viewports->GetSize(); i++) { diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index 09c0001d..e08035c4 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -64,6 +64,7 @@ class Citro3DRenderer : public Direct3DRMRenderer { std::vector m_textures; std::vector m_meshs; ViewportTransform m_viewportTransform; + std::vector m_lights; }; inline static void Citro3DRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) From 6b4f013383b4b02e069698218f4ef1dfb56ba7c7 Mon Sep 17 00:00:00 2001 From: Joshua Peisach Date: Fri, 27 Jun 2025 08:21:54 -0400 Subject: [PATCH 21/22] Set 3dsx smdh metadata --- CMakeLists.txt | 15 +++++++++------ ISLE/res/3ds/isle.png | Bin 0 -> 1813 bytes 2 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 ISLE/res/3ds/isle.png diff --git a/CMakeLists.txt b/CMakeLists.txt index b522293f..35762ced 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -646,13 +646,16 @@ endif() set(CPACK_PACKAGE_DIRECTORY "dist") set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}-${CMAKE_SYSTEM_PROCESSOR}") if(NINTENDO_3DS) - set(APP_TITLE "LEGO Island") - set(APP_DESCRIPTION "LEGO Island port for 3DS") - set(APP_AUTHOR "LEGO Island Team") - set(APP_ICON "${PROJECT_SOURCE_DIR}/CONFIG/res/lego3ds.png") - set(APP_VERSION ${PROJECT_VERSION}) + ctr_generate_smdh(isle.smdh + NAME "LEGO Island" + TITLE "LEGO Island" + DESCRIPTION "LEGO Island for the Nintendo 3DS" + AUTHOR "LEGO Island Decompilation and Porting Team" + VERSION "${PROJECT_VERSION}" + ICON "isle/res/3ds/isle.png" + ) - ctr_create_3dsx(isle) + ctr_create_3dsx(isle SMDH isle.smdh) endif() if(MSVC) set(CPACK_GENERATOR ZIP) diff --git a/ISLE/res/3ds/isle.png b/ISLE/res/3ds/isle.png new file mode 100644 index 0000000000000000000000000000000000000000..161fc60e3f7b10308ecea871904b452087a64452 GIT binary patch literal 1813 zcmZ{kc{J4j8pppVL-Eu2k(f%tgc*!&^c!iBrLkrg#aPA?V{5WUW1B4b*&>Eeev-0B z82eaeQe>&DgCbj&L6*U|)4Au|bM8Iop3iwc?{i+y^E%IePuxvoJ^o{2#{dA}N8ivk zV<+Nw@o=+$YtCIFJI*=ZFf#&x5GVjVj0ONE8$Fx{fcxyhMGOF-o&tb~Z%%{hC3b_` z$v{sVSpEIlK13a56JFmNx9$PJ@jrhT2aug}iVZ<{w2=;Ifg8dncs3mB$0kRo(Aw85 z-090B4v)sIg+Xh5Ua2&wQtVarln&ceRi!Z;T+S?cz&l@7S3+8Vqmy_p{#0T|suo(j zq5AokOGEMLmxZFA^XPNoHrqHwpP26FF}UJAb#Q-n zjse?0y*kxn^O(U5+&H8J@g9r0)oq)ySI-|oQT!BTG98>lhH{&`hL`+Is6e)FfRo(% zPWLE3ogFg@$&c3z0^2e8xK&a-+xNMD>Ev|T!*yq}%JY`0<&$>dtazeSUmo>VDWH_=+h85UX9iu*CGg!XXxWy>qHyPD2~513d@<&q9nQJy$42p*R{hMwjK_;=$f=70{*7^ZQEenN^@KdN!Y&Sb6-?vbT*L9iZ!> z9t3rwS4W_K$!*BA9oU}QrI0O%1gX>qfOiVgV`^V0LJoCgKV)mE9WU+5HM6pjlphX0 z1*YFzL(`fhhvWQD9xI(|<&d>fSB^{J9V`WASPw^{UfZX$ozcZGw>L>uD@*r^XyuyweqO!<-SEvYC_ zh0?I-X;RSx^RvazlQos1y&Gr!4Av~eCZ5*NxwpSn?)P%ZicyyvvSnJL(lxBBzg-J3 z=#E<%cXg{h$lN)%m`I{=U7^lp8|?-^h!$}gc5bVk(oO&k22g2JZI}fw`ux*2mDMQ; zJK@4J8_2^4VVqZb=_K&y6R1R$_FH3qpE$>f@-`ZvE^C}!Liz060%f79kENadC=%PY zg>nC*-4!bO9w|?jOILU&2hOX{k&cawJLI6eR!+f_f7<>G;Avqn9Smj`jlcBLyYMKP0crU)~{`97WJFR zZ_rPYD0*w28Vl_eX?AZB8)ZP zQra5giT*%>jvQBFl8kk&thfjE4V3q7tdo9R=ysJmZ(7=`o%R^pG%nCxl8zoo^04zK$VuryG^ zk~h!ZUa0&+;nz z$u2oHgZ%R8JFOOin%P(0n>LBB+}F;N{Hl}BFQNL*U4WoP= Date: Sat, 28 Jun 2025 21:06:37 +0200 Subject: [PATCH 22/22] Revert "Apply optimizations" This reverts commit 6660082fefb2cebbeeec8428abd8508099f93ad4. --- CMakeLists.txt | 2 +- .../src/d3drm/backends/citro3d/renderer.cpp | 35 ++++++++++--------- miniwin/src/internal/d3drmrenderer_citro3d.h | 3 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5347bd92..a61449f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -652,7 +652,7 @@ if(NINTENDO_3DS) DESCRIPTION "LEGO Island for the Nintendo 3DS" AUTHOR "LEGO Island Decompilation and Porting Team" VERSION "${PROJECT_VERSION}" - ICON "isle/res/3ds/isle.png" + ICON "ISLE/res/3ds/isle.png" ) ctr_create_3dsx(isle SMDH isle.smdh) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 5ebf6b75..e18bb57f 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -264,12 +264,6 @@ Uint32 Citro3DRenderer::GetTextureId(IDirect3DRMTexture* iTexture) return (Uint32) (m_textures.size() - 1); } -struct vertex { - float position[3]; - float texcoord[2]; - float normal[3]; -}; - C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup) { C3DMeshCacheEntry cache{&meshGroup, meshGroup.version}; @@ -293,15 +287,21 @@ C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup) indexBuffer.assign(meshGroup.indices.begin(), meshGroup.indices.end()); } - size_t vertexBufferSize = vertexBuffer.size() * sizeof(vertex); - size_t indexBufferSize = indexBuffer.size() * sizeof(uint16_t); + MINIWIN_NOT_IMPLEMENTED(); + // TODO use ibo instead of flattening verticies, see + // https://github.com/devkitPro/3ds-examples/blob/44faa81d79d5781c0e149e4a7005f2e005edb736/graphics/gpu/loop_subdivision/source/main.c#L104 + std::vector vertexUploadBuffer; + vertexUploadBuffer.reserve(indexBuffer.size()); + + for (size_t i = 0; i < indexBuffer.size(); ++i) { + vertexUploadBuffer.emplace_back(vertexBuffer[indexBuffer[i]]); + } + + size_t vertexBufferSize = vertexUploadBuffer.size() * sizeof(D3DRMVERTEX); cache.vbo = linearAlloc(vertexBufferSize); - memcpy(cache.vbo, vertexBuffer.data(), vertexBufferSize); - cache.ibo = linearAlloc(indexBufferSize); - memcpy(cache.ibo, indexBuffer.data(), indexBufferSize); - - cache.indexCount = indexBuffer.size(); + memcpy(cache.vbo, vertexUploadBuffer.data(), vertexBufferSize); + cache.vertexCount = vertexUploadBuffer.size(); return cache; } @@ -316,7 +316,7 @@ void Citro3DRenderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) if (cacheEntry.meshGroup) { cacheEntry.meshGroup = nullptr; linearFree(cacheEntry.vbo); - linearFree(cacheEntry.ibo); + cacheEntry.vertexCount = 0; } delete ctx; }, @@ -450,7 +450,8 @@ void Citro3DRenderer::SubmitDraw( const Appearance& appearance ) { - C3D_Mtx modelView; + C3D_Mtx projection, modelView; + ConvertPerspective(m_projection, &projection); ConvertMatrix(modelViewMatrix, &modelView); C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); @@ -458,7 +459,7 @@ void Citro3DRenderer::SubmitDraw( C3D_BufInfo* bufInfo = C3D_GetBufInfo(); BufInfo_Init(bufInfo); - BufInfo_Add(bufInfo, mesh.vbo, sizeof(vertex), 3, 0x210); + BufInfo_Add(bufInfo, mesh.vbo, sizeof(D3DRMVERTEX), 3, 0x210); C3D_FVUnifSet( GPU_VERTEX_SHADER, @@ -486,7 +487,7 @@ void Citro3DRenderer::SubmitDraw( C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); } - C3D_DrawElements(GPU_GEOMETRY_PRIM, mesh.indexCount, C3D_UNSIGNED_SHORT, mesh.ibo); + C3D_DrawArrays(GPU_TRIANGLES, 0, mesh.vertexCount); } HRESULT Citro3DRenderer::FinalizeFrame() diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index e08035c4..6c32516f 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -20,8 +20,7 @@ struct C3DMeshCacheEntry { const MeshGroup* meshGroup = nullptr; int version = 0; void* vbo = nullptr; - void* ibo = nullptr; - int indexCount = 0; + int vertexCount = 0; }; class Citro3DRenderer : public Direct3DRMRenderer {