"use strict"; import { writeFile } from "fs/promises"; import { Canvas, loadImage } from 'canvas'; const TILE_WIDTH = 256; // pixels const WIDTH = 8; // tiles const FACE_WIDTH = TILE_WIDTH * WIDTH; // pixels async function main() { const canvas = new Canvas(FACE_WIDTH, FACE_WIDTH); const context = canvas.getContext('2d'); for (let face = 1; face <= 6; face++) { const face4 = toBase(face, 4).padStart(2, "0"); for (let position = 0; position < 64; position++) { const position4 = toBase(position, 4).padStart(3, "0"); const image = await loadImage(`out/${face4}${position4}.jpg`); const [x, y] = quadtreePositionToXY(position4); context.drawImage(image, x * TILE_WIDTH, y * TILE_WIDTH, TILE_WIDTH, TILE_WIDTH); } const output = canvas.toBuffer('image/png'); await writeFile(`stitched/${face}.png`, output); console.log(`Stitched face #${face}.`); } console.log("Done stitching! Resultant files in stitched directory."); console.log("Stitching faces into cubemap"); const canvas2 = new Canvas(FACE_WIDTH * 4, FACE_WIDTH * 3); const context2 = canvas2.getContext("2d"); const positionIndex = [[1, 1], [2, 1], [3, 1], [0, 1], [1, 0], [1, 2]]; for (let i = 1; i <= 6; i++) { const [x, y] = positionIndex[i - 1]; const image = await loadImage(`stitched/${i}.png`); context2.drawImage(image, FACE_WIDTH * x, FACE_WIDTH * y, FACE_WIDTH, FACE_WIDTH); } const output = canvas2.toBuffer('image/png'); await writeFile(`stitched.png`, output); } main(); function quadtreePositionToXY(position) { let x = 0; let y = 0; // Loop through each position value and treat it as an instruction. for (let j = 0; j < position.length; j++) { // The delta value is equal to the width of each tile at the current instruction depth. let delta = WIDTH / (2 ** (j + 1)); switch (position[j]) { case "0": // Do nothing break; case "1": // Move right x = x + delta; break; case "2": // Move down y = y + delta; break; case "3": // Move down and right y = y + delta; x = x + delta; break; } } return [x, y]; } function toBase(number, base) { return number.toString(base); }