Added random dithering, updated headless types

This commit is contained in:
Lucas Dower 2022-07-09 01:42:42 +01:00
parent 173984ad8f
commit b7331740e0
6 changed files with 117 additions and 17 deletions

View File

@ -181,7 +181,7 @@ export class AppContext {
const blockMeshParams: BlockMeshParams = {
textureAtlas: uiElements.textureAtlas.getCachedValue(),
blockPalette: uiElements.blockPalette.getCachedValue(),
ditheringEnabled: uiElements.dithering.getCachedValue() === 'on',
blockAssigner: uiElements.dithering.getCachedValue(),
colourSpace: uiElements.colourSpace.getCachedValue() === 'rgb' ? ColourSpace.RGB : ColourSpace.LAB,
fallable: uiElements.fallable.getCachedValue() as FallableBehaviour,
};

View File

@ -2,6 +2,23 @@ import { BlockAtlas, BlockInfo } from './block_atlas';
import { ASSERT, ColourSpace, RGB } from './util';
import { Vector3 } from './vector';
export type TBlockAssigners = 'basic' | 'ordered-dithering' | 'random-dithering';
export class BlockAssignerFactory {
public static GetAssigner(blockAssigner: TBlockAssigners): IBlockAssigner {
switch (blockAssigner) {
case 'basic':
return new BasicBlockAssigner();
case 'ordered-dithering':
return new OrderedDitheringBlockAssigner();
case 'random-dithering':
return new RandomDitheringBlockAssigner();
default:
ASSERT(false, 'Unreachable');
}
}
}
interface IBlockAssigner {
assignBlock(voxelColour: RGB, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo;
}
@ -53,3 +70,50 @@ export class OrderedDitheringBlockAssigner implements IBlockAssigner {
return BlockAtlas.Get.getBlock(newVoxelColour, colourSpace, exclude);
}
}
export class RandomDitheringBlockAssigner implements IBlockAssigner {
/** 4x4x4 */
private static _size = 4;
private static _threshold = 256 / 8;
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(voxelColour: RGB, 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),
);
const newVoxelColour = new RGB(
((255 * voxelColour.r) + map * RandomDitheringBlockAssigner._threshold) / 255,
((255 * voxelColour.g) + map * RandomDitheringBlockAssigner._threshold) / 255,
((255 * voxelColour.b) + map * RandomDitheringBlockAssigner._threshold) / 255,
);
return BlockAtlas.Get.getBlock(newVoxelColour, colourSpace, exclude);
}
}

View File

