You've already forked Generator
First test features
This commit is contained in:
11
src/index.html
Normal file
11
src/index.html
Normal 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
60
src/libs/alea.mjs
Normal 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
49732
src/libs/three.mjs
Normal file
File diff suppressed because one or more lines are too long
17
src/main.mjs
Normal file
17
src/main.mjs
Normal 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 });
|
||||||
|
})();
|
||||||
14
src/modules/renderer/renderer.mjs
Normal file
14
src/modules/renderer/renderer.mjs
Normal 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
5
src/resources/style.css
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
html, body, div
|
||||||
|
{
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
78
src/utils/processUtils.mjs
Normal file
78
src/utils/processUtils.mjs
Normal 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
207
src/utils/workerUtils.mjs
Normal 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
18
src/workers/base.mjs
Normal 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]);
|
||||||
Reference in New Issue
Block a user