Files
Kepler/engine/resourceManager.js

1097 lines
75 KiB
JavaScript
Raw Normal View History

2025-11-17 17:18:43 +01:00
/**
* Kepler - Core
*
* All rights reserved.
*
* Author: Kaj Dijksta
*
**/
import programInfo from "./programInfo.js";
import {vector2, vector3} from "./math.js";
2025-11-17 17:18:43 +01:00
/**
* ResourceManager object
*/
class resourceManager{
2025-11-17 17:18:43 +01:00
constructor( engine ) {
this.engine = engine;
this.gl = engine.gl;
this.baseUrl = "";
this.directory = "media/textures/";
this.numberOfModels = 0;
this.numberOfTextures = 0;
this.textureTotal = 0;
this.textures = new Array();
this.models = new Array();
this.shaders = new Array();
this.finishCallback = null;
this.loadingList = new Array();
this.fileCache = new Array();
this.programCashe = new Array();
this.preloaderElement = null;
this.preloaderTextElement = null;
this.preloaderBarElement = null;
this.preloaderVisible = false;
this.fileLoading = false;
this.fileLoadedBytes = 0;
this.fileTotalBytes = 0;
this.fileLabel = "";
2025-11-17 17:18:43 +01:00
}
/**
* add program
* @param {string} url
*/
loadShader( url ) {
var shader = new shader();
shader.createFomFile(url);
this.shaders.push(shader);
}
getShader( url ) {
var shaders = this.shaders;
for(var c = 0; c<shaders.length; c++) {
if(shaders[c].url == url) {
return shaders[c];
}
}
console.log("shader "+ url +" not loaded ");
return false;
}
/**
* add program
* @param {string} url
*/
addProgram( programInfo ) {
this.programCashe.push(programInfo);
}
/**
* get shader program by url and array of pragma's
* @param {string} url
*/
getShaderProgram(url, pragmas, librarys) {
var programInfo = this.createShaderProgram(url, pragmas, librarys);
this.addProgram(programInfo);
return programInfo;
}
/**
* Create shader program based on shader url and pragma array
* @param {string} url
*/
createShaderProgram(url, pragmas, librarys) {
var rawShaderText = kepler.resources.loadTextFileSynchronous(url, pragmas);
var splitShader = rawShaderText.split("// #keplerEngine - Split");
var vertextShaderRawText = splitShader[0];
var fragmentShaderRawText = splitShader[1];
var vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
var fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
for(var c = 0; c<librarys.length; c++) {
var lib = librarys[c];
if(lib.type == 0) {
vertextShaderRawText = lib.content + vertextShaderRawText;
} else {
var index = fragmentShaderRawText.indexOf("void main()");
fragmentShaderRawText = insertAt(fragmentShaderRawText, index, lib.content);
}
rawShaderText = lib.content + rawShaderText;
}
//add pragma's
for(var c = 0; c<pragmas.length; c++) {
var currentPragma = pragmas[c];
switch (currentPragma.type) {
case "define":
var pragma = "#define " + currentPragma.name + " " + currentPragma.value + ' \n ';
fragmentShaderRawText = pragma + fragmentShaderRawText;
vertextShaderRawText = pragma + vertextShaderRawText;
break;
}
}
var shader_version_3 = fragmentShaderRawText.includes("#version 300 es");
if(shader_version_3) {
fragmentShaderRawText = fragmentShaderRawText.replace("#version 300 es",'');
fragmentShaderRawText = fragmentShaderRawText.replace("#version 300 es",'');
vertextShaderRawText = vertextShaderRawText.replace("#version 300 es",'');
vertextShaderRawText = vertextShaderRawText.replace("#version 300 es",'');
fragmentShaderRawText = "#version 300 es \n" + fragmentShaderRawText;
vertextShaderRawText = "#version 300 es \n" + vertextShaderRawText;
}
//console.log(fragmentShaderRawText);
//console.log(vertextShaderRawText);
this.rawShader = fragmentShaderRawText + vertextShaderRawText;
this.gl.shaderSource(vertexShader, vertextShaderRawText);
this.gl.compileShader(vertexShader);
this.gl.shaderSource(fragmentShader, fragmentShaderRawText);
this.gl.compileShader(fragmentShader);
var program = this.gl.createProgram();
this.gl.attachShader(program, vertexShader);
this.gl.attachShader(program, fragmentShader);
if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS)) {
alert(this.gl.getShaderInfoLog(vertexShader));
return null;
}
if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS)) {
alert(this.gl.getShaderInfoLog(fragmentShader));
return null;
}
this.gl.linkProgram(program);
if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
this.gl.useProgram(program);
var programInfo = {};
programInfo.program = program;
programInfo.rawData = this.rawShader;
programInfo.vertextShaderRawText = vertextShaderRawText;
programInfo.vertexShader = vertexShader;
programInfo.fragmentShader = fragmentShader;
programInfo.url = url;
programInfo.pragmas = pragmas;
programInfo.librarys = librarys;
console.log('compiled shader: ', programInfo);
return programInfo;
}
get_filesize(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("HEAD", url, true); // Notice "HEAD" instead of "GET",
// to get only the header
xhr.onreadystatechange = function(ev) {
if (this.readyState == this.DONE) {
console.log('ev', ev.target.responseURL);
var url = ev.target.responseURL;
var size = parseInt(ev.target.getResponseHeader("Content-Length"));
//this.engine.loader.add(url, size );
}
};
xhr.send();
}
/**
* load url
* @param {string} url
*/
loadTextFileSynchronous(url) {
///this.get_filesize( url );
var fileContent = this.engine.resources.getFile(url)
if(fileContent) {
return fileContent.data;
}
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
if (request.overrideMimeType) {
request.overrideMimeType('text/plain');
}
} else if (window.ActiveXObject) {
request = new ActiveXObject('MSXML2.XMLHTTP.3.0');
} else {
throw 'XMLHttpRequest is disabled';
}
request.onreadystatechange = function (evt) {
//this.engine.loader.finish( evt.target.responseURL );
};
request.open('GET', this.baseUrl + url, false);
request.send();
if (request.readyState != 4) {
throw error;
}
var data = request.responseText;
if(!fileContent) {
//this.get_filesize( url );
}
this.addFile(data, url);
return data;
}
/**
* Add file
* @param {string} date (Filedata)
* @param {string} url
*/
addFile(data, url) {
var file = {};
file.data = data;
file.url = url;
this.fileCache.push(file);
}
/**
* Get file
* @param {string} url
*/
getFile(url) {
for(var c = 0; c<this.fileCache.length; c++) {
var file = this.fileCache[c];
if(file.url == url)
return file;
}
return false;
}
formatBytesToMegabytes( bytes ) {
if( bytes <= 0 ) {
return "0.00 MB";
}
var megabytes = bytes / ( 1024 * 1024 );
var rounded = Math.round( megabytes * 100 ) / 100;
var text = rounded.toFixed( 2 ) + " MB";
return text;
}
createPreloaderElements( ) {
if( this.preloaderElement !== null ) {
return;
}
var canvasElement = document.getElementById( "keplerEngine" );
var holder = document.createElement( "div" );
holder.className = "kepler-preloader-holder";
holder.style.position = "absolute";
holder.style.left = "0";
holder.style.top = "0";
holder.style.right = "0";
holder.style.bottom = "0";
holder.style.display = "flex";
holder.style.alignItems = "center";
holder.style.justifyContent = "center";
holder.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
var inner = document.createElement( "div" );
inner.className = "kepler-preloader-inner";
inner.style.minWidth = "200px";
inner.style.maxWidth = "400px";
inner.style.width = "60%";
inner.style.backgroundColor = "#202020";
inner.style.padding = "16px";
inner.style.boxSizing = "border-box";
inner.style.borderRadius = "4px";
var textElement = document.createElement( "div" );
textElement.className = "kepler-preloader-text";
textElement.style.color = "#ffffff";
textElement.style.fontFamily = "sans-serif";
textElement.style.fontSize = "14px";
textElement.style.marginBottom = "8px";
textElement.textContent = "Loading textures...";
var barOuter = document.createElement( "div" );
barOuter.className = "kepler-preloader-bar";
barOuter.style.width = "100%";
barOuter.style.height = "6px";
barOuter.style.backgroundColor = "#444444";
barOuter.style.overflow = "hidden";
var barInner = document.createElement( "div" );
barInner.className = "kepler-preloader-bar-inner";
barInner.style.width = "0%";
barInner.style.height = "100%";
barInner.style.backgroundColor = "#66ccff";
barOuter.appendChild( barInner );
inner.appendChild( textElement );
inner.appendChild( barOuter );
holder.appendChild( inner );
this.preloaderElement = holder;
this.preloaderTextElement = textElement;
this.preloaderBarElement = barInner;
if( canvasElement !== null && canvasElement.parentNode !== null ) {
canvasElement.parentNode.style.position = "relative";
canvasElement.parentNode.appendChild( holder );
} else {
document.body.appendChild( holder );
}
}
showPreloaderIfNeeded( ) {
if( this.preloaderVisible ) {
return;
}
this.createPreloaderElements( );
if( this.preloaderElement !== null ) {
this.preloaderElement.style.display = "flex";
}
this.preloaderVisible = true;
}
updatePreloaderProgress( ) {
if( this.preloaderElement === null ) {
return;
}
if( this.textureTotal <= 0 ) {
if( this.fileTotalBytes <= 0 ) {
return;
}
}
var loaded = this.textureTotal - this.numberOfTextures;
if( loaded < 0 ) {
loaded = 0;
}
if( loaded > this.textureTotal ) {
loaded = this.textureTotal;
}
var ratio = 0;
if( this.textureTotal > 0 ) {
ratio = loaded / this.textureTotal;
}
var percentage = Math.round( ratio * 100 );
if( this.preloaderTextElement !== null ) {
var text = "Loading textures " + loaded + " / " + this.textureTotal;
if( this.fileLoading && this.fileTotalBytes > 0 ) {
var label = this.fileLabel;
if( label === "" ) {
label = "model";
}
var loadedText = this.formatBytesToMegabytes( this.fileLoadedBytes );
var totalText = this.formatBytesToMegabytes( this.fileTotalBytes );
text = "Loading model " + label + " " + loadedText + " / " + totalText + " | " + text;
}
this.preloaderTextElement.textContent = text;
}
if( this.preloaderBarElement !== null ) {
this.preloaderBarElement.style.width = percentage + "%";
}
}
hidePreloader( ) {
if( this.preloaderElement === null ) {
return;
}
this.preloaderElement.style.display = "none";
this.preloaderVisible = false;
}
updateFilePreloaderProgress( label, loadedBytes, totalBytes, done ) {
if( done ) {
this.fileLoading = false;
this.fileLabel = "";
this.fileLoadedBytes = 0;
this.fileTotalBytes = 0;
if( this.textureTotal <= 0 ) {
this.hidePreloader( );
} else {
this.updatePreloaderProgress( );
}
} else {
this.fileLoading = true;
this.fileLabel = label;
this.fileLoadedBytes = loadedBytes;
this.fileTotalBytes = totalBytes;
this.showPreloaderIfNeeded( );
this.updatePreloaderProgress( );
}
}
2025-11-17 17:18:43 +01:00
/**
* Add Texture
* @param {string} address
* @param {string} name
* @param {boolean} direct
*/
addTexture(adress, name, direct) {
2025-11-17 17:18:43 +01:00
this.numberOfTextures++;
2025-11-17 17:18:43 +01:00
this.textureTotal++;
this.showPreloaderIfNeeded( );
this.updatePreloaderProgress( );
2025-11-17 17:18:43 +01:00
var texture = {};
2025-11-17 17:18:43 +01:00
texture.adress = adress;
2025-11-17 17:18:43 +01:00
texture.name = name;
2025-11-17 17:18:43 +01:00
texture.loaded = false;
this.loadingList.push( texture );
if( direct ) {
this.loadNextTexture( adress, name );
}
2025-11-17 17:18:43 +01:00
};
/**
* Load next texture
*/
loadNextTexture() {
if(this.loadingList.length > 0) {
var currentTexture = this.loadingList.pop();
this.loadTexture(currentTexture.adress, currentTexture.name);
}
for(var c = 0; c<this.loadingList.length; c++) {
//var currentTexture = this.loadingList.pop();
//this.loadTexture(currentTexture.adress, currentTexture.name);
}
}
/**
* Load Texture
* @param {string} url
* @param {string} name
* @param {string} force
*/
loadTexture(url, name, force){
var parts = url.split('.');
var extension = parts[parts.length-1];
//this.engine.setLoadingText("loading texture: " + url);
// if texture is compressed
if(extension == 'dds') {
var image = loadDDSTexture(url, name, function(texture, name, url){
//var texture = this.engine.textureFromDDS( mipmaps );
texture.name = name;
texture.url = url;
this.engine.resources.textures.push(texture);
this.engine.resources.loadNextTexture();
this.engine.resources.CheckIfAllObjectsTexturesLoaded(texture);
});
} else {
//this.get_filesize( url );
var image = new Image();
image.engine = this.engine;
image.onload = function(a) {
//var texture = this.engine.textureFromImage( this, this.width, this.height);
var texture = {};
texture.image = this;
texture.width = this.width;
texture.height = this.height;
texture.name = this.name;
texture.url = this.relativeSrc;
//updateLoadingBarTexture(this.engine.resources.textureTotal,this.engine.resources.textures.length);
this.engine.resources.textures.push(texture);
this.engine.resources.loadNextTexture();
this.engine.resources.CheckIfAllObjectsTexturesLoaded(texture);
//this.engine.loader.finish( image.src );
}
image.onerror = function (a) {
this.engine.resources.loadNextTexture();
if( this.engine.resources.textureTotal > 0 ) {
this.engine.resources.textureTotal--;
}
2025-11-17 17:18:43 +01:00
this.engine.resources.CheckIfAllObjectsTexturesLoaded();
}
image.name = name;
image.src = this.baseUrl + url;
image.relativeSrc = url;
}
}
hasExtension( name ){
var numOfParts = name.split(".").length;
if(numOfParts > 1) {
return true;
} else {
return false;
}
}
/**
* Get texture by name
* @param {String} name
*/
getTexture( name ) {
var index;
for( index = 0; index < this.textures.length; index++ ) {
if( this.textures[ index ].name === name ) {
return this.textures[ index ];
}
}
for( index = 0; index < this.loadingList.length; index++ ) {
if( this.loadingList[ index ].name === name ) {
return this.loadingList[ index ];
}
}
if( this.hasExtension( name ) ) {
2025-11-17 17:18:43 +01:00
var url = name;
this.addTexture( this.directory + url, name );
2025-11-17 17:18:43 +01:00
}
for( index = 0; index < this.loadingList.length; index++ ) {
if( this.loadingList[ index ].name === name ) {
return this.loadingList[ index ];
2025-11-17 17:18:43 +01:00
}
2025-11-17 17:18:43 +01:00
}
2025-11-17 17:18:43 +01:00
return false;
2025-11-17 17:18:43 +01:00
};
/**
* Get texture by name
* @param {String} name
*/
getLoadedTexture( name, viewport ) {
for(var c = 0;c<this.textures.length; c++) {
if(this.textures[c].name == name){
var textureInfo = this.textures[c];
var texture = this.engine.textureFromImage( textureInfo.image, viewport );
return texture;
}
}
return false;
};
/**
* Check if all objects are loaded
*/
CheckIfAllObjectsTexturesLoaded() {
2025-11-17 17:18:43 +01:00
this.numberOfTextures--;
this.updatePreloaderProgress( );
if( this.numberOfTextures === 0 ) {
this.hidePreloader( );
2025-11-17 17:18:43 +01:00
this.finishCallback();
2025-11-17 17:18:43 +01:00
}
};
/**
* copy a array buffer
* @param {array} source
* @param {array} destination
* @param {int} byteofffset
* @param {int} number of bytes
*/
arrayBufferCopy(src, dst, dstByteOffset, numBytes) {
var dst32Offset = dstByteOffset / 4,
tail = (numBytes % 4),
src32 = new Uint32Array(src.buffer, 0, (numBytes - tail) / 4),
dst32 = new Uint32Array(dst.buffer),
i;
for (i = 0; i < src32.length; i++) {
dst32[dst32Offset + i] = src32[i];
}
for (i = numBytes - tail; i < numBytes; i++) {
dst[dstByteOffset + i] = src[i];
}
}
/**
* load dds texture
* @param {string} source
* @param {string} name
* @param {function} callback
*/
loadDDSTexture(src, name, callback) {
loadDDSTextureEx(src, name, texture, true, callback);
return texture;
}
/**
* load dds textureEx
* @param {string} url
* @param {string} name
* @param {textureObject} texture
* @param {boolean} loadMipmaps
* @param {function} callback
*/
loadDDSTextureEx( url, name, texture, loadMipmaps, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "arraybuffer";
xhr.onload = function (){
if(this.status == 200) {
var mipmaps = uploadDDSLevels(this.engine.extensions.textureCompression, this.response, loadMipmaps);
texture = new texture();
texture.mipmaps = mipmaps;
texture.dataType = 'COMPRESSED_RGBA';
}
if(callback) {
callback(texture, name, url);
}
};
xhr.send(null);
return texture;
}
/**
* Upload DDS Levels
* @param {string} ext
* @param {arrayBuffer} arrayBuffer
* @param {boolean} loadMipmaps
* @param {texture} texture
*/
uploadDDSLevels(ext, arrayBuffer, loadMipmaps, texture) {
var DDS_MAGIC = 0x20534444;
var DDSD_CAPS = 0x1,
DDSD_HEIGHT = 0x2,
DDSD_WIDTH = 0x4,
DDSD_PITCH = 0x8,
DDSD_PIXELFORMAT = 0x1000,
DDSD_MIPMAPCOUNT = 0x20000,
DDSD_LINEARSIZE = 0x80000,
DDSD_DEPTH = 0x800000;
var DDSCAPS_COMPLEX = 0x8,
DDSCAPS_MIPMAP = 0x400000,
DDSCAPS_TEXTURE = 0x1000;
var DDSCAPS2_CUBEMAP = 0x200,
DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
DDSCAPS2_VOLUME = 0x200000;
var DDPF_ALPHAPIXELS = 0x1,
DDPF_ALPHA = 0x2,
DDPF_FOURCC = 0x4,
DDPF_RGB = 0x40,
DDPF_YUV = 0x200,
DDPF_LUMINANCE = 0x20000;
function FourCCToInt32(value) {
return value.charCodeAt(0) +
(value.charCodeAt(1) << 8) +
(value.charCodeAt(2) << 16) +
(value.charCodeAt(3) << 24);
}
function Int32ToFourCC(value) {
return String.fromCharCode(
value & 0xff,
(value >> 8) & 0xff,
(value >> 16) & 0xff,
(value >> 24) & 0xff
);
}
var FOURCC_DXT1 = FourCCToInt32("DXT1");
var FOURCC_DXT5 = FourCCToInt32("DXT5");
var headerLengthInt = 31; // The header length in 32 bit ints
// Offsets into the header array
var off_magic = 0;
var off_size = 1;
var off_flags = 2;
var off_height = 3;
var off_width = 4;
var off_mipmapCount = 7;
var off_pfFlags = 20;
var off_pfFourCC = 21;
var header = new Int32Array(arrayBuffer, 0, headerLengthInt),
fourCC, blockBytes, internalFormat,
width, height, dataLength, dataOffset,
byteArray, mipmapCount, i;
if(header[off_magic] != DDS_MAGIC) {
console.error("Invalid magic number in DDS header");
return 0;
}
if(!header[off_pfFlags] & DDPF_FOURCC) {
console.error("Unsupported format, must contain a FourCC code");
return 0;
}
fourCC = header[off_pfFourCC];
switch(fourCC) {
case FOURCC_DXT1:
blockBytes = 8;
internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case FOURCC_DXT5:
blockBytes = 16;
internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
console.error("Unsupported FourCC code:", Int32ToFourCC(fourCC));
return null;
}
mipmapCount = 1;
if(header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) {
mipmapCount = Math.max(1, header[off_mipmapCount]);
}
width = header[off_width];
height = header[off_height];
dataOffset = header[off_size] + 4;
var mipmaps = [];
for(i = 0; i < mipmapCount; ++i) {
var mipmap = {}; // createObject("mipmap");
mipmap.dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes;
mipmap.byteArray = new Uint8Array(arrayBuffer, dataOffset, mipmap.dataLength);
mipmap.height = width;
mipmap.width = height;
mipmap.internalFormat = internalFormat;
mipmaps.push(mipmap);
dataOffset += dataLength;
width *= 0.5;
height *= 0.5;
}
return mipmaps;
}
}
export {resourceManager as default};
var randomImage = new Uint8Array ( [255,0,255,255,0,143,255,255,62,0,255,255,0,3,255,255,0,0,255,255,0,162,255,255,180,180,255,255,0,0,255,255,255,0,255,255,24,0,255,255,249,55,255,255,0,0,255,255,190,170,255,255,253,31,255,255,172,188,255,255,254,16,255,255,141,0,255,255,219,0,255,255,0,126,255,255,0,0,255,255,27,254,255,255,219,130,255,255,0,255,255,255,141,0,255,255,0,239,255,255,248,61,255,255,203,154,255,255,254,0,255,255,0,170,255,255,0,8,255,255,0,229,255,255,0,0,255,255,55,249,255,255,0,0,255,255,0,241,255,255,254,26,255,255,0,0,255,255,242,81,255,255,255,0,255,255,229,112,255,255,0,238,255,255,207,0,255,255,0,0,255,255,255,0,255,255,254,0,255,255,254,0,255,255,0,89,255,255,0,35,255,255,0,0,255,255,0,0,255,255,0,0,255,255,240,87,255,255,0,255,255,255,0,0,255,255,0,133,255,255,255,0,255,255,104,0,255,255,55,249,255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,201,156,255,255,221,127,255,255,255,0,255,255,0,135,255,255,0,0,255,255,255,0,255,255,0,0,255,255,255,0,255,255,0,67,255,255,148,0,255,255,249,0,255,255,75,0,255,255,0,0,255,255,252,38,255,255,0,1,255,255,0,108,255,255,250,0,255,255,0,0,255,255,170,190,255,255,0,255,255,255,0,15,255,255,0,76,255,255,224,123,255,255,0,231,255,255,0,255,255,255,247,64,255,255,213,140,255,255,0,195,255,255,102,234,255,255,0,0,255,255,241,84,255,255,0,0,255,255,0,0,255,255,0,197,255,255,27,254,255,255,196,0,255,255,71,245,255,255,0,0,255,255,71,245,255,255,0,0,255,255,246,65,255,255,0,3,255,255,0,0,255,255,147,0,255,255,210,0,255,255,255,0,255,255,0,0,255,255,251,0,255,255,0,25,255,255,68,246,255,255,242,81,255,255,0,16,255,255,0,42,255,255,117,0,255,255,250,50,255,255,255,0,255,255,255,6,255,255,255,12,255,255,252,0,255,255,0,0,255,255,0,0,255,255,40,0,255,255,0,0,255,255,165,0,255,255,175,185,255,255,0,221,255,255,0,90,255,255,236,0,255,255,180,0,255,255,0,0,255,255,0,0,255,255,0,253,255,255,255,0,255,255,0,32,255,255,0,0,255,255,0,0,255,255,159,200,255,255,255,0,255,255,195,0,255,255,0,71,255,255,3,255,255,255,255,0,255,255,0,0,255,255,241,0,255,255,238,92,255,255,193,0,255,255,189,0,255,255,0,245,255,255,0,0,255,255,171,189,255,255,242,81,255,255,0,48,255,255,0,42,255,255,0,0,255,255,102,234,255,255,8,0,255,255,180,180,255,255,152,204,255,255,0,15,255,255,150,0,255,255,0,0,255,255,59,248,255,255,167,193,255,255,0,3,255,255,255,10,255,255,124,0,255,255,213,140,255,255,0,0,255,255,0,254,255,255,0,0,255,255,0,0,255,255,0,0,255,255,219,131,255,255,86,240,255,255,0,104,255,255,255,1,255,255,7,0,255,255,0,0,255,255,0,23,255,255,49,0,255,255,0,0,255,255,155,203,255,255,0,254,255,255,180,0,255,255,145,210,255,255,138,0,255,255,255,7,255,255,145,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,8,255,255,0,236,255,255,150,206,255,255,0,102,255,255,187,0,255,255,0,5,255,255,255,1,255,255,182,0,255,255,102,0,255,255,0,0,255,255,242,0,255,255,149,0,255,255,0,0,255,255,0,5,255,255,254,24,255,255,255,0,255,255,0,0,255,255,255,0,255,255,0,1,255,255,0,210,255,255,0,1,255,255,114,0,255,255,0,0,255,255,205,0,255,255,251,43,255,255,189,171,255,255,0,0,255,255,149,207,255,255,0,0,255,255,0,251,255,255,76,0,255,255,0,159,255,255,0,0,255,255,0,68,255,255,0,164,255,255,0,254,255,255,0,169,255,255,202,0,255,255,0,0,255,255,222,0,255,255,0,0,255,255,0,0,255,255,255,0,255,255,178,0,255,255,180,180,255,255,210,0,255,255,210,0,255,255,0,0,255,255,0,121,255,255,247,62,255,255,0,255,255,255,0,163,255,255,0,47,255,255,248,0,255,255,243,0,255,255,0,180,255,255,0,0,255,255,137,215,255,255,192,0,255,255,255,0,255,255,255,0,255,255,244,75,255,255,0,0,255,255,165,0,255,255,0,0,255,255,180,0,255,255,0,147,255,255,246,0,255,255,0,0,255,255,190,170,255,255,0,8,255,255,254,0,255,255,0,165,255,255,50,250,255,255,0,5,255,255,4,0,255,255,195,0,255,255,255,10,255,255,255,0,255,255,0,164,255,255,0,0,255,255,0,0,255,255,0,0,255,255,212,0,255,255,254,21,255,255,0,90,255,255,255,5,255,255,226,118,255,255,0,0,255,255,249,0,255,255,0,178,255,255,0,187,255,255,0,70,255,255,0,33,255,255,0,0,255,255,0,35,255,255,0,188,255,255,0,0,255,255,241,82,255,255,0,249,255,255,237,93,255,255,254,17,255,255,180,180,255,255,0,0,255,2
var randomImage2 = new Uint8Array ( [149,123,253,255, 126,3,96,255, 164,246,98,255, 154,177,13,255,
52,83,220,255, 1,142,142,255, 32,57,79,255, 48,159,32,255,
56,232,114,255, 177,216,203,255, 69,196,217,255, 240,165,81,255,
224,56,85,255, 232,89,189,255, 143,25,202,255, 117,73,12,255] );