@ -1,4 +1,4 @@
import { BasicBlockAssigner, OrderedDitheringBlockAssigner } from './block_assigner';
import { BasicBlockAssigner, BlockAssignerFactory, OrderedDitheringBlockAssigner, RandomDitheringBlockAssigner, TBlockAssigners } from './block_assigner';
import { Voxel, VoxelMesh } from './voxel_mesh';
import { BlockAtlas, BlockInfo } from './block_atlas';
import { ColourSpace, AppError, ASSERT, RESOURCES_DIR } from './util';
@ -20,7 +20,7 @@ export type FallableBehaviour = 'replace-falling' | 'replace-fallable' | 'place-
export interface BlockMeshParams {
textureAtlas: string,
blockPalette: string,
ditheringEnabled: boolean,
blockAssigner: TBlockAssigners,
colourSpace: ColourSpace,
fallable: FallableBehaviour,
}
@ -53,7 +53,7 @@ export class BlockMesh {
BlockAtlas.Get.loadPalette(blockMeshParams.blockPalette);
this._atlasUsed = blockMeshParams.textureAtlas;
const blockAssigner = blockMeshParams.ditheringEnabled ? new OrderedDitheringBlockAssigner() : new BasicBlockAssigner();
const blockAssigner = BlockAssignerFactory.GetAssigner(blockMeshParams.blockAssigner);
let countFalling = 0;
const voxels = this._voxelMesh.getVoxels();

View File

@ -14,6 +14,7 @@ import { MeshType, Renderer } from '../renderer';
import { ArcballCamera } from '../camera';
import { TVoxelisers } from '../voxelisers/voxelisers';
import { TExporters } from '../exporters/exporters';
import { TBlockAssigners } from '../block_assigner';
export interface Group {
label: string;
@ -88,9 +89,10 @@ export class UI {
elements: {
'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' },
'dithering': new ComboBoxElement<TBlockAssigners>('Dithering', [
{ id: 'ordered-dithering', displayText: 'Ordered' },
{ id: 'random-dithering', displayText: 'Random' },
{ id: 'basic', displayText: 'Off' },
]),
'colourSpace': new ComboBoxElement('Colour space', [
{ id: 'rgb', displayText: 'RGB (faster)' },

View File

@ -1,22 +1,27 @@
export const headlessConfig = {
import { THeadlessConfig } from './headless';
import { TVoxelisers } from '../src/voxelisers/voxelisers';
import { TextureFiltering } from '../src/texture';
import { ColourSpace } from '../src/util';
export const headlessConfig: THeadlessConfig = {
import: {
absoluteFilePathLoad: 'C:/Users/<Username>/Desktop/MyModel.obj', // Must be an absolute path to the file (can be anywhere)
},
voxelise: {
voxeliser: 'raybased', // 'raybased' / 'ncrb'
voxeliser: 'bvh-ray',
voxelMeshParams: {
desiredHeight: 80, // 5-320 inclusive
useMultisampleColouring: false,
textureFiltering: 'linear', // 'linear' / 'nearest'
textureFiltering: TextureFiltering.Linear,
},
},
palette: {
blockMeshParams: {
textureAtlas: 'vanilla', // Must be an atlas name that exists in /resources/atlases
blockPalette: 'all-snapshot', // Must be a palette name that exists in /resources/palettes
ditheringEnabled: true,
colourSpace: 'rgb', // 'rgb' / 'lab';
fallable: 'replace-falling', // 'replace-fallable' / 'place-string';
blockAssigner: 'ordered-dithering',
colourSpace: ColourSpace.RGB,
fallable: 'replace-falling',
},
},
export: {

View File

@ -12,17 +12,46 @@ import { TextureFiltering } from '../src/texture';
import { ColourSpace } from '../src/util';
import { log, LogStyle } from './logging';
import { headlessConfig } from './headless-config';
import { TBlockAssigners } from '../src/block_assigner';
import { TVoxelisers, VoxeliserFactory } from '../src/voxelisers/voxelisers';
export type THeadlessConfig = {
import: {
absoluteFilePathLoad: string,
},
voxelise: {
voxeliser: TVoxelisers,
voxelMeshParams: {
desiredHeight: number
useMultisampleColouring: boolean,
textureFiltering: TextureFiltering
},
},
palette: {
blockMeshParams: {
textureAtlas: string,
blockPalette: string,
blockAssigner: TBlockAssigners,
colourSpace: ColourSpace,
fallable: FallableBehaviour,
},
},
export: {
absoluteFilePathSave: 'C:/Users/<Username>/AppData//Roaming/.minecraft/schematics/MySchematic.schematic', // Must be an absolute path to the file (can be anywhere)
exporter: 'schematic', // 'schematic' / 'litematic',
},
}
void async function main() {
const mesh = _import({
absoluteFilePathLoad: headlessConfig.import.absoluteFilePathLoad,
});
const voxelMesh = _voxelise(mesh, {
voxeliser: headlessConfig.voxelise.voxeliser === 'raybased' ? new RayVoxeliser() : new NormalCorrectedRayVoxeliser(),
voxeliser: VoxeliserFactory.GetVoxeliser(headlessConfig.voxelise.voxeliser),
voxelMeshParams: {
desiredHeight: headlessConfig.voxelise.voxelMeshParams.desiredHeight,
useMultisampleColouring: headlessConfig.voxelise.voxelMeshParams.useMultisampleColouring,
textureFiltering: headlessConfig.voxelise.voxelMeshParams.textureFiltering === 'linear' ? TextureFiltering.Linear : TextureFiltering.Nearest,
textureFiltering: headlessConfig.voxelise.voxelMeshParams.textureFiltering,
enableAmbientOcclusion: false,
},
});
@ -30,8 +59,8 @@ void async function main() {
blockMeshParams: {
textureAtlas: headlessConfig.palette.blockMeshParams.textureAtlas,
blockPalette: headlessConfig.palette.blockMeshParams.blockPalette,
ditheringEnabled: headlessConfig.palette.blockMeshParams.ditheringEnabled,
colourSpace: headlessConfig.palette.blockMeshParams.colourSpace === 'rgb' ? ColourSpace.RGB : ColourSpace.LAB,
blockAssigner: headlessConfig.palette.blockMeshParams.blockAssigner as TBlockAssigners,
colourSpace: headlessConfig.palette.blockMeshParams.colourSpace,
fallable: headlessConfig.palette.blockMeshParams.fallable as FallableBehaviour,
},
});