updating drawOcean.js script and instructions

This commit is contained in:
HF 2023-06-22 20:17:41 +02:00
parent bfa3e19c2c
commit 924fb41741
3 changed files with 145 additions and 111 deletions

View File

@ -1,6 +1,7 @@
# ocean tiles
In order to have the ocean and land on the canvas, or any other background pic, we have to create tiles that we can later upload to the canvas with drawOcean.js.
Those are the commands to create tiles in subfolders:
In order to have the ocean and land on the canvas, we have to scale up the ocean.png, create tiles and then upload them to the canvas with drawOcean.js.
Execute those shell commands in this folder (utils/ocean-tiles). Imagemagick needs to be installed.
- create folder for tiles:
@ -33,12 +34,8 @@ for i in {0..31}; do mkdir $i; done
```
for file in ./ocean_tiles*.png; do NUM=`echo $file | sed -e 's/.*ocean_tiles//' -e 's/.png//'`; Y=$(expr $NUM / 32); X=$(expr $NUM % 32); newfile="$X/$Y.png"; mv $file $newfile; done
```
- to remove the subfolders again
- Draws the 2048x2048 tiles from the ./ocean directory on the canvas. Uses localhost:6379 as redis url, if you need a different one, edit it in the drawOcean.js file.
```
for i in {0..31}; do rm -r $i; done
npm run babel-node ./utils/ocean-tiles/drawOcean.js
```
# createOceanTiles.js
createOceanTiles is splitting the generated ocean tiles into 256x256 tiles, skipping the ones that are empty (aka all ocean).

View File

@ -1,81 +0,0 @@
/* @flow
* this script takes black/withe tiles and sets their colors on the canvas
* its used to set the land area on the planet.
*
* The tiles it uses are explained in 3dmodels/ocean-tiles
*
* run this script with --expose-gc or you run out of RAM
*/
import sharp from 'sharp';
import fs from 'fs';
const CANVAS_SIZE = 256 * 256;
const CANVAS_MIN_XY = -(CANVAS_SIZE / 2);
const CANVAS_MAX_XY = (CANVAS_SIZE / 2) - 1;
const TILEFOLDER = './ocean';
const TILE_SIZE = 2048;
const SMALL_TILE_SIZE = 256;
async function createTiles() {
const targetfolder = './tmpfolder';
if (!fs.existsSync(targetfolder)) {
fs.mkdirSync(targetfolder);
}
for (let dx = 0; dx < CANVAS_SIZE / SMALL_TILE_SIZE; dx += 1) {
const dir = `${targetfolder}/${dx}`;
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
}
for (let ty = 0; ty < CANVAS_SIZE / TILE_SIZE; ty += 1) {
for (let tx = 0; tx < CANVAS_SIZE / TILE_SIZE; tx += 1) {
const [ x, y ] = [tx, ty].map(z => z * TILE_SIZE / SMALL_TILE_SIZE);
const filename = `${TILEFOLDER}/${tx}/${ty}.png`;
console.log(`Checking tile ${filename}`);
if (!fs.existsSync(filename)) continue;
let tile = await sharp(filename).ensureAlpha().raw().toBuffer();
tile = new Uint32Array(tile.buffer);
for (let dxc = 0; dxc < TILE_SIZE / SMALL_TILE_SIZE; dxc += 1) {
for (let dyc = 0; dyc < TILE_SIZE / SMALL_TILE_SIZE; dyc += 1) {
const buffer = new Uint32Array(SMALL_TILE_SIZE * SMALL_TILE_SIZE);
let boff = (dxc + dyc * TILE_SIZE) * SMALL_TILE_SIZE;
let soff = 0;
let exists = false;
for (let column = 0; column < SMALL_TILE_SIZE; column += 1) {
for (let row = 0; row < SMALL_TILE_SIZE; row += 1) {
const mask = tile[boff++] & 0x00FFFFFF;
if (!mask && !exists) {
exists = true;
}
buffer[soff++] = (mask) ? 0xFFFFE3CA : 0xFFFFFFFF;
}
boff += TILE_SIZE - SMALL_TILE_SIZE;
}
if (exists) {
const filename = `${targetfolder}/${tx * TILE_SIZE / SMALL_TILE_SIZE + dxc}/${ty * TILE_SIZE / SMALL_TILE_SIZE + dyc}.png`
await sharp(
Buffer.from(
buffer.buffer,
), {
raw: {
width: SMALL_TILE_SIZE,
height: SMALL_TILE_SIZE,
channels: 4,
},
},
).toFile(filename);
}
}
}
tile = null;
}
}
}
createTiles();

View File

