diff --git a/CMakeLists.txt b/CMakeLists.txt index 334580c0..b522293f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,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") @@ -645,6 +645,15 @@ 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_create_3dsx(isle) +endif() if(MSVC) set(CPACK_GENERATOR ZIP) else() diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index b097eb3b..630ba1ad 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -658,8 +658,9 @@ MxResult IsleApp::SetupWindow() SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, m_fullScreen); SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE); #ifdef MINIWIN - SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + // FIXME: 3ds hack + // SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + // SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); #endif window = SDL_CreateWindowWithProperties(props); diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index a1939441..1ae33326 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -52,6 +52,26 @@ else() message(STATUS "🧩 OpenGL ES 2.x support not enabled") endif() +if(NINTENDO_3DS) + find_library(CITRO3D_LIBRARY NAMES citro3dd) + find_library(CTRU_LIBRARY NAMES ctru) + + if(CTRU_LIBRARY AND CITRO3D_LIBRARY) + message(STATUS "Found libctru and citro3d: enabling Citro3D renderer") + + target_sources(miniwin PRIVATE src/d3drm/backends/citro3d/renderer.cpp) + + target_compile_definitions(miniwin PRIVATE USE_CITRO3D) + + 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 @@ -170,3 +190,4 @@ if(ISLE_COMPILE_SHADERS) DEPENDS "${py_gencshadersource}" ${shader_headers} ${shader_jsons}) endif() target_sources(miniwin PRIVATE "${index}" "${index_cpp}") +target_sources(miniwin PRIVATE "${index}" "${index_cpp}") diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp new file mode 100644 index 00000000..a715de7e --- /dev/null +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -0,0 +1,422 @@ +#include "SDL3/SDL_surface.h" +#include "d3drmrenderer_citro3d.h" +#include "miniwin.h" +#include "miniwin/d3d.h" +#include "miniwin/d3drm.h" +#include "miniwin/windows.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 <3ds.h> +#include +#include + +#include "vshader_shbin.h" + +int projectionShaderUniformLocation, modelViewUniformLocation; + +typedef struct { + float positions[3]; + float texcoords[2]; + float normals[3]; +} Vertex; + +// from this wiki: https://github.com/tommai78101/homebrew/wiki/Version-002:-Core-Engine +static const Vertex vertexList[] = +{ + // 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} }, + + // 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} }, + + // 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"); + 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 + + 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"); + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); +} + +Direct3DRMRenderer* Citro3DRenderer::Create(DWORD width, DWORD height) +{ + // TODO: Doesn't SDL call this function? + gfxInitDefault(); + gfxSet3D(false); + consoleInit(GFX_BOTTOM, nullptr); + + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + + return new Citro3DRenderer(width, height); +} + +// constructor parameters not finalized +Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) +{ + static shaderProgram_s program; + DVLB_s *vsh_dvlb; + + m_width = width; + m_height = height; + m_virtualWidth = width; + m_virtualHeight = height; + + // 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(); +} + +Citro3DRenderer::~Citro3DRenderer() +{ + SDL_DestroySurface(m_renderedImage); + C3D_RenderTargetDelete(m_renderTarget); +} + +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 { + Citro3DRenderer* renderer; + Uint32 textureId; +}; + +void Citro3DRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) +{ + auto* ctx = new TextureDestroyContextC3D{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; + } + delete ctx; + }, + ctx + ); +} + +Uint32 Citro3DRenderer::GetTextureId(IDirect3DRMTexture* iTexture) +{ + auto texture = static_cast(iTexture); + auto surface = static_cast(texture->m_surface); + + 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); + + SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); + if (!surf) { + 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; + } + return i; + } + } + + C3D_Tex newTex; + + SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); + if (!surf) { + 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; + AddTextureDestroyCallback(i, texture); + return i; + } + } + + m_textures.push_back({texture, texture->m_version, &newTex}); + 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; + + memset(helDesc, 0, sizeof(D3DDEVICEDESC)); +} + +const char* Citro3DRenderer::GetName() +{ + return "Citro3D"; +} + +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); + return S_OK; +} + +void Citro3DRenderer::EnableTransparency() +{ + MINIWIN_NOT_IMPLEMENTED(); +} + +void Citro3DRenderer::SubmitDraw( + DWORD meshId, + const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, + const Matrix3x3& normalMatrix, + 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); +} + +void Citro3DRenderer::Flip() +{ + MINIWIN_NOT_IMPLEMENTED(); +} + +void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) +{ + MINIWIN_NOT_IMPLEMENTED(); + MINIWIN_TRACE("on draw 2d image"); + 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; + + // TODO: isLeftHanded set to false. Should it be true? + MINIWIN_TRACE("pre orthotilt"); + Mtx_OrthoTilt(&mtx, left, right, bottom, top, -1, 1, false); + + MINIWIN_TRACE("pre fvunifmtx4x4"); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, projectionShaderUniformLocation, &mtx); +} + +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 new file mode 100644 index 00000000..60a43fc1 --- /dev/null +++ b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica @@ -0,0 +1,93 @@ +; 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], 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] + +; 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) +.alias inpos v0 +.alias intex v1 +.alias innrm v2 + +.proc main + ; Force the w component of inpos to be 1.0 + mov r0.xyz, inpos + mov r0.w, ones + + ; 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 + dp4 outtc0.x, texView[0], intex + dp4 outtc0.y, texView[1], 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 + + ; We're finished + end +.end diff --git a/miniwin/src/d3drm/backends/software/renderer.cpp b/miniwin/src/d3drm/backends/software/renderer.cpp index 64adc93b..e60264a7 100644 --- a/miniwin/src/d3drm/backends/software/renderer.cpp +++ b/miniwin/src/d3drm/backends/software/renderer.cpp @@ -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) { diff --git a/miniwin/src/d3drm/d3drm.cpp b/miniwin/src/d3drm/d3drm.cpp index e8df37b1..3b3a4b27 100644 --- a/miniwin/src/d3drm/d3drm.cpp +++ b/miniwin/src/d3drm/d3drm.cpp @@ -13,6 +13,9 @@ #ifdef USE_OPENGLES2 #include "d3drmrenderer_opengles2.h" #endif +#ifdef USE_CITRO3D +#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 USE_CITRO3D + else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) { + DDRenderer = Citro3DRenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + } +#endif #ifdef _WIN32 else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) { DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); 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)); diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index 2765a2a1..af095058 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -4,6 +4,9 @@ #ifdef USE_OPENGLES2 #include "d3drmrenderer_opengles2.h" #endif +#ifdef USE_CITRO3D +#include "d3drmrenderer_citro3d.h" +#endif #ifdef _WIN32 #include "d3drmrenderer_directx9.h" #endif @@ -227,6 +230,9 @@ HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) #ifdef USE_OPENGL1 OpenGL1Renderer_EnumDevice(cb, ctx); #endif +#ifdef USE_CITRO3D + Citro3DRenderer_EnumDevice(cb, ctx); +#endif #ifdef _WIN32 DirectX9Renderer_EnumDevice(cb, ctx); #endif @@ -341,6 +347,11 @@ HRESULT DirectDrawImpl::CreateDevice( DDRenderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif +#ifdef USE_CITRO3D + else if (SDL_memcmp(&guid, &Citro3D_GUID, sizeof(GUID)) == 0) { + DDRenderer = Citro3DRenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + } +#endif #ifdef _WIN32 else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) { DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h new file mode 100644 index 00000000..bad5d18d --- /dev/null +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -0,0 +1,79 @@ +#pragma once + +#include "SDL3/SDL_log.h" +#include "d3drmrenderer.h" +#include "d3drmtexture_impl.h" +#include "ddraw_impl.h" + +#include +#include +#include +#include + +DEFINE_GUID(Citro3D_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07); + +struct C3DTextureCacheEntry { + IDirect3DRMTexture* texture; + Uint32 version; + C3D_Tex* c3dTex; +}; + +class Citro3DRenderer : public Direct3DRMRenderer { +public: + static Direct3DRMRenderer* Create(DWORD width, DWORD height); + + // constructor parameters not finalized + 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) override; + Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; + void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; + const char* GetName() override; + HRESULT BeginFrame() override; + void EnableTransparency() override; + void SubmitDraw( + 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); + D3DRMMATRIX4D m_projection; + C3D_Mtx m_projectionMatrix; + SDL_Surface* m_renderedImage; + C3D_RenderTarget* m_renderTarget; + int m_projectionShaderUniformLocation; + std::vector m_textures; + + // 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) +{ + SDL_Log("Hello, enuming device"); + Direct3DRMRenderer* device = Citro3DRenderer::Create(400, 240); + if (device) { + EnumDevice(cb, ctx, device, Citro3D_GUID); + delete device; + } +}