diff --git a/src/block_atlas.ts b/src/block_atlas.ts index ac18f40..0df5c78 100644 --- a/src/block_atlas.ts +++ b/src/block_atlas.ts @@ -1,5 +1,5 @@ import { HashMap } from './hash_map'; -import { UV, RGB, ASSERT } from './util'; +import { UV, RGB, ASSERT, fileExists } from './util'; import { Vector3 } from './vector'; import fs from 'fs'; @@ -33,12 +33,19 @@ export enum Block { Dirt = 3.0, Cobblestone = 4.0 } + +interface BlockPalette { + blocks: string[]; +} + /* eslint-enable */ export class BlockAtlas { private _cachedBlocks: HashMap; private _blocks: Array; + private _palette: string[]; private _atlasSize: number; private _atlasLoaded: boolean; + private _paletteLoaded: boolean; private static _instance: BlockAtlas; public static get Get() { @@ -50,6 +57,8 @@ export class BlockAtlas { this._blocks = []; this._atlasSize = 0; this._atlasLoaded = false; + this._palette = []; + this._paletteLoaded = false; this.loadAtlas(path.join(__dirname, '../resources/atlases/vanilla.atlas')); } @@ -76,8 +85,21 @@ export class BlockAtlas { this._atlasLoaded = true; } + public loadPalette(paletteID: string) { + this._cachedBlocks = new HashMap(1024); + + const paletteDir = path.join(__dirname, '../resources/palettes', paletteID + '.palette'); + ASSERT(fileExists(paletteDir), `Palette to load does not exist ${paletteDir}`); + + const palette: BlockPalette = JSON.parse(fs.readFileSync(paletteDir, 'utf8')); + this._palette = palette.blocks; + + this._paletteLoaded = true; + } + public getBlock(voxelColour: RGB): BlockInfo { - ASSERT(this._atlasLoaded); + ASSERT(this._atlasLoaded, 'No atlas has been loaded'); + ASSERT(this._paletteLoaded, 'No palette has been loaded'); const cachedBlockIndex = this._cachedBlocks.get(voxelColour.toVector3()); if (cachedBlockIndex) { @@ -89,12 +111,14 @@ export class BlockAtlas { for (let i = 0; i < this._blocks.length; ++i) { const block: BlockInfo = this._blocks[i]; - const blockAvgColour = block.colour as RGB; - const distance = RGB.distance(blockAvgColour, voxelColour); + if (this._palette.includes(block.name)) { + const blockAvgColour = block.colour as RGB; + const distance = RGB.distance(blockAvgColour, voxelColour); - if (distance < minDistance) { - minDistance = distance; - blockChoiceIndex = i; + if (distance < minDistance) { + minDistance = distance; + blockChoiceIndex = i; + } } } diff --git a/src/block_mesh.ts b/src/block_mesh.ts index 1a80101..496296f 100644 --- a/src/block_mesh.ts +++ b/src/block_mesh.ts @@ -1,6 +1,6 @@ import { BasicBlockAssigner, OrderedDitheringBlockAssigner } from './block_assigner'; import { Voxel, VoxelMesh } from './voxel_mesh'; -import { BlockInfo } from './block_atlas'; +import { BlockAtlas, BlockInfo } from './block_atlas'; import { CustomError, LOG } from './util'; import { Renderer } from './renderer'; import { UI } from './ui/layout'; @@ -11,7 +11,6 @@ interface Block { } export class BlockMesh { - private _ditheringEnabled: boolean; private _blockPalette: string[]; private _blocks: Block[]; private _voxelMesh?: VoxelMesh; @@ -19,17 +18,20 @@ export class BlockMesh { public constructor() { LOG('New block mesh'); - this._ditheringEnabled = UI.Get.layout.palette.elements.dithering.getCachedValue() as string === 'on'; this._blockPalette = []; this._blocks = []; } - + public assignBlocks(voxelMesh: VoxelMesh) { LOG('Assigning blocks'); + + const blockPalette = UI.Get.layout.palette.elements.blockPalette.getCachedValue() as string; + BlockAtlas.Get.loadPalette(blockPalette); + const ditheringEnabled = UI.Get.layout.palette.elements.dithering.getCachedValue() as string === 'on'; + const blockAssigner = ditheringEnabled ? new OrderedDitheringBlockAssigner() : new BasicBlockAssigner(); + const voxels = voxelMesh.getVoxels(); - const blockAssigner = this._ditheringEnabled ? new OrderedDitheringBlockAssigner() : new BasicBlockAssigner(); - for (let voxelIndex = 0; voxelIndex < voxels.length; ++voxelIndex) { const voxel = voxels[voxelIndex]; const block = blockAssigner.assignBlock(voxel.colour, voxel.position); diff --git a/src/ui/layout.ts b/src/ui/layout.ts index ab0c71a..61215ee 100644 --- a/src/ui/layout.ts +++ b/src/ui/layout.ts @@ -1,12 +1,15 @@ import { BaseUIElement } from './elements/base'; import { SliderElement } from './elements/slider'; -import { ComboBoxElement } from './elements/combobox'; +import { ComboBoxElement, ComboBoxItem } from './elements/combobox'; import { FileInputElement } from './elements/file_input'; import { ButtonElement } from './elements/button'; import { OutputElement } from './elements/output'; import { Action, AppContext } from '../app_context'; import { LOG } from '../util'; +import fs from 'fs'; +import path from 'path'; + export interface Group { label: string; elements: { [key: string]: BaseUIElement }; @@ -66,18 +69,14 @@ export class UI { 'palette': { label: 'Palette', elements: { - 'blockPalette': new ComboBoxElement('Block palette', [ - { id: 'default', displayText: 'Default' }, - ]), - 'choiceMethod': new ComboBoxElement('Choice method', [ - { id: 'euclidian', displayText: 'Euclidian distance' }, - ]), + 'textureAtlas': new ComboBoxElement('Texture atlas', this._getTextureAtlases()), + 'blockPalette': new ComboBoxElement('Block palette', this._getBlockPalettes()), 'dithering': new ComboBoxElement('Dithering', [ { id: 'on', displayText: 'On (recommended)' }, { id: 'off', displayText: 'Off' }, ]), }, - elementsOrder: ['blockPalette', 'choiceMethod', 'dithering'], + elementsOrder: ['textureAtlas', 'blockPalette', 'dithering'], submitButton: new ButtonElement('Assign blocks', () => { AppContext.Get.do(Action.Palette); }), @@ -228,4 +227,24 @@ export class UI { const key = this.uiOrder[action]; return this._uiDull[key]; } + + private _getTextureAtlases(): ComboBoxItem[] { + return [{ id: 'vanilla', displayText: 'Vanilla' }]; + } + + private _getBlockPalettes(): ComboBoxItem[] { + const blockPalettes: ComboBoxItem[] = []; + const palettesDir = path.join(__dirname, '../../resources/palettes'); + + fs.readdirSync(palettesDir).forEach((file) => { + if (file.endsWith('.palette')) { + const paletteID = file.split('.')[0]; + let paletteName = paletteID.replace('-', ' ').toLowerCase(); + paletteName = paletteName.charAt(0).toUpperCase() + paletteName.slice(1); + blockPalettes.push({ id: paletteID, displayText: paletteName }); + } + }); + + return blockPalettes; + } }