Merge pull request #76 from LucasDower/0.6-colour-accuracy

0.6 colour accuracy
This commit is contained in:
Lucas Dower 2022-10-04 00:45:53 +01:00 committed by GitHub
commit ceee1b0a7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 243 additions and 140 deletions

View File

@ -10,7 +10,7 @@
"scripts": {
"lint": "eslint --fix src tools tests --ext .ts",
"build": "tsc",
"test": "jest --config jestconfig.json --runInBand",
"test": "jest --config jestconfig.json",
"start": "npm run build && electron ./dist/src/main.js --enable-logging --remote-debugging-port=9222",
"atlas": "node ./dist/tools/build-atlas.js",
"palette": "node ./dist/tools/build-palette.js",

View File

@ -1,27 +1,27 @@
{
"fallable_blocks": [
"anvil",
"lime_concrete_powder",
"orange_concrete_powder",
"black_concrete_powder",
"brown_concrete_powder",
"cyan_concrete_powder",
"light_gray_concrete_powder",
"purple_concrete_powder",
"magenta_concrete_powder",
"light_blue_concrete_powder",
"yellow_concrete_powder",
"white_concrete_powder",
"blue_concrete_powder",
"red_concrete_powder",
"gray_concrete_powder",
"pink_concrete_powder",
"green_concrete_powder",
"dragon_egg",
"gravel",
"pointed_dripstone",
"red_sand",
"sand",
"scaffolding"
"minecraft:anvil",
"minecraft:lime_concrete_powder",
"minecraft:orange_concrete_powder",
"minecraft:black_concrete_powder",
"minecraft:brown_concrete_powder",
"minecraft:cyan_concrete_powder",
"minecraft:light_gray_concrete_powder",
"minecraft:purple_concrete_powder",
"minecraft:magenta_concrete_powder",
"minecraft:light_blue_concrete_powder",
"minecraft:yellow_concrete_powder",
"minecraft:white_concrete_powder",
"minecraft:blue_concrete_powder",
"minecraft:red_concrete_powder",
"minecraft:gray_concrete_powder",
"minecraft:pink_concrete_powder",
"minecraft:green_concrete_powder",
"minecraft:dragon_egg",
"minecraft:gravel",
"minecraft:pointed_dripstone",
"minecraft:red_sand",
"minecraft:sand",
"minecraft:scaffolding"
]
}

View File

@ -331,6 +331,7 @@ export class AppContext {
blockAssigner: uiElements.dithering.getCachedValue(),
colourSpace: ColourSpace.RGB,
fallable: uiElements.fallable.getCachedValue() as FallableBehaviour,
resolution: Math.pow(2, uiElements.colourAccuracy.getCachedValue()),
},
};

View File

