mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2025-02-23 13:49:07 +08:00
Added support for custom block palettes
This commit is contained in:
parent
ed2a61af1a
commit
1b9e07d899
@ -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<Vector3, number>;
|
||||
private _blocks: Array<BlockInfo>;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,7 +18,6 @@ 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 = [];
|
||||
}
|
||||
@ -27,9 +25,13 @@ export class BlockMesh {
|
||||
public assignBlocks(voxelMesh: VoxelMesh) {
|
||||
LOG('Assigning blocks');
|
||||
|
||||
const voxels = voxelMesh.getVoxels();
|
||||
const blockAssigner = this._ditheringEnabled ? new OrderedDitheringBlockAssigner() : new BasicBlockAssigner();
|
||||
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();
|
||||
for (let voxelIndex = 0; voxelIndex < voxels.length; ++voxelIndex) {
|
||||
const voxel = voxels[voxelIndex];
|
||||
const block = blockAssigner.assignBlock(voxel.colour, voxel.position);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user