First test features

This commit is contained in:
Peaceultime 2021-11-22 00:20:02 +01:00
commit 8be7991b1d
9 changed files with 50142 additions and 0 deletions

11
src/index.html Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Map Generator</title>
<link rel="stylesheet" type="text/css" href="resources/style.css">
<script type="module" src="main.mjs"></script>
</head>
<body></body>
</html>

60
src/libs/alea.mjs Normal file
View File

@ -0,0 +1,60 @@
/**
* Copyright (C) 2010 by Johannes Baagøe baagoe@baagoe.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function masher() {
let n = 0xefc8249d;
return function(data) {
data = data.toString();
for (let i = 0; i < data.length; i++) {
n += data.charCodeAt(i);
let h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
};
}
export default function alea(seed)
{
let s0 = 0;
let s1 = 0;
let s2 = 0;
let c = 1;
const mash = masher();
s0 = mash(' ');
s1 = mash(' ');
s2 = mash(' ');
s0 -= mash(seed);
if (s0 < 0) {
s0 += 1;
}
s1 -= mash(seed);
if (s1 < 0) {
s1 += 1;
}
s2 -= mash(seed);
if (s2 < 0) {
s2 += 1;
}
return function() {
const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
s0 = s1;
s1 = s2;
return s2 = t - (c = t | 0);
};
}

49732
src/libs/three.mjs Normal file

File diff suppressed because one or more lines are too long

17
src/main.mjs Normal file
View File

@ -0,0 +1,17 @@
import { Thread, supportThreads } from "./utils/workerUtils.mjs";
import Renderer from "./modules/renderer/renderer.mjs";
(async function()
{
globalThis.DEBUG = localStorage.getItem("debug");
globalThis.VERBOSE = DEBUG && localStorage.getItem("verbose");
globalThis.TIMINGS = DEBUG && localStorage.getItem("timings");
globalThis.RELEASE = !DEBUG;
const renderer = new Renderer();
renderer.render();
const thread = new Thread("./workers/base.mjs", "backend");
await thread.setup().complete();
const grid = await thread.test_fillArr({x: 20, y: 20, depth: 2, seed: 0, jitter: 3 });
})();

View File

@ -0,0 +1,14 @@
import * as Three from "../../libs/three.mjs";
import Thread from "../../utils/workerUtils.mjs";
class Renderer
{
static async init()
{
const renderer_thread = new Thread("../../workers/renderer.mjs", "renderer");
await renderer_thread.setup().complete();
renderer_thread.init(document.createElement("canvas").transferControlToOffscreen(), window.innerWidth, window.innerHeight);
}
}
export default Renderer;

5
src/resources/style.css Normal file
View File

@ -0,0 +1,5 @@
html, body, div
{
padding: 0;
margin: 0;
}

View File

@ -0,0 +1,78 @@
// WORKER SIDE //
const Constants = {
STATE: {
WAITING: 1,
OK: 2,
ERROR: 3,
},
REQUEST: {
SETUP: 1,
CALL: 2,
TERMINATE: 3
},
};
function send(ret)
{
globalThis.VERBOSE && console.debug(`Sending ${ret} to the main thread`);
postMessage([Constants.STATE.OK, ret]);
}
class Process
{
static _isSetup = false;
static setup()
{
globalThis.DEBUG && console.log(`Setting up process`);
Process._isSetup = true;
Process._customFn = {};
onmessage = Process.onmessage;
}
static onmessage(e)
{
globalThis.VERBOSE && console.debug(`Received ${e.data}`);
if(e && e.data)
{
switch(e.data[0])
{
case Constants.REQUEST.SETUP:
send(Object.keys(Process._customFn));
break;
case Constants.REQUEST.CALL:
send(Process._customFn[e.data[1]](...e.data[2]));
break;
case Constants.REQUEST.TERMINATE:
Process.cleanUp();
send();
break;
default:
throw new Error("Invalid message received in the Process");
break;
}
}
else
{
throw new Error("Can't find any data");
}
}
static register(name, fn)
{
if(!Process._isSetup)
Process.setup();
if(name in Process._customFn)
throw new Error("This function name is already registered in the process. Please use a different one.");
else
Process._customFn[name] = fn;
}
static cleanUp()
{
}
}
export { Process, Constants };

207
src/utils/workerUtils.mjs Normal file
View File

@ -0,0 +1,207 @@
"use strict";
// MAIN THREAD SIDE //
const supportThreads = !!globalThis.Process;
const Constants = {
STATE: {
WAITING: 1,
OK: 2,
ERROR: 3,
},
REQUEST: {
WAKEUP: 1,
SETUP: 2,
CALL: 3,
TERMINATE: 4
},
};
function cleanUp(thread)
{
thread._waiting = false;
thread._resolver = undefined;
thread._rejecter = undefined;
thread._calledFn = "";
}
function request(thread, req)
{
thread._promise = new Promise(function(res, rej) {
thread._waiting = true;
thread._resolver = res;
thread._rejecter = rej;
thread._worker && thread._worker.postMessage(req);
});
return thread._promise;
}
function parseReturns(data)
{
if(data.length != 2)
{
throw new Error("Invalid message");
}
if(data[0] !== Constants.STATE.OK)
{
throw new Error("Invalid message");
}
return data[1];
}
function wrapper(thread, fn)
{
return function()
{
globalThis.TIMINGS && console.time("Measuring " + fn);
return request(thread, [Constants.REQUEST.CALL, fn, [...arguments]]);
}
}
function onmessage(thread)
{
return function(e) {
const ret = parseReturns(e.data);
globalThis.VERBOSE && console.debug(`Received message from ${thread._name} containing ${ret}`);
if(thread._waiting && ret)
{
globalThis.DEBUG && console.log(`Resolving ${thread._name}`);
thread._ret = ret;
thread._resolver(ret);
cleanUp(thread);
}
}
}
function onerror(thread)
{
return function(e) {
console.error(`Received error from ${thread._name} containing ${e}`);
if(thread._waiting)
{
thread._rejecter(e);
cleanUp();
}
}
}
const Thread = Object.freeze(class
{
/**
* Creates a new Thread loading the linked file. The linked file must follow the Thread expectations.
* You can give it a optionnal name to make the debug easier.
*/
constructor(url, name)
{
this._worker = new Worker(url, {type: "module"});
this._name = name || url;
this._worker.onmessage = onmessage(this);
this._worker.onmerror = onerror(this);
cleanUp(this);
}
/**
* Will ask every custom function registered in the worker and add them in the prototype of this instance.
*/
async setup()
{
const result = await request(this, [Constants.REQUEST.SETUP]);
if(result && result.length)
{
for(let i = 0; i < result.length; i++)
{
const custom = result[i];
this[custom] = wrapper(this, custom);
}
}
}
complete()
{
if(this._waiting)
{
return this._promise;
}
else
{
return Promise.resolve(this._ret);
}
}
});
// WORKER SIDE //
function send(ret)
{
globalThis.VERBOSE && console.debug(`Sending ${ret} to the main thread`);
postMessage([Constants.STATE.OK, ret]);
}
class Process
{
static _isSetup = false;
static setup()
{
globalThis.DEBUG && console.log(`Setting up process`);
Process._isSetup = true;
Process._customFn = {};
globalThis.onmessage = Process.onmessage;
}
static onmessage(e)
{
globalThis.VERBOSE && console.debug(`Received ${e.data}`);
if(e && e.data)
{
switch(e.data[0])
{
case Constants.REQUEST.SETUP:
send(Object.keys(Process._customFn));
break;
case Constants.REQUEST.CALL:
send(Process._customFn[e.data[1]](...e.data[2]));
break;
case Constants.REQUEST.TERMINATE:
Process.cleanUp();
send();
break;
default:
throw new Error("Invalid message received in the Process");
break;
}
}
else
{
throw new Error("Can't find any data");
}
}
static register(mod, arr)
{
if(!Process._isSetup)
Process.setup();
if(!Array.isArray(arr))
arr = [arr];
for(let i = 0; i < arr.length; ++i)
{
if(typeof arr[i] != 'function')
throw new Error("You can only register functions");
const name = mod + "_" arr[i].name;
if(name in Process._customFn)
throw new Error("This function name is already registered in the process. Please use a different one.");
else
Process._customFn[name] = arr[i];
}
}
static cleanUp()
{
}
}
export { Thread, Process, supportThreads };

18
src/workers/base.mjs Normal file
View File

@ -0,0 +1,18 @@
import { Process } from "../utils/workerUtils.mjs";
import Noise from '../libs/alea.mjs'
//Settings contains x, y depth, seed, jitter
function fillArr(settings)
{
const noise = Noise(settings.seed);
const arr = new Float32Array(settings.x * settings.y * settings.depth);
for(let i = arr.length - 1; i >= 0; --i)
{
const _x = i % settings.x;
const _y = (i - _x) / settings.x;
arr[i] =
}
}
//Process.register is the equivalent of export
Process.register("test", [fillArr]);