- infrastructure for savegame pics.

Savepic generation implemented for Duke 3D, but results in a black image.
This commit is contained in:
Christoph Oelckers 2020-01-12 23:16:21 +01:00
parent 95f917a408
commit dc5b8d27f8
24 changed files with 113 additions and 110 deletions

View file

@ -210,7 +210,6 @@ bool GameInterface::LoadGame(FSaveGameNode* node)
bool GameInterface::SaveGame(FSaveGameNode* node)
{
OpenSaveGameForWrite(node->Filename);
LoadSave::hSFile = WriteSavegameChunk("snapshot.bld");
try
@ -231,7 +230,6 @@ bool GameInterface::SaveGame(FSaveGameNode* node)
Printf(TEXTCOLOR_RED "%s\n", err.what());
return false;
}
G_WriteSaveHeader(node->SaveTitle);
LoadSave::hSFile = NULL;
return FinishSavegameWrite();

View file

@ -99,6 +99,7 @@ enum EMenuSounds : int;
struct GameInterface
{
virtual ~GameInterface() {}
virtual bool GenerateSavePic() { return false; }
virtual void faketimerhandler() {} // This is a remnant of older versions, but Blood backend has not updated yet.
virtual int app_main() = 0;
virtual void UpdateScreenSize() {}

View file

@ -10787,5 +10787,3 @@ void renderSetRollAngle(int32_t rolla)
gtang = (float)rolla * (fPI * (1.f/1024.f));
}
#endif

View file

@ -37,6 +37,8 @@
#include "compositesaveame.h"
#include "file_zip.h"
#include "resourcefile.h"
#include "m_png.h"
#include "gamecontrol.h"
bool WriteZip(const char *filename, TArray<FString> &filenames, TArray<FCompressedBuffer> &content);

View file

@ -64,13 +64,15 @@ void FSavegameManager::LoadGame(FSaveGameNode* node)
void FSavegameManager::SaveGame(FSaveGameNode* node, bool ok4q, bool forceq)
{
if (gi->SaveGame(node))
if (OpenSaveGameForWrite(node->Filename, node->SaveTitle))
{
FString fn = node->Filename;
FString desc = node->SaveTitle;
NotifyNewSave(fn, desc, ok4q, forceq);
if (gi->SaveGame(node))
{
FString fn = node->Filename;
FString desc = node->SaveTitle;
NotifyNewSave(fn, desc, ok4q, forceq);
}
}
}
//=============================================================================

View file