@ -1,39 +1,157 @@
/* @flow
* this script takes black/withe tiles and sets their colors on the canvas
/*
* this script takes black/whtie tiles and sets their colors on the canvas
* its used to set the land area on the planet.
*
* The tiles it uses are explained in 3dmodels/ocean-tiles
*
* run this script with --expose-gc or you run out of RAM
*/
import { imagemask2Canvas } from '../../src/core/Image';
import { createClient, commandOptions } from 'redis';
import sharp from 'sharp';
import fs from 'fs';
import canvases from '../../src/canvases.json';
import { getChunkOfPixel } from '../../src/core/utils';
// -----------------------------------------------------------------------------
const CANVAS_SIZE = 256 * 256;
const TILE_SIZE = 256;
const TILEFOLDER = './utils/ocean-tiles/ocean';
const IMG_TILE_SIZE = 2048;
// change redis URL here if you need a different one
const redis = createClient({ url: "redis://localhost:6379" });
// -----------------------------------------------------------------------------
const CANVAS_MIN_XY = -(CANVAS_SIZE / 2);
const CANVAS_MAX_XY = (CANVAS_SIZE / 2) - 1;
const TILEFOLDER = './ocean';
const TILE_SIZE = 2048;
/**
* get Chunk from redis, pad if neccessary
*/
async function getChunk(
canvasId,
i,
j,
padding = null,
) {
const key = `ch:${canvasId}:${i}:${j}`;
let chunk = await redis.get(
commandOptions({ returnBuffers: true }),
key,
);
if (padding > 0 && chunk && chunk.length < padding) {
const pad = Buffer.alloc(padding - chunk.length);
chunk = Buffer.concat([chunk, pad]);
}
return chunk;
}
/**
* Load imagemask from ABGR buffer and execute filter function
* for each black pixel
*/
async function imagemask2Canvas(
canvasId,
x,
y,
data,
width,
height,
filter,
) {
console.info(
`Loading mask with size ${width} / ${height} to ${x} / ${y} to the canvas`,
);
const expectedLength = TILE_SIZE ** 2;
const canvas = canvases[canvasId];
const canvasMinXY = -(canvas.size / 2);
async function applyMasks() {
for (let ty = 0; ty < CANVAS_SIZE / TILE_SIZE; ty += 1) {
for (let tx = 0; tx < CANVAS_SIZE / TILE_SIZE; tx += 1) {
const [ x, y ] = [tx, ty].map(z => z * TILE_SIZE + CANVAS_MIN_XY);
const filename = `${TILEFOLDER}/${tx}/${ty}.png`;
console.log(`Checking tile ${filename}`);
if (!fs.existsSync(filename)) continue;
let tile = await sharp(filename).removeAlpha().raw().toBuffer();
await imagemask2Canvas(0, x, y, tile, TILE_SIZE, TILE_SIZE, (clr) => {
return 1; //set color to index 1 -> land
});
tile = null;
const imageData = new Uint8Array(data.buffer);
const [ucx, ucy] = getChunkOfPixel(canvas.size, x, y);
const [lcx, lcy] = getChunkOfPixel(canvas.size, x + width, y + height);
console.info(`Loading to chunks from ${ucx} / ${ucy} to ${lcx} / ${lcy} ...`);
for (let cx = ucx; cx <= lcx; cx += 1) {
for (let cy = ucy; cy <= lcy; cy += 1) {
let chunk = null;
try {
chunk = await getChunk(canvasId, cx, cy, expectedLength);
} catch (error) {
console.error(
// eslint-disable-next-line max-len
`Could not load chunk ch:${canvasId}:${cx}:${cy} for imagemask-load: ${error.message}`,
);
}
chunk = (chunk && chunk.length)
? new Uint8Array(chunk)
: new Uint8Array(TILE_SIZE * TILE_SIZE);
// offset of chunk in image
const cOffX = cx * TILE_SIZE + canvasMinXY - x;
const cOffY = cy * TILE_SIZE + canvasMinXY - y;
let cOff = 0;
let pxlCnt = 0;
for (let py = 0; py < TILE_SIZE; py += 1) {
for (let px = 0; px < TILE_SIZE; px += 1) {
const clrX = cOffX + px;
const clrY = cOffY + py;
if (clrX >= 0 && clrY >= 0 && clrX < width && clrY < height) {
let offset = (clrX + clrY * width) * 3;
if (!imageData[offset++]
&& !imageData[offset++]
&& !imageData[offset]
) {
chunk[cOff] = filter();
pxlCnt += 1;
}
}
cOff += 1;
}
}
if (pxlCnt) {
const key = `ch:${canvasId}:${cx}:${cy}`;
const ret = await redis.set(key, Buffer.from(chunk.buffer));
if (ret) {
console.info(`Loaded ${pxlCnt} pixels into chunk ${cx}, ${cy}.`);
}
}
chunk = null;
}
global.gc();
}
console.info('Imagemask loading done.');
}
/**
* Load black/white tiles from TILEFOLDER onto canvas
*/
async function applyMasks() {
await redis.connect();
try {
for (let ty = 0; ty < CANVAS_SIZE / IMG_TILE_SIZE; ty += 1) {
for (let tx = 0; tx < CANVAS_SIZE / IMG_TILE_SIZE; tx += 1) {
const [ x, y ] = [tx, ty].map(z => z * IMG_TILE_SIZE + CANVAS_MIN_XY);
const filename = `${TILEFOLDER}/${tx}/${ty}.png`;
console.log(`Checking tile ${filename}`);
if (!fs.existsSync(filename)) {
console.log(`Not Found ${filename}`);
continue;
}
let tile = await sharp(filename).removeAlpha().raw().toBuffer();
// eslint-disable-next-line max-len
await imagemask2Canvas(0, x, y, tile, IMG_TILE_SIZE, IMG_TILE_SIZE, () => {
return 1; //set color to index 1 -> land
});
tile = null;
}
if (global?.gc) {
global.gc();
}
}
await redis.quit();
process.exit(0);
} catch (err) {
console.error(err);
await redis.quit();
process.exit(1);
}
}