mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-01-11 10:31:16 +00:00
3DS Port (#450)
* [WIP] 3ds port
Recommit of everything after the 2d renderer merge
* VERY AWESOME FEATURE FRFR
* Stopped CPU suicide and app crashing for now
* put in Texture3DS function thing
* Fix clear color
* Implement 2D rendering via Citro3D
* Set 3dsx smdh metadata
* Render world content, sort of
* Push mesh dynamically
* Remove Citro3D init hacks
* Clean up Citro3D implementation
* Try to upload meshes and convert matricies
* Fix 3D rendering
* Apply optimizations
* Implement lighting
* Set 3dsx smdh metadata
* Revert "Apply optimizations"
This reverts commit 6660082fef.
* Apply optimizations
* Added a cleaner icon (#4)
* Fix pure buffer clear frames (#9)
* Disable OpenGL on 3DS (#10)
* Fix tiled textures and improve UI image quality (#11)
* Create 3DS default config overrides
* 3ds: implement apt hooks
* remove unused import
* Apply suggestions from code review
Co-authored-by: Christian Semmler <mail@csemmler.com>
Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com>
* Update miniwin/src/d3drm/backends/citro3d/renderer.cpp
Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com>
* Separate 3DS apt hook code + move cmake 3ds into ISLE_BUILD_APP
* miniwin: use citro3dd if debugging
* Optimize texture encoding (#12)
* Cleanup
* Set correct mipmap level for UI textures (#13)
* cpack: include the .3dsx
* Add 3DS CI
* Fix CI
Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com>
* syntax
* Refactor c3d renderer (#14)
* Refactor c3d renderer
* format
* Apply suggestions from code review
Co-authored-by: Anders Jenbo <anders@jenbo.dk>
---------
Co-authored-by: Anders Jenbo <anders@jenbo.dk>
* n3ds: just distribute the .3dsx
* upload 3dsx
* Skip uploading 3DS artifacts
* Update ci.yml
* Update ci.yml
* Remove extraneous ifdef
---------
Co-authored-by: MaxBrick <maximusbrick@gmail.com>
Co-authored-by: Anders Jenbo <anders@jenbo.dk>
Co-authored-by: Steven <139715581+StevenSYS@users.noreply.github.com>
Co-authored-by: Christian Semmler <mail@csemmler.com>
Co-authored-by: Anonymous Maarten <madebr@users.noreply.github.com>
This commit is contained in:
parent
586327b584
commit
1ff768935e
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -25,6 +25,7 @@ jobs:
|
||||
build:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
container: ${{ matrix.container || '' }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell || 'sh' }}
|
||||
@ -41,6 +42,7 @@ jobs:
|
||||
- { name: 'msys2 mingw64', os: 'windows-latest', dx5: false, config: true, mingw: true, werror: true, clang-tidy: true, msystem: 'mingw64', msys-env: 'mingw-w64-x86_64', shell: 'msys2 {0}' }
|
||||
- { name: 'macOS', os: 'macos-latest', dx5: false, config: true, brew: true, werror: true, clang-tidy: false }
|
||||
- { name: 'Emscripten', os: 'ubuntu-latest', dx5: false, config: false, emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' }
|
||||
- { name: 'Nintendo 3DS', os: 'ubuntu-latest', dx5: false, config: false, n3ds: true, werror: true, clang-tidy: false, container: 'devkitpro/devkitarm:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake' }
|
||||
steps:
|
||||
- name: Setup vcvars
|
||||
if: ${{ !!matrix.msvc }}
|
||||
@ -89,6 +91,7 @@ jobs:
|
||||
- name: Configure (CMake)
|
||||
run: |
|
||||
${{ matrix.cmake-wrapper || '' }} cmake -S . -B build -GNinja \
|
||||
${{ matrix.cmake-args || '' }} \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DISLE_USE_DX5=${{ !!matrix.dx5 }} \
|
||||
-DISLE_BUILD_CONFIG=${{ !!matrix.config }} \
|
||||
@ -101,6 +104,7 @@ jobs:
|
||||
run: cmake --build build --verbose
|
||||
|
||||
- name: Package (CPack)
|
||||
if: ${{ !matrix.n3ds }}
|
||||
run: |
|
||||
cd build
|
||||
cpack .
|
||||
@ -128,6 +132,13 @@ jobs:
|
||||
--output appimage && \
|
||||
mv *.AppImage dist/
|
||||
|
||||
- name: Package (3DS)
|
||||
if: ${{ matrix.n3ds }}
|
||||
run: |
|
||||
cd build
|
||||
mkdir dist
|
||||
mv *.3dsx dist/
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@ -135,6 +146,7 @@ jobs:
|
||||
path: |
|
||||
build/dist/isle-*
|
||||
build/dist/*.AppImage
|
||||
build/dist/*.3dsx
|
||||
|
||||
flatpak:
|
||||
name: "Flatpak (${{ matrix.arch }})"
|
||||
|
||||
@ -38,7 +38,7 @@ option(ISLE_WERROR "Treat warnings as errors" OFF)
|
||||
option(ISLE_DEBUG "Enable imgui debug" ON)
|
||||
cmake_dependent_option(ISLE_USE_DX5 "Build with internal DirectX 5 SDK" "${NOT_MINGW}" "WIN32;CMAKE_SIZEOF_VOID_P EQUAL 4" OFF)
|
||||
cmake_dependent_option(ISLE_MINIWIN "Use miniwin" ON "NOT ISLE_USE_DX5" OFF)
|
||||
cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN" OFF)
|
||||
cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN;NOT NINTENDO_3DS" OFF)
|
||||
cmake_dependent_option(ISLE_COMPILE_SHADERS "Compile shaders" ON "SDL_SHADERCROSS_BIN;TARGET Python3::Interpreter" OFF)
|
||||
option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON)
|
||||
option(ENABLE_CLANG_TIDY "Enable clang-tidy")
|
||||
@ -523,6 +523,12 @@ if (ISLE_BUILD_APP)
|
||||
target_compile_definitions(isle PRIVATE "ISLE_EMSCRIPTEN_HOST=\"${ISLE_EMSCRIPTEN_HOST}\"")
|
||||
set_property(TARGET isle PROPERTY SUFFIX ".html")
|
||||
endif()
|
||||
if(NINTENDO_3DS)
|
||||
target_sources(isle PRIVATE
|
||||
ISLE/3ds/apthooks.cpp
|
||||
ISLE/3ds/config.cpp
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ISLE_BUILD_CONFIG)
|
||||
@ -652,9 +658,23 @@ add_subdirectory(packaging)
|
||||
|
||||
set(CPACK_PACKAGE_DIRECTORY "dist")
|
||||
set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}")
|
||||
if(NINTENDO_3DS)
|
||||
ctr_generate_smdh(isle.smdh
|
||||
NAME "LEGO Island"
|
||||
TITLE "LEGO Island"
|
||||
DESCRIPTION "LEGO Island for the Nintendo 3DS"
|
||||
AUTHOR "isledecomp/isle-portable"
|
||||
VERSION "${PROJECT_VERSION}"
|
||||
ICON "ISLE/res/3ds/isle.png"
|
||||
)
|
||||
|
||||
ctr_create_3dsx(isle SMDH isle.smdh)
|
||||
install(FILES "$<TARGET_FILE_DIR:isle>/isle.3dsx" DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
endif()
|
||||
if(MSVC)
|
||||
set(CPACK_GENERATOR ZIP)
|
||||
else()
|
||||
set(CPACK_GENERATOR TGZ)
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
|
||||
30
ISLE/3ds/apthooks.cpp
Normal file
30
ISLE/3ds/apthooks.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "apthooks.h"
|
||||
|
||||
#include "legomain.h"
|
||||
#include "misc.h"
|
||||
|
||||
aptHookCookie g_aptCookie;
|
||||
|
||||
void N3DS_AptHookCallback(APT_HookType hookType, void* param)
|
||||
{
|
||||
switch (hookType) {
|
||||
case APTHOOK_ONSLEEP:
|
||||
case APTHOOK_ONSUSPEND:
|
||||
Lego()->Pause();
|
||||
break;
|
||||
case APTHOOK_ONWAKEUP:
|
||||
case APTHOOK_ONRESTORE:
|
||||
Lego()->Resume();
|
||||
break;
|
||||
case APTHOOK_ONEXIT:
|
||||
Lego()->CloseMainWindow();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void N3DS_SetupAptHooks()
|
||||
{
|
||||
aptHook(&g_aptCookie, N3DS_AptHookCallback, NULL);
|
||||
}
|
||||
9
ISLE/3ds/apthooks.h
Normal file
9
ISLE/3ds/apthooks.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef N3DS_APTHOOKS_H
|
||||
#define N3DS_APTHOOKS_H
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
void N3DS_AptHookCallback(APT_HookType hookType, void* param);
|
||||
void N3DS_SetupAptHooks();
|
||||
|
||||
#endif // N3DS_APTHOOKS_H
|
||||
22
ISLE/3ds/config.cpp
Normal file
22
ISLE/3ds/config.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <iniparser.h>
|
||||
|
||||
void N3DS_SetupDefaultConfigOverrides(dictionary* p_dictionary)
|
||||
{
|
||||
SDL_Log("Overriding default config for 3DS");
|
||||
|
||||
// We are currently not bundling the assets into romfs.
|
||||
// User must place assets in sdmc:/3ds/isle where
|
||||
// sdmc:/3ds/isle/LEGO/SCRIPTS/CREDITS.si exists, for example.
|
||||
iniparser_set(p_dictionary, "isle:diskpath", "sdmc:/3ds/isle/LEGO/disk");
|
||||
iniparser_set(p_dictionary, "isle:cdpath", "sdmc:/3ds/isle");
|
||||
|
||||
// TODO: Save path: can we use libctru FS save data functions? Would be neat, especially for CIA install
|
||||
// Extra / at the end causes some issues
|
||||
iniparser_set(p_dictionary, "isle:savepath", "sdmc:/3ds/isle");
|
||||
|
||||
// Use e_noAnimation/cut transition
|
||||
iniparser_set(p_dictionary, "isle:Transition Type", "1");
|
||||
}
|
||||
8
ISLE/3ds/config.h
Normal file
8
ISLE/3ds/config.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef N3DS_CONFIG_H
|
||||
#define N3DS_CONFIG_H
|
||||
|
||||
#include "dictionary.h"
|
||||
|
||||
void N3DS_SetupDefaultConfigOverrides(dictionary* p_dictionary);
|
||||
|
||||
#endif // N3DS_CONFIG_H
|
||||
@ -50,6 +50,11 @@
|
||||
#include "emscripten/messagebox.h"
|
||||
#endif
|
||||
|
||||
#ifdef __3DS__
|
||||
#include "3ds/apthooks.h"
|
||||
#include "3ds/config.h"
|
||||
#endif
|
||||
|
||||
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
|
||||
|
||||
// GLOBAL: ISLE 0x410030
|
||||
@ -313,6 +318,9 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv)
|
||||
},
|
||||
NULL
|
||||
);
|
||||
#endif
|
||||
#ifdef __3DS__
|
||||
N3DS_SetupAptHooks();
|
||||
#endif
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
@ -654,7 +662,7 @@ MxResult IsleApp::SetupWindow()
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, g_targetHeight);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, m_fullScreen);
|
||||
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE);
|
||||
#ifdef MINIWIN
|
||||
#if defined(MINIWIN) && !defined(__3DS__)
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
@ -825,6 +833,9 @@ bool IsleApp::LoadConfig()
|
||||
iniparser_set(dict, "isle:Max Allowed Extras", SDL_itoa(m_maxAllowedExtras, buf, 10));
|
||||
iniparser_set(dict, "isle:Transition Type", SDL_itoa(m_transitionType, buf, 10));
|
||||
|
||||
#ifdef __3DS__
|
||||
N3DS_SetupDefaultConfigOverrides(dict);
|
||||
#endif
|
||||
iniparser_dump_ini(dict, iniFP);
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig);
|
||||
fclose(iniFP);
|
||||
|
||||
@ -96,6 +96,7 @@ class IsleApp {
|
||||
};
|
||||
|
||||
extern IsleApp* g_isle;
|
||||
extern MxS32 g_closed;
|
||||
|
||||
extern IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice();
|
||||
|
||||
|
||||
@ -309,7 +309,7 @@ void IsleDebug_Render()
|
||||
if (ImGui::TreeNode("Sound Manager")) {
|
||||
LegoSoundManager* soundManager = lego->GetSoundManager();
|
||||
Sint32 oldVolume = soundManager->GetVolume();
|
||||
Sint32 volume = oldVolume;
|
||||
int volume = oldVolume;
|
||||
ImGui::SliderInt("volume", &volume, 0, 100);
|
||||
if (volume != oldVolume) {
|
||||
soundManager->SetVolume(volume);
|
||||
|
||||
BIN
ISLE/res/3ds/isle.png
Normal file
BIN
ISLE/res/3ds/isle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 438 B |
@ -54,6 +54,23 @@ else()
|
||||
message(STATUS "🧩 OpenGL ES 2.x support not enabled")
|
||||
endif()
|
||||
|
||||
if(NINTENDO_3DS)
|
||||
if(ISLE_DEBUG)
|
||||
find_library(CITRO3D_LIBRARY NAMES citro3dd)
|
||||
else()
|
||||
find_library(CITRO3D_LIBRARY NAMES citro3d)
|
||||
endif()
|
||||
if(CITRO3D_LIBRARY)
|
||||
message(STATUS "Found citro3d: enabling Citro3D renderer")
|
||||
target_sources(miniwin PRIVATE src/d3drm/backends/citro3d/renderer.cpp)
|
||||
ctr_add_shader_library(vshader src/d3drm/backends/citro3d/vshader.v.pica)
|
||||
dkp_add_embedded_binary_library(3ds_shaders vshader)
|
||||
target_link_libraries(miniwin PRIVATE ${CITRO3D_LIBRARY} 3ds_shaders)
|
||||
else()
|
||||
message(STATUS "🧩 Citro3D support not enabled")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_sources(miniwin PRIVATE
|
||||
src/d3drm/backends/directx9/actual.cpp
|
||||
|
||||
606
miniwin/src/d3drm/backends/citro3d/renderer.cpp
Normal file
606
miniwin/src/d3drm/backends/citro3d/renderer.cpp
Normal file
@ -0,0 +1,606 @@
|
||||
#include "d3drmrenderer.h"
|
||||
#include "d3drmrenderer_citro3d.h"
|
||||
#include "d3drmtexture_impl.h"
|
||||
#include "ddraw_impl.h"
|
||||
#include "meshutils.h"
|
||||
#include "miniwin.h"
|
||||
#include "vshader_shbin.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
static bool g_rendering = false;
|
||||
|
||||
static DVLB_s* vshader_dvlb;
|
||||
static shaderProgram_s program;
|
||||
static int uLoc_projection;
|
||||
static int uLoc_modelView;
|
||||
static int uLoc_meshColor;
|
||||
static int uLoc_lightVec;
|
||||
static int uLoc_lightClr;
|
||||
static int uLoc_shininess;
|
||||
|
||||
Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height)
|
||||
{
|
||||
m_width = 320;
|
||||
m_height = 240;
|
||||
m_virtualWidth = width;
|
||||
m_virtualHeight = height;
|
||||
|
||||
gfxInitDefault();
|
||||
consoleInit(GFX_TOP, nullptr);
|
||||
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
|
||||
|
||||
m_renderTarget = C3D_RenderTargetCreate(m_height, m_width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
|
||||
C3D_RenderTargetSetOutput(
|
||||
m_renderTarget,
|
||||
GFX_BOTTOM,
|
||||
GFX_LEFT,
|
||||
GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) |
|
||||
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) |
|
||||
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)
|
||||
);
|
||||
|
||||
vshader_dvlb = DVLB_ParseFile((u32*) vshader_shbin, vshader_shbin_size);
|
||||
shaderProgramInit(&program);
|
||||
shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]);
|
||||
C3D_BindProgram(&program);
|
||||
|
||||
C3D_CullFace(GPU_CULL_FRONT_CCW);
|
||||
|
||||
uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection");
|
||||
uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView");
|
||||
uLoc_meshColor = shaderInstanceGetUniformLocation(program.vertexShader, "meshColor");
|
||||
uLoc_lightVec = shaderInstanceGetUniformLocation(program.vertexShader, "lightVec");
|
||||
uLoc_lightClr = shaderInstanceGetUniformLocation(program.vertexShader, "lightClr");
|
||||
uLoc_shininess = shaderInstanceGetUniformLocation(program.vertexShader, "shininess");
|
||||
|
||||
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
|
||||
AttrInfo_Init(attrInfo);
|
||||
AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position
|
||||
AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 3); // v2=normal
|
||||
AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 2); // v1=texcoord
|
||||
}
|
||||
|
||||
Citro3DRenderer::~Citro3DRenderer()
|
||||
{
|
||||
shaderProgramFree(&program);
|
||||
DVLB_Free(vshader_dvlb);
|
||||
C3D_Fini();
|
||||
gfxExit();
|
||||
}
|
||||
|
||||
void Citro3DRenderer::PushLights(const SceneLight* lights, size_t count)
|
||||
{
|
||||
m_lights.assign(lights, lights + count);
|
||||
}
|
||||
|
||||
void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back)
|
||||
{
|
||||
memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D));
|
||||
}
|
||||
|
||||
void Citro3DRenderer::SetFrustumPlanes(const Plane* frustumPlanes)
|
||||
{
|
||||
}
|
||||
|
||||
struct Citro3DCacheDestroyContext {
|
||||
Citro3DRenderer* renderer;
|
||||
Uint32 id;
|
||||
};
|
||||
|
||||
void Citro3DRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture)
|
||||
{
|
||||
auto* ctx = new Citro3DCacheDestroyContext{this, id};
|
||||
texture->AddDestroyCallback(
|
||||
[](IDirect3DRMObject* obj, void* arg) {
|
||||
auto* ctx = static_cast<Citro3DCacheDestroyContext*>(arg);
|
||||
auto& entry = ctx->renderer->m_textures[ctx->id];
|
||||
if (entry.texture) {
|
||||
C3D_TexDelete(&entry.c3dTex);
|
||||
entry.texture = nullptr;
|
||||
}
|
||||
delete ctx;
|
||||
},
|
||||
ctx
|
||||
);
|
||||
}
|
||||
|
||||
static int NearestPowerOfTwoClamp(int val)
|
||||
{
|
||||
static const int sizes[] = {8, 16, 32, 64, 128, 256, 512};
|
||||
for (int size : sizes) {
|
||||
if (val <= size) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
return 512;
|
||||
}
|
||||
|
||||
static SDL_Surface* ConvertAndResizeSurface(SDL_Surface* original, bool isUI, float scale)
|
||||
{
|
||||
SDL_Surface* converted = SDL_ConvertSurface(original, SDL_PIXELFORMAT_RGBA8888);
|
||||
if (!converted) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isUI) {
|
||||
return converted;
|
||||
}
|
||||
|
||||
int scaledW = static_cast<int>(converted->w * scale);
|
||||
int scaledH = static_cast<int>(converted->h * scale);
|
||||
|
||||
int paddedW = NearestPowerOfTwoClamp(scaledW);
|
||||
int paddedH = NearestPowerOfTwoClamp(scaledH);
|
||||
|
||||
SDL_Surface* padded = SDL_CreateSurface(paddedW, paddedH, SDL_PIXELFORMAT_RGBA8888);
|
||||
if (!padded) {
|
||||
SDL_DestroySurface(converted);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDL_Rect dstRect = {0, 0, scaledW, scaledH};
|
||||
SDL_BlitSurfaceScaled(converted, nullptr, padded, &dstRect, SDL_SCALEMODE_LINEAR);
|
||||
SDL_DestroySurface(converted);
|
||||
|
||||
return padded;
|
||||
}
|
||||
|
||||
static void EncodeTextureLayout(const u8* src, u8* dst, int width, int height)
|
||||
{
|
||||
const int tileSize = 8;
|
||||
const int bytesPerPixel = 4;
|
||||
|
||||
int tilesPerRow = (width + tileSize - 1) / tileSize;
|
||||
|
||||
static const uint8_t mortonLUT[64] = {0, 1, 4, 5, 16, 17, 20, 21, 2, 3, 6, 7, 18, 19, 22, 23,
|
||||
8, 9, 12, 13, 24, 25, 28, 29, 10, 11, 14, 15, 26, 27, 30, 31,
|
||||
32, 33, 36, 37, 48, 49, 52, 53, 34, 35, 38, 39, 50, 51, 54, 55,
|
||||
40, 41, 44, 45, 56, 57, 60, 61, 42, 43, 46, 47, 58, 59, 62, 63};
|
||||
|
||||
for (int tileY = 0; tileY < height; tileY += tileSize) {
|
||||
for (int tileX = 0; tileX < width; tileX += tileSize) {
|
||||
int tileIndex = (tileY / tileSize) * tilesPerRow + (tileX / tileSize);
|
||||
tileIndex *= tileSize * tileSize;
|
||||
|
||||
for (int y = 0; y < tileSize; ++y) {
|
||||
for (int x = 0; x < tileSize; ++x) {
|
||||
int srcX = tileX + x;
|
||||
int srcY = tileY + y;
|
||||
|
||||
if (srcX >= width || srcY >= height) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int morton = mortonLUT[y * tileSize + x];
|
||||
int dstIndex = (tileIndex + morton) * bytesPerPixel;
|
||||
int srcIndex = ((height - 1 - srcY) * width + srcX);
|
||||
|
||||
*(u32*) &dst[dstIndex] = ((u32*) src)[srcIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool ConvertAndUploadTexture(C3D_Tex* tex, SDL_Surface* originalSurface, bool isUI, float scale)
|
||||
{
|
||||
SDL_Surface* resized = ConvertAndResizeSurface(originalSurface, isUI, scale);
|
||||
if (!resized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int width = resized->w;
|
||||
int height = resized->h;
|
||||
|
||||
C3D_TexInitParams params = {};
|
||||
params.width = width;
|
||||
params.height = height;
|
||||
params.format = GPU_RGBA8;
|
||||
params.maxLevel = isUI ? 0 : 4;
|
||||
params.type = GPU_TEX_2D;
|
||||
if (!C3D_TexInitWithParams(tex, nullptr, params)) {
|
||||
SDL_DestroySurface(resized);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* tiledData = (uint8_t*) malloc(width * height * 4);
|
||||
if (!tiledData) {
|
||||
SDL_DestroySurface(resized);
|
||||
return false;
|
||||
}
|
||||
|
||||
EncodeTextureLayout((const u8*) resized->pixels, tiledData, width, height);
|
||||
SDL_DestroySurface(resized);
|
||||
|
||||
C3D_TexUpload(tex, tiledData);
|
||||
free(tiledData);
|
||||
|
||||
if (isUI) {
|
||||
C3D_TexSetFilter(tex, GPU_NEAREST, GPU_NEAREST);
|
||||
C3D_TexSetWrap(tex, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE);
|
||||
}
|
||||
else {
|
||||
C3D_TexSetFilter(tex, GPU_LINEAR, GPU_LINEAR);
|
||||
C3D_TexSetWrap(tex, GPU_REPEAT, GPU_REPEAT);
|
||||
C3D_TexSetFilterMipmap(tex, GPU_LINEAR);
|
||||
C3D_TexGenerateMipmap(tex, GPU_TEXFACE_2D);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Uint32 Citro3DRenderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUi)
|
||||
{
|
||||
auto texture = static_cast<Direct3DRMTextureImpl*>(iTexture);
|
||||
auto surface = static_cast<DirectDrawSurfaceImpl*>(texture->m_surface);
|
||||
SDL_Surface* originalSurface = surface->m_surface;
|
||||
|
||||
int originalW = originalSurface->w;
|
||||
int originalH = originalSurface->h;
|
||||
|
||||
for (Uint32 i = 0; i < m_textures.size(); ++i) {
|
||||
auto& tex = m_textures[i];
|
||||
if (tex.texture == texture) {
|
||||
if (tex.version != texture->m_version) {
|
||||
C3D_TexDelete(&tex.c3dTex);
|
||||
if (!ConvertAndUploadTexture(&tex.c3dTex, originalSurface, isUi, m_viewportTransform.scale)) {
|
||||
return NO_TEXTURE_ID;
|
||||
}
|
||||
|
||||
tex.version = texture->m_version;
|
||||
tex.width = NearestPowerOfTwoClamp(originalW * m_viewportTransform.scale);
|
||||
tex.height = NearestPowerOfTwoClamp(originalH * m_viewportTransform.scale);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
C3DTextureCacheEntry entry;
|
||||
entry.texture = texture;
|
||||
entry.version = texture->m_version;
|
||||
entry.width = NearestPowerOfTwoClamp(originalW * m_viewportTransform.scale);
|
||||
entry.height = NearestPowerOfTwoClamp(originalH * m_viewportTransform.scale);
|
||||
|
||||
if (!ConvertAndUploadTexture(&entry.c3dTex, originalSurface, isUi, m_viewportTransform.scale)) {
|
||||
return NO_TEXTURE_ID;
|
||||
}
|
||||
|
||||
for (Uint32 i = 0; i < m_textures.size(); ++i) {
|
||||
if (!m_textures[i].texture) {
|
||||
m_textures[i] = std::move(entry);
|
||||
AddTextureDestroyCallback(i, texture);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
m_textures.push_back(std::move(entry));
|
||||
AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture);
|
||||
return (Uint32) (m_textures.size() - 1);
|
||||
}
|
||||
|
||||
C3DMeshCacheEntry C3DUploadMesh(const MeshGroup& meshGroup)
|
||||
{
|
||||
C3DMeshCacheEntry cache{&meshGroup, meshGroup.version};
|
||||
|
||||
std::vector<D3DRMVERTEX> vertexBuffer;
|
||||
std::vector<uint16_t> indexBuffer;
|
||||
|
||||
if (meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT) {
|
||||
FlattenSurfaces(
|
||||
meshGroup.vertices.data(),
|
||||
meshGroup.vertices.size(),
|
||||
meshGroup.indices.data(),
|
||||
meshGroup.indices.size(),
|
||||
meshGroup.texture != nullptr,
|
||||
vertexBuffer,
|
||||
indexBuffer
|
||||
);
|
||||
}
|
||||
else {
|
||||
vertexBuffer.assign(meshGroup.vertices.begin(), meshGroup.vertices.end());
|
||||
indexBuffer.assign(meshGroup.indices.begin(), meshGroup.indices.end());
|
||||
}
|
||||
|
||||
// Flatten vertices as IBO is buggy on 3DS hardware
|
||||
std::vector<D3DRMVERTEX> vertexUploadBuffer;
|
||||
vertexUploadBuffer.reserve(indexBuffer.size());
|
||||
|
||||
for (size_t i = 0; i < indexBuffer.size(); ++i) {
|
||||
vertexUploadBuffer.emplace_back(vertexBuffer[indexBuffer[i]]);
|
||||
}
|
||||
|
||||
size_t vertexBufferSize = vertexUploadBuffer.size() * sizeof(D3DRMVERTEX);
|
||||
cache.vbo = linearAlloc(vertexBufferSize);
|
||||
memcpy(cache.vbo, vertexUploadBuffer.data(), vertexBufferSize);
|
||||
cache.vertexCount = vertexUploadBuffer.size();
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
void Citro3DRenderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh)
|
||||
{
|
||||
auto* ctx = new Citro3DCacheDestroyContext{this, id};
|
||||
mesh->AddDestroyCallback(
|
||||
[](IDirect3DRMObject* obj, void* arg) {
|
||||
auto* ctx = static_cast<Citro3DCacheDestroyContext*>(arg);
|
||||
auto& cacheEntry = ctx->renderer->m_meshs[ctx->id];
|
||||
if (cacheEntry.meshGroup) {
|
||||
cacheEntry.meshGroup = nullptr;
|
||||
linearFree(cacheEntry.vbo);
|
||||
cacheEntry.vertexCount = 0;
|
||||
}
|
||||
delete ctx;
|
||||
},
|
||||
ctx
|
||||
);
|
||||
}
|
||||
|
||||
Uint32 Citro3DRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup)
|
||||
{
|
||||
for (Uint32 i = 0; i < m_meshs.size(); ++i) {
|
||||
auto& cache = m_meshs[i];
|
||||
if (cache.meshGroup == meshGroup) {
|
||||
if (cache.version != meshGroup->version) {
|
||||
cache = std::move(C3DUploadMesh(*meshGroup));
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
auto newCache = C3DUploadMesh(*meshGroup);
|
||||
|
||||
for (Uint32 i = 0; i < m_meshs.size(); ++i) {
|
||||
auto& cache = m_meshs[i];
|
||||
if (!cache.meshGroup) {
|
||||
cache = std::move(newCache);
|
||||
AddMeshDestroyCallback(i, mesh);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
m_meshs.push_back(std::move(newCache));
|
||||
AddMeshDestroyCallback((Uint32) (m_meshs.size() - 1), mesh);
|
||||
return (Uint32) (m_meshs.size() - 1);
|
||||
}
|
||||
|
||||
void Citro3DRenderer::StartFrame()
|
||||
{
|
||||
if (g_rendering) {
|
||||
return;
|
||||
}
|
||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
||||
C3D_FrameDrawOn(m_renderTarget);
|
||||
g_rendering = true;
|
||||
}
|
||||
|
||||
void ConvertPerspective(const D3DRMMATRIX4D in, C3D_Mtx* out)
|
||||
{
|
||||
float f_h = in[0][0];
|
||||
float f_v = in[1][1];
|
||||
|
||||
float aspect = f_v / f_h;
|
||||
float fovY = 2.0f * atanf(1.0f / f_v);
|
||||
|
||||
float nearZ = -in[3][2] / in[2][2];
|
||||
float farZ = nearZ * in[2][2] / (in[2][2] - 1.0f);
|
||||
|
||||
Mtx_PerspTilt(out, fovY, aspect, nearZ, farZ, true);
|
||||
}
|
||||
|
||||
HRESULT Citro3DRenderer::BeginFrame()
|
||||
{
|
||||
StartFrame();
|
||||
C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL);
|
||||
|
||||
C3D_Mtx projection;
|
||||
ConvertPerspective(m_projection, &projection);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);
|
||||
|
||||
for (const auto& light : m_lights) {
|
||||
FColor lightColor = light.color;
|
||||
if (light.positional == 0.0f && light.directional == 0.0f) {
|
||||
// Ambient light
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 2, lightColor.r, lightColor.g, lightColor.b, 1.0f);
|
||||
}
|
||||
else if (light.directional == 1.0f) {
|
||||
C3D_FVUnifSet(
|
||||
GPU_VERTEX_SHADER,
|
||||
uLoc_lightVec + 1,
|
||||
-light.direction.x,
|
||||
-light.direction.y,
|
||||
-light.direction.z,
|
||||
0.0f
|
||||
);
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 1, lightColor.r, lightColor.g, lightColor.b, 0.0f);
|
||||
}
|
||||
else if (light.positional == 1.0f) {
|
||||
C3D_FVUnifSet(
|
||||
GPU_VERTEX_SHADER,
|
||||
uLoc_lightVec + 0,
|
||||
light.position.x,
|
||||
light.position.y,
|
||||
light.position.z,
|
||||
0.0f
|
||||
);
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 0, lightColor.r, lightColor.g, lightColor.b, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void Citro3DRenderer::EnableTransparency()
|
||||
{
|
||||
C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_COLOR);
|
||||
}
|
||||
|
||||
void ConvertMatrix(const D3DRMMATRIX4D in, C3D_Mtx* out)
|
||||
{
|
||||
for (int i = 0; i < 4; i++) {
|
||||
out->r[i].x = in[0][i];
|
||||
out->r[i].y = in[1][i];
|
||||
out->r[i].z = in[2][i];
|
||||
out->r[i].w = in[3][i];
|
||||
}
|
||||
}
|
||||
|
||||
void Citro3DRenderer::SubmitDraw(
|
||||
DWORD meshId,
|
||||
const D3DRMMATRIX4D& modelViewMatrix,
|
||||
const D3DRMMATRIX4D& worldMatrix,
|
||||
const D3DRMMATRIX4D& viewMatrix,
|
||||
const Matrix3x3& normalMatrix,
|
||||
const Appearance& appearance
|
||||
)
|
||||
{
|
||||
C3D_Mtx modelView;
|
||||
ConvertMatrix(modelViewMatrix, &modelView);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView);
|
||||
|
||||
auto& mesh = m_meshs[meshId];
|
||||
|
||||
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
|
||||
BufInfo_Init(bufInfo);
|
||||
BufInfo_Add(bufInfo, mesh.vbo, sizeof(D3DRMVERTEX), 3, 0x210);
|
||||
|
||||
C3D_FVUnifSet(
|
||||
GPU_VERTEX_SHADER,
|
||||
uLoc_meshColor,
|
||||
appearance.color.r / 255.0f,
|
||||
appearance.color.g / 255.0f,
|
||||
appearance.color.b / 255.0f,
|
||||
appearance.color.a / 255.0f
|
||||
);
|
||||
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_shininess, appearance.shininess / 255.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
if (appearance.textureId != NO_TEXTURE_ID) {
|
||||
C3D_TexBind(0, &m_textures[appearance.textureId].c3dTex);
|
||||
C3D_TexEnv* env = C3D_GetTexEnv(0);
|
||||
C3D_TexEnvInit(env);
|
||||
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR);
|
||||
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
|
||||
}
|
||||
else {
|
||||
C3D_TexBind(0, nullptr);
|
||||
C3D_TexEnv* env = C3D_GetTexEnv(0);
|
||||
C3D_TexEnvInit(env);
|
||||
C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR);
|
||||
C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
|
||||
}
|
||||
|
||||
C3D_DrawArrays(GPU_TRIANGLES, 0, mesh.vertexCount);
|
||||
}
|
||||
|
||||
HRESULT Citro3DRenderer::FinalizeFrame()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void Citro3DRenderer::Resize(int width, int height, const ViewportTransform& viewportTransform)
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_viewportTransform = viewportTransform;
|
||||
}
|
||||
|
||||
void Citro3DRenderer::Clear(float r, float g, float b)
|
||||
{
|
||||
StartFrame();
|
||||
u32 color =
|
||||
(static_cast<u32>(r * 255) << 24) | (static_cast<u32>(g * 255) << 16) | (static_cast<u32>(b * 255) << 8) | 255;
|
||||
C3D_RenderTargetClear(m_renderTarget, C3D_CLEAR_ALL, color, 0);
|
||||
}
|
||||
|
||||
void Citro3DRenderer::Flip()
|
||||
{
|
||||
C3D_FrameEnd(0);
|
||||
gfxFlushBuffers();
|
||||
gspWaitForVBlank();
|
||||
g_rendering = false;
|
||||
}
|
||||
|
||||
void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect)
|
||||
{
|
||||
C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
|
||||
StartFrame();
|
||||
C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_COLOR);
|
||||
|
||||
float left = -m_viewportTransform.offsetX / m_viewportTransform.scale;
|
||||
float right = (m_width - m_viewportTransform.offsetX) / m_viewportTransform.scale;
|
||||
float top = -m_viewportTransform.offsetY / m_viewportTransform.scale;
|
||||
float bottom = (m_height - m_viewportTransform.offsetY) / m_viewportTransform.scale;
|
||||
|
||||
C3D_Mtx projection, modelView;
|
||||
Mtx_OrthoTilt(&projection, left, right, bottom, top, 0.0f, 1.0f, true);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection);
|
||||
Mtx_Identity(&modelView);
|
||||
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView);
|
||||
|
||||
// Set light directions
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec + 0, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec + 1, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Set light colors
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 0, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 1, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 2, 1.0f, 1.0f, 1.0f, 1.0f); // Ambient
|
||||
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_shininess, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_meshColor, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
C3DTextureCacheEntry& texture = m_textures[textureId];
|
||||
|
||||
C3D_TexBind(0, &texture.c3dTex);
|
||||
C3D_TexEnv* env = C3D_GetTexEnv(0);
|
||||
C3D_TexEnvInit(env);
|
||||
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR);
|
||||
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
|
||||
|
||||
float scale = m_viewportTransform.scale;
|
||||
|
||||
float x1 = static_cast<float>(dstRect.x);
|
||||
float y1 = static_cast<float>(dstRect.y);
|
||||
float x2 = x1 + static_cast<float>(dstRect.w);
|
||||
float y2 = y1 + static_cast<float>(dstRect.h);
|
||||
|
||||
float u0 = (srcRect.x * scale) / texture.width;
|
||||
float u1 = ((srcRect.x + srcRect.w) * scale) / texture.width;
|
||||
float v0 = (srcRect.y * scale) / texture.height;
|
||||
float v1 = ((srcRect.y + srcRect.h) * scale) / texture.height;
|
||||
|
||||
C3D_ImmDrawBegin(GPU_TRIANGLES);
|
||||
|
||||
// Triangle 1
|
||||
C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f);
|
||||
C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f);
|
||||
|
||||
C3D_ImmSendAttrib(x2, y1, 0.5f, 0.0f);
|
||||
C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
C3D_ImmSendAttrib(u1, v0, 0.0f, 0.0f);
|
||||
|
||||
C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f);
|
||||
C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f);
|
||||
|
||||
// Triangle 2
|
||||
C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f);
|
||||
C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
C3D_ImmSendAttrib(u1, v1, 0.0f, 0.0f);
|
||||
|
||||
C3D_ImmSendAttrib(x1, y2, 0.5f, 0.0f);
|
||||
C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
C3D_ImmSendAttrib(u0, v1, 0.0f, 0.0f);
|
||||
|
||||
C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f);
|
||||
C3D_ImmSendAttrib(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
C3D_ImmSendAttrib(u0, v0, 0.0f, 0.0f);
|
||||
|
||||
C3D_ImmDrawEnd();
|
||||
}
|
||||
|
||||
void Citro3DRenderer::Download(SDL_Surface* target)
|
||||
{
|
||||
MINIWIN_NOT_IMPLEMENTED();
|
||||
}
|
||||
130
miniwin/src/d3drm/backends/citro3d/vshader.v.pica
Normal file
130
miniwin/src/d3drm/backends/citro3d/vshader.v.pica
Normal file
@ -0,0 +1,130 @@
|
||||
; Uniforms
|
||||
.fvec projection[4], modelView[4], meshColor
|
||||
.fvec lightVec[2], lightClr[3], shininess
|
||||
|
||||
; Constants
|
||||
.constf myconst(0.0, 1.0, -1.0, -0.5)
|
||||
|
||||
; Outputs
|
||||
.out outpos position
|
||||
.out outtc0 texcoord0
|
||||
.out outclr color
|
||||
|
||||
; Inputs
|
||||
.alias inpos v0
|
||||
.alias innrm v1
|
||||
.alias intex v2
|
||||
|
||||
.proc main
|
||||
; Prepare constants in usable temp regs
|
||||
mov r15.x, myconst.x ; 0.0
|
||||
mov r15.y, myconst.y ; 1.0
|
||||
mov r15.z, myconst.z ; -1.0
|
||||
|
||||
; Force the w component of inpos to be 1.0
|
||||
mov r0.xyz, inpos
|
||||
mov r0.w, r15.y
|
||||
|
||||
; r1 = modelView * inpos
|
||||
dp4 r1.x, modelView[0], r0
|
||||
dp4 r1.y, modelView[1], r0
|
||||
dp4 r1.z, modelView[2], r0
|
||||
dp4 r1.w, modelView[3], r0
|
||||
|
||||
; outpos = projection * r1
|
||||
dp4 outpos.x, projection[0], r1
|
||||
dp4 outpos.y, projection[1], r1
|
||||
dp4 outpos.z, projection[2], r1
|
||||
dp4 outpos.w, projection[3], r1
|
||||
|
||||
; outtex = intex
|
||||
mov outtc0, intex
|
||||
mov outtc0.zw, myconst.xy
|
||||
|
||||
; Transform normal
|
||||
mov r2.xyz, innrm
|
||||
mov r2.w, r15.x
|
||||
dp4 r3.x, modelView[0], r2
|
||||
dp4 r3.y, modelView[1], r2
|
||||
dp4 r3.z, modelView[2], r2
|
||||
mov r3.w, r15.x
|
||||
dp3 r4.x, r3, r3
|
||||
rsq r4.x, r4.x
|
||||
mul r3, r4.xxxx, r3 ; r3 = normalized normal
|
||||
|
||||
; Normalize lightVec[0]
|
||||
mov r5, lightVec[0]
|
||||
dp3 r6.x, r5, r5
|
||||
rsq r6.x, r6.x
|
||||
mul r5, r6.xxxx, r5
|
||||
|
||||
; dot(normal, lightVec[0])
|
||||
dp3 r6.x, r3, r5
|
||||
max r6.x, r6.x, r15.xxxx
|
||||
|
||||
; Normalize lightVec[1]
|
||||
mov r7, lightVec[1]
|
||||
dp3 r8.x, r7, r7
|
||||
rsq r8.x, r8.x
|
||||
mul r7, r8.xxxx, r7
|
||||
|
||||
; dot(normal, lightVec[1])
|
||||
dp3 r6.y, r3, r7
|
||||
max r6.y, r6.y, r15.xxxx
|
||||
|
||||
; Load lightClr
|
||||
mov r8, lightClr[2] ; ambient
|
||||
mov r9, lightClr[0] ; point
|
||||
mov r10, lightClr[1] ; directional
|
||||
|
||||
; diffuse = ambient + (lightClr[0] * dot0) + (lightClr[1] * dot1)
|
||||
mul r11, r9, r6.xxxx
|
||||
add r8, r8, r11
|
||||
mul r11, r10, r6.yyyy
|
||||
add r8, r8, r11 ; r8 = diffuse
|
||||
|
||||
; Check if shininess > 0
|
||||
mov r12, shininess
|
||||
slt r13.x, r15.x, r12.x
|
||||
|
||||
; viewVec = normalize(-position.xyz)
|
||||
mov r14.xyz, r1.xyz
|
||||
mul r14.xyz, r14.xyz, r15.zzz
|
||||
dp3 r4.x, r14, r14
|
||||
rsq r4.x, r4.x
|
||||
mul r14, r4.xxxx, r14
|
||||
|
||||
; H = normalize(view + lightVec[1])
|
||||
add r11, r14, r7
|
||||
dp3 r4.x, r11, r11
|
||||
rsq r4.x, r4.x
|
||||
mul r11, r4.xxxx, r11
|
||||
|
||||
; dot(normal, H)
|
||||
dp3 r4.x, r3, r11
|
||||
max r4.x, r4.x, r15.x
|
||||
|
||||
; Approximate pow(dotNH, 10) by repeated multiplication
|
||||
mul r5.x, r4.x, r4.x ; dotNH^2
|
||||
mul r5.x, r5.x, r5.x ; dotNH^4
|
||||
mul r5.x, r5.x, r5.x ; dotNH^8
|
||||
mul r4.x, r5.x, r4.x ; dotNH^9
|
||||
mul r4.x, r4.x, r4.x ; dotNH^10
|
||||
|
||||
; Multiply by shininess > 0 flag
|
||||
mul r4.x, r4.x, r13.x
|
||||
|
||||
; specular = lightClr[1] * spec
|
||||
mul r5, r10, r4.xxxx
|
||||
|
||||
; final = diffuse * meshColor + specular * lightClr[1]
|
||||
mov r9, meshColor
|
||||
mul r6, r8, r9 ; diffuse * meshColor
|
||||
add r7.xyz, r6.xyz, r5.xyz ; add specular (already multiplied by lightClr)
|
||||
min r7.xyz, r7.xyz, r15.yyyy
|
||||
|
||||
mov outclr.xyz, r7.xyz
|
||||
mov outclr.w, meshColor.w
|
||||
|
||||
end
|
||||
.end
|
||||
@ -80,7 +80,7 @@ void Direct3DRMSoftwareRenderer::ClearZBuffer()
|
||||
_mm_empty();
|
||||
}
|
||||
#endif
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
#elif (defined(__arm__) || defined(__aarch64__)) && !defined(__3DS__)
|
||||
if (SDL_HasNEON()) {
|
||||
float32x4_t inf4 = vdupq_n_f32(inf);
|
||||
for (; i + 4 <= size; i += 4) {
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
#ifdef USE_OPENGLES2
|
||||
#include "d3drmrenderer_opengles2.h"
|
||||
#endif
|
||||
#ifdef __3DS__
|
||||
#include "d3drmrenderer_citro3d.h"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include "d3drmrenderer_directx9.h"
|
||||
#endif
|
||||
@ -159,6 +162,11 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface(
|
||||
DDRenderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
|
||||
}
|
||||
#endif
|
||||
#ifdef __3DS__
|
||||
else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) {
|
||||
DDRenderer = new Citro3DRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight);
|
||||
}
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) {
|
||||
DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
|
||||
|
||||
@ -155,6 +155,10 @@ void Direct3DRMDevice2Impl::Resize()
|
||||
{
|
||||
int width, height;
|
||||
SDL_GetWindowSizeInPixels(DDWindow, &width, &height);
|
||||
#ifdef __3DS__
|
||||
width = 320; // We are on the lower screen
|
||||
height = 240;
|
||||
#endif
|
||||
m_viewportTransform = CalculateViewportTransform(m_virtualWidth, m_virtualHeight, width, height);
|
||||
m_renderer->Resize(width, height, m_viewportTransform);
|
||||
for (int i = 0; i < m_viewports->GetSize(); i++) {
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
#ifdef USE_OPENGLES2
|
||||
#include "d3drmrenderer_opengles2.h"
|
||||
#endif
|
||||
#ifdef __3DS__
|
||||
#include "d3drmrenderer_citro3d.h"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include "d3drmrenderer_directx9.h"
|
||||
#endif
|
||||
@ -232,6 +235,9 @@ HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
|
||||
#ifdef USE_OPENGL1
|
||||
OpenGL1Renderer_EnumDevice(cb, ctx);
|
||||
#endif
|
||||
#ifdef __3DS__
|
||||
Citro3DRenderer_EnumDevice(cb, ctx);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
DirectX9Renderer_EnumDevice(cb, ctx);
|
||||
#endif
|
||||
@ -346,6 +352,11 @@ HRESULT DirectDrawImpl::CreateDevice(
|
||||
DDRenderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
|
||||
}
|
||||
#endif
|
||||
#ifdef __3DS__
|
||||
else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) {
|
||||
DDRenderer = new Citro3DRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight);
|
||||
}
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) {
|
||||
DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
|
||||
|
||||
82
miniwin/src/internal/d3drmrenderer_citro3d.h
Normal file
82
miniwin/src/internal/d3drmrenderer_citro3d.h
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "d3drmrenderer.h"
|
||||
#include "ddraw_impl.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <citro3d.h>
|
||||
#include <vector>
|
||||
|
||||
DEFINE_GUID(Citro3D_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x53);
|
||||
|
||||
struct C3DTextureCacheEntry {
|
||||
IDirect3DRMTexture* texture;
|
||||
Uint32 version;
|
||||
C3D_Tex c3dTex;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
};
|
||||
|
||||
struct C3DMeshCacheEntry {
|
||||
const MeshGroup* meshGroup = nullptr;
|
||||
int version = 0;
|
||||
void* vbo = nullptr;
|
||||
int vertexCount = 0;
|
||||
};
|
||||
|
||||
class Citro3DRenderer : public Direct3DRMRenderer {
|
||||
public:
|
||||
Citro3DRenderer(DWORD width, DWORD height);
|
||||
~Citro3DRenderer() override;
|
||||
|
||||
void PushLights(const SceneLight* lightsArray, size_t count) override;
|
||||
void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override;
|
||||
void SetFrustumPlanes(const Plane* frustumPlanes) override;
|
||||
Uint32 GetTextureId(IDirect3DRMTexture* texture, bool isUi) override;
|
||||
Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override;
|
||||
HRESULT BeginFrame() override;
|
||||
void EnableTransparency() override;
|
||||
void SubmitDraw(
|
||||
DWORD meshId,
|
||||
const D3DRMMATRIX4D& modelViewMatrix,
|
||||
const D3DRMMATRIX4D& worldMatrix,
|
||||
const D3DRMMATRIX4D& viewMatrix,
|
||||
const Matrix3x3& normalMatrix,
|
||||
const Appearance& appearance
|
||||
) override;
|
||||
HRESULT FinalizeFrame() override;
|
||||
void Resize(int width, int height, const ViewportTransform& viewportTransform) override;
|
||||
void Clear(float r, float g, float b) override;
|
||||
void Flip() override;
|
||||
void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) override;
|
||||
void Download(SDL_Surface* target) override;
|
||||
|
||||
private:
|
||||
void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture);
|
||||
void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh);
|
||||
void StartFrame();
|
||||
|
||||
D3DRMMATRIX4D m_projection;
|
||||
SDL_Surface* m_renderedImage;
|
||||
C3D_RenderTarget* m_renderTarget;
|
||||
std::vector<C3DTextureCacheEntry> m_textures;
|
||||
std::vector<C3DMeshCacheEntry> m_meshs;
|
||||
ViewportTransform m_viewportTransform;
|
||||
std::vector<SceneLight> m_lights;
|
||||
};
|
||||
|
||||
inline static void Citro3DRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
|
||||
{
|
||||
D3DDEVICEDESC halDesc = {};
|
||||
halDesc.dcmColorModel = D3DCOLOR_RGB;
|
||||
halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH;
|
||||
halDesc.dwDeviceZBufferBitDepth = DDBD_24;
|
||||
halDesc.dwDeviceRenderBitDepth = DDBD_32;
|
||||
halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE;
|
||||
halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND;
|
||||
halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR;
|
||||
|
||||
D3DDEVICEDESC helDesc = {};
|
||||
|
||||
EnumDevice(cb, ctx, "Citro3D", &halDesc, &helDesc, Citro3D_GUID);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user