forked from ppfun/pixelplanet
updating drawOcean.js script and instructions
This commit is contained in:
parent
bfa3e19c2c
commit
924fb41741
|
@ -1,6 +1,7 @@
|
||||||
# ocean tiles
|
# 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.
|
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.
|
||||||
Those are the commands to create tiles in subfolders:
|
|
||||||
|
Execute those shell commands in this folder (utils/ocean-tiles). Imagemagick needs to be installed.
|
||||||
|
|
||||||
- create folder for tiles:
|
- 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
|
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
|
||||||
```
|
```
|
||||||
|
- 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.
|
||||||
- to remove the subfolders again
|
|
||||||
|
|
||||||
```
|
```
|
||||||
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).
|
|
||||||
|
|
|
@ -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();
|
|
|
@ -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.
|
* 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 sharp from 'sharp';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import canvases from '../../src/canvases.json';
|
||||||
|
import { getChunkOfPixel } from '../../src/core/utils';
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
const CANVAS_SIZE = 256 * 256;
|
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_MIN_XY = -(CANVAS_SIZE / 2);
|
||||||
const CANVAS_MAX_XY = (CANVAS_SIZE / 2) - 1;
|
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() {
|
const imageData = new Uint8Array(data.buffer);
|
||||||
for (let ty = 0; ty < CANVAS_SIZE / TILE_SIZE; ty += 1) {
|
|
||||||
for (let tx = 0; tx < CANVAS_SIZE / TILE_SIZE; tx += 1) {
|
const [ucx, ucy] = getChunkOfPixel(canvas.size, x, y);
|
||||||
const [ x, y ] = [tx, ty].map(z => z * TILE_SIZE + CANVAS_MIN_XY);
|
const [lcx, lcy] = getChunkOfPixel(canvas.size, x + width, y + height);
|
||||||
const filename = `${TILEFOLDER}/${tx}/${ty}.png`;
|
|
||||||
console.log(`Checking tile ${filename}`);
|
console.info(`Loading to chunks from ${ucx} / ${ucy} to ${lcx} / ${lcy} ...`);
|
||||||
if (!fs.existsSync(filename)) continue;
|
for (let cx = ucx; cx <= lcx; cx += 1) {
|
||||||
let tile = await sharp(filename).removeAlpha().raw().toBuffer();
|
for (let cy = ucy; cy <= lcy; cy += 1) {
|
||||||
await imagemask2Canvas(0, x, y, tile, TILE_SIZE, TILE_SIZE, (clr) => {
|
let chunk = null;
|
||||||
return 1; //set color to index 1 -> land
|
try {
|
||||||
});
|
chunk = await getChunk(canvasId, cx, cy, expectedLength);
|
||||||
tile = null;
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user