@ -39,6 +39,7 @@
#include "filesystem.h"
#include "i_time.h"
#include "cmdlib.h"
#include "m_png.h"
//#include "swrenderer/r_swscene.h"
//#include "hwrenderer/utility/hw_clock.h"
@ -58,14 +59,13 @@
//#include "r_data/models/models.h"
#include "gl/renderer/gl_postprocessstate.h"
#include "gl/system/gl_buffers.h"
#include "build.h"
EXTERN_CVAR(Int, screenblocks)
EXTERN_CVAR(Bool, cl_capfps)
extern bool NoInterpolateView;
void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown);
namespace OpenGLRenderer
{
@ -190,8 +190,7 @@ void FGLRenderer::BindToFrameBuffer(FMaterial *mat)
//
//===========================================================================
#if 0
void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
void FGLRenderer::WriteSavePic ( FileWriter *file, int width, int height)
{
IntRect bounds;
bounds.left = 0;
@ -204,35 +203,52 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i
// Switch to render buffers dimensioned for the savepic
mBuffers = mSaveBuffers;
hw_ClearFakeFlat();
gl_RenderState.SetVertexBuffer(screen->mVertexData);
screen->mVertexData->Reset();
screen->mLights->Clear();
screen->mViewpoints->Clear();
mBuffers->BindSceneFB(false);
screen->SetViewportRects(&bounds);
// This shouldn't overwrite the global viewpoint even for a short time.
FRenderViewpoint savevp;
sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
glDisable(GL_STENCIL_TEST);
gl_RenderState.SetNoSoftLightLevel();
CopyToBackbuffer(&bounds, false);
int oldx = xdim;
int oldy = ydim;
auto oldwindowxy1 = windowxy1;
auto oldwindowxy2 = windowxy2;
xdim = width;
ydim = height;
videoSetViewableArea(0, 0, width - 1, height - 1);
renderSetAspect(65536, 65536);
calc_ylookup(width, height);
bool didit = gi->GenerateSavePic();
xdim = oldx;
ydim = oldy;
videoSetViewableArea(oldwindowxy1.x, oldwindowxy1.y, oldwindowxy2.x, oldwindowxy2.y);
calc_ylookup(bytesperline, ydim);
modechange = 1;
// The 2D drawers can contain some garbage from the dirty render setup. Get rid of that first.
twodgen.Clear();
twodpsp.Clear();
CopyToBackbuffer(&bounds, false);
// strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers
glFinish();
int numpixels = width * height;
uint8_t * scr = (uint8_t *)M_Malloc(numpixels * 3);
glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr);
DoWriteSavePic(file, SS_RGB, scr, width, height, viewsector, true);
M_Free(scr);
if (didit)
{
int numpixels = width * height;
uint8_t* scr = (uint8_t*)Xmalloc(numpixels * 3);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, scr);
M_CreatePNG(file, scr + ((height - 1) * width), nullptr, SS_RGB, width, height, -width, vid_gamma);
Xfree(scr);
}
// Switch back the screen render buffers
screen->SetViewportRects(nullptr);
mBuffers = mScreenBuffers;
bool useSSAO = (gl_ssao != 0);
mBuffers->BindSceneFB(useSSAO);
}
#endif
//===========================================================================
//
@ -243,7 +259,7 @@ void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, i
void FGLRenderer::BeginFrame()
{
mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
//mSaveBuffers->Setup(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT);
mSaveBuffers->Setup(240, 180, 240, 180);
}
}

View file

@ -81,9 +81,7 @@ public:
void DrawPresentTexture(const IntRect &box, bool applyGamma);
void Flush();
//void Draw2D(F2DDrawer *data);
#if 0
void WriteSavePic(player_t *player, FileWriter *file, int width, int height);
#endif
void WriteSavePic(FileWriter *file, int width, int height);
void BeginFrame();

View file

@ -209,6 +209,17 @@ void OpenGLFrameBuffer::Update()
Super::Update();
}
//===========================================================================
//
// Render the view to a savegame picture
//
//===========================================================================
void OpenGLFrameBuffer::WriteSavePic(FileWriter *file, int width, int height)
{
GLRenderer->WriteSavePic(file, width, height);
}
const char* OpenGLFrameBuffer::DeviceName() const
{
@ -468,4 +479,3 @@ void videoShowFrame(int32_t w)
twodgen.Clear();
GLInterface.ResetFrame();
}

View file

@ -27,6 +27,7 @@ public:
void CleanForRestart() override;
const char* DeviceName() const override;
void WriteSavePic(FileWriter* file, int width, int height);
#ifdef IMPLEMENT_IT
void SetTextureFilterMode() override;
IHardwareTexture *CreateHardwareTexture() override;

View file

@ -234,6 +234,16 @@ FTexture *DFrameBuffer::WipeEndScreen()
return nullptr;
}
//==========================================================================
//
//
//
//==========================================================================
void DFrameBuffer::WriteSavePic(FileWriter *file, int width, int height)
{
}
//==========================================================================
//
// Calculates the viewport values needed for 2D and 3D operations

View file

@ -339,6 +339,7 @@ public:
virtual int Backend() { return 0; }
virtual const char* DeviceName() const { return "Unknown"; }
virtual void Draw2D() {}
virtual void WriteSavePic(FileWriter *file, int width, int height);
// Screen wiping
virtual FTexture *WipeStartScreen();

