Upload occlusion mod
This commit is contained in:
parent
73a500e27e
commit
e6ec165987
|
|
@ -0,0 +1,149 @@
|
|||
const DEFAULT_LIGHT_FACTOR = 0.8;
|
||||
const MIN_LIGHT_INTENSITY = 0.6; // If pixel is completely obscured, it will still have this amount of light
|
||||
|
||||
const MAX_DIRECT_NEIGHBORS = 4; // getNeighbors() returns max 4 pixels
|
||||
const FOLLOWUP_COORDS_TO_CHECK = [
|
||||
[-1, -1], [-1, 1], [1, -1], [1, 1],
|
||||
[-2, 0], [2, 0], [0, -2], [0, 2],
|
||||
[-3, 0], [3, 0], [0, -3], [0, 3],
|
||||
[-4, 0], [4, 0], [0, -4], [0, 4]
|
||||
];
|
||||
|
||||
// Pre-initialize the list of transparent elements
|
||||
let transparentElementsTmp = "glass,stained_glass,glass_shard,solid_diamond,ice,led_r,led_g,led_b".split(",");
|
||||
let transparentElements = [];
|
||||
|
||||
// Function to create the list of transparent elements based on their properties
|
||||
function initializeTransparentElements() {
|
||||
Object.keys(elements).forEach(elementName => {
|
||||
const element = elements[elementName];
|
||||
|
||||
// Check if the element is in a gas or liquid state
|
||||
if (element.state === "gas") {
|
||||
transparentElements.push(elementName);
|
||||
}
|
||||
|
||||
// Check if the element's category is "special"
|
||||
if (element.category === "special") {
|
||||
transparentElements.push(elementName);
|
||||
}
|
||||
|
||||
// Check if the element has a custom flag for transparency
|
||||
if (element.putInTransparentList) {
|
||||
transparentElements.push(elementName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Call the function once at startup to populate the transparentElements list
|
||||
initializeTransparentElements();
|
||||
|
||||
// Customizable frame interval for recalculating brightness
|
||||
const calculateEveryNFrames = 2;
|
||||
let frameCounter = 0;
|
||||
|
||||
// Cache for storing pixel brightnesses
|
||||
let pixelBrightnessCache = {};
|
||||
|
||||
// scaleList should only be defined once
|
||||
if (typeof scaleList === 'undefined') {
|
||||
function scaleList(numbers, scale) {
|
||||
return numbers.map(number => number * scale);
|
||||
}
|
||||
}
|
||||
|
||||
function isOutOfBounds(x, y) {
|
||||
return x >= width || y >= height || x < 0 || y < 0;
|
||||
}
|
||||
|
||||
function getOutOfBoundsNeighbors(pixel) {
|
||||
const outOfBoundsNeighbors = [];
|
||||
|
||||
// Define the 4 direct neighbors: left, right, top, bottom
|
||||
const neighborsToCheck = [
|
||||
{ x: pixel.x - 1, y: pixel.y },
|
||||
{ x: pixel.x + 1, y: pixel.y },
|
||||
{ x: pixel.x, y: pixel.y - 1 },
|
||||
{ x: pixel.x, y: pixel.y + 1 }
|
||||
];
|
||||
|
||||
// Check each of the neighbors to see if they are out of bounds
|
||||
neighborsToCheck.forEach(neighbor => {
|
||||
if (isOutOfBounds(neighbor.x, neighbor.y)) {
|
||||
outOfBoundsNeighbors.push(neighbor);
|
||||
}
|
||||
});
|
||||
|
||||
return outOfBoundsNeighbors;
|
||||
}
|
||||
|
||||
// Iterate over each pixel and either calculate or draw occlusion lighting
|
||||
function applyLightingToPixels(ctx) {
|
||||
// Recalculate pixel brightnesses every `n` frames
|
||||
if (frameCounter % calculateEveryNFrames === 0) {
|
||||
currentPixels.forEach(pixel => {
|
||||
const brightness = calculateBrightness(pixel);
|
||||
pixelBrightnessCache[`${pixel.x},${pixel.y}`] = brightness; // Cache the brightness
|
||||
});
|
||||
}
|
||||
|
||||
// Draw pixels based on cached brightness
|
||||
currentPixels.forEach(pixel => {
|
||||
const brightness = pixelBrightnessCache[`${pixel.x},${pixel.y}`] || 1; // Default to full brightness if not calculated yet
|
||||
drawPixelShade(ctx, pixel, brightness);
|
||||
});
|
||||
|
||||
// Increment the frame counter
|
||||
frameCounter++;
|
||||
}
|
||||
|
||||
// Darken a pixel based on brightness
|
||||
function drawPixelShade(ctx, pixel, brightness) {
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.fillStyle = `rgba(0, 0, 0, ${1 - brightness})`;
|
||||
ctx.fillRect(pixel.x * pixelSize, pixel.y * pixelSize, pixelSize, pixelSize);
|
||||
}
|
||||
|
||||
// Compute brightness for a given pixel
|
||||
function calculateBrightness(pixel) {
|
||||
const neighboringPixelsCount = getNeighbors(pixel).length + getOutOfBoundsNeighbors(pixel).length;
|
||||
|
||||
// If the pixel has enough light-blocking neighbors, perform a deeper search
|
||||
if (neighboringPixelsCount >= MAX_DIRECT_NEIGHBORS) {
|
||||
const lightFactor = computeBrightnessFurther(pixel);
|
||||
return adjustBrightness(lightFactor);
|
||||
}
|
||||
|
||||
return 1; // Full brightness
|
||||
}
|
||||
|
||||
// Compute brightness based on further pixels that block light
|
||||
function computeBrightnessFurther(pixel) {
|
||||
let lightBlockers = 0;
|
||||
|
||||
FOLLOWUP_COORDS_TO_CHECK.forEach(offset => {
|
||||
const [dx, dy] = offset;
|
||||
const xCoord = pixel.x + dx;
|
||||
const yCoord = pixel.y + dy;
|
||||
|
||||
if (isOutOfBounds(xCoord, yCoord)) {
|
||||
lightBlockers++;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the element is transparent
|
||||
const element = pixelMap[xCoord][yCoord];
|
||||
if (element != undefined && !transparentElements.includes(element.element)) {
|
||||
lightBlockers++;
|
||||
}
|
||||
});
|
||||
|
||||
return 1 - (lightBlockers / FOLLOWUP_COORDS_TO_CHECK.length);
|
||||
}
|
||||
|
||||
// Adjust brightness based on light factor
|
||||
function adjustBrightness(lightFactor) {
|
||||
return lightFactor * DEFAULT_LIGHT_FACTOR + MIN_LIGHT_INTENSITY;
|
||||
}
|
||||
|
||||
renderPostPixel(applyLightingToPixels);
|
||||
Loading…
Reference in New Issue