Added Screenshot feature (ALT+S).

This commit is contained in:
SpinDizzy 2020-09-19 07:54:26 +00:00
parent ca57659fcb
commit 981cdc33f5
3 changed files with 120 additions and 99 deletions

View file

@ -58,6 +58,7 @@ CInputs::CInputs(CInputSystem *system)
uiToggleFrLimit = AddSwitchInput("UIToggleFrameLimit", "Toggle Frame Limiting", Game::INPUT_UI, "KEY_ALT+KEY_T"); uiToggleFrLimit = AddSwitchInput("UIToggleFrameLimit", "Toggle Frame Limiting", Game::INPUT_UI, "KEY_ALT+KEY_T");
uiDumpInpState = AddSwitchInput("UIDumpInputState", "Dump Input State", Game::INPUT_UI, "KEY_ALT+KEY_U"); uiDumpInpState = AddSwitchInput("UIDumpInputState", "Dump Input State", Game::INPUT_UI, "KEY_ALT+KEY_U");
uiDumpTimings = AddSwitchInput("UIDumpTimings", "Dump Frame Timings", Game::INPUT_UI, "KEY_ALT+KEY_O"); uiDumpTimings = AddSwitchInput("UIDumpTimings", "Dump Frame Timings", Game::INPUT_UI, "KEY_ALT+KEY_O");
uiScreenshot = AddSwitchInput("UIScreenShot", "Screenshot", Game::INPUT_UI, "KEY_ALT+KEY_S");
#ifdef SUPERMODEL_DEBUGGER #ifdef SUPERMODEL_DEBUGGER
uiEnterDebugger = AddSwitchInput("UIEnterDebugger", "Enter Debugger", Game::INPUT_UI, "KEY_ALT+KEY_B"); uiEnterDebugger = AddSwitchInput("UIEnterDebugger", "Enter Debugger", Game::INPUT_UI, "KEY_ALT+KEY_B");
#endif #endif

View file

@ -105,6 +105,7 @@ public:
CSwitchInput *uiToggleFrLimit; CSwitchInput *uiToggleFrLimit;
CSwitchInput *uiDumpInpState; CSwitchInput *uiDumpInpState;
CSwitchInput *uiDumpTimings; CSwitchInput *uiDumpTimings;
CSwitchInput *uiScreenshot;
#ifdef SUPERMODEL_DEBUGGER #ifdef SUPERMODEL_DEBUGGER
CSwitchInput *uiEnterDebugger; CSwitchInput *uiEnterDebugger;
#endif #endif

View file

