Index: ps/trunk/binaries/data/config/default.cfg =================================================================== --- ps/trunk/binaries/data/config/default.cfg +++ ps/trunk/binaries/data/config/default.cfg @@ -59,6 +59,11 @@ forceglmajorversion = 3 forceglminorversion = 3 +; Big screenshot tiles +screenshot.tiles = 4 +screenshot.tilewidth = 480 +screenshot.tileheight = 270 + ; Emulate right-click with Ctrl+Click on Mac mice macmouse = false Index: ps/trunk/source/main.cpp =================================================================== --- ps/trunk/source/main.cpp +++ ps/trunk/source/main.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -208,7 +208,14 @@ } else if (hotkey == "bigscreenshot") { - WriteBigScreenshot(L".bmp", 10); + int tiles = 4, tileWidth = 256, tileHeight = 256; + CFG_GET_VAL("screenshot.tiles", tiles); + CFG_GET_VAL("screenshot.tilewidth", tileWidth); + CFG_GET_VAL("screenshot.tileheight", tileHeight); + if (tiles > 0 && tileWidth > 0 && tileHeight > 0) + WriteBigScreenshot(L".bmp", tiles, tileWidth, tileHeight); + else + LOGWARNING("Invalid big screenshot size: tiles=%d tileWidth=%d tileHeight=%d", tiles, tileWidth, tileHeight); return IN_HANDLED; } else if (hotkey == "togglefullscreen") Index: ps/trunk/source/ps/Util.h =================================================================== --- ps/trunk/source/ps/Util.h +++ ps/trunk/source/ps/Util.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -30,7 +30,7 @@ OsPath createDateIndexSubdirectory(const OsPath& parentDir); void WriteScreenshot(const VfsPath& extension); -void WriteBigScreenshot(const VfsPath& extension, int tiles); +void WriteBigScreenshot(const VfsPath& extension, int tiles = 4, int tileWidth = 640, int tileHeight = 480); Status tex_write(Tex* t, const VfsPath& filename); Index: ps/trunk/source/ps/Util.cpp =================================================================== --- ps/trunk/source/ps/Util.cpp +++ ps/trunk/source/ps/Util.cpp @@ -298,11 +298,15 @@ -// Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles. -void WriteBigScreenshot(const VfsPath& extension, int tiles) +// Similar to WriteScreenshot, but generates an image of size tileWidth*tiles x tileHeight*tiles. +void WriteBigScreenshot(const VfsPath& extension, int tiles, int tileWidth, int tileHeight) { // If the game hasn't started yet then use WriteScreenshot to generate the image. - if(g_Game == NULL){ WriteScreenshot(L".bmp"); return; } + if (g_Game == nullptr) + { + WriteScreenshot(L".bmp"); + return; + } // get next available numbered filename // note: %04d -> always 4 digits, so sorting by filename works correctly. @@ -313,10 +317,9 @@ // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and // hope the screen is actually large enough for that. - const int tile_w = 640, tile_h = 480; - ENSURE(g_xres >= tile_w && g_yres >= tile_h); + ENSURE(g_xres >= tileWidth && g_yres >= tileHeight); - const int img_w = tile_w*tiles, img_h = tile_h*tiles; + const int img_w = tileWidth * tiles, img_h = tileHeight * tiles; const int bpp = 24; GLenum fmt = GL_RGB; int flags = TEX_BOTTOM_UP; @@ -331,7 +334,7 @@ } const size_t img_size = img_w * img_h * bpp/8; - const size_t tile_size = tile_w * tile_h * bpp/8; + const size_t tile_size = tileWidth * tileHeight * bpp/8; const size_t hdr_size = tex_hdr_size(filename); void* tile_data = malloc(tile_size); if(!tile_data) @@ -340,7 +343,7 @@ return; } shared_ptr img_buf; - AllocateAligned(img_buf, hdr_size+img_size, maxSectorSize); + AllocateAligned(img_buf, hdr_size + img_size, maxSectorSize); Tex t; GLvoid* img = img_buf.get() + hdr_size; @@ -356,8 +359,8 @@ // Resize various things so that the sizes and aspect ratios are correct { - g_Renderer.Resize(tile_w, tile_h); - SViewPort vp = { 0, 0, tile_w, tile_h }; + g_Renderer.Resize(tileWidth, tileHeight); + SViewPort vp = { 0, 0, tileWidth, tileHeight }; g_Game->GetView()->SetViewport(vp); } @@ -380,6 +383,7 @@ // Render each tile CMatrix3D projection; projection.SetIdentity(); + const float aspectRatio = 1.0f * tileWidth / tileHeight; for (int tile_y = 0; tile_y < tiles; ++tile_y) { for (int tile_x = 0; tile_x < tiles; ++tile_x) @@ -387,7 +391,7 @@ // Adjust the camera to render the appropriate region if (oldCamera.GetProjectionType() == CCamera::PERSPECTIVE) { - projection.SetPerspectiveTile(oldCamera.GetFOV(), oldCamera.GetAspectRatio(), oldCamera.GetNearPlane(), oldCamera.GetFarPlane(), tiles, tile_x, tile_y); + projection.SetPerspectiveTile(oldCamera.GetFOV(), aspectRatio, oldCamera.GetNearPlane(), oldCamera.GetFarPlane(), tiles, tile_x, tile_y); } g_Game->GetView()->GetCamera()->SetProjection(projection); @@ -398,12 +402,12 @@ RenderLogger(true); // Copy the tile pixels into the main image - glReadPixels(0, 0, tile_w, tile_h, fmt, GL_UNSIGNED_BYTE, tile_data); - for (int y = 0; y < tile_h; ++y) + glReadPixels(0, 0, tileWidth, tileHeight, fmt, GL_UNSIGNED_BYTE, tile_data); + for (int y = 0; y < tileHeight; ++y) { - void* dest = (char*)img + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * bpp/8; - void* src = (char*)tile_data + y * tile_w * bpp/8; - memcpy(dest, src, tile_w * bpp/8); + void* dest = static_cast(img) + ((tile_y * tileHeight + y) * img_w + (tile_x * tileWidth)) * bpp / 8; + void* src = static_cast(tile_data) + y * tileWidth * bpp / 8; + memcpy(dest, src, tileWidth * bpp / 8); } } } Index: ps/trunk/source/renderer/Renderer.cpp =================================================================== --- ps/trunk/source/renderer/Renderer.cpp +++ ps/trunk/source/renderer/Renderer.cpp @@ -926,13 +926,17 @@ { WaterManager& wm = m->waterManager; - ENSURE(m_ViewCamera.GetProjectionType() == CCamera::PERSPECTIVE); - float fov = m_ViewCamera.GetFOV(); - - // Expand fov slightly since ripples can reflect parts of the scene that - // are slightly outside the normal camera view, and we want to avoid any - // noticeable edge-filtering artifacts - fov *= 1.05f; + CMatrix3D projection; + if (m_ViewCamera.GetProjectionType() == CCamera::PERSPECTIVE) + { + const float aspectRatio = 1.0f; + // Expand fov slightly since ripples can reflect parts of the scene that + // are slightly outside the normal camera view, and we want to avoid any + // noticeable edge-filtering artifacts + projection.SetPerspective(m_ViewCamera.GetFOV() * 1.05f, aspectRatio, m_ViewCamera.GetNearPlane(), m_ViewCamera.GetFarPlane()); + } + else + projection = m_ViewCamera.GetProjection(); camera = m_ViewCamera; @@ -942,7 +946,7 @@ // the whole screen despite being rendered into a square, and cover slightly more // of the view so we can see wavy reflections of slightly off-screen objects. camera.m_Orientation.Scale(1, -1, 1); - camera.m_Orientation.Translate(0, 2*wm.m_WaterHeight, 0); + camera.m_Orientation.Translate(0, 2 * wm.m_WaterHeight, 0); camera.UpdateFrustum(scissor); // Clip slightly above the water to improve reflections of objects on the water // when the reflections are distorted. @@ -954,27 +958,30 @@ vp.m_X = 0; vp.m_Y = 0; camera.SetViewPort(vp); - camera.SetPerspectiveProjection(m_ViewCamera.GetNearPlane(), m_ViewCamera.GetFarPlane(), fov); + camera.SetProjection(projection); CMatrix3D scaleMat; - scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f); + scaleMat.SetScaling(m_Height / static_cast(std::max(1, m_Width)), 1.0f, 1.0f); camera.SetProjection(scaleMat * camera.GetProjection()); CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight + 0.5f); SetObliqueFrustumClipping(camera, camPlane); - } void CRenderer::ComputeRefractionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const { WaterManager& wm = m->waterManager; - ENSURE(m_ViewCamera.GetProjectionType() == CCamera::PERSPECTIVE); - float fov = m_ViewCamera.GetFOV(); - - // Expand fov slightly since ripples can reflect parts of the scene that - // are slightly outside the normal camera view, and we want to avoid any - // noticeable edge-filtering artifacts - fov *= 1.05f; + CMatrix3D projection; + if (m_ViewCamera.GetProjectionType() == CCamera::PERSPECTIVE) + { + const float aspectRatio = 1.0f; + // Expand fov slightly since ripples can reflect parts of the scene that + // are slightly outside the normal camera view, and we want to avoid any + // noticeable edge-filtering artifacts + projection.SetPerspective(m_ViewCamera.GetFOV() * 1.05f, aspectRatio, m_ViewCamera.GetNearPlane(), m_ViewCamera.GetFarPlane()); + } + else + projection = m_ViewCamera.GetProjection(); camera = m_ViewCamera; @@ -991,9 +998,9 @@ vp.m_X = 0; vp.m_Y = 0; camera.SetViewPort(vp); - camera.SetPerspectiveProjection(m_ViewCamera.GetNearPlane(), m_ViewCamera.GetFarPlane(), fov); + camera.SetProjection(projection); CMatrix3D scaleMat; - scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f); + scaleMat.SetScaling(m_Height / static_cast(std::max(1, m_Width)), 1.0f, 1.0f); camera.SetProjection(scaleMat * camera.GetProjection()); }