mirror of
https://github.com/mtheall/ftpd.git
synced 2024-11-23 01:29:51 +00:00
Add stereoscopic feature to 3DS
This commit is contained in:
parent
503e49854b
commit
0144af2a83
BIN
3ds/gfx/bubble.png
Normal file
BIN
3ds/gfx/bubble.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
@ -5,6 +5,7 @@ battery2.png
|
||||
battery3.png
|
||||
battery4.png
|
||||
batteryCharge.png
|
||||
bubble.png
|
||||
c3dlogo.png
|
||||
wifiNull.png
|
||||
wifi1.png
|
||||
|
@ -214,7 +214,7 @@ elseif(NINTENDO_3DS)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${FTPD_TARGET} PRIVATE
|
||||
ANTI_ALIAS=1
|
||||
ANTI_ALIAS=0
|
||||
)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
@ -250,6 +250,7 @@ elseif(NINTENDO_3DS)
|
||||
3ds/gfx/battery3.png
|
||||
3ds/gfx/battery4.png
|
||||
3ds/gfx/batteryCharge.png
|
||||
3ds/gfx/bubble.png
|
||||
3ds/gfx/c3dlogo.png
|
||||
3ds/gfx/wifi1.png
|
||||
3ds/gfx/wifi2.png
|
||||
|
@ -5,7 +5,7 @@
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (C) 2023 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -35,6 +35,7 @@
|
||||
#include "imgui.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@ -42,6 +43,13 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
/// \brief Slider value
|
||||
float s_slider;
|
||||
/// \brief Z offset (for stereoscopic effect)
|
||||
float s_z;
|
||||
/// \brief Z offset uniform location
|
||||
int s_zLocation;
|
||||
|
||||
/// \brief 3DS font glyph ranges
|
||||
std::vector<ImWchar> s_fontRanges;
|
||||
|
||||
@ -175,6 +183,9 @@ void imgui::citro3d::init ()
|
||||
shaderProgramInit (&s_program);
|
||||
shaderProgramSetVsh (&s_program, &s_vsh->DVLE[0]);
|
||||
|
||||
// get projection matrix uniform location
|
||||
s_zLocation = shaderInstanceGetUniformLocation (s_program.vertexShader, "z");
|
||||
|
||||
// get projection matrix uniform location
|
||||
s_projLocation = shaderInstanceGetUniformLocation (s_program.vertexShader, "proj");
|
||||
|
||||
@ -417,7 +428,9 @@ void imgui::citro3d::exit ()
|
||||
DVLB_Free (s_vsh);
|
||||
}
|
||||
|
||||
void imgui::citro3d::render (C3D_RenderTarget *const top_, C3D_RenderTarget *const bottom_)
|
||||
void imgui::citro3d::render (C3D_RenderTarget *const topLeft_,
|
||||
C3D_RenderTarget *const topRight_,
|
||||
C3D_RenderTarget *const bottom_)
|
||||
{
|
||||
// get draw data
|
||||
auto const drawData = ImGui::GetDrawData ();
|
||||
@ -497,12 +510,27 @@ void imgui::citro3d::render (C3D_RenderTarget *const top_, C3D_RenderTarget *con
|
||||
offsetIdx += cmdList.IdxBuffer.Size;
|
||||
}
|
||||
|
||||
for (auto const &screen : {GFX_TOP, GFX_BOTTOM})
|
||||
auto const slider = osGet3DSliderState ();
|
||||
|
||||
for (auto const &[target, screen, side] : {
|
||||
// clang-format off
|
||||
std::make_tuple (topLeft_, GFX_TOP, GFX_LEFT),
|
||||
std::make_tuple (slider ? topRight_ : nullptr, GFX_TOP, GFX_RIGHT),
|
||||
std::make_tuple (bottom_, GFX_BOTTOM, GFX_LEFT)
|
||||
// clang-format on
|
||||
})
|
||||
{
|
||||
if (screen == GFX_TOP)
|
||||
C3D_FrameDrawOn (top_);
|
||||
if (!target)
|
||||
continue;
|
||||
|
||||
if (target == topLeft_)
|
||||
s_slider = slider;
|
||||
else if (target == topRight_)
|
||||
s_slider = -slider;
|
||||
else
|
||||
C3D_FrameDrawOn (bottom_);
|
||||
s_slider = 0.0f;
|
||||
|
||||
C3D_FrameDrawOn (target);
|
||||
|
||||
setupRenderState (screen);
|
||||
|
||||
@ -534,14 +562,24 @@ void imgui::citro3d::render (C3D_RenderTarget *const top_, C3D_RenderTarget *con
|
||||
clip.z = (cmd.ClipRect.z - clipOff.x) * clipScale.x;
|
||||
clip.w = (cmd.ClipRect.w - clipOff.y) * clipScale.y;
|
||||
|
||||
if (s_slider && s_z && screen == GFX_TOP)
|
||||
{
|
||||
clip.x -= s_slider * s_z;
|
||||
clip.z += s_slider * s_z;
|
||||
}
|
||||
|
||||
if (clip.x >= width || clip.y >= height || clip.z < 0.0f || clip.w < 0.0f)
|
||||
continue;
|
||||
|
||||
if (clip.x < 0.0f)
|
||||
clip.x = 0.0f;
|
||||
|
||||
if (clip.y < 0.0f)
|
||||
clip.y = 0.0f;
|
||||
|
||||
if (clip.z > width)
|
||||
clip.z = width;
|
||||
|
||||
if (clip.w > height)
|
||||
clip.z = height;
|
||||
|
||||
@ -732,4 +770,13 @@ void imgui::citro3d::render (C3D_RenderTarget *const top_, C3D_RenderTarget *con
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void imgui::citro3d::setZ (ImDrawList const *const drawList_, ImDrawCmd const *const drawCmd_)
|
||||
{
|
||||
(void)drawList_;
|
||||
|
||||
s_z = std::bit_cast<float> (drawCmd_->UserCallbackData);
|
||||
|
||||
C3D_FVUnifSet (GPU_VERTEX_SHADER, s_zLocation, s_slider * s_z, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
#endif
|
||||
|
@ -5,7 +5,7 @@
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -30,6 +30,9 @@
|
||||
#ifndef CLASSIC
|
||||
#include <citro3d.h>
|
||||
|
||||
struct ImDrawList;
|
||||
struct ImDrawCmd;
|
||||
|
||||
namespace imgui
|
||||
{
|
||||
namespace citro3d
|
||||
@ -40,7 +43,15 @@ void init ();
|
||||
void exit ();
|
||||
|
||||
/// \brief Render ImGui draw list
|
||||
void render (C3D_RenderTarget *top_, C3D_RenderTarget *bottom_);
|
||||
/// \param topLeft_ Top left render target
|
||||
/// \param topRight_ Top right render target (skipped if not stereoscopic)
|
||||
/// \param bottom_ Bottom render target
|
||||
void render (C3D_RenderTarget *topLeft_, C3D_RenderTarget *topRight_, C3D_RenderTarget *bottom_);
|
||||
|
||||
/// \brief Set Z offset (for stereoscopic effect)
|
||||
/// \param drawList_ Draw list
|
||||
/// \param drawCmd_ Draw command
|
||||
void setZ (ImDrawList const *drawList_, ImDrawCmd const *drawCmd_);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -39,12 +39,15 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#ifdef CLASSIC
|
||||
PrintConsole g_statusConsole;
|
||||
@ -116,8 +119,10 @@ constexpr auto DISPLAY_TRANSFER_FLAGS =
|
||||
GX_TRANSFER_IN_FORMAT (GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT (GX_TRANSFER_FMT_RGB8) |
|
||||
GX_TRANSFER_SCALING (TRANSFER_SCALING);
|
||||
|
||||
/// \brief Top screen render target
|
||||
C3D_RenderTarget *s_top = nullptr;
|
||||
/// \brief Top left screen render target
|
||||
C3D_RenderTarget *s_topLeft = nullptr;
|
||||
/// \brief Top right screen render target
|
||||
C3D_RenderTarget *s_topRight = nullptr;
|
||||
/// \brief Bottom screen render target
|
||||
C3D_RenderTarget *s_bottom = nullptr;
|
||||
|
||||
@ -271,12 +276,15 @@ void drawLogo ()
|
||||
auto const uv1 = ImVec2 (subTex->left, subTex->top);
|
||||
auto const uv2 = ImVec2 (subTex->right, subTex->bottom);
|
||||
|
||||
auto const drawList = ImGui::GetBackgroundDrawList ();
|
||||
|
||||
// draw to top screen
|
||||
ImGui::GetBackgroundDrawList ()->AddImage (
|
||||
&s_gfxTexture, ImVec2 (x1, y1), ImVec2 (x2, y2), uv1, uv2);
|
||||
drawList->AddCallback (&imgui::citro3d::setZ, std::bit_cast<void *> (-5.0f));
|
||||
drawList->AddImage (&s_gfxTexture, ImVec2 (x1, y1), ImVec2 (x2, y2), uv1, uv2);
|
||||
drawList->AddCallback (&imgui::citro3d::setZ, std::bit_cast<void *> (0.0f));
|
||||
|
||||
// draw to bottom screen
|
||||
ImGui::GetBackgroundDrawList ()->AddImage (&s_gfxTexture,
|
||||
drawList->AddImage (&s_gfxTexture,
|
||||
ImVec2 (x1, y1 + screenHeight * 0.5f),
|
||||
ImVec2 (x2, y2 + screenHeight * 0.5f),
|
||||
uv1,
|
||||
@ -284,6 +292,94 @@ void drawLogo ()
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef CLASSIC
|
||||
struct Bubble
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float scale;
|
||||
float dy;
|
||||
};
|
||||
|
||||
std::vector<Bubble> &getBubbles ()
|
||||
{
|
||||
static std::vector<Bubble> bubbles;
|
||||
|
||||
if (!bubbles.empty ())
|
||||
return bubbles;
|
||||
|
||||
auto eng = std::default_random_engine (std::random_device{}());
|
||||
auto dist = std::uniform_real_distribution<float> (0.0f, 1.0f);
|
||||
|
||||
constexpr auto COUNT = 250;
|
||||
|
||||
bubbles.reserve (COUNT);
|
||||
for (unsigned i = 0; i < COUNT; ++i)
|
||||
{
|
||||
auto &bubble = bubbles.emplace_back ();
|
||||
|
||||
bubble.x = 500.0f * dist (eng) - 50.0f;
|
||||
bubble.y = 240.0f * dist (eng);
|
||||
bubble.z = std::floor (-5.0f * dist (eng));
|
||||
bubble.scale = std::max (dist (eng) / 8.0f, 0.0625f);
|
||||
bubble.dy = std::max (1.5f * dist (eng), 0.25f);
|
||||
}
|
||||
|
||||
std::ranges::sort (
|
||||
bubbles, [] (auto const &lhs_, auto const &rhs_) { return lhs_.z < rhs_.z; });
|
||||
|
||||
return bubbles;
|
||||
}
|
||||
#endif
|
||||
|
||||
void drawBubbles ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
// only draw in stereoscopy
|
||||
if (!osGet3DSliderState ())
|
||||
return;
|
||||
|
||||
auto const &io = ImGui::GetIO ();
|
||||
|
||||
auto const screenHeight = io.DisplaySize.y / 2.0f;
|
||||
|
||||
auto const tex = Tex3DS_GetSubTexture (s_gfxT3x, gfx_bubble_idx);
|
||||
|
||||
auto const uv1 = ImVec2 (tex->left, tex->top);
|
||||
auto const uv2 = ImVec2 (tex->right, tex->bottom);
|
||||
|
||||
float lastZ = 0.0f;
|
||||
|
||||
auto const drawList = ImGui::GetBackgroundDrawList ();
|
||||
for (auto &bubble : getBubbles ())
|
||||
{
|
||||
if (bubble.z != lastZ)
|
||||
{
|
||||
lastZ = bubble.z;
|
||||
drawList->AddCallback (&imgui::citro3d::setZ, std::bit_cast<void *> (lastZ));
|
||||
}
|
||||
|
||||
bubble.y -= bubble.dy;
|
||||
|
||||
if (bubble.y < 0.0f)
|
||||
bubble.y = screenHeight;
|
||||
|
||||
auto const width = bubble.scale * tex->width;
|
||||
auto const height = bubble.scale * tex->height;
|
||||
|
||||
auto const p1 = ImVec2 (
|
||||
bubble.x + 100.0f * bubble.scale * std::sin (bubble.z + bubble.y / 40.0f), bubble.y);
|
||||
auto const p2 = ImVec2 (p1.x + width, p1.y + height);
|
||||
|
||||
drawList->AddImage (&s_gfxTexture, p1, p2, uv1, uv2);
|
||||
}
|
||||
|
||||
if (lastZ != 0.0f)
|
||||
drawList->AddCallback (&imgui::citro3d::setZ, std::bit_cast<void *> (0.0f));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Draw status
|
||||
void drawStatus ()
|
||||
{
|
||||
@ -389,9 +485,10 @@ bool platform::init ()
|
||||
romfsInit ();
|
||||
#endif
|
||||
gfxInitDefault ();
|
||||
gfxSet3D (false);
|
||||
|
||||
#ifdef CLASSIC
|
||||
gfxSet3D (false);
|
||||
|
||||
consoleInit (GFX_TOP, &g_statusConsole);
|
||||
consoleInit (GFX_TOP, &g_logConsole);
|
||||
consoleInit (GFX_BOTTOM, &g_sessionConsole);
|
||||
@ -399,6 +496,8 @@ bool platform::init ()
|
||||
consoleSetWindow (&g_statusConsole, 0, 0, 50, 1);
|
||||
consoleSetWindow (&g_logConsole, 0, 1, 50, 29);
|
||||
consoleSetWindow (&g_sessionConsole, 0, 0, 40, 30);
|
||||
#else
|
||||
gfxSet3D (true);
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
@ -410,12 +509,17 @@ bool platform::init ()
|
||||
|
||||
#ifndef CLASSIC
|
||||
// initialize citro3d
|
||||
C3D_Init (2 * C3D_DEFAULT_CMDBUF_SIZE);
|
||||
C3D_Init (4 * C3D_DEFAULT_CMDBUF_SIZE);
|
||||
|
||||
// create top screen render target
|
||||
s_top =
|
||||
// create top left screen render target
|
||||
s_topLeft =
|
||||
C3D_RenderTargetCreate (FB_HEIGHT * 0.5f, FB_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
|
||||
C3D_RenderTargetSetOutput (s_top, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS);
|
||||
C3D_RenderTargetSetOutput (s_topLeft, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS);
|
||||
|
||||
// create top right screen render target
|
||||
s_topRight =
|
||||
C3D_RenderTargetCreate (FB_HEIGHT * 0.5f, FB_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
|
||||
C3D_RenderTargetSetOutput (s_topRight, GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS);
|
||||
|
||||
// create bottom screen render target
|
||||
s_bottom = C3D_RenderTargetCreate (
|
||||
@ -530,6 +634,7 @@ bool platform::loop ()
|
||||
void platform::render ()
|
||||
{
|
||||
drawLogo ();
|
||||
drawBubbles ();
|
||||
drawStatus ();
|
||||
|
||||
#ifdef CLASSIC
|
||||
@ -542,10 +647,11 @@ void platform::render ()
|
||||
C3D_FrameBegin (C3D_FRAME_SYNCDRAW);
|
||||
|
||||
// clear frame/depth buffers
|
||||
C3D_RenderTargetClear (s_top, C3D_CLEAR_ALL, CLEAR_COLOR, 0);
|
||||
C3D_RenderTargetClear (s_topLeft, C3D_CLEAR_ALL, CLEAR_COLOR, 0);
|
||||
C3D_RenderTargetClear (s_topRight, C3D_CLEAR_ALL, CLEAR_COLOR, 0);
|
||||
C3D_RenderTargetClear (s_bottom, C3D_CLEAR_ALL, CLEAR_COLOR, 0);
|
||||
|
||||
imgui::citro3d::render (s_top, s_bottom);
|
||||
imgui::citro3d::render (s_topLeft, s_topRight, s_bottom);
|
||||
|
||||
C3D_FrameEnd (0);
|
||||
#endif
|
||||
@ -562,7 +668,8 @@ void platform::exit ()
|
||||
|
||||
// free render targets
|
||||
C3D_RenderTargetDelete (s_bottom);
|
||||
C3D_RenderTargetDelete (s_top);
|
||||
C3D_RenderTargetDelete (s_topRight);
|
||||
C3D_RenderTargetDelete (s_topLeft);
|
||||
|
||||
// deinitialize citro3d
|
||||
C3D_Fini ();
|
||||
|
@ -3,7 +3,7 @@
|
||||
; - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
; - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
;
|
||||
; Copyright (C) 2020 Michael Theall
|
||||
; Copyright (C) 2024 Michael Theall
|
||||
;
|
||||
; This program is free software: you can redistribute it and/or modify
|
||||
; it under the terms of the GNU General Public License as published by
|
||||
@ -23,6 +23,7 @@
|
||||
; uniforms
|
||||
; Projection matrix
|
||||
.fvec proj[4]
|
||||
.fvec z
|
||||
|
||||
; constants
|
||||
; [1.0, 0.0, 1.0/255.0, 0.0]
|
||||
@ -43,6 +44,9 @@
|
||||
mov r0.xy, inPos.xy
|
||||
mov r0.zw, constants.yx
|
||||
|
||||
; offset stereoscopy
|
||||
add r0.x, z.x, r0.x
|
||||
|
||||
; outPos = proj * inPos
|
||||
dp4 outPos.x, proj[0], r0
|
||||
dp4 outPos.y, proj[1], r0
|
||||
|
Loading…
Reference in New Issue
Block a user