@ -71,6 +71,7 @@
#include "SDLIncludes.h" #include "SDLIncludes.h"
#include <iostream> #include <iostream>
#include "Util/BMPFile.h"
/****************************************************************************** /******************************************************************************
@ -195,7 +196,7 @@ static bool CreateGLScreen(const std::string &caption, bool focusWindow, unsigne
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,8); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
// Set video mode // Set video mode
@ -413,6 +414,27 @@ static void DumpPPCRegisters(IBus *bus)
} }
#endif #endif
static void SaveFrameBuffer(const std::string& file)
{
std::shared_ptr<uint8_t> pixels(new uint8_t[totalXRes * totalYRes * 4], std::default_delete<uint8_t[]>());
glReadPixels(0, 0, totalXRes, totalYRes, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
Util::WriteSurfaceToBMP<Util::RGBA8>(file, pixels.get(), totalXRes, totalYRes, true);
}
void Screenshot()
{
// Make a screenshot
char file[128];
string info = "Screenshot created: ";
time_t now = time(0);
tm* ltm = localtime(&now);
sprintf(file, "Screenshot %.4d-%.2d-%.2d (%.2d-%.2d-%.2d).bmp", 1900 + ltm->tm_year, 1 + ltm->tm_mon, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
info += file;
puts(info.c_str());
SaveFrameBuffer(file);
}
/****************************************************************************** /******************************************************************************
Render State Analysis Render State Analysis
@ -421,17 +443,9 @@ static void DumpPPCRegisters(IBus *bus)
#ifdef DEBUG #ifdef DEBUG
#include "Model3/Model3GraphicsState.h" #include "Model3/Model3GraphicsState.h"
#include "Util/BMPFile.h"
#include "OSD/SDL/PolyAnalysis.h" #include "OSD/SDL/PolyAnalysis.h"
#include <fstream> #include <fstream>
static void SaveFrameBuffer(const std::string &file)
{
std::shared_ptr<uint8_t> pixels(new uint8_t[totalXRes*totalYRes*4], std::default_delete<uint8_t[]>());
glReadPixels(0, 0, totalXRes, totalYRes, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
Util::WriteSurfaceToBMP<Util::RGBA8>(file, pixels.get(), totalXRes, totalYRes, true);
}
static std::string s_gfxStatePath; static std::string s_gfxStatePath;
static std::string GetFileBaseName(const std::string &file) static std::string GetFileBaseName(const std::string &file)
@ -715,91 +729,91 @@ static void PrintGLError(GLenum error)
case GL_NO_ERROR: break; case GL_NO_ERROR: break;
default: printf("unknown error\n"); break; default: printf("unknown error\n"); break;
} }
} }
*/ */
static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned crosshairs) static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned crosshairs)
{ {
bool offscreenTrigger[2]; bool offscreenTrigger[2];
float x[2], y[2]; float x[2], y[2];
crosshairs &= 3; crosshairs &= 3;
if (!crosshairs) if (!crosshairs)
return; return;
// Set up the viewport and orthogonal projection // Set up the viewport and orthogonal projection
glUseProgram(0); // no shaders glUseProgram(0); // no shaders
glViewport(xOffset, yOffset, xRes, yRes); glViewport(xOffset, yOffset, xRes, yRes);
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
gluOrtho2D(0.0, 1.0, 1.0, 0.0); gluOrtho2D(0.0, 1.0, 1.0, 0.0);
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
glDisable(GL_TEXTURE_2D); // no texture mapping glDisable(GL_TEXTURE_2D); // no texture mapping
glDisable(GL_BLEND); // no blending glDisable(GL_BLEND); // no blending
glDisable(GL_DEPTH_TEST); // no Z-buffering needed glDisable(GL_DEPTH_TEST); // no Z-buffering needed
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
// Convert gun coordinates to viewspace coordinates // Convert gun coordinates to viewspace coordinates
if (currentInputs & Game::INPUT_ANALOG_GUN1) if (currentInputs & Game::INPUT_ANALOG_GUN1)
{ {
x[0] = ((float)Inputs->analogGunX[0]->value / 255.0f); x[0] = ((float)Inputs->analogGunX[0]->value / 255.0f);
y[0] = ((255.0f - (float)Inputs->analogGunY[0]->value) / 255.0f); y[0] = ((255.0f - (float)Inputs->analogGunY[0]->value) / 255.0f);
offscreenTrigger[0] = Inputs->analogTriggerLeft[0]->value || Inputs->analogTriggerRight[0]->value; offscreenTrigger[0] = Inputs->analogTriggerLeft[0]->value || Inputs->analogTriggerRight[0]->value;
} }
else if (currentInputs & Game::INPUT_GUN1) else if (currentInputs & Game::INPUT_GUN1)
{ {
x[0] = (float)Inputs->gunX[0]->value; x[0] = (float)Inputs->gunX[0]->value;
y[0] = (float)Inputs->gunY[0]->value; y[0] = (float)Inputs->gunY[0]->value;
GunToViewCoords(&x[0], &y[0]); GunToViewCoords(&x[0], &y[0]);
offscreenTrigger[0] = (Inputs->trigger[0]->offscreenValue) > 0; offscreenTrigger[0] = (Inputs->trigger[0]->offscreenValue) > 0;
} }
if (currentInputs & Game::INPUT_ANALOG_GUN2) if (currentInputs & Game::INPUT_ANALOG_GUN2)
{ {
x[1] = ((float)Inputs->analogGunX[1]->value / 255.0f); x[1] = ((float)Inputs->analogGunX[1]->value / 255.0f);
y[1] = ((255.0f - (float)Inputs->analogGunY[1]->value) / 255.0f); y[1] = ((255.0f - (float)Inputs->analogGunY[1]->value) / 255.0f);
offscreenTrigger[1] = Inputs->analogTriggerLeft[1]->value || Inputs->analogTriggerRight[1]->value; offscreenTrigger[1] = Inputs->analogTriggerLeft[1]->value || Inputs->analogTriggerRight[1]->value;
} }
else if (currentInputs & Game::INPUT_GUN2) else if (currentInputs & Game::INPUT_GUN2)
{ {
x[1] = (float)Inputs->gunX[1]->value; x[1] = (float)Inputs->gunX[1]->value;
y[1] = (float)Inputs->gunY[1]->value; y[1] = (float)Inputs->gunY[1]->value;
GunToViewCoords(&x[1], &y[1]); GunToViewCoords(&x[1], &y[1]);
offscreenTrigger[1] = (Inputs->trigger[1]->offscreenValue) > 0; offscreenTrigger[1] = (Inputs->trigger[1]->offscreenValue) > 0;
} }
// Draw visible crosshairs // Draw visible crosshairs
glBegin(GL_TRIANGLES); glBegin(GL_TRIANGLES);
if ((crosshairs & 1) && !offscreenTrigger[0]) // Player 1 if ((crosshairs & 1) && !offscreenTrigger[0]) // Player 1
DrawCrosshair(x[0], y[0], 1.0f, 0.0f, 0.0f); DrawCrosshair(x[0], y[0], 1.0f, 0.0f, 0.0f);
if ((crosshairs & 2) && !offscreenTrigger[1]) // Player 2 if ((crosshairs & 2) && !offscreenTrigger[1]) // Player 2
DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f); DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f);
glEnd(); glEnd();
//PrintGLError(glGetError()); //PrintGLError(glGetError());
} }
/****************************************************************************** /******************************************************************************
Video Callbacks Video Callbacks
******************************************************************************/ ******************************************************************************/
static CInputs *videoInputs = NULL; static CInputs *videoInputs = NULL;
static uint32_t currentInputs = 0; static uint32_t currentInputs = 0;
bool BeginFrameVideo() bool BeginFrameVideo()
{ {
return true; return true;
} }
void EndFrameVideo() void EndFrameVideo()
{ {
// Show crosshairs for light gun games // Show crosshairs for light gun games
if (videoInputs) if (videoInputs)
UpdateCrosshairs(currentInputs, videoInputs, s_runtime_config["Crosshairs"].ValueAs<unsigned>()); UpdateCrosshairs(currentInputs, videoInputs, s_runtime_config["Crosshairs"].ValueAs<unsigned>());
// Swap the buffers // Swap the buffers
SDL_GL_SwapWindow(s_window); SDL_GL_SwapWindow(s_window);
} }
static void SuperSleep(UINT32 time) static void SuperSleep(UINT32 time)
@ -863,14 +877,14 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
if (OKAY != OpenAudio()) if (OKAY != OpenAudio())
return 1; return 1;
// Hide mouse if fullscreen, enable crosshairs for gun games // Hide mouse if fullscreen, enable crosshairs for gun games
Inputs->GetInputSystem()->SetMouseVisibility(!s_runtime_config["FullScreen"].ValueAs<bool>()); Inputs->GetInputSystem()->SetMouseVisibility(!s_runtime_config["FullScreen"].ValueAs<bool>());
gameHasLightguns = !!(game.inputs & (Game::INPUT_GUN1|Game::INPUT_GUN2)); gameHasLightguns = !!(game.inputs & (Game::INPUT_GUN1|Game::INPUT_GUN2));
gameHasLightguns |= game.name == "lostwsga"; gameHasLightguns |= game.name == "lostwsga";
currentInputs = game.inputs; currentInputs = game.inputs;
if (gameHasLightguns) if (gameHasLightguns)
videoInputs = Inputs; videoInputs = Inputs;
else else
videoInputs = NULL; videoInputs = NULL;
// Attach the inputs to the emulator // Attach the inputs to the emulator
@ -1175,6 +1189,11 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
s_runtime_config.Get("Throttle").SetValue(!s_runtime_config["Throttle"].ValueAs<bool>()); s_runtime_config.Get("Throttle").SetValue(!s_runtime_config["Throttle"].ValueAs<bool>());
printf("Frame limiting: %s\n", s_runtime_config["Throttle"].ValueAs<bool>() ? "On" : "Off"); printf("Frame limiting: %s\n", s_runtime_config["Throttle"].ValueAs<bool>() ? "On" : "Off");
} }
else if (Inputs->uiScreenshot->Pressed())
{
// Make a screenshot
Screenshot();
}
#ifdef SUPERMODEL_DEBUGGER #ifdef SUPERMODEL_DEBUGGER
else if (Debugger != NULL && Inputs->uiEnterDebugger->Pressed()) else if (Debugger != NULL && Inputs->uiEnterDebugger->Pressed())
{ {
@ -1320,16 +1339,16 @@ static void PrintGameList(const std::string &xml_file, const std::map<std::strin
for (auto &v: games) for (auto &v: games)
{ {
const Game &game = v.second; const Game &game = v.second;
printf(" %s", game.name.c_str()); printf(" %s", game.name.c_str());
for (int i = game.name.length(); i < 9; i++) // pad for alignment (no game ID should be more than 9 letters) for (int i = game.name.length(); i < 9; i++) // pad for alignment (no game ID should be more than 9 letters)
printf(" "); printf(" ");
if (!game.version.empty()) if (!game.version.empty())
printf(" %s (%s)\n", game.title.c_str(), game.version.c_str()); printf(" %s (%s)\n", game.title.c_str(), game.version.c_str());
else else
printf(" %s\n", game.title.c_str()); printf(" %s\n", game.title.c_str());
} }
} }
static void LogConfig(const Util::Config::Node &config) static void LogConfig(const Util::Config::Node &config)
{ {
InfoLog("Runtime configuration:"); InfoLog("Runtime configuration:");