@ -1,9 +1,9 @@
import { AtlasPalette } from '../block_assigner';
import { AtlasPalette, TBlockCollection } from '../block_assigner';
import { BlockInfo } from '../block_atlas';
import { RGBA } from '../colour';
import { RGBA, RGBAUtil } from '../colour';
import { ColourSpace } from '../util';
import { Vector3 } from '../vector';
export interface IBlockAssigner {
assignBlock(atlasPalette: AtlasPalette, voxelColour: RGBA, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo;
assignBlock(atlasPalette: AtlasPalette, voxelColour: RGBA, voxelPosition: Vector3, resolution: RGBAUtil.TColourAccuracy, colourSpace: ColourSpace, blockCollection: TBlockCollection): BlockInfo;
}

View File

@ -1,12 +1,12 @@
import { AtlasPalette } from '../block_assigner';
import { AtlasPalette, TBlockCollection } from '../block_assigner';
import { BlockInfo } from '../block_atlas';
import { RGBA } from '../colour';
import { RGBA, RGBAUtil } from '../colour';
import { ColourSpace } from '../util';
import { Vector3 } from '../vector';
import { IBlockAssigner } from './base_assigner';
export class BasicBlockAssigner implements IBlockAssigner {
assignBlock(atlasPalette: AtlasPalette, voxelColour: RGBA, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo {
return atlasPalette.getBlock(voxelColour, exclude);
assignBlock(atlasPalette: AtlasPalette, voxelColour: RGBA, voxelPosition: Vector3, resolution: RGBAUtil.TColourAccuracy, colourSpace: ColourSpace, blockCollection: TBlockCollection): BlockInfo {
return atlasPalette.getBlock(voxelColour, blockCollection, resolution);
}
}

View File

@ -1,6 +1,6 @@
import { AtlasPalette } from '../block_assigner';
import { AtlasPalette, TBlockCollection } from '../block_assigner';
import { BlockInfo } from '../block_atlas';
import { RGBA } from '../colour';
import { RGBA, RGBAUtil } from '../colour';
import { ColourSpace } from '../util';
import { ASSERT } from '../util/error_util';
import { Vector3 } from '../vector';
@ -30,7 +30,7 @@ export class OrderedDitheringBlockAssigner implements IBlockAssigner {
return (OrderedDitheringBlockAssigner._mapMatrix[index] / (size * size * size)) - 0.5;
}
assignBlock(atlasPalette: AtlasPalette, voxelColour: RGBA, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo {
assignBlock(atlasPalette: AtlasPalette, voxelColour: RGBA, voxelPosition: Vector3, resolution: RGBAUtil.TColourAccuracy, colourSpace: ColourSpace, blockCollection: TBlockCollection): BlockInfo {
const size = OrderedDitheringBlockAssigner._size;
const map = this._getThresholdValue(
Math.abs(voxelPosition.x % size),
@ -45,6 +45,6 @@ export class OrderedDitheringBlockAssigner implements IBlockAssigner {
a: ((255 * voxelColour.a) + map * OrderedDitheringBlockAssigner._threshold) / 255,
};
return atlasPalette.getBlock(newVoxelColour, exclude);
return atlasPalette.getBlock(newVoxelColour, blockCollection, resolution);
}
}

View File

@ -1,55 +1,23 @@
import { AtlasPalette } from '../block_assigner';
import { AtlasPalette, TBlockCollection } from '../block_assigner';
import { BlockInfo } from '../block_atlas';
import { RGBA } from '../colour';
import { RGBA, RGBAUtil } from '../colour';
import { ColourSpace } from '../util';
import { ASSERT } from '../util/error_util';
import { Vector3 } from '../vector';
import { IBlockAssigner } from './base_assigner';
export class RandomDitheringBlockAssigner implements IBlockAssigner {
/** 4x4x4 */
private static _size = 4;
private static _threshold = 256 / 8;
private static _deviation = 32;
private _mapMatrix = [
0, 16, 2, 18, 48, 32, 50, 34,
6, 22, 4, 20, 54, 38, 52, 36,
24, 40, 26, 42, 8, 56, 10, 58,
30, 46, 28, 44, 14, 62, 12, 60,
3, 19, 5, 21, 51, 35, 53, 37,
1, 17, 7, 23, 49, 33, 55, 39,
27, 43, 29, 45, 11, 59, 13, 61,
25, 41, 31, 47, 9, 57, 15, 63,
];
private _getThresholdValue(x: number, y: number, z: number) {
const size = RandomDitheringBlockAssigner._size;
ASSERT(0 <= x && x < size && 0 <= y && y < size && 0 <= z && z < size);
const index = (x + (size * y) + (size * size * z));
ASSERT(0 <= index && index < size * size * size);
return (this._mapMatrix[index] / (size * size * size)) - 0.5;
}
assignBlock(atlasPalette: AtlasPalette, voxelColour: RGBA, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo {
this._mapMatrix = this._mapMatrix
.map((value) => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value);
const size = RandomDitheringBlockAssigner._size;
const map = this._getThresholdValue(
Math.abs(voxelPosition.x % size),
Math.abs(voxelPosition.y % size),
Math.abs(voxelPosition.z % size),
);
assignBlock(atlasPalette: AtlasPalette, voxelColour: RGBA, voxelPosition: Vector3, resolution: RGBAUtil.TColourAccuracy, colourSpace: ColourSpace, blockCollection: TBlockCollection): BlockInfo {
const map = Math.random() - 0.5;
const newVoxelColour: RGBA = {
r: ((255 * voxelColour.r) + map * RandomDitheringBlockAssigner._threshold) / 255,
g: ((255 * voxelColour.g) + map * RandomDitheringBlockAssigner._threshold) / 255,
b: ((255 * voxelColour.b) + map * RandomDitheringBlockAssigner._threshold) / 255,
a: ((255 * voxelColour.a) + map * RandomDitheringBlockAssigner._threshold) / 255,
r: ((255 * voxelColour.r) + map * RandomDitheringBlockAssigner._deviation) / 255,
g: ((255 * voxelColour.g) + map * RandomDitheringBlockAssigner._deviation) / 255,
b: ((255 * voxelColour.b) + map * RandomDitheringBlockAssigner._deviation) / 255,
a: ((255 * voxelColour.a) + map * RandomDitheringBlockAssigner._deviation) / 255,
};
return atlasPalette.getBlock(newVoxelColour, exclude);
return atlasPalette.getBlock(newVoxelColour, blockCollection, resolution);
}
}

View File

@ -1,8 +1,18 @@
import { Atlas } from './atlas';
import { RGBA } from './colour';
import { Atlas, TAtlasBlock } from './atlas';
import { RGBA, RGBAUtil } from './colour';
import { Palette } from './palette';
import { AppTypes } from './util';
import { AppTypes, TOptional } from './util';
import { ASSERT } from './util/error_util';
export type TBlockCollection = {
blocks: Map<AppTypes.TNamespacedBlockName, TAtlasBlock>,
cache: Map<number, TAtlasBlock>,
}
/**
* A new instance of AtlasPalette is created each time
* a new voxel mesh is voxelised.
*/
export class AtlasPalette {
private _atlas: Atlas;
private _palette: Palette;
@ -14,7 +24,71 @@ export class AtlasPalette {
this._palette.removeMissingAtlasBlocks(this._atlas);
}
public getBlock(colour: RGBA, exclude?: AppTypes.TNamespacedBlockName[]) {
return this._palette.getBlock(colour, this._atlas, exclude);
public createBlockCollection(blocksToExclude: AppTypes.TNamespacedBlockName[]): TBlockCollection {
const blocksNamesToUse = this._palette.getBlocks();
{
// Remove excluded blocks
for (const blockToExclude of blocksToExclude) {
const index = blocksNamesToUse.indexOf(blockToExclude);
if (index != -1) {
blocksNamesToUse.splice(index, 1);
}
}
}
const blocksToUse: TBlockCollection = {
blocks: new Map(),
cache: new Map(),
};
const atlasBlocks = this._atlas.getBlocks();
{
// Only add block data for blocks in the palette
atlasBlocks.forEach((atlasBlock, blockName) => {
if (blocksNamesToUse.includes(blockName)) {
blocksToUse.blocks.set(blockName, atlasBlock);
}
});
}
ASSERT(blocksToUse.blocks.size >= 1, 'Must have at least one block cached');
return blocksToUse;
}
/**
* Convert a colour into a Minecraft block.
* @param colour The colour that the returned block should match with.
* @param resolution The colour accuracy, a uint8 from 1 to 255, inclusive.
* @param blockToExclude A list of blocks that should not be used, this should be a subset of the palette blocks.
* @returns
*/
public getBlock(colour: RGBA, blockCollection: TBlockCollection, resolution: RGBAUtil.TColourAccuracy) {
const { colourHash, binnedColour } = RGBAUtil.bin(colour, resolution);
// If we've already calculated the block associated with this colour, return it.
const cachedBlock = blockCollection.cache.get(colourHash);
if (cachedBlock !== undefined) {
return cachedBlock;
}
// Find closest block in colour
let minDistance = Infinity;
let blockChoice: TOptional<TAtlasBlock>;
{
blockCollection.blocks.forEach((blockData) => {
const colourDistance = RGBAUtil.squaredDistance(binnedColour, blockData.colour);
if (colourDistance < minDistance) {
minDistance = colourDistance;
blockChoice = blockData;
}
});
}
if (blockChoice !== undefined) {
blockCollection.cache.set(colourHash, blockChoice);
return blockChoice;
}
ASSERT(false, 'Unreachable, always at least one possible block');
}
}

View File

@ -64,6 +64,8 @@ export class BlockMesh {
ASSERT(palette !== undefined, 'Could not load palette');
const atlasPalette = new AtlasPalette(atlas, palette);
const allBlockCollection = atlasPalette.createBlockCollection([]);
const nonFallableBlockCollection = atlasPalette.createBlockCollection(this._fallableBlocks);
const blockAssigner = BlockAssignerFactory.GetAssigner(blockMeshParams.blockAssigner);
@ -74,7 +76,15 @@ export class BlockMesh {
ProgressManager.Get.progress(taskHandle, voxelIndex / voxels.length);
const voxel = voxels[voxelIndex];
let block = blockAssigner.assignBlock(atlasPalette, voxel.colour, voxel.position, blockMeshParams.colourSpace);
let block = blockAssigner.assignBlock(
atlasPalette,
voxel.colour,
voxel.position,
blockMeshParams.resolution,
blockMeshParams.colourSpace,
allBlockCollection,
);
const isFallable = this._fallableBlocks.includes(block.name);
const isSupported = this._voxelMesh.isVoxelAt(Vector3.add(voxel.position, new Vector3(0, -1, 0)));
@ -87,8 +97,14 @@ export class BlockMesh {
shouldReplace ||= (blockMeshParams.fallable === 'replace-falling' && isFallable && !isSupported);
if (shouldReplace) {
const replacedBlock = blockAssigner.assignBlock(atlasPalette, voxel.colour, voxel.position, blockMeshParams.colourSpace, this._fallableBlocks);
// LOG(`Replacing ${block.name} with ${replacedBlock.name}`);
const replacedBlock = blockAssigner.assignBlock(
atlasPalette,
voxel.colour,
voxel.position,
blockMeshParams.resolution,
ColourSpace.RGB,
nonFallableBlockCollection,
);
block = replacedBlock;
}

View File

@ -56,6 +56,51 @@ export namespace RGBAUtil {
export function toArray(a: RGBA): number[] {
return [a.r, a.g, a.b, a.a];
}
export function bin(col: RGBA, resolution: TColourAccuracy) {
const r = Math.floor(col.r * resolution);
const g = Math.floor(col.g * resolution);
const b = Math.floor(col.b * resolution);
const a = Math.ceil(col.a * resolution);
let hash = r;
hash = (hash << 8) + g;
hash = (hash << 8) + b;
hash = (hash << 8) + a;
const binnedColour: RGBA = {
r: r / resolution,
g: g / resolution,
b: b / resolution,
a: a / resolution,
};
return {
colourHash: hash,
binnedColour: binnedColour,
};
}
/**
* Encodes a colour as a single number.
* Note this will bin colours together.
* @param col The colour to hash.
* @param resolution An uint8, the larger the more accurate the hash.
*/
export function hash(col: RGBA, resolution: TColourAccuracy): number {
const r = Math.floor(col.r * resolution);
const g = Math.floor(col.g * resolution);
const b = Math.floor(col.b * resolution);
const a = Math.floor(col.a * resolution);
let hash = r;
hash = (hash << 8) + g;
hash = (hash << 8) + b;
hash = (hash << 8) + a;
return hash;
}
export type TColourAccuracy = number;
}
export namespace RGBAColours {

View File

@ -80,18 +80,17 @@ export class ObjExporter extends IExporter {
indicesIndex += buffer.indices.data.length;
});
const writeStream = fs.createWriteStream(filepath);
writeStream.write('# Created with ObjToSchematic\n');
writeStream.write('# https://github.com/LucasDower/ObjToSchematic/\n\n');
const file = fs.openSync(filepath, 'w');
fs.writeSync(file, '# Created with ObjToSchematic\n');
fs.writeSync(file, '# https://github.com/LucasDower/ObjToSchematic/\n\n');
if (positionData && normalData && texcoordData && indexData && blockTexcoordData) {
const numTris = indexData.length / 3;
// Add vertex data
writeStream.write(`mtllib ${mtlRelativePath}\n`);
writeStream.write(`o Object\n`);
fs.writeSync(file, `mtllib ${mtlRelativePath}\n`);
fs.writeSync(file, `o Object\n`);
for (let i = 0; i < positionData.length / 3; ++i) {
writeStream.write(`v ${positionData[3 * i + 0]} ${positionData[3 * i + 1]} ${positionData[3 * i + 2]}\n`);
fs.writeSync(file, `v ${positionData[3 * i + 0]} ${positionData[3 * i + 1]} ${positionData[3 * i + 2]}\n`);
}
// Add texcoord data
const atlasSize = blockMesh.getAtlas().getAtlasSize();
@ -99,25 +98,25 @@ export class ObjExporter extends IExporter {
// vec2 tex = v_blockTexcoord + (v_texcoord / (u_atlasSize * 3.0));
const u = blockTexcoordData[2 * i + 0] + (texcoordData[2 * i + 0] / (atlasSize * 3.0));
const v = blockTexcoordData[2 * i + 1] + (texcoordData[2 * i + 1] / (atlasSize * 3.0));
writeStream.write(`vt ${u} ${1.0 - v}\n`);
fs.writeSync(file, `vt ${u} ${1.0 - v}\n`);
}
// Add normal data
for (let i = 0; i < normalData.length / 3; ++i) {
writeStream.write(`vn ${normalData[3 * i + 0]} ${normalData[3 * i + 1]} ${normalData[3 * i + 2]}\n`);
fs.writeSync(file, `vn ${normalData[3 * i + 0]} ${normalData[3 * i + 1]} ${normalData[3 * i + 2]}\n`);
}
writeStream.write(`usemtl Default\n`);
fs.writeSync(file, `usemtl Default\n`);
// Add face data
for (let i = 0; i < numTris * 3; i += 3) {
const a = indexData[i + 0] + 1;
const b = indexData[i + 1] + 1;
const c = indexData[i + 2] + 1;
writeStream.write(`f ${a}/${a}/${a} ${b}/${b}/${b} ${c}/${c}/${c}\n`);
fs.writeSync(file, `f ${a}/${a}/${a} ${b}/${b}/${b} ${c}/${c}/${c}\n`);
}
// Export to file
}
writeStream.end();
fs.closeSync(file);
}
private _exportMTL(filepathMTL: string, filepathTexture: string, blockMesh: BlockMesh) {

View File

@ -15,6 +15,14 @@ export namespace AppMath {
export function degreesToRadians(degrees: number) {
return degrees * (Math.PI / 180.0);
}
/**
* Converts a float in [0, 1] to an int in [0, 255]
* @param decimal A number in [0, 1]
*/
export function uint8(decimal: number) {
return Math.floor(decimal * 255);
}
}
export const argMax = (array: [number]) => {
@ -42,7 +50,7 @@ export const between = (value: number, min: number, max: number) => {
};
export const mapRange = (value: number, fromMin: number, fromMax: number, toMin: number, toMax: number) => {
return (value - fromMin)/(fromMax - fromMin) * (toMax - toMin) + toMin;
return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
};
export const wayThrough = (value: number, min: number, max: number) => {

View File

@ -2,10 +2,9 @@ import fs from 'fs';
import path from 'path';
import { Atlas } from './atlas';
import { RGBA, RGBAUtil } from './colour';
import { StatusHandler } from './status';
import { AppTypes, AppUtil, TOptional } from './util';
import { AppError, ASSERT } from './util/error_util';
import { ASSERT } from './util/error_util';
import { LOG_WARN } from './util/log_util';
import { AppPaths, PathUtil } from './util/path_util';
@ -119,42 +118,6 @@ export class Palette {
return this._blocks.length;
}
public getBlock(voxelColour: RGBA, atlas: Atlas, blocksToExclude?: AppTypes.TNamespacedBlockName[]) {
const blocksToUse = this.getBlocks();
const atlasBlocks = atlas.getBlocks();
// Remove excluded blocks
if (blocksToExclude !== undefined) {
for (const blockToExclude of blocksToExclude) {
const index = blocksToUse.indexOf(blockToExclude);
if (index != -1) {
blocksToUse.splice(index, 1);
}
}
}
// Find closest block in colour
let minDistance = Infinity;
let blockChoice: TOptional<AppTypes.TNamespacedBlockName>;
for (const blockName of blocksToUse) {
const blockData = atlasBlocks.get(blockName);
ASSERT(blockData);
const colourDistance = RGBAUtil.squaredDistance(voxelColour, blockData.colour);
if (colourDistance < minDistance) {
minDistance = colourDistance;
blockChoice = blockName;
}
}
if (blockChoice !== undefined) {
return atlasBlocks.get(blockChoice)!;
}
throw new AppError('Could not find a suitable block');
}
public getBlocks() {
return this._blocks;
}

View File

@ -13,7 +13,7 @@ export class LabelElement {
}
public generateHTML(): string {
const description = this._description ? `<br><div style="font-weight: 300; font-size: 85%; color: var(--text-disabled);">
const description = false && this._description ? `<br><div style="font-weight: 300; font-size: 85%; color: var(--text-disabled);">
${this._description}
</div>` : '';
return `

View File

@ -26,7 +26,7 @@ export class SliderElement extends LabelledElement<number> {
return `
<div style="display: flex; flex-direction: row;">
<div class="slider-value" id="${this._id + '-value'}">
${this._value}
${this._value?.toFixed(this._decimals)}
</div>
<div class="new-slider" id="${this._id}" style="flex-grow: 1;">
<div class="new-slider-bar" id="${this._id}-bar"style="width: ${norm * 100}%;">

View File

@ -77,7 +77,7 @@ export class UI {
displayText: 'Off (faster)',
},
]),
'multisampleColouring': new ComboBoxElement('Multisample colouring', [
'multisampleColouring': new ComboBoxElement('Multisampling', [
{
id: 'on',
displayText: 'On (recommended)',
@ -150,8 +150,9 @@ export class UI {
tooltip: 'Let the block fall',
},
]),
'colourAccuracy': new SliderElement('Colour accuracy', 1, 8, 1, 5, 0.1),
},
elementsOrder: ['textureAtlas', 'blockPalette', 'dithering', 'fallable'],
elementsOrder: ['textureAtlas', 'blockPalette', 'dithering', 'fallable', 'colourAccuracy'],
submitButton: new ButtonElement('Assign blocks', () => {
this._appContext.do(EAction.Assign);
}),

View File

@ -1,6 +1,7 @@
import { TBlockAssigners } from './assigners/assigners';
import { FallableBehaviour } from './block_mesh';
import { TBlockMeshBufferDescription, TMeshBufferDescription, TVoxelMeshBufferDescription } from './buffer';
import { RGBAUtil } from './colour';
import { TExporters } from './exporters/exporters';
import { StatusMessage } from './status';
import { TextureFiltering } from './texture';
@ -94,6 +95,7 @@ export namespace AssignParams {
blockAssigner: TBlockAssigners,
colourSpace: ColourSpace,
fallable: FallableBehaviour,
resolution: RGBAUtil.TColourAccuracy,
}
export type Output = {

View File

@ -22,6 +22,7 @@ const baseConfig: THeadlessConfig = {
blockAssigner: 'ordered-dithering',
colourSpace: ColourSpace.RGB,
fallable: 'replace-falling',
resolution: 32,
},
export: {
filepath: '', // Must be an absolute path to the file (can be anywhere)

View File

@ -22,6 +22,7 @@ const baseConfig: THeadlessConfig = {
blockAssigner: 'ordered-dithering',
colourSpace: ColourSpace.RGB,
fallable: 'replace-falling',
resolution: 32,
},
export: {
filepath: '', // Must be an absolute path to the file (can be anywhere)

View File

@ -22,6 +22,7 @@ const baseConfig: THeadlessConfig = {
blockAssigner: 'ordered-dithering',
colourSpace: ColourSpace.RGB,
fallable: 'replace-falling',
resolution: 32,
},
export: {
filepath: '', // Must be an absolute path to the file (can be anywhere)

View File

@ -22,6 +22,7 @@ const baseConfig: THeadlessConfig = {
blockAssigner: 'ordered-dithering',
colourSpace: ColourSpace.RGB,
fallable: 'replace-falling',
resolution: 32,
},
export: {
filepath: '', // Must be an absolute path to the file (can be anywhere)

View File

@ -0,0 +1,21 @@
import { StatusHandler, StatusID } from '../src/status';
import { AppPaths, PathUtil } from '../src/util/path_util';
import { WorkerClient } from '../src/worker_client';
import { headlessConfig } from '../tools/headless-config';
import { TEST_PREAMBLE } from './preamble';
test('Random-dither', () => {
TEST_PREAMBLE();
const config = headlessConfig;
config.import.filepath = PathUtil.join(AppPaths.Get.resources, './samples/skull.obj');
config.assign.blockAssigner = 'random-dithering';
const worker = WorkerClient.Get;
worker.import(headlessConfig.import);
worker.voxelise(headlessConfig.voxelise);
worker.assign(headlessConfig.assign);
expect(StatusHandler.Get.hasId(StatusID.SchematicUnsupportedBlocks)).toBe(false);
});

View File

@ -20,6 +20,7 @@ export const headlessConfig: THeadlessConfig = {
blockAssigner: 'ordered-dithering',
colourSpace: ColourSpace.RGB,
fallable: 'replace-falling',
resolution: 32,
},
export: {
filepath: '/Users/lucasdower/Documents/out.obj', // Must be an absolute path to the file (can be anywhere)