mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-07 07:42:57 +12:00
Initial screen layout implementation
This commit is contained in:
parent
d06f600b3a
commit
62748eef47
11 changed files with 335 additions and 126 deletions
162
include/PICA/screen_layout.hpp
Normal file
162
include/PICA/screen_layout.hpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
#pragma once
|
||||
#include <algorithm>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace ScreenLayout {
|
||||
static constexpr u32 TOP_SCREEN_WIDTH = 400;
|
||||
static constexpr u32 BOTTOM_SCREEN_WIDTH = 320;
|
||||
static constexpr u32 TOP_SCREEN_HEIGHT = 240;
|
||||
static constexpr u32 BOTTOM_SCREEN_HEIGHT = 240;
|
||||
|
||||
// The bottom screen is less wide by 80 pixels, so we center it by offsetting it 40 pixels right
|
||||
static constexpr u32 BOTTOM_SCREEN_X_OFFSET = 40;
|
||||
static constexpr u32 CONSOLE_HEIGHT = TOP_SCREEN_HEIGHT + BOTTOM_SCREEN_HEIGHT;
|
||||
|
||||
enum class Layout {
|
||||
Default, // Top screen up, bottom screen down
|
||||
DefaultFlipped, // Top screen down, bottom screen up
|
||||
SideBySide, // Top screen left, bottom screen right,
|
||||
SideBySideFlipped, // Top screen right, bottom screen left,
|
||||
};
|
||||
|
||||
// For properly handling touchscreen, we have to remember what window coordinates our screens map to
|
||||
// We also remember some more information that is useful to our renderers, particularly for the final screen blit.
|
||||
struct WindowCoordinates {
|
||||
u32 topScreenX = 0;
|
||||
u32 topScreenY = 0;
|
||||
u32 topScreenWidth = TOP_SCREEN_WIDTH;
|
||||
u32 topScreenHeight = TOP_SCREEN_HEIGHT;
|
||||
|
||||
u32 bottomScreenX = BOTTOM_SCREEN_X_OFFSET;
|
||||
u32 bottomScreenY = TOP_SCREEN_HEIGHT;
|
||||
u32 bottomScreenWidth = BOTTOM_SCREEN_WIDTH;
|
||||
u32 bottomScreenHeight = BOTTOM_SCREEN_HEIGHT;
|
||||
|
||||
u32 windowWidth = topScreenWidth;
|
||||
u32 windowHeight = topScreenHeight + bottomScreenHeight;
|
||||
|
||||
// Information used when optimizing the final screen blit into a single blit
|
||||
struct {
|
||||
int destX = 0, destY = 0;
|
||||
int destWidth = TOP_SCREEN_WIDTH;
|
||||
int destHeight = CONSOLE_HEIGHT;
|
||||
} singleBlitInfo;
|
||||
|
||||
float scale = 1.0f;
|
||||
};
|
||||
|
||||
// Calculate screen coordinates on the screen for a given layout & a given size for the output window
|
||||
// Used in both the renderers and in the frontends (To eg calculate touch screen boundaries)
|
||||
static void calculateCoordinates(WindowCoordinates& coordinates, u32 outputWindowWidth, u32 outputWindowHeight, Layout layout) {
|
||||
layout = Layout::SideBySideFlipped;
|
||||
const float destAspect = float(outputWindowWidth) / float(outputWindowHeight);
|
||||
|
||||
if (layout == Layout::Default || layout == Layout::DefaultFlipped) {
|
||||
const float srcAspect = 400.0 / 480.0;
|
||||
|
||||
int destX = 0, destY = 0, destWidth = outputWindowWidth, destHeight = outputWindowHeight;
|
||||
|
||||
if (destAspect > srcAspect) {
|
||||
// Window is wider than source
|
||||
destWidth = int(outputWindowHeight * srcAspect + 0.5f);
|
||||
destX = (outputWindowWidth - destWidth) / 2;
|
||||
} else {
|
||||
// Window is taller than source
|
||||
destHeight = int(outputWindowWidth / srcAspect + 0.5f);
|
||||
destY = (outputWindowHeight - destHeight) / 2;
|
||||
}
|
||||
|
||||
// How much we'll scale the output by
|
||||
const float scale = float(destWidth) / float(TOP_SCREEN_WIDTH);
|
||||
|
||||
// Calculate coordinates and return them
|
||||
// TODO: This will break when we allow screens to be scaled separately
|
||||
coordinates.topScreenX = u32(destX);
|
||||
coordinates.topScreenWidth = u32(float(TOP_SCREEN_WIDTH) * scale);
|
||||
coordinates.bottomScreenX = u32(destX) + BOTTOM_SCREEN_X_OFFSET * scale;
|
||||
coordinates.bottomScreenWidth = u32(float(BOTTOM_SCREEN_WIDTH) * scale);
|
||||
|
||||
if (layout == Layout::Default) {
|
||||
coordinates.topScreenY = u32(destY + (destHeight - int(CONSOLE_HEIGHT * scale)) / 2);
|
||||
coordinates.topScreenHeight = u32(float(TOP_SCREEN_HEIGHT) * scale);
|
||||
|
||||
coordinates.bottomScreenY = coordinates.topScreenY + TOP_SCREEN_HEIGHT * scale;
|
||||
coordinates.bottomScreenHeight = coordinates.topScreenHeight;
|
||||
} else {
|
||||
// Flip the screens vertically
|
||||
coordinates.bottomScreenY = u32(destY + (destHeight - int(CONSOLE_HEIGHT * scale)) / 2);
|
||||
coordinates.bottomScreenHeight = u32(float(BOTTOM_SCREEN_HEIGHT) * scale);
|
||||
|
||||
coordinates.topScreenY = coordinates.bottomScreenY + TOP_SCREEN_HEIGHT * scale;
|
||||
coordinates.topScreenHeight = coordinates.bottomScreenHeight;
|
||||
}
|
||||
|
||||
coordinates.windowWidth = outputWindowWidth;
|
||||
coordinates.windowHeight = outputWindowHeight;
|
||||
coordinates.scale = scale;
|
||||
|
||||
coordinates.singleBlitInfo.destX = destX;
|
||||
coordinates.singleBlitInfo.destY = destY;
|
||||
coordinates.singleBlitInfo.destWidth = destWidth;
|
||||
coordinates.singleBlitInfo.destHeight = destHeight;
|
||||
} else if (layout == Layout::SideBySide || layout == Layout::SideBySideFlipped) {
|
||||
// For side-by-side layouts, the 3DS aspect ratio is calculated as (top width + bottom width) / height
|
||||
const float srcAspect = float(TOP_SCREEN_WIDTH + BOTTOM_SCREEN_WIDTH) / float(CONSOLE_HEIGHT);
|
||||
|
||||
int destX = 0, destY = 0, destWidth = outputWindowWidth, destHeight = outputWindowHeight;
|
||||
|
||||
if (destAspect > srcAspect) {
|
||||
// Window is wider than the side-by-side layout — center horizontally
|
||||
destWidth = int(outputWindowHeight * srcAspect + 0.5f);
|
||||
destX = (outputWindowWidth - destWidth) / 2;
|
||||
} else {
|
||||
// Window is taller — center vertically
|
||||
destHeight = int(outputWindowWidth / srcAspect + 0.5f);
|
||||
destY = (outputWindowHeight - destHeight) / 2;
|
||||
}
|
||||
|
||||
// How much we'll scale the output by. Again, we want to take both screens into account
|
||||
const float scale = float(destWidth) / float(TOP_SCREEN_WIDTH + BOTTOM_SCREEN_WIDTH);
|
||||
|
||||
// Annoyingly, the top screen is wider than the bottom screen, so to display them side-by-side and centered
|
||||
// vertically, we have to account for that
|
||||
|
||||
// The top screen is currently always larger, but it's still best to check which screen is taller anyways
|
||||
// Since eventually we'll want to let users scale each screen separately.
|
||||
const int topHeightScaled = int(TOP_SCREEN_HEIGHT * scale + 0.5f);
|
||||
const int bottomHeightScaled = int(BOTTOM_SCREEN_HEIGHT * scale + 0.5f);
|
||||
const int maxHeight = std::max(topHeightScaled, bottomHeightScaled);
|
||||
const int centerY = destY + (destHeight - maxHeight) / 2;
|
||||
|
||||
coordinates.topScreenY = u32(centerY + (maxHeight - topHeightScaled) / 2);
|
||||
coordinates.topScreenWidth = u32(TOP_SCREEN_WIDTH * scale);
|
||||
coordinates.topScreenHeight = u32(TOP_SCREEN_HEIGHT * scale);
|
||||
|
||||
coordinates.bottomScreenY = u32(centerY + (maxHeight - bottomHeightScaled) / 2);
|
||||
coordinates.bottomScreenWidth = u32(BOTTOM_SCREEN_WIDTH * scale);
|
||||
coordinates.bottomScreenHeight = u32(BOTTOM_SCREEN_HEIGHT * scale);
|
||||
|
||||
if (layout == Layout::SideBySide) {
|
||||
coordinates.topScreenX = destX;
|
||||
coordinates.bottomScreenX = destX + coordinates.topScreenWidth;
|
||||
} else {
|
||||
// Flip the screens horizontally
|
||||
coordinates.bottomScreenX = destX;
|
||||
coordinates.topScreenX = destX + coordinates.bottomScreenWidth;
|
||||
}
|
||||
|
||||
coordinates.windowWidth = outputWindowWidth;
|
||||
coordinates.windowHeight = outputWindowHeight;
|
||||
coordinates.scale = scale;
|
||||
|
||||
// Set singleBlitInfo values to dummy values. Side-by-side screens can't be rendere in only 1 blit.
|
||||
coordinates.singleBlitInfo.destX = 0;
|
||||
coordinates.singleBlitInfo.destY = 0;
|
||||
coordinates.singleBlitInfo.destWidth = 0;
|
||||
coordinates.singleBlitInfo.destHeight = 0;
|
||||
} else {
|
||||
Helpers::panic("Unimplemented screen layout");
|
||||
}
|
||||
}
|
||||
} // namespace ScreenLayout
|
|
@ -3,6 +3,7 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "PICA/screen_layout.hpp"
|
||||
#include "gl/context.h"
|
||||
#include "window_info.h"
|
||||
|
||||
|
@ -29,6 +30,10 @@ class ScreenWidget : public QWidget {
|
|||
u32 previousWidth = 0;
|
||||
u32 previousHeight = 0;
|
||||
|
||||
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen regardless
|
||||
// of layout or resizing
|
||||
ScreenLayout::WindowCoordinates screenCoordinates;
|
||||
|
||||
private:
|
||||
std::unique_ptr<GL::Context> glContext = nullptr;
|
||||
ResizeCallback resizeCallback;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <filesystem>
|
||||
|
||||
#include "PICA/screen_layout.hpp"
|
||||
#include "emulator.hpp"
|
||||
#include "input_mappings.hpp"
|
||||
|
||||
|
@ -27,7 +28,11 @@ class FrontendSDL {
|
|||
u32 windowHeight = 480;
|
||||
int gameControllerID;
|
||||
bool programRunning = true;
|
||||
|
||||
|
||||
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen regardless
|
||||
// of layout or resizing
|
||||
ScreenLayout::WindowCoordinates screenCoordinates;
|
||||
|
||||
// For tracking whether to update gyroscope
|
||||
// We bind gyro to right click + mouse movement
|
||||
bool holdingRightClick = false;
|
||||
|
@ -38,5 +43,7 @@ class FrontendSDL {
|
|||
bool keyboardAnalogX = false;
|
||||
bool keyboardAnalogY = false;
|
||||
|
||||
private:
|
||||
void setupControllerSensors(SDL_GameController* controller);
|
||||
void handleLeftClick(int mouseX, int mouseY);
|
||||
};
|
|
@ -147,12 +147,24 @@ class RendererGL final : public Renderer {
|
|||
OpenGL::Driver driverInfo;
|
||||
|
||||
// Information about the final 3DS screen -> Window blit, accounting for things like scaling and shifting the output based on
|
||||
// the window's dimensions.
|
||||
// the window's dimensions. Updated whenever the screen size or layout changes.
|
||||
struct {
|
||||
int topScreenX = 0;
|
||||
int topScreenY = 0;
|
||||
int topScreenWidth = 400;
|
||||
int topScreenHeight = 240;
|
||||
|
||||
int bottomScreenX = 40;
|
||||
int bottomScreenY = 240;
|
||||
int bottomScreenWidth = 320;
|
||||
int bottomScreenHeight = 240;
|
||||
|
||||
// For optimizing the final screen blit into a single blit instead of 2 when possible:
|
||||
int destX = 0;
|
||||
int destY = 0;
|
||||
int destWidth = 400;
|
||||
int destHeight = 480;
|
||||
bool canDoSingleBlit = true;
|
||||
} blitInfo;
|
||||
|
||||
MAKE_LOG_FUNCTION(log, rendererLogger)
|
||||
|
|
|
@ -89,7 +89,7 @@ class RendererMTL final : public Renderer {
|
|||
MTL::Texture* lastDepthTexture = nullptr;
|
||||
|
||||
// Information about the final 3DS screen -> Window blit, accounting for things like scaling and shifting the output based on
|
||||
// the window's dimensions.
|
||||
// the window's dimensions. Updated whenever the screen size or layout changes.
|
||||
struct {
|
||||
float topScreenX = 0;
|
||||
float topScreenY = 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue