Merge ce9b656271 into 9bc10bd391
This commit is contained in:
commit
3ce82c884e
|
|
@ -0,0 +1,330 @@
|
||||||
|
// realistic_system.js - Global Realistic Lighting, Shadows, and Water Effects for ALL elements (including addons)
|
||||||
|
// Features:
|
||||||
|
// - Dynamic colored light propagation from fire, lights, hot pixels, etc.
|
||||||
|
// - Realistic occlusion shadows (darker in crevices/caves), modulated by light intensity
|
||||||
|
// - Wavy foam on ALL liquids (water, oils, mod liquids)
|
||||||
|
// - Works with any mods/addons automatically (liquids get waves, all get lit/shadowed)
|
||||||
|
// Performance: Low-res lightmap + cached shadows = smooth even on large views
|
||||||
|
// Install: Save as realistic_system.js in mods folder, load via Mod Manager
|
||||||
|
|
||||||
|
// === LIGHTMAP SYSTEM (Dynamic Glow/Light Propagation) ===
|
||||||
|
var lightmap = [];
|
||||||
|
var nextLightmap = [];
|
||||||
|
var lightmapScale = 4; // Higher = lower quality/faster (2-8 recommended)
|
||||||
|
var lightSourceBoost = 3;
|
||||||
|
var falloff = 0.85;
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
function rgbToArray(colorString) {
|
||||||
|
if (typeof colorString !== "string") return [255,255,255];
|
||||||
|
if (colorString.startsWith("rgb")) {
|
||||||
|
return colorString.slice(4, -1).split(",").map(val => parseInt(val.trim()));
|
||||||
|
} else if (colorString.startsWith("#")) {
|
||||||
|
let hex = colorString.slice(1);
|
||||||
|
if (hex.length === 3) hex = hex.split("").map(char => char + char).join("");
|
||||||
|
let r = parseInt(hex.slice(0, 2), 16);
|
||||||
|
let g = parseInt(hex.slice(2, 4), 16);
|
||||||
|
let b = parseInt(hex.slice(4, 6), 16);
|
||||||
|
return [r, g, b];
|
||||||
|
}
|
||||||
|
return [255,255,255];
|
||||||
|
}
|
||||||
|
|
||||||
|
function scaleList(numbers, scale) {
|
||||||
|
return numbers.map(number => number * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rgbToHsv(r, g, b) {
|
||||||
|
r /= 255; g /= 255; b /= 255;
|
||||||
|
let max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||||
|
let h, s, v = max;
|
||||||
|
let d = max - min;
|
||||||
|
s = max === 0 ? 0 : d / max;
|
||||||
|
if (max === min) h = 0;
|
||||||
|
else {
|
||||||
|
switch (max) {
|
||||||
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||||
|
case g: h = (b - r) / d + 2; break;
|
||||||
|
case b: h = (r - g) / d + 4; break;
|
||||||
|
}
|
||||||
|
h /= 6;
|
||||||
|
}
|
||||||
|
return [h, s, v];
|
||||||
|
}
|
||||||
|
|
||||||
|
function hsvToRgb(h, s, v) {
|
||||||
|
let i = Math.floor(h * 6);
|
||||||
|
let f = h * 6 - i;
|
||||||
|
let p = v * (1 - s);
|
||||||
|
let q = v * (1 - f * s);
|
||||||
|
let t = v * (1 - (1 - f) * s);
|
||||||
|
let r, g, b;
|
||||||
|
switch (i % 6) {
|
||||||
|
case 0: r = v; g = t; b = p; break;
|
||||||
|
case 1: r = q; g = v; b = p; break;
|
||||||
|
case 2: r = p; g = v; b = t; break;
|
||||||
|
case 3: r = p; g = q; b = v; break;
|
||||||
|
case 4: r = t; g = p; b = v; break;
|
||||||
|
case 5: r = v; g = p; b = q; break;
|
||||||
|
}
|
||||||
|
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeLightmap(w, h) {
|
||||||
|
let lw = Math.ceil(w / lightmapScale) + 1;
|
||||||
|
let lh = Math.ceil(h / lightmapScale) + 1;
|
||||||
|
function createArray(width_, height_) {
|
||||||
|
return Array.from({length: height_}, () => Array.from({length: width_}, () => ({color: [0, 0, 0]})));
|
||||||
|
}
|
||||||
|
lightmap = createArray(lw, lh);
|
||||||
|
nextLightmap = createArray(lw, lh);
|
||||||
|
}
|
||||||
|
|
||||||
|
function propagateLightmap() {
|
||||||
|
if (!lightmap[0]) return;
|
||||||
|
let width = lightmap[0].length;
|
||||||
|
let height = lightmap.length;
|
||||||
|
let neighbors = [{dx:1,dy:0},{dx:-1,dy:0},{dx:0,dy:1},{dx:0,dy:-1}];
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
let totalColor = [0,0,0];
|
||||||
|
let neighborCount = 0;
|
||||||
|
neighbors.forEach(n => {
|
||||||
|
let nx = x + n.dx, ny = y + n.dy;
|
||||||
|
if (nx >= 0 && ny >= 0 && nx < width && ny < height) {
|
||||||
|
totalColor[0] += lightmap[ny][nx].color[0];
|
||||||
|
totalColor[1] += lightmap[ny][nx].color[1];
|
||||||
|
totalColor[2] += lightmap[ny][nx].color[2];
|
||||||
|
neighborCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
nextLightmap[y][x] = {
|
||||||
|
color: [
|
||||||
|
Math.min(765, Math.max(0, (totalColor[0] / neighborCount) * falloff)),
|
||||||
|
Math.min(765, Math.max(0, (totalColor[1] / neighborCount) * falloff)),
|
||||||
|
Math.min(765, Math.max(0, (totalColor[2] / neighborCount) * falloff))
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy next to current
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
lightmap[y][x] = {...nextLightmap[y][x]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLightmap(ctx) {
|
||||||
|
if (!lightmap[0]) return;
|
||||||
|
let lw = lightmap[0].length;
|
||||||
|
let lh = lightmap.length;
|
||||||
|
for (let y = 0; y < lh; y++) {
|
||||||
|
for (let x = 0; x < lw; x++) {
|
||||||
|
let color = lightmap[y][x].color;
|
||||||
|
let r = color[0], g = color[1], b = color[2];
|
||||||
|
if (r > 16 || g > 16 || b > 16) {
|
||||||
|
let hsv = rgbToHsv(r, g, b);
|
||||||
|
let newColor = hsvToRgb(hsv[0], hsv[1], 1);
|
||||||
|
let alpha = hsv[2];
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
ctx.fillStyle = `rgba(${newColor[0]},${newColor[1]},${newColor[2]},${alpha * 0.4})`;
|
||||||
|
ctx.fillRect(x * pixelSize * lightmapScale, y * pixelSize * lightmapScale, pixelSize * lightmapScale, pixelSize * lightmapScale);
|
||||||
|
ctx.fillStyle = `rgba(${newColor[0]},${newColor[1]},${newColor[2]},${alpha * 0.25})`;
|
||||||
|
ctx.fillRect((x * pixelSize - pixelSizeHalf) * lightmapScale, (y * pixelSize - pixelSizeHalf) * lightmapScale,
|
||||||
|
pixelSize * lightmapScale * 2, pixelSize * lightmapScale * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function glowItsOwnColor(pixel) {
|
||||||
|
if (!pixel.color) return;
|
||||||
|
let x = Math.floor(pixel.x / lightmapScale);
|
||||||
|
let y = Math.floor(pixel.y / lightmapScale);
|
||||||
|
if (x < 0 || y < 0 || x >= lightmap[0]?.length || y >= lightmap?.length) return;
|
||||||
|
lightmap[y][x].color = scaleList(rgbToArray(pixel.color), lightSourceBoost);
|
||||||
|
}
|
||||||
|
|
||||||
|
function glowPowered(pixel) {
|
||||||
|
if (!pixel.charge || pixel.charge <= 0 || !pixel.color) return;
|
||||||
|
glowItsOwnColor(pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override ticks for common light sources (works with addons if they use these elements)
|
||||||
|
let lightEmitters = [
|
||||||
|
"fire", "cold_fire", "plasma", "lava", "magma", "sun", "light", "liquid_light", "laser", "flash", "rainbow",
|
||||||
|
"ember", "fw_ember", "explosion", "n_explosion", "supernova", "fireball", "blaster", "lightning", "electric",
|
||||||
|
"positron", "neutron", "proton", "radiation", "fallout", "rad_cloud", "rad_steam", "uranium", "molten_uranium"
|
||||||
|
];
|
||||||
|
lightEmitters.forEach(elName => {
|
||||||
|
let el = elements[elName];
|
||||||
|
if (el && el.tick) {
|
||||||
|
let origTick = el.tick;
|
||||||
|
el.tick = function(pixel) {
|
||||||
|
origTick(pixel);
|
||||||
|
glowItsOwnColor(pixel);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
["neon", "led", "light_bulb"].forEach(elName => {
|
||||||
|
let el = elements[elName];
|
||||||
|
if (el && el.tick) {
|
||||||
|
let origTick = el.tick;
|
||||||
|
el.tick = function(pixel) {
|
||||||
|
origTick(pixel);
|
||||||
|
glowPowered(pixel);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Temp-based glow for hot pixels (metals, etc.)
|
||||||
|
function glowTemp(pixel) {
|
||||||
|
let t = pixel.temp;
|
||||||
|
if (t < 500) return;
|
||||||
|
let intensity = Math.min(1, (t - 500) / 2000);
|
||||||
|
let r = Math.min(255, 100 + 155 * intensity);
|
||||||
|
let g = Math.min(255, 50 * intensity);
|
||||||
|
let b = Math.min(255, 10 * intensity);
|
||||||
|
let x = Math.floor(pixel.x / lightmapScale);
|
||||||
|
let y = Math.floor(pixel.y / lightmapScale);
|
||||||
|
if (x < 0 || y < 0 || x >= lightmap[0]?.length || y >= lightmap?.length) return;
|
||||||
|
lightmap[y][x].color = scaleList([r, g, b], lightSourceBoost * intensity);
|
||||||
|
}
|
||||||
|
runPerPixel(glowTemp);
|
||||||
|
|
||||||
|
// Lightmap render hook
|
||||||
|
renderPrePixel(function(ctx) {
|
||||||
|
if (!paused) propagateLightmap();
|
||||||
|
renderLightmap(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Init lightmap
|
||||||
|
if (typeof runAfterReset !== 'undefined') {
|
||||||
|
runAfterReset(() => initializeLightmap(width, height));
|
||||||
|
} else {
|
||||||
|
setTimeout(() => initializeLightmap(width, height), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === SHADOWS / OCCLUSION (Realistic Darkness in Shadows) ===
|
||||||
|
const DEFAULT_LIGHT_FACTOR = 0.8;
|
||||||
|
const MIN_LIGHT_INTENSITY = 0.4;
|
||||||
|
const MAX_DIRECT_NEIGHBORS = 4;
|
||||||
|
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]
|
||||||
|
];
|
||||||
|
let transparentElements = [];
|
||||||
|
function initTransparent() {
|
||||||
|
transparentElements = [];
|
||||||
|
Object.keys(elements).forEach(name => {
|
||||||
|
let el = elements[name];
|
||||||
|
if (el.state === "gas" || el.category === "special" || el.putInTransparentList) {
|
||||||
|
transparentElements.push(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Add common transparents
|
||||||
|
["glass","stained_glass","glass_shard","ice","led"].forEach(t => transparentElements.includes(t) || transparentElements.push(t));
|
||||||
|
}
|
||||||
|
initTransparent();
|
||||||
|
|
||||||
|
let frameCounter = 0;
|
||||||
|
let pixelBrightnessCache = {};
|
||||||
|
|
||||||
|
function isOutOfBounds(x, y) {
|
||||||
|
return x >= width || y >= height || x < 0 || y < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOutOfBoundsNeighbors(pixel) {
|
||||||
|
let out = [];
|
||||||
|
[[-1,0],[1,0],[0,-1],[0,1]].forEach(([dx,dy]) => {
|
||||||
|
let nx = pixel.x + dx, ny = pixel.y + dy;
|
||||||
|
if (isOutOfBounds(nx, ny)) out.push({x:nx,y:ny});
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateBrightness(pixel) {
|
||||||
|
let neighCount = getNeighbors(pixel).length + getOutOfBoundsNeighbors(pixel).length;
|
||||||
|
if (neighCount >= MAX_DIRECT_NEIGHBORS) {
|
||||||
|
let lightFactor = computeBrightnessFurther(pixel);
|
||||||
|
return adjustBrightness(lightFactor);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeBrightnessFurther(pixel) {
|
||||||
|
let blockers = 0;
|
||||||
|
FOLLOWUP_COORDS_TO_CHECK.forEach(([dx,dy]) => {
|
||||||
|
let nx = pixel.x + dx, ny = pixel.y + dy;
|
||||||
|
if (isOutOfBounds(nx, ny)) {
|
||||||
|
blockers++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let elName = pixelMap[nx]?.[ny]?.element;
|
||||||
|
if (elName && !transparentElements.includes(elName)) blockers++;
|
||||||
|
});
|
||||||
|
return 1 - (blockers / FOLLOWUP_COORDS_TO_CHECK.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustBrightness(factor) {
|
||||||
|
return factor * DEFAULT_LIGHT_FACTOR + MIN_LIGHT_INTENSITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyShadows(ctx) {
|
||||||
|
if (frameCounter % 2 === 0) {
|
||||||
|
currentPixels.forEach(pixel => {
|
||||||
|
let brightness = calculateBrightness(pixel);
|
||||||
|
pixelBrightnessCache[`${pixel.x},${pixel.y}`] = brightness;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
currentPixels.forEach(pixel => {
|
||||||
|
let brightness = pixelBrightnessCache[`${pixel.x},${pixel.y}`] || 1;
|
||||||
|
// Sample local lightmap intensity
|
||||||
|
let lx = Math.floor(pixel.x / lightmapScale);
|
||||||
|
let ly = Math.floor(pixel.y / lightmapScale);
|
||||||
|
let lightInt = 0;
|
||||||
|
if (ly >= 0 && ly < lightmap?.length && lx >= 0 && lx < lightmap[0]?.length) {
|
||||||
|
let lm = lightmap[ly][lx].color;
|
||||||
|
lightInt = (lm[0] + lm[1] + lm[2]) / (255 * 3);
|
||||||
|
}
|
||||||
|
let shadeAlpha = (1 - brightness) * 0.7 * Math.max(0.2, 1 - lightInt * 0.8);
|
||||||
|
ctx.globalAlpha = shadeAlpha;
|
||||||
|
ctx.fillStyle = "#000";
|
||||||
|
ctx.fillRect(pixel.x * pixelSize, pixel.y * pixelSize, pixelSize, pixelSize);
|
||||||
|
});
|
||||||
|
frameCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPostPixel(applyShadows);
|
||||||
|
|
||||||
|
// === REALISTIC WATER / LIQUIDS (Waves & Foam on ALL Liquids) ===
|
||||||
|
renderEachPixel(function(pixel, ctx) {
|
||||||
|
let el = elements[pixel.element];
|
||||||
|
if (el && el.state === "liquid") {
|
||||||
|
// Wavy foam surface
|
||||||
|
let time = (pixelTicks * 0.01 + pixel.x * 0.15 + pixel.y * 0.03) % (Math.PI * 2);
|
||||||
|
let waveOffset = Math.sin(time) * 0.35 - 0.15;
|
||||||
|
let foamY = Math.floor(pixel.y + waveOffset);
|
||||||
|
let foamAlpha = 0.6 + Math.sin(time * 1.5) * 0.3;
|
||||||
|
let foamColor = "#e8f4ff"; // Light blue-white foam
|
||||||
|
drawSquare(ctx, foamColor, pixel.x, foamY, 1, foamAlpha * 0.4);
|
||||||
|
// Subtle caustic glow if lit (bonus realism)
|
||||||
|
let lx = Math.floor(pixel.x / lightmapScale);
|
||||||
|
let ly = Math.floor(pixel.y / lightmapScale);
|
||||||
|
if (ly >= 0 && ly < lightmap?.length && lx >= 0 && lx < lightmap[0]?.length) {
|
||||||
|
let lmBright = (lightmap[ly][lx].color[0] + lightmap[ly][lx].color[1] + lightmap[ly][lx].color[2]) / (255 * 3);
|
||||||
|
if (lmBright > 0.2) {
|
||||||
|
let causticAlpha = lmBright * 0.3;
|
||||||
|
let causticX = pixel.x + Math.sin(time * 0.7) * 0.2;
|
||||||
|
let causticY = pixel.y + 0.5 + Math.cos(time * 1.2) * 0.15;
|
||||||
|
drawSquare(ctx, "#00ff88", causticX, causticY, 0.8, causticAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-init transparent on element changes (for mods)
|
||||||
|
if (typeof runEveryTick !== 'undefined') {
|
||||||
|
runEveryTick(initTransparent);
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,72 @@
|
||||||
|
// xVS_cal_wasm.c
|
||||||
|
#include <emscripten.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define CLAMP(x, low, high) ((x) < (low) ? (low) : ((x) > (high) ? (high) : (x)))
|
||||||
|
|
||||||
|
// === LIGHT PROPAGATION ===
|
||||||
|
EMSCRIPTEN_KEEPALIVE
|
||||||
|
void propagate_lightmap_f32(
|
||||||
|
const float* in_r, const float* in_g, const float* in_b,
|
||||||
|
float* out_r, float* out_g, float* out_b,
|
||||||
|
int width, int height, float falloff
|
||||||
|
) {
|
||||||
|
const int dx[4] = {1, -1, 0, 0};
|
||||||
|
const int dy[4] = {0, 0, 1, -1};
|
||||||
|
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
float sum_r = 0, sum_g = 0, sum_b = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (int d = 0; d < 4; ++d) {
|
||||||
|
int nx = x + dx[d];
|
||||||
|
int ny = y + dy[d];
|
||||||
|
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
||||||
|
int nidx = ny * width + nx;
|
||||||
|
sum_r += in_r[nidx];
|
||||||
|
sum_g += in_g[nidx];
|
||||||
|
sum_b += in_b[nidx];
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int idx = y * width + x;
|
||||||
|
if (count > 0) {
|
||||||
|
float factor = falloff / count;
|
||||||
|
out_r[idx] = CLAMP(sum_r * factor, 0.0f, 765.0f);
|
||||||
|
out_g[idx] = CLAMP(sum_g * factor, 0.0f, 765.0f);
|
||||||
|
out_b[idx] = CLAMP(sum_b * factor, 0.0f, 765.0f);
|
||||||
|
} else {
|
||||||
|
out_r[idx] = out_g[idx] = out_b[idx] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === SHADOW OCCLUSION (blocker count only) ===
|
||||||
|
// Input: 1D array of element IDs (0 = empty, 1 = transparent, 2+ = opaque)
|
||||||
|
// Output: blocker count per pixel
|
||||||
|
EMSCRIPTEN_KEEPALIVE
|
||||||
|
void compute_blockers_u8(
|
||||||
|
const uint8_t* grid, // width × height
|
||||||
|
uint8_t* blockers_out, // same size
|
||||||
|
int width, int height,
|
||||||
|
const int8_t* coords, // [dx0,dy0,dx1,dy1,...] length = 2*N
|
||||||
|
int coord_count // N
|
||||||
|
) {
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
uint8_t blockers = 0;
|
||||||
|
for (int i = 0; i < coord_count; ++i) {
|
||||||
|
int nx = x + coords[i * 2];
|
||||||
|
int ny = y + coords[i * 2 + 1];
|
||||||
|
if (nx < 0 || nx >= width || ny < 0 || ny >= height) {
|
||||||
|
blockers++;
|
||||||
|
} else {
|
||||||
|
uint8_t val = grid[ny * width + nx];
|
||||||
|
if (val >= 2) blockers++; // opaque
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockers_out[y * width + x] = blockers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,893 @@
|
||||||
|
// xVS_cal_wasm.js - xVS wasm version calculation kernel, IT IS NOT A MOD.
|
||||||
|
|
||||||
|
// include: shell.js
|
||||||
|
// include: minimum_runtime_check.js
|
||||||
|
// end include: minimum_runtime_check.js
|
||||||
|
var Module = typeof Module != 'undefined' ? Module : {};
|
||||||
|
|
||||||
|
// Determine the runtime environment we are in. You can customize this by
|
||||||
|
// setting the ENVIRONMENT setting at compile time (see settings.js).
|
||||||
|
|
||||||
|
// Attempt to auto-detect the environment
|
||||||
|
var ENVIRONMENT_IS_WEB = !!globalThis.window;
|
||||||
|
var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope;
|
||||||
|
// N.b. Electron.js environment is simultaneously a NODE-environment, but
|
||||||
|
// also a web environment.
|
||||||
|
var ENVIRONMENT_IS_NODE = globalThis.process?.versions?.node && globalThis.process?.type != 'renderer';
|
||||||
|
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
|
||||||
|
|
||||||
|
// --pre-jses are emitted after the Module integration code, so that they can
|
||||||
|
// refer to Module (if they choose; they can also define Module)
|
||||||
|
|
||||||
|
|
||||||
|
var arguments_ = [];
|
||||||
|
var thisProgram = './this.program';
|
||||||
|
var quit_ = (status, toThrow) => {
|
||||||
|
throw toThrow;
|
||||||
|
};
|
||||||
|
|
||||||
|
// In MODULARIZE mode _scriptName needs to be captured already at the very top of the page immediately when the page is parsed, so it is generated there
|
||||||
|
// before the page load. In non-MODULARIZE modes generate it here.
|
||||||
|
var _scriptName = globalThis.document?.currentScript?.src;
|
||||||
|
|
||||||
|
if (typeof __filename != 'undefined') { // Node
|
||||||
|
_scriptName = __filename;
|
||||||
|
} else
|
||||||
|
if (ENVIRONMENT_IS_WORKER) {
|
||||||
|
_scriptName = self.location.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
// `/` should be present at the end if `scriptDirectory` is not empty
|
||||||
|
var scriptDirectory = '';
|
||||||
|
function locateFile(path) {
|
||||||
|
if (Module['locateFile']) {
|
||||||
|
return Module['locateFile'](path, scriptDirectory);
|
||||||
|
}
|
||||||
|
return scriptDirectory + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks that are implemented differently in different runtime environments.
|
||||||
|
var readAsync, readBinary;
|
||||||
|
|
||||||
|
if (ENVIRONMENT_IS_NODE) {
|
||||||
|
|
||||||
|
// These modules will usually be used on Node.js. Load them eagerly to avoid
|
||||||
|
// the complexity of lazy-loading.
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
scriptDirectory = __dirname + '/';
|
||||||
|
|
||||||
|
// include: node_shell_read.js
|
||||||
|
readBinary = (filename) => {
|
||||||
|
// We need to re-wrap `file://` strings to URLs.
|
||||||
|
filename = isFileURI(filename) ? new URL(filename) : filename;
|
||||||
|
var ret = fs.readFileSync(filename);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
readAsync = async (filename, binary = true) => {
|
||||||
|
// See the comment in the `readBinary` function.
|
||||||
|
filename = isFileURI(filename) ? new URL(filename) : filename;
|
||||||
|
var ret = fs.readFileSync(filename, binary ? undefined : 'utf8');
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
// end include: node_shell_read.js
|
||||||
|
if (process.argv.length > 1) {
|
||||||
|
thisProgram = process.argv[1].replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments_ = process.argv.slice(2);
|
||||||
|
|
||||||
|
// MODULARIZE will export the module in the proper place outside, we don't need to export here
|
||||||
|
if (typeof module != 'undefined') {
|
||||||
|
module['exports'] = Module;
|
||||||
|
}
|
||||||
|
|
||||||
|
quit_ = (status, toThrow) => {
|
||||||
|
process.exitCode = status;
|
||||||
|
throw toThrow;
|
||||||
|
};
|
||||||
|
|
||||||
|
} else
|
||||||
|
|
||||||
|
// Note that this includes Node.js workers when relevant (pthreads is enabled).
|
||||||
|
// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and
|
||||||
|
// ENVIRONMENT_IS_NODE.
|
||||||
|
if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
|
||||||
|
try {
|
||||||
|
scriptDirectory = new URL('.', _scriptName).href; // includes trailing slash
|
||||||
|
} catch {
|
||||||
|
// Must be a `blob:` or `data:` URL (e.g. `blob:http://site.com/etc/etc`), we cannot
|
||||||
|
// infer anything from them.
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// include: web_or_worker_shell_read.js
|
||||||
|
if (ENVIRONMENT_IS_WORKER) {
|
||||||
|
readBinary = (url) => {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', url, false);
|
||||||
|
xhr.responseType = 'arraybuffer';
|
||||||
|
xhr.send(null);
|
||||||
|
return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
readAsync = async (url) => {
|
||||||
|
// Fetch has some additional restrictions over XHR, like it can't be used on a file:// url.
|
||||||
|
// See https://github.com/github/fetch/pull/92#issuecomment-140665932
|
||||||
|
// Cordova or Electron apps are typically loaded from a file:// url.
|
||||||
|
// So use XHR on webview if URL is a file URL.
|
||||||
|
if (isFileURI(url)) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', url, true);
|
||||||
|
xhr.responseType = 'arraybuffer';
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
|
||||||
|
resolve(xhr.response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reject(xhr.status);
|
||||||
|
};
|
||||||
|
xhr.onerror = reject;
|
||||||
|
xhr.send(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var response = await fetch(url, { credentials: 'same-origin' });
|
||||||
|
if (response.ok) {
|
||||||
|
return response.arrayBuffer();
|
||||||
|
}
|
||||||
|
throw new Error(response.status + ' : ' + response.url);
|
||||||
|
};
|
||||||
|
// end include: web_or_worker_shell_read.js
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = console.log.bind(console);
|
||||||
|
var err = console.error.bind(console);
|
||||||
|
|
||||||
|
// end include: shell.js
|
||||||
|
|
||||||
|
// include: preamble.js
|
||||||
|
// === Preamble library stuff ===
|
||||||
|
|
||||||
|
// Documentation for the public APIs defined in this file must be updated in:
|
||||||
|
// site/source/docs/api_reference/preamble.js.rst
|
||||||
|
// A prebuilt local version of the documentation is available at:
|
||||||
|
// site/build/text/docs/api_reference/preamble.js.txt
|
||||||
|
// You can also build docs locally as HTML or other formats in site/
|
||||||
|
// An online HTML version (which may be of a different version of Emscripten)
|
||||||
|
// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html
|
||||||
|
|
||||||
|
var wasmBinary;
|
||||||
|
|
||||||
|
// Wasm globals
|
||||||
|
|
||||||
|
//========================================
|
||||||
|
// Runtime essentials
|
||||||
|
//========================================
|
||||||
|
|
||||||
|
// whether we are quitting the application. no code should run after this.
|
||||||
|
// set in exit() and abort()
|
||||||
|
var ABORT = false;
|
||||||
|
|
||||||
|
// set by exit() and abort(). Passed to 'onExit' handler.
|
||||||
|
// NOTE: This is also used as the process return code code in shell environments
|
||||||
|
// but only when noExitRuntime is false.
|
||||||
|
var EXITSTATUS;
|
||||||
|
|
||||||
|
// In STRICT mode, we only define assert() when ASSERTIONS is set. i.e. we
|
||||||
|
// don't define it at all in release modes. This matches the behaviour of
|
||||||
|
// MINIMAL_RUNTIME.
|
||||||
|
// TODO(sbc): Make this the default even without STRICT enabled.
|
||||||
|
/** @type {function(*, string=)} */
|
||||||
|
function assert(condition, text) {
|
||||||
|
if (!condition) {
|
||||||
|
// This build was created without ASSERTIONS defined. `assert()` should not
|
||||||
|
// ever be called in this configuration but in case there are callers in
|
||||||
|
// the wild leave this simple abort() implementation here for now.
|
||||||
|
abort(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether filename is delivered via file protocol (as opposed to http/https)
|
||||||
|
* @noinline
|
||||||
|
*/
|
||||||
|
var isFileURI = (filename) => filename.startsWith('file://');
|
||||||
|
|
||||||
|
// include: runtime_common.js
|
||||||
|
// include: runtime_stack_check.js
|
||||||
|
// end include: runtime_stack_check.js
|
||||||
|
// include: runtime_exceptions.js
|
||||||
|
// end include: runtime_exceptions.js
|
||||||
|
// include: runtime_debug.js
|
||||||
|
// end include: runtime_debug.js
|
||||||
|
// include: binaryDecode.js
|
||||||
|
// Prevent Closure from minifying the binaryDecode() function, or otherwise
|
||||||
|
// Closure may analyze through the WASM_BINARY_DATA placeholder string into this
|
||||||
|
// function, leading into incorrect results.
|
||||||
|
/** @noinline */
|
||||||
|
function binaryDecode(bin) {
|
||||||
|
for (var i = 0, l = bin.length, o = new Uint8Array(l), c; i < l; ++i) {
|
||||||
|
c = bin.charCodeAt(i);
|
||||||
|
o[i] = ~c >> 8 & c; // Recover the null byte in a manner that is compatible with https://crbug.com/453961758
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
// end include: binaryDecode.js
|
||||||
|
// Memory management
|
||||||
|
var
|
||||||
|
/** @type {!Int8Array} */
|
||||||
|
HEAP8,
|
||||||
|
/** @type {!Uint8Array} */
|
||||||
|
HEAPU8,
|
||||||
|
/** @type {!Int16Array} */
|
||||||
|
HEAP16,
|
||||||
|
/** @type {!Uint16Array} */
|
||||||
|
HEAPU16,
|
||||||
|
/** @type {!Int32Array} */
|
||||||
|
HEAP32,
|
||||||
|
/** @type {!Uint32Array} */
|
||||||
|
HEAPU32,
|
||||||
|
/** @type {!Float32Array} */
|
||||||
|
HEAPF32,
|
||||||
|
/** @type {!Float64Array} */
|
||||||
|
HEAPF64;
|
||||||
|
|
||||||
|
// BigInt64Array type is not correctly defined in closure
|
||||||
|
var
|
||||||
|
/** not-@type {!BigInt64Array} */
|
||||||
|
HEAP64,
|
||||||
|
/* BigUint64Array type is not correctly defined in closure
|
||||||
|
/** not-@type {!BigUint64Array} */
|
||||||
|
HEAPU64;
|
||||||
|
|
||||||
|
var runtimeInitialized = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function updateMemoryViews() {
|
||||||
|
var b = wasmMemory.buffer;
|
||||||
|
HEAP8 = new Int8Array(b);
|
||||||
|
HEAP16 = new Int16Array(b);
|
||||||
|
HEAPU8 = new Uint8Array(b);
|
||||||
|
HEAPU16 = new Uint16Array(b);
|
||||||
|
HEAP32 = new Int32Array(b);
|
||||||
|
HEAPU32 = new Uint32Array(b);
|
||||||
|
HEAPF32 = new Float32Array(b);
|
||||||
|
HEAPF64 = new Float64Array(b);
|
||||||
|
HEAP64 = new BigInt64Array(b);
|
||||||
|
HEAPU64 = new BigUint64Array(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// include: memoryprofiler.js
|
||||||
|
// end include: memoryprofiler.js
|
||||||
|
// end include: runtime_common.js
|
||||||
|
function preRun() {
|
||||||
|
if (Module['preRun']) {
|
||||||
|
if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
|
||||||
|
while (Module['preRun'].length) {
|
||||||
|
addOnPreRun(Module['preRun'].shift());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Begin ATPRERUNS hooks
|
||||||
|
callRuntimeCallbacks(onPreRuns);
|
||||||
|
// End ATPRERUNS hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
function initRuntime() {
|
||||||
|
runtimeInitialized = true;
|
||||||
|
|
||||||
|
// No ATINITS hooks
|
||||||
|
|
||||||
|
wasmExports['__wasm_call_ctors']();
|
||||||
|
|
||||||
|
// No ATPOSTCTORS hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
function postRun() {
|
||||||
|
// PThreads reuse the runtime from the main thread.
|
||||||
|
|
||||||
|
if (Module['postRun']) {
|
||||||
|
if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
|
||||||
|
while (Module['postRun'].length) {
|
||||||
|
addOnPostRun(Module['postRun'].shift());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin ATPOSTRUNS hooks
|
||||||
|
callRuntimeCallbacks(onPostRuns);
|
||||||
|
// End ATPOSTRUNS hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {string|number=} what */
|
||||||
|
function abort(what) {
|
||||||
|
Module['onAbort']?.(what);
|
||||||
|
|
||||||
|
what = 'Aborted(' + what + ')';
|
||||||
|
// TODO(sbc): Should we remove printing and leave it up to whoever
|
||||||
|
// catches the exception?
|
||||||
|
err(what);
|
||||||
|
|
||||||
|
ABORT = true;
|
||||||
|
|
||||||
|
what += '. Build with -sASSERTIONS for more info.';
|
||||||
|
|
||||||
|
// Use a wasm runtime error, because a JS error might be seen as a foreign
|
||||||
|
// exception, which means we'd run destructors on it. We need the error to
|
||||||
|
// simply make the program stop.
|
||||||
|
// FIXME This approach does not work in Wasm EH because it currently does not assume
|
||||||
|
// all RuntimeErrors are from traps; it decides whether a RuntimeError is from
|
||||||
|
// a trap or not based on a hidden field within the object. So at the moment
|
||||||
|
// we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that
|
||||||
|
// allows this in the wasm spec.
|
||||||
|
|
||||||
|
// Suppress closure compiler warning here. Closure compiler's builtin extern
|
||||||
|
// definition for WebAssembly.RuntimeError claims it takes no arguments even
|
||||||
|
// though it can.
|
||||||
|
// TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed.
|
||||||
|
/** @suppress {checkTypes} */
|
||||||
|
var e = new WebAssembly.RuntimeError(what);
|
||||||
|
|
||||||
|
// Throw the error whether or not MODULARIZE is set because abort is used
|
||||||
|
// in code paths apart from instantiation where an exception is expected
|
||||||
|
// to be thrown when abort is called.
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wasmBinaryFile;
|
||||||
|
|
||||||
|
function findWasmBinary() {
|
||||||
|
return binaryDecode(' | ||||||