Added light threshold option

This commit is contained in:
Lucas Dower 2022-11-05 22:00:31 +00:00
parent db8be030de
commit 53c7bbc2ae
No known key found for this signature in database
GPG Key ID: B3EE6B8499593605
7 changed files with 92 additions and 24 deletions

View File

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

View File

@ -21,6 +21,8 @@ interface Block {
blockInfo: BlockInfo;
}
type LightAction = { pos: Vector3, value: number };
export type FallableBehaviour = 'replace-falling' | 'replace-fallable' | 'place-string' | 'do-nothing';
export interface BlockMeshParams {
@ -46,7 +48,7 @@ export class BlockMesh {
public static createFromVoxelMesh(voxelMesh: VoxelMesh, blockMeshParams: AssignParams.Input) {
const blockMesh = new BlockMesh(voxelMesh);
blockMesh._assignBlocks(blockMeshParams);
blockMesh._calculateLighting();
blockMesh._calculateLighting(blockMeshParams.lightThreshold);
return blockMesh;
}
@ -179,7 +181,16 @@ export class BlockMesh {
}
}
private _calculateLighting() {
private _setEmissiveBlock(pos: Vector3) {
const emissiveBlockName = 'minecraft:glowstone';
const emissiveBlockData = this._atlas.getBlocks().get(emissiveBlockName);
ASSERT(emissiveBlockData !== undefined, 'No emissive block data found');
const blockIndex = this._voxelMesh.getVoxelIndex(pos);
ASSERT(blockIndex !== undefined, 'Setting emissive block of block that doesn\'t exist');
this._blocks[blockIndex].blockInfo = emissiveBlockData;
}
private _calculateLighting(lightThreshold: number) {
const blocksBounds = this._voxelMesh.getBounds();
const sizeVector = blocksBounds.getDimensions().add(1).add(2);
@ -207,8 +218,7 @@ export class BlockMesh {
// TODO: Cache stringify
const actions: { pos: Vector3, value: number }[] = []; // = [{ pos: blocksBounds.min, value: 15 }];
const actions: LightAction[] = []; // = [{ pos: blocksBounds.min, value: 15 }];
// Add initial light emitters to top of mesh to simulate sunlight
for (let x = blocksBounds.min.x - 1; x <= blocksBounds.max.x + 1; ++x) {
for (let z = blocksBounds.min.z - 1; z <= blocksBounds.max.z + 1; ++z) {
@ -218,10 +228,50 @@ export class BlockMesh {
});
}
}
this._handleActionsList(actions);
ASSERT(actions.length === 0, 'Actions still remaining');
if (lightThreshold > 0) {
const blocksNeedAttention: Vector3[] = [];
this.getBlocks().forEach((block) => {
if (this._internalGetLight(block.voxel.position)! <= lightThreshold) {
// TODO: Add additional requirement that the block must have a visible face
// before being added to the list otherwise all blocks that are not visible
// will be turned into an emissive block
blocksNeedAttention.push(block.voxel.position);
}
});
while (blocksNeedAttention.length > 0) {
const blockPos = blocksNeedAttention.pop()!;
const currentLightLevel = this._internalGetLight(blockPos)!;
const newLightLevel = 14;
if (currentLightLevel < lightThreshold) {
this._internalSetLight(blockPos, newLightLevel);
this._setEmissiveBlock(blockPos);
actions.push({ pos: new Vector3(0, 1, 0).add(blockPos), value: newLightLevel - 1 });
actions.push({ pos: new Vector3(1, 0, 0).add(blockPos), value: newLightLevel - 1 });
actions.push({ pos: new Vector3(0, 0, 1).add(blockPos), value: newLightLevel - 1 });
actions.push({ pos: new Vector3(-1, 0, 0).add(blockPos), value: newLightLevel - 1 });
actions.push({ pos: new Vector3(0, 0, -1).add(blockPos), value: newLightLevel - 1 });
actions.push({ pos: new Vector3(0, -1, 0).add(blockPos), value: newLightLevel - 1 });
// Assuming emissive block cannot be transparent!
}
this._handleActionsList(actions);
ASSERT(actions.length === 0, 'Actions still remaining');
}
}
}
private _handleActionsList(actions: LightAction[]) {
while (actions.length > 0) {
const action = actions.pop()!;
const newLightValue = action.value;
let newLightValue = action.value;
if (!this._posIsValid(action.pos)) {
continue;
@ -230,31 +280,45 @@ export class BlockMesh {
// Update lighting values only if the new value is lighter than the current brightness.
const blockHere = this.getBlockAt(action.pos);
if (blockHere !== undefined && this._emissiveBlocks.includes(blockHere.blockInfo.name)) {
if (this._internalGetLight(action.pos)! < 14) {
this._internalSetLight(action.pos, 14);
actions.push({ pos: new Vector3(0, 1, 0).add(action.pos), value: 14 });
actions.push({ pos: new Vector3(1, 0, 0).add(action.pos), value: 14 });
actions.push({ pos: new Vector3(0, 0, 1).add(action.pos), value: 14 });
actions.push({ pos: new Vector3(-1, 0, 0).add(action.pos), value: 14 });
actions.push({ pos: new Vector3(0, 0, -1).add(action.pos), value: 14 });
actions.push({ pos: new Vector3(0, -1, 0).add(action.pos), value: 14 });
}
} else if (newLightValue > currentLightValue) {
if (blockHere === undefined || this._transparentBlocks.includes(blockHere.blockInfo.name)) {
this._internalSetLight(action.pos, newLightValue);
const isBlockHere = blockHere !== undefined;
actions.push({ pos: new Vector3(0, 1, 0).add(action.pos), value: newLightValue - 1 }); // up
if (isBlockHere) {
const isEmissiveBlock = this._emissiveBlocks.includes(blockHere.blockInfo.name);
const isTransparentBlock = this._transparentBlocks.includes(this.getBlockAt(action.pos)!.blockInfo.name);
if (isEmissiveBlock) {
newLightValue = 14;
}
if (newLightValue > currentLightValue) {
this._internalSetLight(action.pos, newLightValue);
if (isEmissiveBlock || isTransparentBlock) {
actions.push({ pos: new Vector3(0, 1, 0).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(1, 0, 0).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(0, 0, 1).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(-1, 0, 0).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(0, 0, -1).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(0, -1, 0).add(action.pos), value: isTransparentBlock ? newLightValue : newLightValue - 1 });
}
}
} else {
if (newLightValue > currentLightValue) {
this._internalSetLight(action.pos, newLightValue);
actions.push({ pos: new Vector3(0, 1, 0).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(1, 0, 0).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(0, 0, 1).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(-1, 0, 0).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(0, 0, -1).add(action.pos), value: newLightValue - 1 });
actions.push({ pos: new Vector3(0, -1, 0).add(action.pos), value: newLightValue === 15 ? 15 : newLightValue - 1 }); // down
actions.push({ pos: new Vector3(0, -1, 0).add(action.pos), value: newLightValue === 15 ? 15 : newLightValue - 1 });
}
}
}
}
private _updateBlockLightLevel(pos: Vector3, value: number) {
}
public getBlockAt(pos: Vector3): TOptional<Block> {
const index = this._voxelMesh.getVoxelIndex(pos);
if (index !== undefined) {

View File

@ -24,7 +24,7 @@ export abstract class BaseUIElement<Type> {
}
protected getValue(): Type {
ASSERT(this._value);
ASSERT(this._value !== undefined);
return this._value;
}

View File

@ -90,7 +90,7 @@ export class SliderElement extends LabelledElement<number> {
if (!this._isEnabled) {
return;
}
ASSERT(this._value);
ASSERT(this._value !== undefined);
this._value -= (e.deltaY / 150) * this._step;
this._value = clamp(this._value, this._min, this._max);
@ -109,7 +109,7 @@ export class SliderElement extends LabelledElement<number> {
const box = element.getBoundingClientRect();
const left = box.x;
const right = box.x + box.width;
this._value = mapRange(e.clientX, left, right, this._min, this._max);
this._value = clamp(this._value, this._min, this._max);

View File

@ -151,8 +151,9 @@ export class UI {
},
]),
'colourAccuracy': new SliderElement('Colour accuracy', 1, 8, 1, 5, 0.1),
'lightThreshold': new SliderElement('Light threshold', 0, 14, 0, 0, 1),
},
elementsOrder: ['textureAtlas', 'blockPalette', 'dithering', 'fallable', 'colourAccuracy'],
elementsOrder: ['textureAtlas', 'blockPalette', 'dithering', 'fallable', 'colourAccuracy', 'lightThreshold'],
submitButton: new ButtonElement('Assign blocks', () => {
this._appContext.do(EAction.Assign);
}),

View File

@ -96,6 +96,7 @@ export namespace AssignParams {
colourSpace: ColourSpace,
fallable: FallableBehaviour,
resolution: RGBAUtil.TColourAccuracy,
lightThreshold: number,
}
export type Output = {

View File

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