Initial lighting commit, very unoptimised

This commit is contained in:
Lucas Dower 2022-10-23 00:48:45 +01:00
parent 1a0615260c
commit f5932e61b1
9 changed files with 148 additions and 8 deletions

View File

@ -1,7 +1,7 @@
{
"AMBIENT_OCCLUSION_OVERRIDE_CORNER": true,
"LOG_TO_FILE": true,
"USE_WORKER_THREAD": true,
"USE_WORKER_THREAD": false,
"MULTISAMPLE_COUNT": 16,
"OLD_SPACE_SIZE_MB": 8192,
"ALPHA_BIAS": 1.0,

View File

@ -2,11 +2,13 @@ precision mediump float;
uniform sampler2D u_texture;
uniform float u_atlasSize;
uniform bool u_nightVision;
varying float v_lighting;
varying vec4 v_occlusion;
varying vec2 v_texcoord;
varying vec2 v_blockTexcoord;
varying float v_blockLighting;
float dither8x8(vec2 position, float alpha) {
int x = int(mod(position.x, 8.0));
@ -103,5 +105,5 @@ void main() {
discard;
}
gl_FragColor = vec4(diffuse.rgb * v_lighting * g, 1.0);
gl_FragColor = vec4(diffuse.rgb * v_lighting * g * (u_nightVision ? 1.0 : v_blockLighting), 1.0);
}

View File

@ -4,17 +4,20 @@ uniform mat4 u_worldViewProjection;
uniform sampler2D u_texture;
uniform float u_voxelSize;
uniform vec3 u_gridOffset;
uniform bool u_nightVision;
attribute vec3 position;
attribute vec3 normal;
attribute vec4 occlusion;
attribute vec2 texcoord;
attribute vec2 blockTexcoord;
attribute float lighting;
varying float v_lighting;
varying vec4 v_occlusion;
varying vec2 v_texcoord;
varying vec2 v_blockTexcoord;
varying float v_blockLighting;
vec3 light = vec3(0.78, 0.98, 0.59);
@ -23,6 +26,7 @@ void main() {
v_occlusion = occlusion;
v_blockTexcoord = blockTexcoord;
v_lighting = dot(light, abs(normal));
v_blockLighting = lighting;
gl_Position = u_worldViewProjection * vec4((position.xyz + u_gridOffset) * u_voxelSize, 1.0);
}

5
res/static/bulb.svg Normal file
View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-sun" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#00abfb" fill="none" stroke-linecap="round" stroke-linejoin="round" id="bulb-svg">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="4" />
<path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" />
</svg>

After

Width:  |  Height:  |  Size: 435 B

View File

@ -37,10 +37,12 @@ export class BlockMesh {
private _voxelMesh: VoxelMesh;
private _fallableBlocks: string[];
private _atlas: Atlas;
private _lighting: Map<string, number>;
public static createFromVoxelMesh(voxelMesh: VoxelMesh, blockMeshParams: AssignParams.Input) {
const blockMesh = new BlockMesh(voxelMesh);
blockMesh._assignBlocks(blockMeshParams);
blockMesh._calculateLighting();
return blockMesh;
}
@ -49,6 +51,7 @@ export class BlockMesh {
this._blocks = [];
this._voxelMesh = voxelMesh;
this._atlas = Atlas.getVanillaAtlas()!;
this._lighting = new Map<string, number>();
//this._recreateBuffer = true;
const fallableBlocksString = fs.readFileSync(PathUtil.join(AppPaths.Get.resources, 'fallable_blocks.json'), 'utf-8');
@ -76,7 +79,7 @@ export class BlockMesh {
ProgressManager.Get.progress(taskHandle, voxelIndex / voxels.length);
const voxel = voxels[voxelIndex];
let block = blockAssigner.assignBlock(
atlasPalette,
voxel.colour,
@ -123,6 +126,83 @@ export class BlockMesh {
}
}
// Face order: ['north', 'south', 'up', 'down', 'east', 'west']
public getBlockLighting(position: Vector3) {
/*
return {
up: this._lighting.get(new Vector3(0, 1, 0).add(position).stringify()) ?? 15,
north: this._lighting.get(new Vector3(1, 0, 0).add(position).stringify()) ?? 15,
east: this._lighting.get(new Vector3(0, 0, 1).add(position).stringify()) ?? 15,
south: this._lighting.get(new Vector3(-1, 0, 0).add(position).stringify()) ?? 15,
west: this._lighting.get(new Vector3(0, 0, -1).add(position).stringify()) ?? 15,
down: this._lighting.get(new Vector3(0, -1, 0).add(position).stringify()) ?? 15,
};
*/
return [
this._lighting.get(new Vector3(1, 0, 0).add(position).stringify()) ?? 15,
this._lighting.get(new Vector3(-1, 0, 0).add(position).stringify()) ?? 15,
this._lighting.get(new Vector3(0, 1, 0).add(position).stringify()) ?? 15,
this._lighting.get(new Vector3(0, -1, 0).add(position).stringify()) ?? 15,
this._lighting.get(new Vector3(0, 0, 1).add(position).stringify()) ?? 15,
this._lighting.get(new Vector3(0, 0, -1).add(position).stringify()) ?? 15,
];
}
private _calculateLighting() {
this._lighting.clear();
const blocksBounds = this._voxelMesh.getBounds();
// Todo replace with Buffer as each value should only 4-bits instead of 64-bits.
const tmp = new Vector3(0, 0, 0);
for (let x = blocksBounds.min.x - 1; x <= blocksBounds.max.x + 1; ++x) {
tmp.x = x;
for (let y = blocksBounds.min.y - 1; y <= blocksBounds.max.y + 1; ++y) {
tmp.y = y;
for (let z = blocksBounds.min.z - 1; z <= blocksBounds.max.z + 1; ++z) {
tmp.z = z;
this._lighting.set(tmp.stringify(), 0);
}
}
}
// TODO: Cache stringify
const actions: { pos: Vector3, value: number }[] = []; // = [{ 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) {
actions.push({
pos: new Vector3(x, blocksBounds.max.y + 1, z),
value: 15,
});
}
}
while (actions.length > 0) {
const action = actions.pop();
ASSERT(action !== undefined);
const newLightValue = action.value;
const currentLightValue = this._lighting.get(action.pos.stringify());
// We're trying to update the lighting value of an out-of-bounds block, skip.
if (currentLightValue === undefined) {
continue;
}
// Update lighting values only if the new value is lighter than the current brightness.
if (newLightValue > currentLightValue && !this._voxelMesh.isVoxelAt(action.pos)) {
this._lighting.set(action.pos.stringify(), newLightValue);
actions.push({ pos: new Vector3(0, 1, 0).add(action.pos), value: newLightValue - 1 }); // up
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
}
}
}
public getBlocks(): Block[] {
return this._blocks;
}

View File

@ -45,6 +45,7 @@ export type TBlockMeshBuffer = {
texcoord: { numComponents: 2, data: Float32Array },
normal: { numComponents: 3, data: Float32Array },
blockTexcoord: { numComponents: 2, data: Float32Array },
lighting: { numComponents: 1, data: Float32Array },
indices: { numComponents: 3, data: Uint32Array },
};
@ -70,7 +71,7 @@ export class ChunkedBufferGenerator {
for (let i = 0; i < numBufferVoxels; ++i) {
const voxelIndex = i + voxelsStartIndex;
const voxel = voxels[voxelIndex];
const voxelColourArray = [voxel.colour.r, voxel.colour.g, voxel.colour.b, voxel.colour.a];
const voxelPositionArray = voxel.position.toArray();
@ -114,6 +115,24 @@ export class ChunkedBufferGenerator {
public static fromBlockMesh(blockMesh: BlockMesh, chunkIndex: number): TBlockMeshBufferDescription & { moreBlocksToBuffer: boolean, progress: number } {
const blocks = blockMesh.getBlocks();
const lightingRamp = new Map<number, number>();
lightingRamp.set(15, 40 / 40);
lightingRamp.set(14, 40 / 40);
lightingRamp.set(13, 39 / 40);
lightingRamp.set(12, 37 / 40);
lightingRamp.set(11, 35 / 40);
lightingRamp.set(10, 32 / 40);
lightingRamp.set(9, 29 / 40);
lightingRamp.set(8, 26 / 40);
lightingRamp.set(7, 23 / 40);
lightingRamp.set(6, 20 / 40);
lightingRamp.set(5, 17 / 40);
lightingRamp.set(4, 14 / 40);
lightingRamp.set(3, 12 / 40);
lightingRamp.set(2, 9 / 40);
lightingRamp.set(1, 7 / 40);
lightingRamp.set(0, 5 / 40);
const numTotalBlocks = blocks.length;
const blocksStartIndex = chunkIndex * AppConfig.Get.VOXEL_BUFFER_CHUNK_SIZE;
const blocksEndIndex = Math.min((chunkIndex + 1) * AppConfig.Get.VOXEL_BUFFER_CHUNK_SIZE, numTotalBlocks);
@ -126,16 +145,22 @@ export class ChunkedBufferGenerator {
const faceOrder = ['north', 'south', 'up', 'down', 'east', 'west'];
let insertIndex = 0;
let lightingInsertIndex = 0;
for (let i = 0; i < numBufferBlocks; ++i) {
const blockIndex = i + blocksStartIndex;
const blockLighting = blockMesh.getBlockLighting(blocks[blockIndex].voxel.position);
for (let f = 0; f < AppConstants.FACES_PER_VOXEL; ++f) {
const faceName = faceOrder[f];
const faceLighting = lightingRamp.get(blockLighting[f]) ?? 1.0;
const texcoord = blocks[blockIndex].blockInfo.faces[faceName].texcoord;
for (let v = 0; v < AppConstants.VERTICES_PER_FACE; ++v) {
newBuffer.blockTexcoord.data[insertIndex++] = texcoord.u;
newBuffer.blockTexcoord.data[insertIndex++] = texcoord.v;
newBuffer.lighting.data[lightingInsertIndex++] = faceLighting;
}
}
}
@ -394,6 +419,10 @@ export class BufferGenerator {
numComponents: AppConstants.ComponentSize.TEXCOORD,
data: new Float32Array(numBlocks * AppConstants.VoxelMeshBufferComponentOffsets.TEXCOORD),
},
lighting: {
numComponents: AppConstants.ComponentSize.LIGHTING,
data: new Float32Array(numBlocks * AppConstants.VoxelMeshBufferComponentOffsets.LIGHTING),
},
};
}
}

View File

@ -5,6 +5,7 @@ export namespace AppConstants {
export const COMPONENT_PER_SIZE_OFFSET = FACES_PER_VOXEL * VERTICES_PER_FACE;
export namespace ComponentSize {
export const LIGHTING = 1;
export const TEXCOORD = 2;
export const POSITION = 3;
export const COLOUR = 4;
@ -12,8 +13,9 @@ export namespace AppConstants {
export const INDICES = 3;
export const OCCLUSION = 4;
}
export namespace VoxelMeshBufferComponentOffsets {
export const LIGHTING = ComponentSize.LIGHTING * COMPONENT_PER_SIZE_OFFSET;
export const TEXCOORD = ComponentSize.TEXCOORD * COMPONENT_PER_SIZE_OFFSET;
export const POSITION = ComponentSize.POSITION * COMPONENT_PER_SIZE_OFFSET;
export const COLOUR = ComponentSize.COLOUR * COMPONENT_PER_SIZE_OFFSET;

View File

@ -57,6 +57,7 @@ export class Renderer {
private _isGridComponentEnabled: { [bufferComponent: string]: boolean };
private _axesEnabled: boolean;
private _nightVisionEnabled: boolean;
private _gridBuffers: {
x: { [meshType: string]: RenderBuffer };
@ -90,6 +91,7 @@ export class Renderer {
this._isGridComponentEnabled = {};
this._axesEnabled = false;
this._nightVisionEnabled = true;
this._axisBuffer = new RenderBuffer([
{ name: 'position', numComponents: 3 },
@ -140,6 +142,14 @@ export class Renderer {
this._axesEnabled = !this._axesEnabled;
}
public toggleIsNightVisionEnabled() {
this._nightVisionEnabled = !this._nightVisionEnabled;
}
public isNightVisionEnabled() {
return this._nightVisionEnabled;
}
public toggleIsWireframeEnabled() {
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Wireframe];
this._isGridComponentEnabled[EDebugBufferComponents.Wireframe] = isEnabled;
@ -228,7 +238,7 @@ export class Renderer {
this._gridBuffers.x[MeshType.VoxelMesh] = DebugGeometryTemplates.gridX(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
this._gridBuffers.y[MeshType.VoxelMesh] = DebugGeometryTemplates.gridY(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
this._gridBuffers.z[MeshType.VoxelMesh] = DebugGeometryTemplates.gridZ(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
this._modelsAvailable = 2;
this.setModelToUse(MeshType.VoxelMesh);
}
@ -258,7 +268,7 @@ export class Renderer {
this.setModelToUse(MeshType.VoxelMesh);
}
*/
public useBlockMeshChunk(params: RenderNextBlockMeshChunkParams.Output) {
if (params.isFirstChunk) {
this._blockBuffer = [];
@ -393,6 +403,7 @@ export class Renderer {
u_voxelSize: this._voxelSize,
u_atlasSize: this._atlasSize,
u_gridOffset: this._gridOffset.toArray(),
u_nightVision: this.isNightVisionEnabled(),
};
this._blockBuffer?.forEach((buffer) => {
this._gl.useProgram(shader.program);

View File

@ -233,8 +233,15 @@ export class UI {
.isActive(() => {
return Renderer.Get.isAxesEnabled();
}),
'night-vision': new ToolbarItemElement({ icon: 'bulb' })
.onClick(() => {
Renderer.Get.toggleIsNightVisionEnabled();
})
.isActive(() => {
return Renderer.Get.isNightVisionEnabled();
}),
},
elementsOrder: ['grid', 'axes'],
elementsOrder: ['grid', 'axes', 'night-vision'],
},
},