View file

@ -46,6 +46,7 @@
#include "s_music.h"
#include "quotemgr.h"
#include "mapinfo.h"
#include "v_video.h"
static CompositeSavegameWriter savewriter;
static FResourceFile *savereader;
@ -64,12 +65,6 @@ static FResourceFile *savereader;
//=============================================================================
void OpenSaveGameForWrite(const char *name)
{
savewriter.Clear();
savewriter.SetFileName(name);
}
bool OpenSaveGameForRead(const char *name)
{
if (savereader) delete savereader;
@ -126,16 +121,20 @@ void FinishSavegameRead()
//=============================================================================
//
// Writes the header which is used to display the savegame in the menu.
// Creates the savegame and writes all cross-game content.
//
//=============================================================================
void G_WriteSaveHeader(const char *name)
bool OpenSaveGameForWrite(const char* filename, const char *name)
{
savewriter.Clear();
savewriter.SetFileName(filename);
sjson_context* ctx = sjson_create_context(0, 0, NULL);
if (!ctx)
{
return;
savewriter.Clear();
return false;
}
sjson_node* root = sjson_mkobject(ctx);
auto savesig = gi->GetSaveSig();
@ -158,7 +157,11 @@ void G_WriteSaveHeader(const char *name)
auto mapfile = fileSystem.GetFileContainer(fileno);
auto mapcname = fileSystem.GetResourceFileName(mapfile);
if (mapcname) sjson_put_string(ctx, root, "Map Resource", mapcname);
else return; // this should never happen. Saving on a map that isn't present is impossible.
else
{
savewriter.Clear();
return false; // this should never happen. Saving on a map that isn't present is impossible.
}
}
char* encoded = sjson_stringify(ctx, root, " ");
@ -167,7 +170,8 @@ void G_WriteSaveHeader(const char *name)
if (!fil)
{
sjson_destroy_context(ctx);
return;
savewriter.Clear();
return false;
}
fil->Write(encoded, strlen(encoded));
@ -180,6 +184,9 @@ void G_WriteSaveHeader(const char *name)
SECRET_Save();
MUS_Save();
quoteMgr.WriteToSavegame();
auto picfile = WriteSavegameChunk("savepic.png");
screen->WriteSavePic(picfile, 240, 180);
return true;
}
//=============================================================================

View file

@ -2,7 +2,7 @@
#include "filesystem/resourcefile.h"
void OpenSaveGameForWrite(const char *name);
bool OpenSaveGameForWrite(const char *fname, const char *name);
bool OpenSaveGameForRead(const char *name);
FileWriter *WriteSavegameChunk(const char *name);
@ -16,7 +16,6 @@ class FileReader;
FString G_BuildSaveName (const char *prefix);
int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu);
void G_WriteSaveHeader(const char* name);
#define SAVEGAME_EXT ".dsave"

View file

