1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

Improve screenshot dialog and algorithm

The CSS for checkbox and enum form elements is modified to make them fit better.

Map mode is made a separate setting in the screenshot dialog,
and the rendering process is made to match how it is done ingame.
This commit is contained in:
EmeraldBlock 2021-03-31 18:15:50 -05:00
parent 96170f8d22
commit 4ceb15051a
3 changed files with 79 additions and 44 deletions

View File

@ -215,24 +215,28 @@
} }
} }
.checkBoxFormElem { .checkBoxFormElem,
.enumFormElem {
display: flex;
align-items: center;
@include S(margin, 10px, 0); @include S(margin, 10px, 0);
> label { > label {
@include S(margin-right, 10px); @include S(margin-right, 10px);
} }
display: flex;
align-items: center;
} }
.enum { .enum {
@include S(margin, 10px, 0);
display: grid; display: grid;
grid-template-columns: auto 1fr auto; grid-template-columns: auto 1fr auto;
@include S(grid-gap, 4px); @include S(grid-gap, 4px);
@include S(width, 200px); @include S(min-width, 160px);
> * { > div {
background: #fff; background: #fff;
display: flex;
justify-content: center;
align-items: center;
text-align: center; text-align: center;
pointer-events: all; pointer-events: all;
cursor: pointer; cursor: pointer;
@ -253,7 +257,7 @@
} }
&.toggle { &.toggle {
@include S(width, 20px); @include S(width, 16px);
} }
&.value { &.value {

View File

@ -9,37 +9,28 @@ import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { KEYMAPPINGS } from "../../key_action_mapper"; import { KEYMAPPINGS } from "../../key_action_mapper";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { DialogWithForm } from "../../../core/modal_dialog_elements"; import { DialogWithForm } from "../../../core/modal_dialog_elements";
import { FormElementInput, FormElementCheckbox, FormElementEnum } from "../../../core/modal_dialog_forms"; import { FormElementCheckbox, FormElementEnum } from "../../../core/modal_dialog_forms";
import { ORIGINAL_SPRITE_SCALE } from "../../../core/sprites";
import { getDeviceDPI } from "../../../core/dpi_manager";
const logger = createLogger("screenshot_exporter"); const logger = createLogger("screenshot_exporter");
/**
* @typedef {{mode: string, resolution?: number}} QualityOptions
*/
/**
* @type {{id: string, options: QualityOptions}[]}
*/
const screenshotQualities = [ const screenshotQualities = [
{ {
id: "high", id: "high",
options: { mode: "regular", resolution: 16384 }, resolution: 16384,
}, },
{ {
id: "medium", id: "medium",
options: { mode: "regular", resolution: 4096 }, resolution: 4096,
}, },
{ {
id: "low", id: "low",
options: { mode: "regular", resolution: 1024 }, resolution: 1024,
},
{
id: "map",
options: { mode: "map" },
}, },
]; ];
// @TODO: translation (T.dialogs.exportScreenshotWarning.qualities) // @TODO: translation (T.dialogs.exportScreenshotWarning.qualities)
const qualityNames = { high: "High", medium: "Medium", low: "Low", map: "Map" }; const qualityNames = { high: "High", medium: "Medium", low: "Low" };
export class HUDScreenshotExporter extends BaseHUDPart { export class HUDScreenshotExporter extends BaseHUDPart {
createElements() {} createElements() {}
@ -56,10 +47,17 @@ export class HUDScreenshotExporter extends BaseHUDPart {
const qualityInput = new FormElementEnum({ const qualityInput = new FormElementEnum({
id: "screenshotQuality", id: "screenshotQuality",
label: "Quality",
options: screenshotQualities, options: screenshotQualities,
valueGetter: quality => quality.options, valueGetter: quality => quality.resolution,
// @TODO: translation (T.dialogs.exportScreenshotWarning.qualityLabel) // @TODO: translation (T.dialogs.exportScreenshotWarning.qualityLabel)
textGetter: quality => "Quality:" + " " + qualityNames[quality.id], textGetter: quality => qualityNames[quality.id],
});
const overlayInput = new FormElementCheckbox({
id: "screenshotView",
// @TODO: translation (T.dialogs.exportScreenshotWarning.descOverlay)
label: "Map view",
defaultValue: this.root.camera.getIsMapOverlayActive() ? true : false,
}); });
const layerInput = new FormElementCheckbox({ const layerInput = new FormElementCheckbox({
id: "screenshotLayer", id: "screenshotLayer",
@ -71,22 +69,24 @@ export class HUDScreenshotExporter extends BaseHUDPart {
app: this.root.app, app: this.root.app,
title: T.dialogs.exportScreenshotWarning.title, title: T.dialogs.exportScreenshotWarning.title,
desc: T.dialogs.exportScreenshotWarning.desc, desc: T.dialogs.exportScreenshotWarning.desc,
formElements: [qualityInput, layerInput], formElements: [qualityInput, overlayInput, layerInput],
buttons: ["cancel:good", "ok:bad"], buttons: ["cancel:good", "ok:bad"],
}); });
this.root.hud.parts.dialogs.internalShowDialog(dialog); this.root.hud.parts.dialogs.internalShowDialog(dialog);
dialog.buttonSignals.ok.add( dialog.buttonSignals.ok.add(
() => this.doExport(layerInput.getValue(), qualityInput.getValue()), () => this.doExport(qualityInput.getValue(), overlayInput.getValue(), layerInput.getValue()),
this this
); );
} }
/** /**
* Renders a screenshot of the entire base as closely as possible to the ingame camera
* @param {number} resolution
* @param {boolean} overlay
* @param {boolean} wiresLayer * @param {boolean} wiresLayer
* @param {QualityOptions} options
*/ */
doExport(wiresLayer, options) { doExport(resolution, overlay, wiresLayer) {
logger.log("Starting export ..."); logger.log("Starting export ...");
// Find extends // Find extends
@ -114,15 +114,28 @@ export class HUDScreenshotExporter extends BaseHUDPart {
// we want integer pixels per tile // we want integer pixels per tile
// if resolution too low, we want integer pixels per chunk // if resolution too low, we want integer pixels per chunk
const chunkSizePixels = const chunkSizePixels =
maxDimensions * globalConfig.mapChunkSize > options.resolution maxDimensions * globalConfig.mapChunkSize > resolution
? Math.max(1, Math.floor(options.resolution / maxDimensions)) ? Math.max(1, Math.floor(resolution / maxDimensions))
: Math.floor(options.resolution / (maxDimensions * globalConfig.mapChunkSize)) * : Math.floor(resolution / (maxDimensions * globalConfig.mapChunkSize)) *
globalConfig.mapChunkSize; globalConfig.mapChunkSize;
logger.log("ChunkSizePixels:", chunkSizePixels); logger.log("ChunkSizePixels:", chunkSizePixels);
// equivalent to zoomLevel
const chunkScale = chunkSizePixels / globalConfig.mapChunkWorldSize; const chunkScale = chunkSizePixels / globalConfig.mapChunkWorldSize;
logger.log("Scale:", chunkScale); logger.log("Scale:", chunkScale);
// Compute atlas scale
const lowQuality = this.root.app.settings.getAllSettings().lowQualityTextures;
const effectiveZoomLevel =
(chunkScale / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness;
let desiredAtlasScale = "0.25";
if (effectiveZoomLevel > 0.5 && !lowQuality) {
desiredAtlasScale = ORIGINAL_SPRITE_SCALE;
} else if (effectiveZoomLevel > 0.35 && !lowQuality) {
desiredAtlasScale = "0.5";
}
logger.log("Allocating buffer, if the factory grew too big it will crash here"); logger.log("Allocating buffer, if the factory grew too big it will crash here");
const [canvas, context] = makeOffscreenBuffer( const [canvas, context] = makeOffscreenBuffer(
dimensions.x * chunkSizePixels, dimensions.x * chunkSizePixels,
@ -144,7 +157,7 @@ export class HUDScreenshotExporter extends BaseHUDPart {
const parameters = new DrawParameters({ const parameters = new DrawParameters({
context, context,
visibleRect, visibleRect,
desiredAtlasScale: 0.25, desiredAtlasScale,
root: this.root, root: this.root,
zoomLevel: chunkScale, zoomLevel: chunkScale,
}); });
@ -152,17 +165,36 @@ export class HUDScreenshotExporter extends BaseHUDPart {
context.scale(chunkScale, chunkScale); context.scale(chunkScale, chunkScale);
context.translate(-visibleRect.x, -visibleRect.y); context.translate(-visibleRect.x, -visibleRect.y);
// hack but works
const currentLayer = this.root.currentLayer;
const currentAlpha = this.root.hud.parts.wiresOverlay.currentAlpha;
if (wiresLayer) {
this.root.currentLayer = "wires";
this.root.hud.parts.wiresOverlay.currentAlpha = 1;
} else {
this.root.currentLayer = "regular";
this.root.hud.parts.wiresOverlay.currentAlpha = 0;
}
// Render all relevant chunks // Render all relevant chunks
this.root.signals.gameFrameStarted.dispatch(); this.root.signals.gameFrameStarted.dispatch();
this.root.map.drawBackground(parameters); if (overlay) {
this.root.systemMgr.systems.belt.drawBeltItems(parameters); this.root;
this.root.map.drawForeground(parameters); this.root.map.drawOverlay(parameters);
this.root.systemMgr.systems.hub.draw(parameters); } else {
if (wiresLayer) { this.root.map.drawBackground(parameters);
this.root.hud.parts.wiresOverlay.draw(parameters, true); this.root.systemMgr.systems.belt.drawBeltItems(parameters);
this.root.map.drawWiresForegroundLayer(parameters); this.root.map.drawForeground(parameters);
this.root.systemMgr.systems.hub.draw(parameters);
this.root.hud.parts.wiresOverlay.draw(parameters);
if (this.root.currentLayer === "wires") {
this.root.map.drawWiresForegroundLayer(parameters);
}
} }
this.root.currentLayer = currentLayer;
this.root.hud.parts.wiresOverlay.currentAlpha = currentAlpha;
// Offer export // Offer export
logger.log("Rendered buffer, exporting ..."); logger.log("Rendered buffer, exporting ...");
const image = canvas.toDataURL("image/png"); const image = canvas.toDataURL("image/png");

View File

@ -114,10 +114,9 @@ export class HUDWiresOverlay extends BaseHUDPart {
/** /**
* *
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
* @param {boolean=} forced
*/ */
draw(parameters, forced = false) { draw(parameters) {
if (!forced && this.currentAlpha < 0.02) { if (this.currentAlpha < 0.02) {
return; return;
} }
@ -128,7 +127,7 @@ export class HUDWiresOverlay extends BaseHUDPart {
const bounds = parameters.visibleRect; const bounds = parameters.visibleRect;
parameters.context.globalAlpha = forced ? 1 : this.currentAlpha; parameters.context.globalAlpha = this.currentAlpha;
const scaleFactor = 1 / wiresBackgroundDpi; const scaleFactor = 1 / wiresBackgroundDpi;
parameters.context.globalCompositeOperation = "overlay"; parameters.context.globalCompositeOperation = "overlay";