Updated atlas script, added palette script

This commit is contained in:
Lucas Dower 2022-02-19 21:50:17 +00:00
parent 24a4038362
commit ed2a61af1a
19 changed files with 3936 additions and 5181 deletions

View File

@ -26,7 +26,7 @@
"func-call-spacing": "off",
"no-trailing-spaces": "off",
"new-cap": "off",
"no-console": "error",
"no-console": "warn",
"no-unused-vars": "warn"
}
}

View File

@ -7,11 +7,12 @@
"node": ">=14.0.0"
},
"scripts": {
"lint": "eslint --fix ./src/**/*.ts",
"lint": "eslint --fix ./src/**/*.ts && eslint --fix ./src/**/*.ts",
"debug": "tsc && electron ./dist/main.js --enable-logging",
"build": "npm run lint && tsc",
"start": "npm run build && electron ./dist/main.js --enable-logging",
"atlas": "npx ts-node tools/build-atlas.ts",
"palette": "npx ts-node tools/build-palette.ts",
"export": "electron-packager . ObjToSchematic --platform=win32 --arch=x64"
},
"repository": {

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

View File

@ -0,0 +1,245 @@
{
"blocks": [
"acacia_log",
"acacia_planks",
"acacia_wood",
"ancient_debris",
"andesite",
"basalt",
"bedrock",
"birch_log",
"birch_planks",
"birch_wood",
"blackstone",
"black_concrete",
"black_concrete_powder",
"black_glazed_terracotta",
"black_terracotta",
"black_wool",
"blue_concrete",
"blue_concrete_powder",
"blue_glazed_terracotta",
"blue_ice",
"blue_terracotta",
"blue_wool",
"bone_block",
"bookshelf",
"brain_coral_block",
"bricks",
"brown_concrete",
"brown_concrete_powder",
"brown_glazed_terracotta",
"brown_mushroom_block",
"brown_terracotta",
"brown_wool",
"bubble_coral_block",
"cartography_table",
"chiseled_nether_bricks",
"chiseled_polished_blackstone",
"chiseled_quartz_block",
"chiseled_red_sandstone",
"chiseled_sandstone",
"chiseled_stone_bricks",
"clay",
"coal_block",
"coal_ore",
"coarse_dirt",
"cobblestone",
"cracked_nether_bricks",
"cracked_polished_blackstone_bricks",
"cracked_stone_bricks",
"crafting_table",
"crimson_hyphae",
"crimson_planks",
"crimson_stem",
"crying_obsidian",
"cut_red_sandstone",
"cut_sandstone",
"cyan_concrete",
"cyan_concrete_powder",
"cyan_glazed_terracotta",
"cyan_terracotta",
"cyan_wool",
"dark_oak_log",
"dark_oak_planks",
"dark_oak_wood",
"dark_prismarine",
"dead_brain_coral_block",
"dead_bubble_coral_block",
"dead_fire_coral_block",
"dead_horn_coral_block",
"dead_tube_coral_block",
"diamond_block",
"diamond_ore",
"diorite",
"dirt",
"emerald_block",
"emerald_ore",
"end_stone",
"end_stone_bricks",
"fire_coral_block",
"fletching_table",
"frosted_ice_0",
"frosted_ice_1",
"frosted_ice_2",
"frosted_ice_3",
"gilded_blackstone",
"glowstone",
"gold_block",
"gold_ore",
"granite",
"gravel",
"gray_concrete",
"gray_concrete_powder",
"gray_glazed_terracotta",
"gray_stained_glass",
"gray_terracotta",
"gray_wool",
"green_concrete",
"green_concrete_powder",
"green_glazed_terracotta",
"green_terracotta",
"green_wool",
"hay_block",
"honeycomb_block",
"horn_coral_block",
"ice",
"iron_block",
"iron_ore",
"jungle_log",
"jungle_planks",
"jungle_wood",
"lapis_block",
"lapis_ore",
"light_blue_concrete",
"light_blue_concrete_powder",
"light_blue_glazed_terracotta",
"light_blue_terracotta",
"light_blue_wool",
"light_gray_concrete",
"light_gray_concrete_powder",
"light_gray_glazed_terracotta",
"light_gray_terracotta",
"light_gray_wool",
"lime_concrete",
"lime_concrete_powder",
"lime_glazed_terracotta",
"lime_terracotta",
"lime_wool",
"lodestone",
"magenta_concrete",
"magenta_concrete_powder",
"magenta_glazed_terracotta",
"magenta_terracotta",
"magenta_wool",
"magma_block",
"melon",
"mossy_cobblestone",
"mossy_stone_bricks",
"mushroom_stem",
"mushroom_stem_inventory",
"netherite_block",
"netherrack",
"nether_bricks",
"nether_gold_ore",
"nether_quartz_ore",
"nether_wart_block",
"note_block",
"oak_log",
"oak_planks",
"oak_wood",
"obsidian",
"orange_concrete",
"orange_concrete_powder",
"orange_glazed_terracotta",
"orange_terracotta",
"orange_wool",
"packed_ice",
"pink_concrete",
"pink_concrete_powder",
"pink_glazed_terracotta",
"pink_terracotta",
"pink_wool",
"polished_andesite",
"polished_basalt",
"polished_blackstone",
"polished_blackstone_bricks",
"polished_diorite",
"polished_granite",
"prismarine",
"prismarine_bricks",
"purple_concrete",
"purple_concrete_powder",
"purple_glazed_terracotta",
"purple_terracotta",
"purple_wool",
"purpur_block",
"purpur_pillar",
"quartz_block",
"quartz_bricks",
"quartz_pillar",
"redstone_block",
"redstone_lamp",
"redstone_ore",
"red_concrete",
"red_concrete_powder",
"red_glazed_terracotta",
"red_mushroom_block",
"red_nether_bricks",
"red_sand",
"red_terracotta",
"red_wool",
"sand",
"sea_lantern",
"shroomlight",
"smithing_table",
"smooth_quartz",
"smooth_red_sandstone",
"smooth_sandstone",
"smooth_stone",
"smooth_stone_slab_double",
"snow_block",
"soul_sand",
"soul_soil",
"sponge",
"spruce_log",
"spruce_planks",
"spruce_wood",
"stone",
"stone_bricks",
"stripped_acacia_log",
"stripped_acacia_wood",
"stripped_birch_log",
"stripped_birch_wood",
"stripped_crimson_hyphae",
"stripped_crimson_stem",
"stripped_dark_oak_log",
"stripped_dark_oak_wood",
"stripped_jungle_log",
"stripped_jungle_wood",
"stripped_oak_log",
"stripped_oak_wood",
"stripped_spruce_log",
"stripped_spruce_wood",
"stripped_warped_hyphae",
"stripped_warped_stem",
"target",
"terracotta",
"tube_coral_block",
"warped_hyphae",
"warped_planks",
"warped_stem",
"warped_wart_block",
"wet_sponge",
"white_concrete",
"white_concrete_powder",
"white_glazed_terracotta",
"white_terracotta",
"white_wool",
"yellow_concrete",
"yellow_concrete_powder",
"yellow_glazed_terracotta",
"yellow_terracotta",
"yellow_wool"
]
}

View File

@ -0,0 +1,84 @@
{
"blocks": [
"black_concrete",
"black_concrete_powder",
"black_glazed_terracotta",
"black_terracotta",
"black_wool",
"blue_concrete",
"blue_concrete_powder",
"blue_glazed_terracotta",
"blue_terracotta",
"blue_wool",
"brown_concrete",
"brown_concrete_powder",
"brown_glazed_terracotta",
"brown_terracotta",
"brown_wool",
"cyan_concrete",
"cyan_concrete_powder",
"cyan_glazed_terracotta",
"cyan_terracotta",
"cyan_wool",
"gray_concrete",
"gray_concrete_powder",
"gray_glazed_terracotta",
"gray_terracotta",
"gray_wool",
"green_concrete",
"green_concrete_powder",
"green_glazed_terracotta",
"green_terracotta",
"green_wool",
"light_blue_concrete",
"light_blue_concrete_powder",
"light_blue_glazed_terracotta",
"light_blue_terracotta",
"light_blue_wool",
"light_gray_concrete",
"light_gray_concrete_powder",
"light_gray_glazed_terracotta",
"light_gray_terracotta",
"light_gray_wool",
"lime_concrete",
"lime_concrete_powder",
"lime_glazed_terracotta",
"lime_terracotta",
"lime_wool",
"magenta_concrete",
"magenta_concrete_powder",
"magenta_glazed_terracotta",
"magenta_terracotta",
"magenta_wool",
"orange_concrete",
"orange_concrete_powder",
"orange_glazed_terracotta",
"orange_terracotta",
"orange_wool",
"pink_concrete",
"pink_concrete_powder",
"pink_glazed_terracotta",
"pink_terracotta",
"pink_wool",
"purple_concrete",
"purple_concrete_powder",
"purple_glazed_terracotta",
"purple_terracotta",
"purple_wool",
"red_concrete",
"red_concrete_powder",
"red_glazed_terracotta",
"red_terracotta",
"red_wool",
"white_concrete",
"white_concrete_powder",
"white_glazed_terracotta",
"white_terracotta",
"white_wool",
"yellow_concrete",
"yellow_concrete_powder",
"yellow_glazed_terracotta",
"yellow_terracotta",
"yellow_wool"
]
}

View File

@ -0,0 +1,56 @@
{
"blocks": [
"andesite",
"basalt",
"bedrock",
"blackstone",
"black_concrete",
"black_concrete_powder",
"black_terracotta",
"black_wool",
"bone_block",
"chiseled_polished_blackstone",
"chiseled_quartz_block",
"chiseled_stone_bricks",
"coal_block",
"coal_ore",
"cobblestone",
"cracked_polished_blackstone_bricks",
"cracked_stone_bricks",
"cyan_terracotta",
"dead_brain_coral_block",
"dead_bubble_coral_block",
"dead_fire_coral_block",
"dead_horn_coral_block",
"dead_tube_coral_block",
"diorite",
"gravel",
"gray_concrete",
"gray_concrete_powder",
"gray_wool",
"iron_block",
"light_gray_concrete",
"light_gray_concrete_powder",
"light_gray_terracotta",
"light_gray_wool",
"netherite_block",
"polished_andesite",
"polished_basalt",
"polished_blackstone",
"polished_blackstone_bricks",
"polished_diorite",
"quartz_block",
"quartz_bricks",
"quartz_pillar",
"smooth_quartz",
"smooth_stone",
"smooth_stone_slab_double",
"snow_block",
"stone",
"stone_bricks",
"white_concrete",
"white_concrete_powder",
"white_terracotta",
"white_wool"
]
}

View File

@ -8,7 +8,6 @@ import { ASSERT, CustomError, CustomWarning, LOG, LOG_ERROR } from './util';
import { remote } from 'electron';
import { VoxelMesh } from './voxel_mesh';
import { BlockMesh } from './block_mesh';
import { TextureFiltering } from './texture';
/* eslint-disable */
export enum ActionReturnType {

View File

@ -1,5 +1,5 @@
import { HashMap } from './hash_map';
import { UV, RGB } from './util';
import { UV, RGB, ASSERT } from './util';
import { Vector3 } from './vector';
import fs from 'fs';
@ -36,22 +36,30 @@ export enum Block {
/* eslint-enable */
export class BlockAtlas {
private _cachedBlocks: HashMap<Vector3, number>;
private readonly _blocks: Array<BlockInfo>;
public readonly _atlasSize: number;
private _blocks: Array<BlockInfo>;
private _atlasSize: number;
private _atlasLoaded: boolean;
private static _instance: BlockAtlas;
public static get Get() {
return this._instance || (this._instance = new this());
}
private constructor() {
this._cachedBlocks = new HashMap(0);
this._blocks = [];
this._atlasSize = 0;
this._atlasLoaded = false;
this.loadAtlas(path.join(__dirname, '../resources/atlases/vanilla.atlas'));
}
public loadAtlas(absolutePath: string) {
this._cachedBlocks = new HashMap(1024);
const _path = path.join(__dirname, '../resources/blocks.json');
const blocksString = fs.readFileSync(_path, 'utf-8');
const blocksString = fs.readFileSync(absolutePath, 'utf-8');
if (!blocksString) {
throw Error('Could not load blocks.json');
throw Error('Could not load vanilla.atlas');
}
const json = JSON.parse(blocksString);
@ -64,10 +72,13 @@ export class BlockAtlas {
block.colour.b,
);
}
this._atlasLoaded = true;
}
public getBlock(voxelColour: RGB): BlockInfo {
ASSERT(this._atlasLoaded);
const cachedBlockIndex = this._cachedBlocks.get(voxelColour.toVector3());
if (cachedBlockIndex) {
return this._blocks[cachedBlockIndex];
@ -90,4 +101,9 @@ export class BlockAtlas {
this._cachedBlocks.add(voxelColour.toVector3(), blockChoiceIndex);
return this._blocks[blockChoiceIndex];
}
public getAtlasSize() {
ASSERT(this._atlasLoaded);
return this._atlasSize;
}
}

View File

@ -8,7 +8,7 @@ import { RenderBuffer } from './buffer';
import { GeometryTemplates } from './geometry';
import { Mesh, SolidMaterial, TexturedMaterial, MaterialType } from './mesh';
import { BlockAtlas } from './block_atlas';
import { LOG, RGB } from './util';
import { ASSERT, fileExists, LOG, RGB } from './util';
import { VoxelMesh } from './voxel_mesh';
import { BlockMesh } from './block_mesh';
@ -53,8 +53,10 @@ export class Renderer {
this._blockBuffer = new RenderBuffer([]);
this._materialBuffers = [];
const texturePath = path.join(__dirname, '../resources/atlases/vanilla.png');
ASSERT(fileExists(texturePath), `Atlas cannot be found at ${texturePath}`);
this._atlasTexture = twgl.createTexture(this._gl, {
src: path.join(__dirname, '../resources/blocks.png'),
src: texturePath,
mag: this._gl.NEAREST,
});
}
@ -179,7 +181,7 @@ export class Renderer {
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
u_texture: this._atlasTexture,
u_voxelSize: this._voxelSize,
u_atlasSize: BlockAtlas.Get._atlasSize,
u_atlasSize: BlockAtlas.Get.getAtlasSize(),
});
}

View File

@ -1,6 +1,8 @@
import { AppConfig } from './config';
import { Vector3 } from './vector';
import fs from 'fs';
export class UV {
public u: number;
public v: number;
@ -111,29 +113,12 @@ export function ASSERT(condition: any, errorMessage = 'Assertion Failed'): asser
}
}
export function LOG(message: any) {
if (AppConfig.LOGGING_ENABLED) {
/* eslint-disable */
console.log(message);
/* eslint-enable */
}
}
/* eslint-disable */
export const LOG = console.log;
export const LOG_WARN = console.warn;
export const LOG_ERROR = console.error;
export function LOG_WARN(message: any) {
if (AppConfig.LOGGING_ENABLED) {
/* eslint-disable */
console.warn(message);
/* eslint-enable */
}
}
export function LOG_ERROR(message: any) {
if (AppConfig.LOGGING_ENABLED) {
/* eslint-disable */
console.error(message);
/* eslint-enable */
}
}
/* eslint-enable */
export class CustomError extends Error {
constructor(msg: string) {
@ -148,3 +133,7 @@ export class CustomWarning extends Error {
Object.setPrototypeOf(this, CustomWarning.prototype);
}
}
export function fileExists(absolutePath: string) {
return fs.existsSync(absolutePath);
}

View File

@ -0,0 +1,239 @@
acacia_log
acacia_planks
acacia_wood
ancient_debris
andesite
basalt
bedrock
birch_log
birch_planks
birch_wood
blackstone
black_concrete
black_concrete_powder
black_glazed_terracotta
black_terracotta
black_wool
blue_concrete
blue_concrete_powder
blue_glazed_terracotta
blue_ice
blue_terracotta
blue_wool
bone_block
bookshelf
brain_coral_block
bricks
brown_concrete
brown_concrete_powder
brown_glazed_terracotta
brown_mushroom_block
brown_terracotta
brown_wool
bubble_coral_block
cartography_table
chiseled_nether_bricks
chiseled_polished_blackstone
chiseled_quartz_block
chiseled_red_sandstone
chiseled_sandstone
chiseled_stone_bricks
clay
coal_block
coal_ore
coarse_dirt
cobblestone
cracked_nether_bricks
cracked_polished_blackstone_bricks
cracked_stone_bricks
crafting_table
crimson_hyphae
crimson_planks
crimson_stem
crying_obsidian
cut_red_sandstone
cut_sandstone
cyan_concrete
cyan_concrete_powder
cyan_glazed_terracotta
cyan_terracotta
cyan_wool
dark_oak_log
dark_oak_planks
dark_oak_wood
dark_prismarine
dead_brain_coral_block
dead_bubble_coral_block
dead_fire_coral_block
dead_horn_coral_block
dead_tube_coral_block
diamond_block
diamond_ore
diorite
dirt
emerald_block
emerald_ore
end_stone
end_stone_bricks
fire_coral_block
fletching_table
frosted_ice_0
frosted_ice_1
frosted_ice_2
frosted_ice_3
gilded_blackstone
glowstone
gold_block
gold_ore
granite
gravel
gray_concrete
gray_concrete_powder
gray_glazed_terracotta
gray_terracotta
gray_wool
green_concrete
green_concrete_powder
green_glazed_terracotta
green_terracotta
green_wool
hay_block
honeycomb_block
horn_coral_block
ice
iron_block
iron_ore
jungle_log
jungle_planks
jungle_wood
lapis_block
lapis_ore
light_blue_concrete
light_blue_concrete_powder
light_blue_glazed_terracotta
light_blue_terracotta
light_blue_wool
light_gray_concrete
light_gray_concrete_powder
light_gray_glazed_terracotta
light_gray_terracotta
light_gray_wool
lime_concrete
lime_concrete_powder
lime_glazed_terracotta
lime_terracotta
lime_wool
lodestone
magenta_concrete
magenta_concrete_powder
magenta_glazed_terracotta
magenta_terracotta
magenta_wool
magma_block
melon
mossy_cobblestone
mossy_stone_bricks
mushroom_stem
netherite_block
netherrack
nether_bricks
nether_gold_ore
nether_quartz_ore
nether_wart_block
note_block
oak_log
oak_planks
oak_wood
obsidian
orange_concrete
orange_concrete_powder
orange_glazed_terracotta
orange_terracotta
orange_wool
packed_ice
pink_concrete
pink_concrete_powder
pink_glazed_terracotta
pink_terracotta
pink_wool
polished_andesite
polished_basalt
polished_blackstone
polished_blackstone_bricks
polished_diorite
polished_granite
prismarine
prismarine_bricks
purple_concrete
purple_concrete_powder
purple_glazed_terracotta
purple_terracotta
purple_wool
purpur_block
purpur_pillar
quartz_block
quartz_bricks
quartz_pillar
redstone_block
redstone_lamp
redstone_ore
red_concrete
red_concrete_powder
red_glazed_terracotta
red_mushroom_block
red_nether_bricks
red_sand
red_terracotta
red_wool
sand
sea_lantern
shroomlight
smithing_table
smooth_quartz
smooth_red_sandstone
smooth_sandstone
smooth_stone
smooth_stone_slab_double
snow_block
soul_sand
soul_soil
sponge
spruce_log
spruce_planks
spruce_wood
stone
stone_bricks
stripped_acacia_log
stripped_acacia_wood
stripped_birch_log
stripped_birch_wood
stripped_crimson_hyphae
stripped_crimson_stem
stripped_dark_oak_log
stripped_dark_oak_wood
stripped_jungle_log
stripped_jungle_wood
stripped_oak_log
stripped_oak_wood
stripped_spruce_log
stripped_spruce_wood
stripped_warped_hyphae
stripped_warped_stem
target
terracotta
tube_coral_block
warped_hyphae
warped_planks
warped_stem
warped_wart_block
wet_sponge
white_concrete
white_concrete_powder
white_glazed_terracotta
white_terracotta
white_wool
yellow_concrete
yellow_concrete_powder
yellow_glazed_terracotta
yellow_terracotta
yellow_wool

View File

@ -1,43 +1,50 @@
import fs from "fs";
import path from "path";
import images from "images";
import { RGB, UV } from "../src/util";
import { log, LogStyle } from "./logging";
import { PNG } from "pngjs";
import { isDirSetup, ASSERT, getAverageColour } from "./misc";
import chalk from "chalk";
import prompt from "prompt";
const AdmZip = require("adm-zip");
import { RGB, UV } from '../src/util';
import { log, LogStyle } from './logging';
import { isDirSetup, ASSERT, getAverageColour } from './misc';
prompt.start();
prompt.get([{
name: 'response',
description: 'Do you want to fetch textures and models? (Y/n)',
type: 'string',
required: true,
message: 'Respond with yes or no',
conform: (response) => {
return ["yes", "Y", "y", "no", "N", "n"].includes(response);
}
}], (err, res) => {
if (err) {
process.exit(1);
}
handleResponse(res);
});
import fs from 'fs';
import path from 'path';
import images from 'images';
import { PNG } from 'pngjs';
import chalk from 'chalk';
import prompt from 'prompt';
const AdmZip = require('adm-zip');
const handleResponse = (results: any) => {
const responseYes = ["yes", "Y", "y"].includes(results.response as any);
if (!responseYes) {
buildAtlas();
const schemaFetch: prompt.Schema = {
properties: {
response: {
pattern: /^[YyNn]$/,
description: 'Do you want to fetch textures and models? (Y/n)',
message: 'Response must be Y or N',
required: true,
},
name: {
pattern: /^[a-zA-Z\-]+$/,
description: 'What do you want to call this texture atlas?',
message: 'Must be only letters or dash',
required: true,
},
},
};
void async function main() {
const promptUser = await prompt.get(schemaFetch);
const responseYes = ['Y', 'y'].includes(promptUser.response as string);
if (responseYes) {
fetchModelsAndTextures();
}
const versionsDir = path.join(process.env.APPDATA!, "./.minecraft/versions");
buildAtlas(promptUser.name as string);
}();
function fetchModelsAndTextures() {
const versionsDir = path.join(process.env.APPDATA!, './.minecraft/versions');
if (!fs.existsSync(versionsDir)) {
log(LogStyle.Failure, "Could not fid .minecraft/versions\n");
log(LogStyle.Failure, 'Could not fid .minecraft/versions\n');
process.exit(1);
}
log(LogStyle.Success, ".minecraft/versions found successfully\n");
log(LogStyle.Success, '.minecraft/versions found successfully\n');
const versions = fs.readdirSync(versionsDir)
.filter((file) => fs.lstatSync(path.join(versionsDir, file)).isDirectory())
@ -45,75 +52,66 @@ const handleResponse = (results: any) => {
.sort((a, b) => b.birthtime.getTime() - a.birthtime.getTime());
for (let i = 0; i < versions.length; ++i) {
const versionName = versions[i].file
const versionName = versions[i].file;
log(LogStyle.Info, `Searching in ${versionName} for ${versionName}.jar`);
const versionDir = path.join(versionsDir, versionName);
const versionFiles = fs.readdirSync(versionDir);
if (!versionFiles.includes(versionName + ".jar")) {
if (!versionFiles.includes(versionName + '.jar')) {
continue;
}
log(LogStyle.Success, `Up ${versionName}.jar successfully\n`);
log(LogStyle.Success, `Found ${versionName}.jar successfully\n`);
const versionJarPath = path.join(versionDir, `${versionName}.jar`);
log(LogStyle.Info, `Upzipping ${versionName}.jar...`);
var zip = new AdmZip(versionJarPath);
const zip = new AdmZip(versionJarPath);
const zipEntries = zip.getEntries();
zipEntries.forEach((zipEntry: any) => {
if (zipEntry.entryName.startsWith("assets/minecraft/textures/block")) {
zip.extractEntryTo(zipEntry.entryName, path.join(__dirname, "./blocks"), false, true);
} else if (zipEntry.entryName.startsWith("assets/minecraft/models/block")) {
zip.extractEntryTo(zipEntry.entryName, path.join(__dirname, "./models"), false, true);
if (zipEntry.entryName.startsWith('assets/minecraft/textures/block')) {
zip.extractEntryTo(zipEntry.entryName, path.join(__dirname, './blocks'), false, true);
} else if (zipEntry.entryName.startsWith('assets/minecraft/models/block')) {
zip.extractEntryTo(zipEntry.entryName, path.join(__dirname, './models'), false, true);
}
});
log(LogStyle.Success, `Extracted textures and models successfully\n`);
buildAtlas();
return;
}
};
}
const buildAtlas = () => {
// Check /blocks and /models is setup correctly
log(LogStyle.None, "Checking Minecraft assets are provided...");
function buildAtlas(atlasName: string) {
// Check /blocks and /models is setup correctly
log(LogStyle.None, 'Checking Minecraft assets are provided...');
const texturesDirSetup = isDirSetup("./models", "assets/minecraft/textures/block");
ASSERT(texturesDirSetup, "/blocks is not setup correctly");
const texturesDirSetup = isDirSetup('./models', 'assets/minecraft/textures/block');
ASSERT(texturesDirSetup, '/blocks is not setup correctly');
const modelsDirSetup = isDirSetup("./models", "assets/minecraft/models/block");
ASSERT(modelsDirSetup, "/models is not setup correctly");
const modelsDirSetup = isDirSetup('./models', 'assets/minecraft/models/block');
ASSERT(modelsDirSetup, '/models is not setup correctly');
// Load the ignore list
log(LogStyle.None, "Loading ignore list...")
log(LogStyle.None, 'Loading ignore list...');
let ignoreList: Array<string> = [];
const ignoreListPath = path.join(__dirname, "./ignore-list.txt");
const defaultIgnoreListPath = path.join(__dirname, "./default-ignore-list.txt");
const ignoreListPath = path.join(__dirname, './ignore-list.txt');
if (fs.existsSync(ignoreListPath)) {
log(LogStyle.Success, "Found custom ignore list");
ignoreList = fs.readFileSync(ignoreListPath, "utf-8").replace(/\r/g, "").split("\n");
} else if (fs.existsSync(defaultIgnoreListPath)) {
log(LogStyle.Success, "Found default ignore list");
ignoreList = fs.readFileSync(defaultIgnoreListPath, "utf-8").replace(/\r/g, "").split("\n");
log(LogStyle.Success, 'Found custom ignore list');
ignoreList = fs.readFileSync(ignoreListPath, 'utf-8').replace(/\r/g, '').split('\n');
} else {
log(LogStyle.Warning, "No ignore list found, looked for ignore-list.txt and default-ignore-list.txt");
log(LogStyle.Warning, 'No ignore list found, looked for ignore-list.txt');
}
log(LogStyle.Info, `${ignoreList.length} blocks found in ignore list\n`);
//
log(LogStyle.None, "Loading block models...")
/* eslint-disable */
enum parentModel {
Cube = "minecraft:block/cube",
CubeAll = "minecraft:block/cube_all",
CubeColumn = "minecraft:block/cube_column",
CubeColumnHorizontal = "minecraft:block/cube_column_horizontal",
TemplateSingleFace = "minecraft:block/template_single_face",
TemplateGlazedTerracotta = "minecraft:block/template_glazed_terracotta",
Cube = 'minecraft:block/cube',
CubeAll = 'minecraft:block/cube_all',
CubeColumn = 'minecraft:block/cube_column',
CubeColumnHorizontal = 'minecraft:block/cube_column_horizontal',
TemplateSingleFace = 'minecraft:block/template_single_face',
TemplateGlazedTerracotta = 'minecraft:block/template_glazed_terracotta',
}
/* eslint-enable */
interface Model {
name: string,
colour?: RGB,
@ -121,23 +119,24 @@ const buildAtlas = () => {
[face: string]: Texture
}
}
interface Texture {
name: string,
texcoord?: UV,
colour?: RGB
}
const faces = ["north", "south", "up", "down", "east", "west"];
let allModels: Array<Model> = [];
let usedTextures: Set<string> = new Set();
fs.readdirSync(path.join(__dirname, "./models")).forEach(filename => {
if (path.extname(filename) !== ".json") {
log(LogStyle.None, 'Loading block models...');
const faces = ['north', 'south', 'up', 'down', 'east', 'west'];
const allModels: Array<Model> = [];
const usedTextures: Set<string> = new Set();
fs.readdirSync(path.join(__dirname, './models')).forEach((filename) => {
if (path.extname(filename) !== '.json') {
return;
};
const filePath = path.join(__dirname, "./models", filename);
const fileData = fs.readFileSync(filePath, "utf8");
const filePath = path.join(__dirname, './models', filename);
const fileData = fs.readFileSync(filePath, 'utf8');
const modelData = JSON.parse(fileData);
const parsedPath = path.parse(filePath);
const modelName = parsedPath.name;
@ -148,58 +147,58 @@ const buildAtlas = () => {
let faceData: { [face: string]: Texture } = {};
switch (modelData.parent) {
case parentModel.CubeAll:
faceData = {
up: { name: modelData.textures.all },
down: { name: modelData.textures.all },
north: { name: modelData.textures.all },
south: { name: modelData.textures.all },
east: { name: modelData.textures.all },
west: { name: modelData.textures.all }
}
break;
case parentModel.CubeColumn:
faceData = {
up: { name: modelData.textures.end },
down: { name: modelData.textures.end },
north: { name: modelData.textures.side },
south: { name: modelData.textures.side },
east: { name: modelData.textures.side },
west: { name: modelData.textures.side }
}
break;
case parentModel.Cube:
faceData = {
up: { name: modelData.textures.up },
down: { name: modelData.textures.down },
north: { name: modelData.textures.north },
south: { name: modelData.textures.south },
east: { name: modelData.textures.east },
west: { name: modelData.textures.west }
}
break;
case parentModel.TemplateSingleFace:
faceData = {
up: { name: modelData.textures.texture },
down: { name: modelData.textures.texture },
north: { name: modelData.textures.texture },
south: { name: modelData.textures.texture },
east: { name: modelData.textures.texture },
west: { name: modelData.textures.texture }
}
break;
case parentModel.TemplateGlazedTerracotta:
faceData = {
up: { name: modelData.textures.pattern },
down: { name: modelData.textures.pattern },
north: { name: modelData.textures.pattern },
south: { name: modelData.textures.pattern },
east: { name: modelData.textures.pattern },
west: { name: modelData.textures.pattern }
}
break;
default:
return;
case parentModel.CubeAll:
faceData = {
up: { name: modelData.textures.all },
down: { name: modelData.textures.all },
north: { name: modelData.textures.all },
south: { name: modelData.textures.all },
east: { name: modelData.textures.all },
west: { name: modelData.textures.all },
};
break;
case parentModel.CubeColumn:
faceData = {
up: { name: modelData.textures.end },
down: { name: modelData.textures.end },
north: { name: modelData.textures.side },
south: { name: modelData.textures.side },
east: { name: modelData.textures.side },
west: { name: modelData.textures.side },
};
break;
case parentModel.Cube:
faceData = {
up: { name: modelData.textures.up },
down: { name: modelData.textures.down },
north: { name: modelData.textures.north },
south: { name: modelData.textures.south },
east: { name: modelData.textures.east },
west: { name: modelData.textures.west },
};
break;
case parentModel.TemplateSingleFace:
faceData = {
up: { name: modelData.textures.texture },
down: { name: modelData.textures.texture },
north: { name: modelData.textures.texture },
south: { name: modelData.textures.texture },
east: { name: modelData.textures.texture },
west: { name: modelData.textures.texture },
};
break;
case parentModel.TemplateGlazedTerracotta:
faceData = {
up: { name: modelData.textures.pattern },
down: { name: modelData.textures.pattern },
north: { name: modelData.textures.pattern },
south: { name: modelData.textures.pattern },
east: { name: modelData.textures.pattern },
west: { name: modelData.textures.pattern },
};
break;
default:
return;
}
for (const face of faces) {
@ -208,7 +207,7 @@ const buildAtlas = () => {
allModels.push({
name: modelName,
faces: faceData
faces: faceData,
});
});
log(LogStyle.Success, `${allModels.length} blocks loaded\n`);
@ -216,15 +215,16 @@ const buildAtlas = () => {
const atlasSize = Math.ceil(Math.sqrt(usedTextures.size));
const atlasWidth = atlasSize * 16;
let offsetX = 0, offsetY = 0;
let offsetX = 0;
let offsetY = 0;
const outputImage = images(atlasWidth * 3, atlasWidth * 3);
let textureDetails: { [textureName: string]: { texcoord: UV, colour: RGB } } = {};
const textureDetails: { [textureName: string]: { texcoord: UV, colour: RGB } } = {};
log(LogStyle.None, "Building blocks.png...");
usedTextures.forEach(textureName => {
const shortName = textureName.split("/")[1]; // Eww
const absolutePath = path.join(__dirname, "./blocks", shortName + ".png");
log(LogStyle.None, `Building ${atlasName}.png...`);
usedTextures.forEach((textureName) => {
const shortName = textureName.split('/')[1]; // Eww
const absolutePath = path.join(__dirname, './blocks', shortName + '.png');
const fileData = fs.readFileSync(absolutePath);
const pngData = PNG.sync.read(fileData);
const image = images(absolutePath);
@ -238,10 +238,10 @@ const buildAtlas = () => {
textureDetails[textureName] = {
texcoord: {
u: 16 * (3 * offsetX + 1) / (atlasWidth * 3),
v: 16 * (3 * offsetY + 1) / (atlasWidth * 3)
v: 16 * (3 * offsetY + 1) / (atlasWidth * 3),
},
colour: getAverageColour(pngData)
}
colour: getAverageColour(pngData),
},
++offsetX;
if (offsetX >= atlasSize) {
@ -252,9 +252,9 @@ const buildAtlas = () => {
// Build up the output JSON
log(LogStyle.None, "Building blocks.json...\n");
log(LogStyle.None, `Building ${atlasName}.atlas...\n`);
for (const model of allModels) {
let blockColour = { r: 0, g: 0, b: 0 };
const blockColour = new RGB(0, 0, 0);
for (const face of faces) {
const faceTexture = textureDetails[model.faces[face].name];
const faceColour = faceTexture.colour;
@ -270,12 +270,19 @@ const buildAtlas = () => {
}
log(LogStyle.None, "Exporting...");
outputImage.save(path.join(__dirname, "../resources/blocks.png"));
log(LogStyle.Success, "blocks.png exported to /resources");
let outputJSON = { atlasSize: atlasSize, blocks: allModels };
fs.writeFileSync(path.join(__dirname, "../resources/blocks.json"), JSON.stringify(outputJSON, null, 4));
log(LogStyle.Success, "blocks.json exported to /resources\n");
log(LogStyle.None, 'Exporting...');
const atlasDir = path.join(__dirname, `../resources/atlases/${atlasName}.png`);
outputImage.save(atlasDir);
log(LogStyle.Success, `${atlasName}.png exported to /resources/atlases/`);
const outputJSON = {
atlasSize: atlasSize,
atlasTexture: atlasDir,
blocks: allModels,
};
fs.writeFileSync(path.join(__dirname, `../resources/atlases/${atlasName}.atlas`), JSON.stringify(outputJSON, null, 4));
log(LogStyle.Success, `${atlasName}.atlas exported to /resources/atlases/\n`);
console.log(chalk.cyanBright(chalk.inverse("DONE") + " Now run " + chalk.inverse(" npm start ") + " and the new blocks will be used"));
}
/* eslint-disable */
console.log(chalk.cyanBright(chalk.inverse('DONE') + ' Now run ' + chalk.inverse(' npm start ') + ' and the new blocks will be used'));
/* eslint-enable */
}

51
tools/build-palette.ts Normal file
View File

@ -0,0 +1,51 @@
import { log, LogStyle } from './logging';
import fs from 'fs';
import path from 'path';
import prompt from 'prompt';
void async function main() {
test();
}();
async function test() {
log(LogStyle.Info, 'Creating a new palette...');
const paletteBlocksDir = path.join(__dirname, './new-palette-blocks.txt');
if (!fs.existsSync(paletteBlocksDir)) {
log(LogStyle.Failure, 'Could not find /tools/new-palette-blocks.txt');
return;
}
log(LogStyle.Success, 'Found list of blocks to use in /tools/new-palette-blocks.txt');
let blocksToUse: string[] = fs.readFileSync(paletteBlocksDir, 'utf8').replace(/\r/g, '').split('\n');
blocksToUse = blocksToUse.filter((block) => {
return block.length !== 0;
});
if (blocksToUse.length === 0) {
log(LogStyle.Failure, 'No blocks listed for palette');
log(LogStyle.Info, 'List the blocks you want from /tools/all-supported-blocks.txt ');
return;
}
log(LogStyle.Info, `Found ${blocksToUse.length} blocks to use`);
const schema: prompt.Schema = {
properties: {
paletteName: {
pattern: /^[a-zA-Z\-]+$/,
description: 'What do you want to call this block palette? (e.g. my-block-palette)',
message: 'Must be only letters or dash',
required: true,
},
},
};
const promptUser = await prompt.get(schema);
const paletteJSON = {
blocks: blocksToUse,
};
fs.writeFileSync(path.join(__dirname, `../resources/palettes/${promptUser.paletteName}.palette`), JSON.stringify(paletteJSON, null, 4));
log(LogStyle.Success, `Successfully created ${promptUser.paletteName}.palette in /resources/palettes/`);
}

View File

@ -14,6 +14,7 @@ black_stained_glass.json
light_blue_stained_glass.json
green_stained_glass.json
light_gray_stained_glass.json
gray_stained_glass.json
magenta_stained_glass.json
pink_stained_glass.json
blue_stained_glass.json
@ -24,6 +25,5 @@ glass.json
spawner.json
brown_mushroom_block_inventory.json
red_mushroom_block_inventory.json
cartography_table.json
fletching_table.json
mushroom_stem_inventory.json
redstone_lamp_on.json

View File

@ -1,24 +1,28 @@
import chalk from "chalk";
import chalk from 'chalk';
export enum LogStyle {
None = "None",
Info = "Info",
Warning = "Warning",
Failure = "Failure",
Success = "Success"
None = 'None',
Info = 'Info',
Warning = 'Warning',
Failure = 'Failure',
Success = 'Success'
}
const LogStyleDetails: {[style: string]: {style: chalk.Chalk, prefix: string}} = {};
LogStyleDetails[LogStyle.Info] = {style: chalk.white, prefix: chalk.white.inverse("INFO")};
LogStyleDetails[LogStyle.Warning] = {style: chalk.yellow, prefix: chalk.yellow.inverse("WARN")};
LogStyleDetails[LogStyle.Failure] = {style: chalk.red, prefix: chalk.red.inverse("UHOH")};
LogStyleDetails[LogStyle.Success] = {style: chalk.green, prefix: chalk.green.inverse(" OK ")};
LogStyleDetails[LogStyle.Info] = {style: chalk.white, prefix: chalk.white.inverse('INFO')};
LogStyleDetails[LogStyle.Warning] = {style: chalk.yellow, prefix: chalk.yellow.inverse('WARN')};
LogStyleDetails[LogStyle.Failure] = {style: chalk.red, prefix: chalk.red.inverse('UHOH')};
LogStyleDetails[LogStyle.Success] = {style: chalk.green, prefix: chalk.green.inverse(' OK ')};
export function log(style: LogStyle, message: string) {
if (style === LogStyle.None) {
/* eslint-disable */
console.log(chalk.whiteBright(message));
/* eslint-enable */
} else {
const details: {style: chalk.Chalk, prefix: string} = LogStyleDetails[style];
console.log(details.prefix + " " + details.style(message));
/* eslint-disable */
console.log(details.prefix + ' ' + details.style(message));
/* eslint-enable */
}
}
}

View File

@ -1,12 +1,19 @@
import fs from "fs";
import path from "path";
import { PNG, PNGWithMetadata } from "pngjs";
import { log, LogStyle } from "./logging";
import { log, LogStyle } from './logging';
import { RGB } from '../src/util';
export const ASSERT = (condition: boolean, onFailMessage: string) => { if (!condition) { log(LogStyle.Failure, onFailMessage); process.exit(0); } }
import fs from 'fs';
import path from 'path';
import { PNG } from 'pngjs';
export const ASSERT = (condition: boolean, onFailMessage: string) => {
if (!condition) {
log(LogStyle.Failure, onFailMessage);
process.exit(0);
}
};
export function isDirSetup(relativePath: string, jarAssetDir: string) {
const dir = path.join(__dirname, relativePath)
const dir = path.join(__dirname, relativePath);
if (fs.existsSync(dir)) {
if (fs.readdirSync(dir).length > 0) {
return true;
@ -19,16 +26,18 @@ export function isDirSetup(relativePath: string, jarAssetDir: string) {
}
export function getAverageColour(image: PNG) {
let r = 0, g = 0, b = 0;
let r = 0;
let g = 0;
let b = 0;
for (let x = 0; x < image.width; ++x) {
for (let y = 0; y < image.height; ++y) {
const index = 4 * (image.width * y + x);
const rgba = image.data.slice(index, index + 4);
const rgba = image.data.slice(index, index + 4);
r += rgba[0];
g += rgba[1];
b += rgba[2];
}
}
const numPixels = image.width * image.height;
return { r: r / (255 * numPixels), g: g / (255 * numPixels), b: b / (255 * numPixels) };
}
return new RGB(r / (255 * numPixels), g / (255 * numPixels), b / (255 * numPixels));
}

View File

@ -0,0 +1,80 @@
black_concrete
black_concrete_powder
black_glazed_terracotta
black_terracotta
black_wool
blue_concrete
blue_concrete_powder
blue_glazed_terracotta
blue_terracotta
blue_wool
brown_concrete
brown_concrete_powder
brown_glazed_terracotta
brown_terracotta
brown_wool
cyan_concrete
cyan_concrete_powder
cyan_glazed_terracotta
cyan_terracotta
cyan_wool
gray_concrete
gray_concrete_powder
gray_glazed_terracotta
gray_terracotta
gray_wool
green_concrete
green_concrete_powder
green_glazed_terracotta
green_terracotta
green_wool
light_blue_concrete
light_blue_concrete_powder
light_blue_glazed_terracotta
light_blue_terracotta
light_blue_wool
light_gray_concrete
light_gray_concrete_powder
light_gray_glazed_terracotta
light_gray_terracotta
light_gray_wool
lime_concrete
lime_concrete_powder
lime_glazed_terracotta
lime_terracotta
lime_wool
magenta_concrete
magenta_concrete_powder
magenta_glazed_terracotta
magenta_terracotta
magenta_wool
orange_concrete
orange_concrete_powder
orange_glazed_terracotta
orange_terracotta
orange_wool
pink_concrete
pink_concrete_powder
pink_glazed_terracotta
pink_terracotta
pink_wool
purple_concrete
purple_concrete_powder
purple_glazed_terracotta
purple_terracotta
purple_wool
red_concrete
red_concrete_powder
red_glazed_terracotta
red_terracotta
red_wool
white_concrete
white_concrete_powder
white_glazed_terracotta
white_terracotta
white_wool
yellow_concrete
yellow_concrete_powder
yellow_glazed_terracotta
yellow_terracotta
yellow_wool