@ -172,7 +172,7 @@ void G_OpenDemoWrite(void)
if (g_demo_filePtr == NULL)
return;
i=sv_saveandmakesnapshot(*g_demo_filePtr, nullptr, -1);
i=sv_saveandmakesnapshot(*g_demo_filePtr, -1);
if (i)
{
delete g_demo_filePtr;

View file

@ -145,6 +145,7 @@ struct GameInterface : ::GameInterface
int app_main() override;
void UpdateScreenSize() override;
void FreeGameData() override;
bool GenerateSavePic() override;
bool validate_hud(int) override;
void set_hud_layout(int size) override;
void set_hud_scale(int size) override;

View file

@ -762,14 +762,7 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
else
renderSetAspect(mulscale16(vr, viewingrange), yxaspect);
if (g_screenCapture)
{
TileFiles.tileCreate(TILE_SAVESHOT, 200, 320);
//if (videoGetRenderMode() == REND_CLASSIC)
renderSetTarget(TILE_SAVESHOT, 200, 320);
}
else if (screenTilting)
if (screenTilting)
{
int32_t oviewingrange = viewingrange; // save it from renderSetAspect()
const int16_t tang = (ud.screen_tilting) ? pPlayer->rotscrnang : 0;
@ -850,23 +843,15 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
}
else if (videoGetRenderMode() >= REND_POLYMOST)
{
if (ud.screen_tilting
#ifdef SPLITSCREEN_MOD_HACKS
&& !g_fakeMultiMode
#endif
)
if (ud.screen_tilting)
{
#ifdef USE_OPENGL
renderSetRollAngle(pPlayer->orotscrnang + mulscale16(((pPlayer->rotscrnang - pPlayer->orotscrnang + 1024)&2047)-1024, smoothRatio));
#endif
pPlayer->orotscrnang = pPlayer->rotscrnang;
}
#ifdef USE_OPENGL
else
{
renderSetRollAngle(0);
}
#endif
}
if (pPlayer->newowner < 0)
@ -1016,22 +1001,7 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
screen->FinishScene();
}
if (g_screenCapture)
{
g_screenCapture = 0;
tileInvalidate(TILE_SAVESHOT, 0, 255);
//if (videoGetRenderMode() == REND_CLASSIC)
{
renderRestoreTarget();
}
#ifdef USE_OPENGL
//else
// G_ReadGLFrame();
#endif
}
else if (screenTilting)
if (screenTilting)
{
const int16_t tang = (ud.screen_tilting) ? pPlayer->rotscrnang : 0;
@ -1115,6 +1085,14 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
VM_OnEvent(EVENT_DISPLAYROOMSEND, g_player[screenpeek].ps->i, screenpeek);
}
bool GameInterface::GenerateSavePic()
{
G_DrawRooms(myconnectindex, 65536);
return true;
}
void G_DumpDebugInfo(void)
{
static char const s_WEAPON[] = "WEAPON";

View file

@ -147,7 +147,6 @@ G_EXTERN input_t inputfifo[MOVEFIFOSIZ][MAXPLAYERS];
G_EXTERN int32_t g_noEnemies;
G_EXTERN int32_t g_restorePalette;
G_EXTERN int32_t g_screenCapture;
G_EXTERN projectile_t SpriteProjectile[MAXSPRITES];
G_EXTERN uint32_t everyothertime;
G_EXTERN uint32_t g_moveThingsCount;

View file

@ -536,10 +536,6 @@ static void G_RestoreTimers(void)
bool G_SavePlayer(FSaveGameNode *sv)
{
#ifdef __ANDROID__
G_SavePalette();
#endif
G_SaveTimers();
Net_WaitForServer();
@ -550,7 +546,6 @@ bool G_SavePlayer(FSaveGameNode *sv)
errno = 0;
FileWriter *fil;
OpenSaveGameForWrite(sv->Filename);
fil = WriteSavegameChunk("snapshot.dat");
// The above call cannot fail.
{
@ -564,7 +559,7 @@ bool G_SavePlayer(FSaveGameNode *sv)
portableBackupSave(sv->Filename, sv->SaveTitle, ud.last_stateless_volume, ud.last_stateless_level);
// SAVE!
sv_saveandmakesnapshot(fw, sv->SaveTitle, 0);
sv_saveandmakesnapshot(fw, 0);
fw.Close();
@ -620,9 +615,6 @@ bool GameInterface::SaveGame(FSaveGameNode* sv)
else
{
videoNextPage(); // no idea if this is needed here.
g_screenCapture = 1;
//G_DrawRooms(myconnectindex, 65536);
g_screenCapture = 0;
return G_SavePlayer(sv);
}
@ -1361,7 +1353,7 @@ static void SV_AllocSnap(int32_t allocinit)
}
// make snapshot only if spot < 0 (demo)
int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot)
int32_t sv_saveandmakesnapshot(FileWriter &fil, int8_t spot)
{
savehead_t h;
@ -1400,8 +1392,6 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot)
// savegame
auto fw = WriteSavegameChunk("header.dat");
fw->Write(&h, sizeof(savehead_t));
G_WriteSaveHeader(name);
}
else
{

View file

@ -68,7 +68,7 @@ int32_t sv_readdiff(FileReader& fil);
uint32_t sv_writediff(FileWriter *fil);
int32_t sv_loadheader(FileReader &fil, int32_t spot, savehead_t *h);
int32_t sv_loadsnapshot(FileReader &fil, int32_t spot, savehead_t *h);
int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot);
int32_t sv_saveandmakesnapshot(FileWriter &fil, int8_t spot);
void sv_freemem();
int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh);
void ReadSaveGameHeaders(void);

