implement mojang lightning of a1.2.6
This commit is contained in:
@@ -12,7 +12,7 @@ window.GameWindow = class {
|
|||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
this.stats = new Stats()
|
this.stats = new Stats()
|
||||||
this.stats.showPanel(0);
|
this.stats.showPanel(1);
|
||||||
wrapper.appendChild(this.stats.dom);
|
wrapper.appendChild(this.stats.dom);
|
||||||
|
|
||||||
// Add web renderer canvas to wrapper
|
// Add web renderer canvas to wrapper
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ window.Minecraft = class {
|
|||||||
|
|
||||||
// Render the game
|
// Render the game
|
||||||
this.worldRenderer.render(partialTicks);
|
this.worldRenderer.render(partialTicks);
|
||||||
|
|
||||||
|
while (this.world.updateLights()) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
onTick() {
|
onTick() {
|
||||||
@@ -99,7 +101,6 @@ window.Minecraft = class {
|
|||||||
if (button === 0) {
|
if (button === 0) {
|
||||||
if (hitResult != null) {
|
if (hitResult != null) {
|
||||||
this.world.setBlockAt(hitResult.x, hitResult.y, hitResult.z, 0);
|
this.world.setBlockAt(hitResult.x, hitResult.y, hitResult.z, 0);
|
||||||
this.world.updateBlockLightAt(hitResult.x, hitResult.y, hitResult.z);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +126,6 @@ window.Minecraft = class {
|
|||||||
// Don't place blocks if the player is standing there
|
// Don't place blocks if the player is standing there
|
||||||
if (!placedBoundingBox.intersects(this.player.boundingBox)) {
|
if (!placedBoundingBox.intersects(this.player.boundingBox)) {
|
||||||
this.world.setBlockAt(x, y, z, this.pickedBlock);
|
this.world.setBlockAt(x, y, z, this.pickedBlock);
|
||||||
this.world.updateBlockLightAt(x, y, z);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
window.BlockRenderer = class {
|
window.BlockRenderer = class {
|
||||||
|
|
||||||
static CLASSIC_LIGHTNING = false;
|
static CLASSIC_LIGHTNING = true;
|
||||||
|
|
||||||
constructor(worldRenderer) {
|
constructor(worldRenderer) {
|
||||||
this.worldRenderer = worldRenderer;
|
this.worldRenderer = worldRenderer;
|
||||||
@@ -125,7 +125,7 @@ window.BlockRenderer = class {
|
|||||||
if (typeId === 0 || Block.getById(typeId).isTransparent()) {
|
if (typeId === 0 || Block.getById(typeId).isTransparent()) {
|
||||||
|
|
||||||
// Sum up the light levels
|
// Sum up the light levels
|
||||||
totalLightLevel += world.getLightAt(x + offsetX, y + offsetY, z + offsetZ);
|
totalLightLevel += world.getTotalLightAt(x + offsetX, y + offsetY, z + offsetZ);
|
||||||
totalBlocks++;
|
totalBlocks++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,8 +103,7 @@ window.WorldRenderer = class {
|
|||||||
renderChunks(cameraChunkX, cameraChunkZ) {
|
renderChunks(cameraChunkX, cameraChunkZ) {
|
||||||
let world = this.minecraft.world;
|
let world = this.minecraft.world;
|
||||||
|
|
||||||
for (let i in world.chunks) {
|
for (let [index, chunk] of world.chunks) {
|
||||||
let chunk = world.chunks[i];
|
|
||||||
|
|
||||||
let distanceX = Math.abs(cameraChunkX - chunk.x);
|
let distanceX = Math.abs(cameraChunkX - chunk.x);
|
||||||
let distanceZ = Math.abs(cameraChunkZ - chunk.z);
|
let distanceZ = Math.abs(cameraChunkZ - chunk.z);
|
||||||
|
|||||||
@@ -18,10 +18,141 @@ window.Chunk = class {
|
|||||||
this.sections[y] = section;
|
this.sections[y] = section;
|
||||||
this.group.add(section.group);
|
this.group.add(section.group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create height map
|
||||||
|
this.heightMap = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
setBlockAt(x, y, z, typeId) {
|
setBlockAt(x, y, z, typeId) {
|
||||||
this.getSection(y >> 4).setBlockAt(x, y & 15, z, typeId);
|
let section = this.getSection(y >> 4);
|
||||||
|
let prevTypeId = section.getBlockAt(x, y & 15, z);
|
||||||
|
if (prevTypeId === typeId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update block type id
|
||||||
|
section.setBlockAt(x, y & 15, z, typeId);
|
||||||
|
|
||||||
|
let block = Block.getById(typeId);
|
||||||
|
let heightLevel = this.heightMap[z << 4 | x] & 0xff;
|
||||||
|
|
||||||
|
if (typeId !== 0 && block.isSolid()) {
|
||||||
|
if (y >= heightLevel) {
|
||||||
|
// Update height map if it is the new highest block now
|
||||||
|
this.updateHeightMap(x, y + 1, z);
|
||||||
|
}
|
||||||
|
} else if (y === heightLevel - 1) {
|
||||||
|
// Update height map if it is below the highest block because it could block the sun
|
||||||
|
this.updateHeightMap(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update light at block
|
||||||
|
//this.world.updateLight(EnumSkyBlock.SKY, x, y, z, x, y, z);
|
||||||
|
//this.world.updateLight(EnumSkyBlock.BLOCK, x, y, z, x, y, z);
|
||||||
|
|
||||||
|
// Notify neighbors
|
||||||
|
//this.notifyNeighbors(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyNeighbors(x, z) {
|
||||||
|
let height = this.getHeightAt(x, z);
|
||||||
|
let totalX = this.x * 16 + x;
|
||||||
|
let totalZ = this.z * 16 + z;
|
||||||
|
|
||||||
|
this.updateSkyLight(totalX - 1, totalZ, height);
|
||||||
|
this.updateSkyLight(totalX + 1, totalZ, height);
|
||||||
|
this.updateSkyLight(totalX, totalZ - 1, height);
|
||||||
|
this.updateSkyLight(totalX, totalZ + 1, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSkyLight(x, z, y) {
|
||||||
|
let height = this.world.getHeightAt(x, z);
|
||||||
|
if (height > y) {
|
||||||
|
this.world.updateLight(EnumSkyBlock.SKY, x, y, z, x, height, z);
|
||||||
|
} else if (height < y) {
|
||||||
|
this.world.updateLight(EnumSkyBlock.SKY, x, height, z, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockAt(x, y, z) {
|
||||||
|
return this.getSection(y >> 4).getBlockAt(x, y & 15, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
isHighestBlock(x, y, z) {
|
||||||
|
return y >= (this.heightMap[z << 4 | x] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeightAt(x, z) {
|
||||||
|
return this.heightMap[z << 4 | x] & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHeightMap(x, y, z) {
|
||||||
|
let currentHighestY = this.heightMap[z << 4 | x] & 0xff;
|
||||||
|
let highestY = currentHighestY;
|
||||||
|
if (y > currentHighestY) {
|
||||||
|
highestY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find new highest blocks
|
||||||
|
while (highestY > 0) {
|
||||||
|
let typeId = this.getBlockAt(x, highestY, z);
|
||||||
|
let block = Block.getById(typeId);
|
||||||
|
if (typeId !== 0 && block.isSolid()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
highestY--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if highest block changed
|
||||||
|
if (highestY === currentHighestY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save in height map
|
||||||
|
this.heightMap[z << 4 | x] = highestY;
|
||||||
|
|
||||||
|
|
||||||
|
let totalX = this.x * 16 + x;
|
||||||
|
let totalZ = this.z * 16 + z;
|
||||||
|
|
||||||
|
if (highestY < currentHighestY) {
|
||||||
|
for (let hy = highestY; hy < currentHighestY; hy++) {
|
||||||
|
this.getSection(hy >> 4).setLightAt(EnumSkyBlock.SKY, x, hy, z, 15);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.world.updateLight(EnumSkyBlock.SKY, totalX, currentHighestY, totalZ, totalX, highestY, totalZ);
|
||||||
|
|
||||||
|
for (let hy = currentHighestY; hy < highestY; hy++) {
|
||||||
|
this.getSection(hy >> 4).setLightAt(EnumSkyBlock.SKY, x, hy, z, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let lightLevel = 15;
|
||||||
|
let currentY = highestY;
|
||||||
|
while (currentY > 0 && lightLevel > 0) {
|
||||||
|
currentY--;
|
||||||
|
|
||||||
|
let typeId = this.getBlockAt(x, currentY, z);
|
||||||
|
let block = Block.getById(typeId);
|
||||||
|
|
||||||
|
// Reduce light level by opacity
|
||||||
|
if (typeId !== 0) {
|
||||||
|
lightLevel *= (1 - block.getOpacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min light level
|
||||||
|
if (lightLevel < 0) {
|
||||||
|
lightLevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update light level
|
||||||
|
this.getSection(currentY >> 4).setLightAt(EnumSkyBlock.SKY, x & 15, currentY, z & 15, lightLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentY !== highestY) {
|
||||||
|
this.world.updateLight(EnumSkyBlock.SKY, x - 1, currentY, z - 1, x + 1, currentY, z + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSection(y) {
|
getSection(y) {
|
||||||
|
|||||||
@@ -24,15 +24,16 @@ window.ChunkSection = class {
|
|||||||
|
|
||||||
this.blocks = [];
|
this.blocks = [];
|
||||||
this.blockLight = [];
|
this.blockLight = [];
|
||||||
|
this.skyLight = [];
|
||||||
|
|
||||||
// Fill chunk with air and light
|
// Fill chunk with air and light
|
||||||
for (let lightX = 0; lightX < ChunkSection.SIZE; lightX++) {
|
for (let tX = 0; tX < ChunkSection.SIZE; tX++) {
|
||||||
for (let lightY = 0; lightY < ChunkSection.SIZE; lightY++) {
|
for (let tY = 0; tY < ChunkSection.SIZE; tY++) {
|
||||||
for (let lightZ = 0; lightZ < ChunkSection.SIZE; lightZ++) {
|
for (let tZ = 0; tZ < ChunkSection.SIZE; tZ++) {
|
||||||
let index = lightY << 8 | lightZ << 4 | lightX;
|
let index = tY << 8 | tZ << 4 | tX;
|
||||||
|
|
||||||
this.blocks[index] = 0;
|
this.blocks[index] = 0;
|
||||||
this.blockLight[index] = 15;
|
this.blockLight[index] = 0;
|
||||||
|
this.skyLight[index] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,14 +82,30 @@ window.ChunkSection = class {
|
|||||||
this.blocks[index] = typeId;
|
this.blocks[index] = typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLightAt(x, y, z, lightLevel) {
|
setLightAt(sourceType, x, y, z, lightLevel) {
|
||||||
let index = y << 8 | z << 4 | x;
|
let index = y << 8 | z << 4 | x;
|
||||||
this.blockLight[index] = lightLevel;
|
this.blockLight[index] = lightLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLightAt(x, y, z) {
|
getTotalLightAt(x, y, z) {
|
||||||
let index = y << 8 | z << 4 | x;
|
let index = y << 8 | z << 4 | x;
|
||||||
return this.blockLight[index];
|
let skyLight = this.skyLight[index];
|
||||||
|
let blockLight = this.blockLight[index];
|
||||||
|
if (blockLight > skyLight) {
|
||||||
|
skyLight = blockLight;
|
||||||
|
}
|
||||||
|
return skyLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLightAt(sourceType, x, y, z) {
|
||||||
|
let index = y << 8 | z << 4 | x;
|
||||||
|
if (sourceType === EnumSkyBlock.SKY) {
|
||||||
|
return this.skyLight[index];
|
||||||
|
}
|
||||||
|
if (sourceType === EnumSkyBlock.Block) {
|
||||||
|
return this.blockLight[index];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmpty() {
|
isEmpty() {
|
||||||
|
|||||||
@@ -5,32 +5,19 @@ window.World = class {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.group = new THREE.Object3D();
|
this.group = new THREE.Object3D();
|
||||||
this.group.matrixAutoUpdate = false;
|
this.group.matrixAutoUpdate = false;
|
||||||
this.chunks = [];
|
this.chunks = new Map();
|
||||||
|
|
||||||
// Load world
|
// Load world
|
||||||
this.generator = new WorldGenerator(this, Date.now() % 100000);
|
this.generator = new WorldGenerator(this, Date.now() % 100000);
|
||||||
|
|
||||||
this.lightUpdateQueue = [];
|
this.lightUpdateQueue = [];
|
||||||
|
|
||||||
|
this.lightUpdateProcesses = 0;
|
||||||
|
this.lightUpdates = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
onTick() {
|
onTick() {
|
||||||
// Handle 128 light updates per tick
|
|
||||||
for (let i = 0; i < 128; i++) {
|
|
||||||
|
|
||||||
// Light updates
|
|
||||||
if (this.lightUpdateQueue.length !== 0) {
|
|
||||||
|
|
||||||
// Get next position to update
|
|
||||||
let positionIndex = this.lightUpdateQueue.shift();
|
|
||||||
if (positionIndex != null) {
|
|
||||||
let z = positionIndex >> 16;
|
|
||||||
let x = positionIndex - (z << 16);
|
|
||||||
this.updateBlockLightsAtXZ(x, z);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadChunk(chunk) {
|
loadChunk(chunk) {
|
||||||
@@ -71,6 +58,113 @@ window.World = class {
|
|||||||
return boundingBoxList;
|
return boundingBoxList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateLight(sourceType, x1, y1, z1, x2, y2, z2, notifyNeighbor = true) {
|
||||||
|
if (this.lightUpdates >= 50) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lightUpdates++;
|
||||||
|
|
||||||
|
let size = this.lightUpdateQueue.length;
|
||||||
|
|
||||||
|
if (notifyNeighbor) {
|
||||||
|
let max = 4;
|
||||||
|
if (max > size) {
|
||||||
|
max = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < max; i++) {
|
||||||
|
let meta = this.lightUpdateQueue[size - i - 1];
|
||||||
|
if (meta.type === sourceType && meta.isOutsideOf(x1, y1, z1, x2, y2, z2)) {
|
||||||
|
this.lightUpdates--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push light update to queue
|
||||||
|
this.lightUpdateQueue.push(new MetadataChunkBlock(sourceType, x1, y1, z1, x2, y2, z2));
|
||||||
|
if (this.lightUpdateQueue.length > 0x186a0) {
|
||||||
|
this.lightUpdateQueue = [];
|
||||||
|
}
|
||||||
|
this.lightUpdates--;
|
||||||
|
}
|
||||||
|
|
||||||
|
neighborLightPropagationChanged(sourceType, x, y, z, lightLevel) {
|
||||||
|
if (sourceType === EnumSkyBlock.SKY) {
|
||||||
|
if (this.isHighestBlock(x, y, z)) {
|
||||||
|
lightLevel = 15;
|
||||||
|
}
|
||||||
|
} else if (sourceType === EnumSkyBlock.BLOCK) {
|
||||||
|
let typeId = this.getBlockAt(x, y, z);
|
||||||
|
let block = Block.getById(typeId);
|
||||||
|
let blockLight = block.getLightValue();
|
||||||
|
|
||||||
|
if (blockLight > lightLevel) {
|
||||||
|
lightLevel = blockLight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.getSavedLightValue(sourceType, x, y, z) !== lightLevel) {
|
||||||
|
this.updateLight(sourceType, x, y, z, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLights() {
|
||||||
|
if (this.lightUpdateProcesses >= 50) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.lightUpdateProcesses++;
|
||||||
|
|
||||||
|
// Update lights in queue
|
||||||
|
let i = 5000;
|
||||||
|
while (this.lightUpdateQueue.length > 0) {
|
||||||
|
if (i <= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.lightUpdateQueue.shift().updateBlockLightning(this);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lightUpdateProcesses--;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeightAt(x, z) {
|
||||||
|
return this.getChunkAt(x >> 4, z >> 4).getHeightAt(x & 15, z & 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
isHighestBlock(x, y, z) {
|
||||||
|
let chunk = this.getChunkAt(x >> 4, z >> 4)
|
||||||
|
return y >= chunk.getHeightAt(x & 15, z & 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTotalLightAt(x, y, z) {
|
||||||
|
if (y < 0) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
let section = this.getChunkSectionAt(x >> 4, y >> 4, z >> 4)
|
||||||
|
return section.getTotalLightAt(x & 15, y & 15, z & 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSavedLightValue(sourceType, x, y, z) {
|
||||||
|
if (y < 0) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
let section = this.getChunkSectionAt(x >> 4, y >> 4, z >> 4)
|
||||||
|
return section.getLightAt(sourceType, x & 15, y & 15, z & 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLightAt(sourceType, x, y, z, lightLevel) {
|
||||||
|
if (y < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let section = this.getChunkSectionAt(x >> 4, y >> 4, z >> 4)
|
||||||
|
section.setLightAt(sourceType, x & 15, y & 15, z & 15, lightLevel);
|
||||||
|
}
|
||||||
|
|
||||||
isSolidBlockAt(x, y, z) {
|
isSolidBlockAt(x, y, z) {
|
||||||
let typeId = this.getBlockAt(x, y, z);
|
let typeId = this.getBlockAt(x, y, z);
|
||||||
return typeId !== 0 && Block.getById(typeId).isSolid();
|
return typeId !== 0 && Block.getById(typeId).isSolid();
|
||||||
@@ -82,11 +176,10 @@ window.World = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setBlockAt(x, y, z, type) {
|
setBlockAt(x, y, z, type) {
|
||||||
let chunkSection = this.getChunkAtBlock(x, y, z);
|
let chunk = this.getChunkAt(x >> 4, z >> 4);
|
||||||
if (chunkSection != null) {
|
chunk.setBlockAt(x & 15, y, z & 15, type);
|
||||||
chunkSection.setBlockAt(x & 15, y & 15, z & 15, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Rebuild chunk
|
||||||
this.onBlockChanged(x, y, z);
|
this.onBlockChanged(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,127 +198,14 @@ window.World = class {
|
|||||||
|
|
||||||
getChunkAt(x, z) {
|
getChunkAt(x, z) {
|
||||||
let index = x + (z << 16);
|
let index = x + (z << 16);
|
||||||
let chunk = this.chunks[index];
|
let chunk = this.chunks.get(index);
|
||||||
if (typeof chunk === 'undefined') {
|
if (typeof chunk === 'undefined') {
|
||||||
this.chunks[index] = chunk = new Chunk(this, x, z);
|
this.chunks.set(index, chunk = new Chunk(this, x, z));
|
||||||
this.group.add(chunk.group);
|
this.group.add(chunk.group);
|
||||||
}
|
}
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
getHighestBlockYAt(x, z) {
|
|
||||||
for (let y = World.TOTAL_HEIGHT; y > 0; y--) {
|
|
||||||
if (this.isSolidBlockAt(x, y, z)) {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBlockLightAt(x, y, z) {
|
|
||||||
// Calculate brightness for target block
|
|
||||||
let lightLevel = this.isHighestBlockAt(x, y, z) ? 15 : this.calculateLightAt(x, y, z);
|
|
||||||
|
|
||||||
// Update target block light
|
|
||||||
this.getChunkAtBlock(x, y, z).setLightAt(x & 15, y & 15, z & 15, lightLevel);
|
|
||||||
|
|
||||||
// Update block lights below the target block and the surrounding blocks
|
|
||||||
for (let offsetX = -1; offsetX <= 1; offsetX++) {
|
|
||||||
for (let offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
|
||||||
this.updateBlockLightsAtXZ(x + offsetX, z + offsetZ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBlockLightsAtXZ(x, z) {
|
|
||||||
let lightChanged = false;
|
|
||||||
let skyLevel = 15;
|
|
||||||
|
|
||||||
// Scan from the top to the bottom
|
|
||||||
for (let y = World.TOTAL_HEIGHT; y >= 0; y--) {
|
|
||||||
if (!this.isTransparentBlockAt(x, y, z)) {
|
|
||||||
// Sun is blocked because of solid block
|
|
||||||
skyLevel = 0;
|
|
||||||
} else {
|
|
||||||
// Get opacity of this block
|
|
||||||
let typeId = this.getBlockAt(x, y, z);
|
|
||||||
let translucence = typeId === 0 ? 1.0 : 1.0 - Block.getById(typeId).getOpacity();
|
|
||||||
|
|
||||||
// Decrease strength of the skylight by the opacity of the block
|
|
||||||
skyLevel *= translucence;
|
|
||||||
|
|
||||||
// Get previous block light
|
|
||||||
let prevBlockLight = this.getLightAt(x, y, z);
|
|
||||||
|
|
||||||
// Combine skylight with the calculated block light and decrease strength by the opacity of the block
|
|
||||||
let blockLight = Math.floor(Math.max(skyLevel, this.calculateLightAt(x, y, z)) * translucence);
|
|
||||||
|
|
||||||
// Did one of the light change inside of the range?
|
|
||||||
if (prevBlockLight !== blockLight) {
|
|
||||||
lightChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the new light to the block
|
|
||||||
this.setLightAt(x, y, z, blockLight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chain reaction, update next affected blocks
|
|
||||||
if (lightChanged && this.lightUpdateQueue.length < 512) {
|
|
||||||
for (let offsetX = -1; offsetX <= 1; offsetX++) {
|
|
||||||
for (let offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
|
||||||
let positionIndex = (x + offsetX) + ((z + offsetZ) << 16);
|
|
||||||
|
|
||||||
// Add block range to update queue
|
|
||||||
if (!this.lightUpdateQueue.includes(positionIndex)) {
|
|
||||||
this.lightUpdateQueue.push(positionIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setLightAt(x, y, z, light) {
|
|
||||||
let chunkSection = this.getChunkAtBlock(x, y, z);
|
|
||||||
if (chunkSection != null) {
|
|
||||||
chunkSection.setLightAt(x & 15, y & 15, z & 15, light);
|
|
||||||
chunkSection.queueForRebuild();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getLightAt(x, y, z) {
|
|
||||||
let chunkSection = this.getChunkAtBlock(x, y, z);
|
|
||||||
return chunkSection == null ? 15 : chunkSection.getLightAt(x & 15, y & 15, z & 15);
|
|
||||||
}
|
|
||||||
|
|
||||||
isHighestBlockAt(x, y, z) {
|
|
||||||
for (let i = y + 1; i < World.TOTAL_HEIGHT; i++) {
|
|
||||||
if (this.isSolidBlockAt(x, i, z)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateLightAt(x, y, z) {
|
|
||||||
let maxBrightness = 0;
|
|
||||||
|
|
||||||
// Get maximal brightness of surround blocks
|
|
||||||
let values = EnumBlockFace.values();
|
|
||||||
for (let i in values) {
|
|
||||||
let face = values[i];
|
|
||||||
|
|
||||||
if (this.isTransparentBlockAt(x + face.x, y + face.y, z + face.z)) {
|
|
||||||
let brightness = this.getLightAt(x + face.x, y + face.y, z + face.z);
|
|
||||||
|
|
||||||
maxBrightness = Math.max(maxBrightness, brightness);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrease maximum brightness by 6%
|
|
||||||
return Math.max(0, maxBrightness - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
onBlockChanged(x, y, z) {
|
onBlockChanged(x, y, z) {
|
||||||
this.queueForRebuildInRegion(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1);
|
this.queueForRebuildInRegion(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
window.Block = class {
|
window.Block = class {
|
||||||
|
|
||||||
static blocks = [];
|
static blocks = new Map();
|
||||||
|
|
||||||
static create() {
|
static create() {
|
||||||
Block.STONE = new BlockStone(1, 0);
|
Block.STONE = new BlockStone(1, 0);
|
||||||
@@ -19,7 +19,7 @@ window.Block = class {
|
|||||||
this.boundingBox = new BoundingBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
|
this.boundingBox = new BoundingBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
|
||||||
|
|
||||||
// Register block
|
// Register block
|
||||||
Block.blocks[id] = this;
|
Block.blocks.set(id, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getId() {
|
getId() {
|
||||||
@@ -39,6 +39,10 @@ window.Block = class {
|
|||||||
return typeId === 0 || Block.getById(typeId).isTransparent();
|
return typeId === 0 || Block.getById(typeId).isTransparent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLightValue() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
isSolid() {
|
isSolid() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -167,7 +171,7 @@ window.Block = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static getById(typeId) {
|
static getById(typeId) {
|
||||||
return Block.blocks[typeId];
|
return Block.blocks.get(typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -116,8 +116,8 @@ window.WorldGenerator = class {
|
|||||||
let perlin = this.forestNoise.perlin(absoluteX * 10, absoluteZ * 10);
|
let perlin = this.forestNoise.perlin(absoluteX * 10, absoluteZ * 10);
|
||||||
if (perlin > 0 && this.random.nextInt(2) === 0) {
|
if (perlin > 0 && this.random.nextInt(2) === 0) {
|
||||||
|
|
||||||
// Get highest block at this position
|
// Get the highest block at this position
|
||||||
let highestY = this.world.getHighestBlockYAt(absoluteX, absoluteZ);
|
let highestY = this.world.getHeightAt(absoluteX, absoluteZ);
|
||||||
|
|
||||||
// Don't place a tree if there is no grass
|
// Don't place a tree if there is no grass
|
||||||
if (this.world.getBlockAt(absoluteX, highestY, absoluteZ) === Block.GRASS.getId()
|
if (this.world.getBlockAt(absoluteX, highestY, absoluteZ) === Block.GRASS.getId()
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
window.EnumSkyBlock = class {
|
||||||
|
static SKY = 0;
|
||||||
|
static BLOCK = 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
window.MetadataChunkBlock = class {
|
||||||
|
|
||||||
|
constructor(type, x1, y1, z1, x2, y2, z2) {
|
||||||
|
this.type = type;
|
||||||
|
this.x1 = x1;
|
||||||
|
this.y1 = y1;
|
||||||
|
this.z1 = z1;
|
||||||
|
this.x2 = x2;
|
||||||
|
this.y2 = y2;
|
||||||
|
this.z2 = z2;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBlockLightning(world) {
|
||||||
|
for (let x = this.x1; x <= this.x2; x++) {
|
||||||
|
for (let z = this.z1; z <= this.z2; z++) {
|
||||||
|
for (let y = this.y1; y <= this.y2; y++) {
|
||||||
|
|
||||||
|
let savedLightValue = world.getSavedLightValue(this.type, x, y, z);
|
||||||
|
|
||||||
|
let level = 0;
|
||||||
|
let newLevel = 0;
|
||||||
|
|
||||||
|
let typeId = world.getBlockAt(x, y, z);
|
||||||
|
let block = Block.getById(typeId);
|
||||||
|
|
||||||
|
let opacity = typeId === 0 ? 0 : (block.getOpacity() * 255);
|
||||||
|
if (opacity === 0) {
|
||||||
|
opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === EnumSkyBlock.SKY) {
|
||||||
|
if (world.isHighestBlock(x, y, z)) {
|
||||||
|
level = 15;
|
||||||
|
}
|
||||||
|
} else if (this.type === EnumSkyBlock.BLOCK) {
|
||||||
|
level = typeId === 0 ? 0 : block.getLightValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opacity >= 15 && level === 0) {
|
||||||
|
newLevel = 0;
|
||||||
|
} else {
|
||||||
|
let x1Level = world.getSavedLightValue(this.type, x - 1, y, z);
|
||||||
|
let x2Level = world.getSavedLightValue(this.type, x + 1, y, z);
|
||||||
|
let bottomLevel = world.getSavedLightValue(this.type, x, y - 1, z);
|
||||||
|
let topLevel = world.getSavedLightValue(this.type, x, y + 1, z);
|
||||||
|
let z1Level = world.getSavedLightValue(this.type, x, y, z - 1);
|
||||||
|
let z2Level = world.getSavedLightValue(this.type, x, y, z + 1);
|
||||||
|
|
||||||
|
newLevel = x1Level;
|
||||||
|
|
||||||
|
if (x2Level > newLevel) {
|
||||||
|
newLevel = x2Level;
|
||||||
|
}
|
||||||
|
if (bottomLevel > newLevel) {
|
||||||
|
newLevel = bottomLevel;
|
||||||
|
}
|
||||||
|
if (topLevel > newLevel) {
|
||||||
|
newLevel = topLevel;
|
||||||
|
}
|
||||||
|
if (z1Level > newLevel) {
|
||||||
|
newLevel = z1Level;
|
||||||
|
}
|
||||||
|
if (z2Level > newLevel) {
|
||||||
|
newLevel = z2Level;
|
||||||
|
}
|
||||||
|
|
||||||
|
newLevel -= opacity;
|
||||||
|
|
||||||
|
if (newLevel < 0) {
|
||||||
|
newLevel = 0;
|
||||||
|
}
|
||||||
|
if (level > newLevel) {
|
||||||
|
newLevel = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (savedLightValue === newLevel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
world.setLightAt(this.type, x, y, z, newLevel);
|
||||||
|
|
||||||
|
let decreasedLevel = newLevel - 1;
|
||||||
|
if (decreasedLevel < 0) {
|
||||||
|
decreasedLevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
world.neighborLightPropagationChanged(this.type, x - 1, y, z, decreasedLevel);
|
||||||
|
world.neighborLightPropagationChanged(this.type, x, y - 1, z, decreasedLevel);
|
||||||
|
world.neighborLightPropagationChanged(this.type, x, y, z - 1, decreasedLevel);
|
||||||
|
|
||||||
|
if (x + 1 >= this.x2) {
|
||||||
|
world.neighborLightPropagationChanged(this.type, x + 1, y, z, decreasedLevel);
|
||||||
|
}
|
||||||
|
if (y + 1 >= this.y2) {
|
||||||
|
world.neighborLightPropagationChanged(this.type, x, y + 1, z, decreasedLevel);
|
||||||
|
}
|
||||||
|
if (z + 1 >= this.z2) {
|
||||||
|
world.neighborLightPropagationChanged(this.type, x, y, z + 1, decreasedLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isOutsideOf(x1, y1, z1, x2, y2, z2) {
|
||||||
|
if (x1 >= this.x1 && y1 >= this.y1 && z1 >= this.z1 && x2 <= this.x2 && y2 <= this.y2 && z2 <= this.z2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let radius = 1;
|
||||||
|
if (x1 >= this.x1 - radius
|
||||||
|
&& y1 >= this.y1 - radius
|
||||||
|
&& z1 >= this.z1 - radius
|
||||||
|
&& x2 <= this.x2 + radius
|
||||||
|
&& y2 <= this.y2 + radius
|
||||||
|
&& z2 <= this.z2 + radius) {
|
||||||
|
|
||||||
|
let distanceX = this.x2 - this.x1;
|
||||||
|
let distanceY = this.y2 - this.y1;
|
||||||
|
let distanceZ = this.z2 - this.z1;
|
||||||
|
|
||||||
|
if (x1 > this.x1) {
|
||||||
|
x1 = this.x1;
|
||||||
|
}
|
||||||
|
if (y1 > this.y1) {
|
||||||
|
y1 = this.y1;
|
||||||
|
}
|
||||||
|
if (z1 > this.z1) {
|
||||||
|
z1 = this.z1;
|
||||||
|
}
|
||||||
|
if (x2 < this.x2) {
|
||||||
|
x2 = this.x2;
|
||||||
|
}
|
||||||
|
if (y2 < this.y2) {
|
||||||
|
y2 = this.y2;
|
||||||
|
}
|
||||||
|
if (z2 < this.z2) {
|
||||||
|
z2 = this.z2;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newDistanceX = x2 - x1;
|
||||||
|
let newDistanceY = y2 - y1;
|
||||||
|
let newDistanceZ = z2 - z1;
|
||||||
|
|
||||||
|
let size = distanceX * distanceY * distanceZ;
|
||||||
|
let newSize = newDistanceX * newDistanceY * newDistanceZ;
|
||||||
|
|
||||||
|
if (newSize - size <= 2) {
|
||||||
|
this.x1 = x1;
|
||||||
|
this.y1 = y1;
|
||||||
|
this.z1 = z1;
|
||||||
|
this.x2 = x2;
|
||||||
|
this.y2 = y2;
|
||||||
|
this.z2 = z2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -51,6 +51,9 @@ loadScripts([
|
|||||||
"src/js/net/minecraft/util/EnumBlockFace.js",
|
"src/js/net/minecraft/util/EnumBlockFace.js",
|
||||||
"src/js/net/minecraft/util/Timer.js",
|
"src/js/net/minecraft/util/Timer.js",
|
||||||
"src/js/net/minecraft/util/Random.js",
|
"src/js/net/minecraft/util/Random.js",
|
||||||
|
"src/js/net/minecraft/util/EnumBlockFace.js",
|
||||||
|
"src/js/net/minecraft/util/EnumSkyBlock.js",
|
||||||
|
"src/js/net/minecraft/util/MetadataChunkBlock.js",
|
||||||
"src/js/net/minecraft/util/Vector3.js",
|
"src/js/net/minecraft/util/Vector3.js",
|
||||||
"src/js/net/minecraft/util/MovingObjectPosition.js",
|
"src/js/net/minecraft/util/MovingObjectPosition.js",
|
||||||
"src/js/net/minecraft/util/MathHelper.js",
|
"src/js/net/minecraft/util/MathHelper.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user