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 { HashMap } from './hash_map';
|
||||||
import { UV, RGB, ASSERT } from './util';
|
import { UV, RGB, ASSERT, fileExists } from './util';
|
||||||
import { Vector3 } from './vector';
|
import { Vector3 } from './vector';
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
@ -33,12 +33,19 @@ export enum Block {
|
|||||||
Dirt = 3.0,
|
Dirt = 3.0,
|
||||||
Cobblestone = 4.0
|
Cobblestone = 4.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BlockPalette {
|
||||||
|
blocks: string[];
|
||||||
|
}
|
||||||
|
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
export class BlockAtlas {
|
export class BlockAtlas {
|
||||||
private _cachedBlocks: HashMap<Vector3, number>;
|
private _cachedBlocks: HashMap<Vector3, number>;
|
||||||
private _blocks: Array<BlockInfo>;
|
private _blocks: Array<BlockInfo>;
|
||||||
|
private _palette: string[];
|
||||||
private _atlasSize: number;
|
private _atlasSize: number;
|
||||||
private _atlasLoaded: boolean;
|
private _atlasLoaded: boolean;
|
||||||
|
private _paletteLoaded: boolean;
|
||||||
|
|
||||||
private static _instance: BlockAtlas;
|
private static _instance: BlockAtlas;
|
||||||
public static get Get() {
|
public static get Get() {
|
||||||
@ -50,6 +57,8 @@ export class BlockAtlas {
|
|||||||
this._blocks = [];
|
this._blocks = [];
|
||||||
this._atlasSize = 0;
|
this._atlasSize = 0;
|
||||||
this._atlasLoaded = false;
|
this._atlasLoaded = false;
|
||||||
|
this._palette = [];
|
||||||
|
this._paletteLoaded = false;
|
||||||
|
|
||||||
this.loadAtlas(path.join(__dirname, '../resources/atlases/vanilla.atlas'));
|
this.loadAtlas(path.join(__dirname, '../resources/atlases/vanilla.atlas'));
|
||||||
}
|
}
|
||||||
@ -76,8 +85,21 @@ export class BlockAtlas {
|
|||||||
this._atlasLoaded = true;
|
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 {
|
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());
|
const cachedBlockIndex = this._cachedBlocks.get(voxelColour.toVector3());
|
||||||
if (cachedBlockIndex) {
|
if (cachedBlockIndex) {
|
||||||
@ -89,12 +111,14 @@ export class BlockAtlas {
|
|||||||
|
|
||||||
for (let i = 0; i < this._blocks.length; ++i) {
|
for (let i = 0; i < this._blocks.length; ++i) {
|
||||||
const block: BlockInfo = this._blocks[i];
|
const block: BlockInfo = this._blocks[i];
|
||||||
const blockAvgColour = block.colour as RGB;
|
if (this._palette.includes(block.name)) {
|
||||||
const distance = RGB.distance(blockAvgColour, voxelColour);
|
const blockAvgColour = block.colour as RGB;
|
||||||
|
const distance = RGB.distance(blockAvgColour, voxelColour);
|
||||||
|
|
||||||
if (distance < minDistance) {
|
if (distance < minDistance) {
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
blockChoiceIndex = i;
|
blockChoiceIndex = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BasicBlockAssigner, OrderedDitheringBlockAssigner } from './block_assigner';
|
import { BasicBlockAssigner, OrderedDitheringBlockAssigner } from './block_assigner';
|
||||||
import { Voxel, VoxelMesh } from './voxel_mesh';
|
import { Voxel, VoxelMesh } from './voxel_mesh';
|
||||||
import { BlockInfo } from './block_atlas';
|
import { BlockAtlas, BlockInfo } from './block_atlas';
|
||||||
import { CustomError, LOG } from './util';
|
import { CustomError, LOG } from './util';
|
||||||
import { Renderer } from './renderer';
|
import { Renderer } from './renderer';
|
||||||
import { UI } from './ui/layout';
|
import { UI } from './ui/layout';
|
||||||
@ -11,7 +11,6 @@ interface Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class BlockMesh {
|
export class BlockMesh {
|
||||||
private _ditheringEnabled: boolean;
|
|
||||||
private _blockPalette: string[];
|
private _blockPalette: string[];
|
||||||
private _blocks: Block[];
|
private _blocks: Block[];
|
||||||
private _voxelMesh?: VoxelMesh;
|
private _voxelMesh?: VoxelMesh;
|
||||||
@ -19,17 +18,20 @@ export class BlockMesh {
|
|||||||
public constructor() {
|
public constructor() {
|
||||||
LOG('New block mesh');
|
LOG('New block mesh');
|
||||||
|
|
||||||
this._ditheringEnabled = UI.Get.layout.palette.elements.dithering.getCachedValue() as string === 'on';
|
|
||||||
this._blockPalette = [];
|
this._blockPalette = [];
|
||||||
this._blocks = [];
|
this._blocks = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public assignBlocks(voxelMesh: VoxelMesh) {
|
public assignBlocks(voxelMesh: VoxelMesh) {
|
||||||
LOG('Assigning blocks');
|
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 voxels = voxelMesh.getVoxels();
|
||||||
const blockAssigner = this._ditheringEnabled ? new OrderedDitheringBlockAssigner() : new BasicBlockAssigner();
|
|
||||||
|
|
||||||
for (let voxelIndex = 0; voxelIndex < voxels.length; ++voxelIndex) {
|
for (let voxelIndex = 0; voxelIndex < voxels.length; ++voxelIndex) {
|
||||||
const voxel = voxels[voxelIndex];
|
const voxel = voxels[voxelIndex];
|
||||||
const block = blockAssigner.assignBlock(voxel.colour, voxel.position);
|
const block = blockAssigner.assignBlock(voxel.colour, voxel.position);
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import { BaseUIElement } from './elements/base';
|
import { BaseUIElement } from './elements/base';
|
||||||
import { SliderElement } from './elements/slider';
|
import { SliderElement } from './elements/slider';
|
||||||
import { ComboBoxElement } from './elements/combobox';
|
import { ComboBoxElement, ComboBoxItem } from './elements/combobox';
|
||||||
import { FileInputElement } from './elements/file_input';
|
import { FileInputElement } from './elements/file_input';
|
||||||
import { ButtonElement } from './elements/button';
|
import { ButtonElement } from './elements/button';
|
||||||
import { OutputElement } from './elements/output';
|
import { OutputElement } from './elements/output';
|
||||||
import { Action, AppContext } from '../app_context';
|
import { Action, AppContext } from '../app_context';
|
||||||
import { LOG } from '../util';
|
import { LOG } from '../util';
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export interface Group {
|
export interface Group {
|
||||||
label: string;
|
label: string;
|
||||||
elements: { [key: string]: BaseUIElement };
|
elements: { [key: string]: BaseUIElement };
|
||||||
@ -66,18 +69,14 @@ export class UI {
|
|||||||
'palette': {
|
'palette': {
|
||||||
label: 'Palette',
|
label: 'Palette',
|
||||||
elements: {
|
elements: {
|
||||||
'blockPalette': new ComboBoxElement('Block palette', [
|
'textureAtlas': new ComboBoxElement('Texture atlas', this._getTextureAtlases()),
|
||||||
{ id: 'default', displayText: 'Default' },
|
'blockPalette': new ComboBoxElement('Block palette', this._getBlockPalettes()),
|
||||||
]),
|
|
||||||
'choiceMethod': new ComboBoxElement('Choice method', [
|
|
||||||
{ id: 'euclidian', displayText: 'Euclidian distance' },
|
|
||||||
]),
|
|
||||||
'dithering': new ComboBoxElement('Dithering', [
|
'dithering': new ComboBoxElement('Dithering', [
|
||||||
{ id: 'on', displayText: 'On (recommended)' },
|
{ id: 'on', displayText: 'On (recommended)' },
|
||||||
{ id: 'off', displayText: 'Off' },
|
{ id: 'off', displayText: 'Off' },
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
elementsOrder: ['blockPalette', 'choiceMethod', 'dithering'],
|
elementsOrder: ['textureAtlas', 'blockPalette', 'dithering'],
|
||||||
submitButton: new ButtonElement('Assign blocks', () => {
|
submitButton: new ButtonElement('Assign blocks', () => {
|
||||||
AppContext.Get.do(Action.Palette);
|
AppContext.Get.do(Action.Palette);
|
||||||
}),
|
}),
|
||||||
@ -228,4 +227,24 @@ export class UI {
|
|||||||
const key = this.uiOrder[action];
|
const key = this.uiOrder[action];
|
||||||
return this._uiDull[key];
|
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