View file

@ -40,10 +40,6 @@ static TArray<SavegameHelper*> sghelpers(TArray<SavegameHelper*>::NoInit);
bool GameInterface::SaveGame(FSaveGameNode* sv)
{
OpenSaveGameForWrite(sv->Filename);
// workaround until the level info here has been transitioned.
G_WriteSaveHeader(sv->SaveTitle);
auto fw = WriteSavegameChunk("engine");
fw->Write(&numsectors, sizeof(numsectors));
fw->Write(sector, sizeof(sectortype) * numsectors);

View file

@ -172,7 +172,7 @@ void G_OpenDemoWrite(void)
if (g_demo_filePtr == NULL)
return;
i=sv_saveandmakesnapshot(*g_demo_filePtr, nullptr, -1, (demorec_seeds_cvar<<1));
i=sv_saveandmakesnapshot(*g_demo_filePtr, -1, (demorec_seeds_cvar<<1));
if (i)
{
delete g_demo_filePtr;

View file

@ -300,7 +300,6 @@ bool G_SavePlayer(FSaveGameNode *sv)
errno = 0;
FileWriter *fil;
OpenSaveGameForWrite(sv->Filename);
fil = WriteSavegameChunk("snapshot.dat");
// The above call cannot fail.
{
@ -311,7 +310,7 @@ bool G_SavePlayer(FSaveGameNode *sv)
// SAVE!
sv_saveandmakesnapshot(fw, sv->SaveTitle, 0, 0);
sv_saveandmakesnapshot(fw, 0, 0);
fw.Close();
@ -1068,7 +1067,7 @@ static void SV_AllocSnap(int32_t allocinit)
}
// make snapshot only if spot < 0 (demo)
int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, bool isAutoSave)
int32_t sv_saveandmakesnapshot(FileWriter &fil, int8_t spot, bool isAutoSave)
{
savehead_t h;
@ -1105,7 +1104,6 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, b
// savegame
auto fw = WriteSavegameChunk("header.dat");
fw->Write(&h, sizeof(savehead_t));
G_WriteSaveHeader(name);
}
else
{

View file

@ -59,7 +59,7 @@ int32_t sv_readdiff(FileReader& fil);
uint32_t sv_writediff(FileWriter *fil);
int32_t sv_loadheader(FileReader &fil, int32_t spot, savehead_t *h);
int32_t sv_loadsnapshot(FileReader &fil, int32_t spot, savehead_t *h);
int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, bool isAutoSave = false);
int32_t sv_saveandmakesnapshot(FileWriter &fil, int8_t spot, bool isAutoSave = false);
void sv_freemem();
int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh);
void ReadSaveGameHeaders(void);

View file

@ -236,9 +236,7 @@ bool GameInterface::SaveGame(FSaveGameNode *sv)
Saveable_Init();
OpenSaveGameForWrite(sv->Filename);
// workaround until the level info here has been transitioned.
G_WriteSaveHeader(sv->SaveTitle);
fil = WriteSavegameChunk("snapshot.sw");
MWRITE(&GameVersion,sizeof(GameVersion),1,fil);