save in webp

adjust Tile creation
This commit is contained in:
HF 2022-06-27 14:03:52 +02:00
parent c8409c6e5f
commit 61ec34d7b4
5 changed files with 90 additions and 81 deletions

View File

@ -241,7 +241,7 @@ function addIndexedSubtiletoTile(
*/ */
function tileFileName(canvasTileFolder, cell) { function tileFileName(canvasTileFolder, cell) {
const [z, x, y] = cell; const [z, x, y] = cell;
const filename = `${canvasTileFolder}/${z}/${x}/${y}.png`; const filename = `${canvasTileFolder}/${z}/${x}/${y}.webp`;
return filename; return filename;
} }
@ -271,24 +271,17 @@ export async function createZoomTileFromChunk(
const xabs = x * TILE_ZOOM_LEVEL; const xabs = x * TILE_ZOOM_LEVEL;
const yabs = y * TILE_ZOOM_LEVEL; const yabs = y * TILE_ZOOM_LEVEL;
const na = []; const na = [];
for (let dy = 0; dy < TILE_ZOOM_LEVEL; dy += 1) {
for (let dx = 0; dx < TILE_ZOOM_LEVEL; dx += 1) { const prom = async (dx, dy) => {
let chunk = null; try {
try { const chunk = await RedisCanvas.getChunk(
chunk = await RedisCanvas.getChunk( canvasId,
canvasId, xabs + dx,
xabs + dx, yabs + dy,
yabs + dy, );
);
} catch (error) {
console.error(
// eslint-disable-next-line max-len
`Tiling: Failed to get Chunk ch:${canvasId}:${xabs + dx}${yabs + dy} with error ${error.message}`,
);
}
if (!chunk || !chunk.length) { if (!chunk || !chunk.length) {
na.push([dx, dy]); na.push([dx, dy]);
continue; return;
} }
addIndexedSubtiletoTile( addIndexedSubtiletoTile(
palette, palette,
@ -297,8 +290,22 @@ export async function createZoomTileFromChunk(
chunk, chunk,
tileRGBBuffer, tileRGBBuffer,
); );
} catch (error) {
na.push([dx, dy]);
console.error(
// eslint-disable-next-line max-len
`Tiling: Failed to get Chunk ch:${canvasId}:${xabs + dx}${yabs + dy} with error ${error.message}`,
);
}
};
const promises = [];
for (let dy = 0; dy < TILE_ZOOM_LEVEL; dy += 1) {
for (let dx = 0; dx < TILE_ZOOM_LEVEL; dx += 1) {
promises.push(prom(dx, dy));
} }
} }
await Promise.all(promises);
if (na.length !== TILE_ZOOM_LEVEL * TILE_ZOOM_LEVEL) { if (na.length !== TILE_ZOOM_LEVEL * TILE_ZOOM_LEVEL) {
na.forEach((element) => { na.forEach((element) => {
@ -313,7 +320,7 @@ export async function createZoomTileFromChunk(
const filename = tileFileName(canvasTileFolder, [maxTiledZoom - 1, x, y]); const filename = tileFileName(canvasTileFolder, [maxTiledZoom - 1, x, y]);
try { try {
await sharp(Buffer.from(tileRGBBuffer.buffer), { await sharp(tileRGBBuffer, {
raw: { raw: {
width: TILE_SIZE * TILE_ZOOM_LEVEL, width: TILE_SIZE * TILE_ZOOM_LEVEL,
height: TILE_SIZE * TILE_ZOOM_LEVEL, height: TILE_SIZE * TILE_ZOOM_LEVEL,
@ -321,7 +328,7 @@ export async function createZoomTileFromChunk(
}, },
}) })
.resize(TILE_SIZE) .resize(TILE_SIZE)
.png({ options: { compressionLevel: 6 } }) .webp({ quality: 100, smartSubsample: true })
.toFile(filename); .toFile(filename);
} catch (error) { } catch (error) {
console.error( console.error(
@ -361,7 +368,7 @@ export async function createZoomedTile(
const prom = async (dx, dy) => { const prom = async (dx, dy) => {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const chunkfile = `${canvasTileFolder}/${z + 1}/${x * TILE_ZOOM_LEVEL + dx}/${y * TILE_ZOOM_LEVEL + dy}.png`; const chunkfile = `${canvasTileFolder}/${z + 1}/${x * TILE_ZOOM_LEVEL + dx}/${y * TILE_ZOOM_LEVEL + dy}.webp`;
try { try {
if (!fs.existsSync(chunkfile)) { if (!fs.existsSync(chunkfile)) {
na.push([dx, dy]); na.push([dx, dy]);
@ -404,17 +411,15 @@ export async function createZoomedTile(
const filename = tileFileName(canvasTileFolder, [z, x, y]); const filename = tileFileName(canvasTileFolder, [z, x, y]);
try { try {
await sharp( await sharp(tileRGBBuffer, {
Buffer.from( raw: {
tileRGBBuffer.buffer, width: TILE_SIZE,
), { height: TILE_SIZE,
raw: { channels: 3,
width: TILE_SIZE,
height: TILE_SIZE,
channels: 3,
},
}, },
).toFile(filename); })
.webp({ quality: 100, smartSubsample: true })
.toFile(filename);
} catch (error) { } catch (error) {
console.error( console.error(
`Tiling: Error on createZoomedTile: ${error.message}`, `Tiling: Error on createZoomedTile: ${error.message}`,
@ -452,16 +457,16 @@ async function createEmptyTile(
// eslint-disable-next-line prefer-destructuring // eslint-disable-next-line prefer-destructuring
tileRGBBuffer[i++] = palette.rgb[2]; tileRGBBuffer[i++] = palette.rgb[2];
} }
const filename = `${canvasTileFolder}/emptytile.png`; const filename = `${canvasTileFolder}/emptytile.webp`;
try { try {
await sharp(Buffer.from(tileRGBBuffer.buffer), { await sharp(tileRGBBuffer, {
raw: { raw: {
width: TILE_SIZE, width: TILE_SIZE,
height: TILE_SIZE, height: TILE_SIZE,
channels: 3, channels: 3,
}, },
}) })
.png({ options: { compressionLevel: 6 } }) .webp({ quality: 100, smartSubsample: true })
.toFile(filename); .toFile(filename);
} catch (error) { } catch (error) {
console.error( console.error(
@ -494,45 +499,36 @@ export async function createTexture(
const startTime = Date.now(); const startTime = Date.now();
const na = []; const na = [];
if (targetSize !== canvasSize) {
for (let dy = 0; dy < amount; dy += 1) { const prom = (targetSize !== canvasSize)
for (let dx = 0; dx < amount; dx += 1) { ? async (dx, dy) => {
let chunk = null; const chunkfile = `${canvasTileFolder}/${zoom}/${dx}/${dy}.webp`;
const chunkfile = `${canvasTileFolder}/${zoom}/${dx}/${dy}.png`; try {
if (!fs.existsSync(chunkfile)) { if (!fs.existsSync(chunkfile)) {
na.push([dx, dy]); na.push([dx, dy]);
continue; return;
}
try {
chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer();
addRGBSubtiletoTile(amount, [dx, dy], chunk, textureBuffer);
} catch (error) {
console.error(
// eslint-disable-next-line max-len
`Tiling: Error on createTexture in chunk ${chunkfile}: ${error.message}`,
);
} }
const chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer();
addRGBSubtiletoTile(amount, [dx, dy], chunk, textureBuffer);
} catch (error) {
na.push([dx, dy]);
console.error(
// eslint-disable-next-line max-len
`Tiling: Error on createTexture in chunk ${chunkfile}: ${error.message}`,
);
} }
} }
} else { : async (dx, dy) => {
for (let dy = 0; dy < amount; dy += 1) { try {
for (let dx = 0; dx < amount; dx += 1) {
let chunk = null; let chunk = null;
try { chunk = await RedisCanvas.getChunk(
chunk = await RedisCanvas.getChunk( canvasId,
canvasId, dx,
dx, dy,
dy, );
);
} catch (error) {
console.error(
// eslint-disable-next-line max-len
`Tiling: Failed to get Chunk ch:${canvasId}:${dx}${dy} with error ${error.message}`,
);
}
if (!chunk || !chunk.length) { if (!chunk || !chunk.length) {
na.push([dx, dy]); na.push([dx, dy]);
continue; return;
} }
addIndexedSubtiletoTile( addIndexedSubtiletoTile(
palette, palette,
@ -541,25 +537,36 @@ export async function createTexture(
chunk, chunk,
textureBuffer, textureBuffer,
); );
} catch (error) {
na.push([dx, dy]);
console.error(
// eslint-disable-next-line max-len
`Tiling: Failed to get Chunk ch:${canvasId}:${dx}${dy} with error ${error.message}`,
);
} }
};
const promises = [];
for (let dy = 0; dy < amount; dy += 1) {
for (let dx = 0; dx < amount; dx += 1) {
promises.push(prom(dx, dy));
} }
} }
await Promise.all(promises);
na.forEach((element) => { na.forEach((element) => {
deleteSubtilefromTile(TILE_SIZE, palette, amount, element, textureBuffer); deleteSubtilefromTile(TILE_SIZE, palette, amount, element, textureBuffer);
}); });
const filename = `${canvasTileFolder}/texture.png`; const filename = `${canvasTileFolder}/texture.webp`;
try { try {
await sharp( await sharp(textureBuffer, {
Buffer.from(textureBuffer.buffer), { raw: {
raw: { width: targetSize,
width: targetSize, height: targetSize,
height: targetSize, channels: 3,
channels: 3,
},
}, },
).toFile(filename); }).toFile(filename);
} catch (error) { } catch (error) {
console.error( console.error(
`Tiling: Error on createTexture: ${error.message}`, `Tiling: Error on createTexture: ${error.message}`,
@ -605,7 +612,7 @@ export async function initializeTiles(
const tileDir = `${canvasTileFolder}/${zoom}/${cx}`; const tileDir = `${canvasTileFolder}/${zoom}/${cx}`;
if (!fs.existsSync(tileDir)) fs.mkdirSync(tileDir); if (!fs.existsSync(tileDir)) fs.mkdirSync(tileDir);
for (let cy = 0; cy < maxBase; cy += 1) { for (let cy = 0; cy < maxBase; cy += 1) {
const filename = `${canvasTileFolder}/${zoom}/${cx}/${cy}.png`; const filename = `${canvasTileFolder}/${zoom}/${cx}/${cy}.webp`;
if (force || !fs.existsSync(filename)) { if (force || !fs.existsSync(filename)) {
const ret = await createZoomTileFromChunk( const ret = await createZoomTileFromChunk(
canvasId, canvasId,
@ -634,7 +641,7 @@ export async function initializeTiles(
const tileDir = `${canvasTileFolder}/${zoom}/${cx}`; const tileDir = `${canvasTileFolder}/${zoom}/${cx}`;
if (!fs.existsSync(tileDir)) fs.mkdirSync(tileDir); if (!fs.existsSync(tileDir)) fs.mkdirSync(tileDir);
for (let cy = 0; cy < maxZ; cy += 1) { for (let cy = 0; cy < maxZ; cy += 1) {
const filename = `${canvasTileFolder}/${zoom}/${cx}/${cy}.png`; const filename = `${canvasTileFolder}/${zoom}/${cx}/${cy}.webp`;
if (force || !fs.existsSync(filename)) { if (force || !fs.existsSync(filename)) {
const ret = await createZoomedTile( const ret = await createZoomedTile(
canvas, canvas,

View File

@ -161,7 +161,9 @@ class CanvasUpdater {
} }
for (let c = 0; c < this.maxTiledZoom; c += 1) { for (let c = 0; c < this.maxTiledZoom; c += 1) {
this.TileLoadingQueues.push([]); this.TileLoadingQueues.push([]);
const timeout = (7 ** (this.maxTiledZoom - c - 1)) * 5 * 1000; const invZoom = this.maxTiledZoom - c - 1;
// eslint-disable-next-line max-len
const timeout = TILE_ZOOM_LEVEL ** (2 * invZoom + 2) * (6 / TILE_ZOOM_LEVEL ** 2) * 1000;
logger.info( logger.info(
`Tiling: Set interval for zoomlevel ${c} update to ${timeout / 1000}`, `Tiling: Set interval for zoomlevel ${c} update to ${timeout / 1000}`,
); );

View File

@ -51,7 +51,7 @@ document.addEventListener('DOMContentLoaded', () => {
const [canvasIdent, canvasId, canvasSize, x, y] = parseHashCoords(); const [canvasIdent, canvasId, canvasSize, x, y] = parseHashCoords();
const canvasTexture = new THREE.MeshPhongMaterial({ const canvasTexture = new THREE.MeshPhongMaterial({
map: new THREE.TextureLoader().load(`./tiles/${canvasId}/texture.png`), map: new THREE.TextureLoader().load(`./tiles/${canvasId}/texture.webp`),
bumpMap: new THREE.TextureLoader().load(`./assets3d/normal${canvasId}.jpg`), bumpMap: new THREE.TextureLoader().load(`./assets3d/normal${canvasId}.jpg`),
bumpScale: 0.02, bumpScale: 0.02,
specularMap: new THREE.TextureLoader() specularMap: new THREE.TextureLoader()

View File

@ -23,12 +23,12 @@ router.use('/', express.static(TILE_FOLDER, {
/* /*
* catch File Not Found: Send empty tile * catch File Not Found: Send empty tile
*/ */
router.use('/:c([0-9]+)/:z([0-9]+)/:x([0-9]+)/:y([0-9]+).png', router.use('/:c([0-9]+)/:z([0-9]+)/:x([0-9]+)/:y([0-9]+).webp',
async (req, res) => { async (req, res) => {
const { c: paramC } = req.params; const { c: paramC } = req.params;
const c = parseInt(paramC, 10); const c = parseInt(paramC, 10);
const filename = `${TILE_FOLDER}/${c}/emptytile.png`; const filename = `${TILE_FOLDER}/${c}/emptytile.webp`;
if (!fs.existsSync(filename)) { if (!fs.existsSync(filename)) {
res.set({ res.set({
'Cache-Control': `public, s-maxage=${24 * 3600}, max-age=${24 * 3600}`, 'Cache-Control': `public, s-maxage=${24 * 3600}, max-age=${24 * 3600}`,
@ -39,7 +39,7 @@ router.use('/:c([0-9]+)/:z([0-9]+)/:x([0-9]+)/:y([0-9]+).png',
res.set({ res.set({
'Cache-Control': `public, s-maxage=${2 * 3600}, max-age=${1 * 3600}`, 'Cache-Control': `public, s-maxage=${2 * 3600}, max-age=${1 * 3600}`,
'Content-Type': 'image/png', 'Content-Type': 'image/webp',
}); });
res.status(200); res.status(200);
res.sendFile(filename); res.sendFile(filename);

View File

@ -304,7 +304,7 @@ class ChunkLoader {
const center = [zoom, cx, cy]; const center = [zoom, cx, cy];
this.store.dispatch(requestBigChunk(center)); this.store.dispatch(requestBigChunk(center));
try { try {
const url = `tiles/${this.canvasId}/${zoom}/${cx}/${cy}.png`; const url = `tiles/${this.canvasId}/${zoom}/${cx}/${cy}.webp`;
const img = await loadImage(url); const img = await loadImage(url);
chunkRGB.fromImage(img); chunkRGB.fromImage(img);
this.store.dispatch(receiveBigChunk(center, chunkRGB)); this.store.dispatch(receiveBigChunk(center, chunkRGB));