From a00c0aab1eb5a7a55bef8ca08115bdd722ab5699 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Sun, 16 May 2021 19:50:15 -0400 Subject: Moved the frontend directory up so that it no longer exists --- 2d/_collisions/pill_line/#main.cpp# | 360 +++ 2d/_collisions/pill_line/build.sh | 16 + 2d/_collisions/pill_line/dist/output.js | 3377 +++++++++++++++++++++++++++++ 2d/_collisions/pill_line/dist/output.wasm | Bin 0 -> 58666 bytes 2d/_collisions/pill_line/main.cpp | 358 +++ 5 files changed, 4111 insertions(+) create mode 100644 2d/_collisions/pill_line/#main.cpp# create mode 100755 2d/_collisions/pill_line/build.sh create mode 100644 2d/_collisions/pill_line/dist/output.js create mode 100755 2d/_collisions/pill_line/dist/output.wasm create mode 100644 2d/_collisions/pill_line/main.cpp (limited to '2d/_collisions/pill_line') diff --git a/2d/_collisions/pill_line/#main.cpp# b/2d/_collisions/pill_line/#main.cpp# new file mode 100644 index 0000000..a3ede40 --- /dev/null +++ b/2d/_collisions/pill_line/#main.cpp# @@ -0,0 +1,360 @@ +#include "../../../shared_cpp/OrthographicRenderer.h" +#include "../../../shared_cpp/types.h" +#include "../../../shared_cpp/WebglContext.h" +#include "../../../shared_cpp/mathlib.h" +#include "../../../shared_cpp/MainLoop.h" +#include +#include +#include +#include +#include +#include + +// Side note: It is Eastertime, so I chose this easter color palette. Enjoy: https://htmlcolors.com/palette/144/easter + +struct Rigidbody { + Vector2 force = { 0, 0 }; + Vector2 velocity = { 0, 0 }; + Vector2 position = { 0, 0 }; + float32 rotationalVelocity = 0.f; + float32 rotation = 0.f; + float32 mass = 1.f; + float32 cofOfRestition = 1.f; + float32 momentOfInertia = 0.f; + + void reset() { + force = { 0, 0 }; + velocity = { 0, 0 }; + rotationalVelocity = 0.f; + rotation = 0.f; + } + + void applyForce(Vector2 f) { + force += f; + } + + void applyGravity() { + force += Vector2 { 0.f, -100.f }; + } + + void update(float32 deltaTimeSeconds) { + Vector2 acceleration = force / mass; + velocity += (acceleration * deltaTimeSeconds); + position += (velocity * deltaTimeSeconds); + force = Vector2 { 0.f, 0.f }; + + rotation += (rotationalVelocity * deltaTimeSeconds); + } + + void setMomentOfInertia(float32 moi) { + momentOfInertia = moi; + } +}; + +struct Pill { + OrthographicShape shape; + Rigidbody body; + float32 a = 0; + float32 b = 0; + + Pill copy() { + Pill retval; + retval.shape = shape; + retval.body = body; + retval.a = a; + retval.b = b; + return retval; + } + + void load(OrthographicRenderer* renderer, float32 numSegments, float32 width, float32 height) { + // Note that a so-called "pill" is simply an ellipse. + // Equation of an ellipse is: + // + // x^2 / a^2 + y^2 / b^2 = 1 + // + // or, in parametric form: + // + // x = a * cos(t), y = b * sin(t) + // + float32 angleIncrements = (2.f * PI) / numSegments; + uint32 numVertices = static_cast(numSegments * 3.f); + OrthographicVertex* vertices = new OrthographicVertex[numVertices]; + + a = width / 2.f; + b = height / 2.f; + + Vector4 color = Vector4().fromColor(243,166,207, 255); + + for (uint32 vertexIndex = 0; vertexIndex < numVertices; vertexIndex += 3) { + // Create a single "slice" of the ellipse (like a pizza) + float32 currAngle = (vertexIndex / 3.f) * angleIncrements; + float32 nextAngle = (vertexIndex / 3.f + 1.f) * angleIncrements; + + vertices[vertexIndex].position = Vector2 { 0.f, 0.f }; + vertices[vertexIndex].color = color; + + vertices[vertexIndex + 1].position = Vector2 { a * cosf(currAngle), b * sinf(currAngle) }; + vertices[vertexIndex + 1].color = color; + + vertices[vertexIndex + 2].position = Vector2 { a * cosf(nextAngle), b * sinf(nextAngle) }; + vertices[vertexIndex + 2].color = color; + } + + shape.load(vertices, numVertices, renderer); + body.reset(); + + // https://byjus.com/jee/moment-of-inertia-of-ellipse/ + body.momentOfInertia = (body.mass * (a * a + b * b)) / 4.f; + + a = width / 2.f; + b = height / 2.f; + + delete[] vertices; + } + + void update(float32 deltaTimeSeconds) { + body.update(deltaTimeSeconds); + shape.model = Mat4x4().translateByVec2(body.position).rotate2D(body.rotation); + } + + void render(OrthographicRenderer* renderer) { + shape.render(renderer); + } + + void unload() { + shape.unload(); + } + + float32 getArea() { + return 0.f; + } +}; + +struct LineSegment { + OrthographicShape shape; + Rigidbody body; + Vector2 start; + Vector2 end; + float32 length; + Vector2 normal; + OrthographicVertex vertices[2]; + + void load(OrthographicRenderer* renderer, Vector4 color, Vector2 inStart, Vector2 inEnd) { + start = inStart; + end = inEnd; + length = (start - end).length(); + vertices[0].position = start; + vertices[0].color = color; + vertices[1].position = end; + vertices[1].color = color; + normal = (end - start).getPerp().normalize(); + shape.load(vertices, 2, renderer); + + body.reset(); + body.mass = 1000000000.f; + body.cofOfRestition = 1.f; + body.rotationalVelocity = 0; + body.velocity = Vector2(); + body.momentOfInertia = body.mass * (length / 2.f); + } + + void render(OrthographicRenderer* renderer) { + shape.render(renderer, GL_LINES); + } + + void unload() { + shape.unload(); + } + + Vector2 getPointOnLine(float32 t) { + return { + start.x + (end.x - start.x) * t, + start.y + (end.y - start.y) * t, + }; + } + + Vector2 getNormal() { + return (end - start).getPerp().normalize(); + } +}; + +struct IntersectionResult { + bool intersect = false; + Vector2 collisionNormal; + Vector2 relativeVelocity; + Vector2 firstPointOfApplication; + Vector2 secondPointOfApplication; +}; + +EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); + +void load(); +void update(float32 time, void* userData); +void unload(); +IntersectionResult getIntersection(Pill* pill, LineSegment* segment); +void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* ir); + +// Global Variables +WebglContext context; +OrthographicRenderer renderer; +Pill pill; +MainLoop mainLoop; +LineSegment segmentList[4]; + +int main() { + context.init("#gl_canvas"); + emscripten_set_click_callback("#gl_canvas_play", NULL, false, onPlayClicked); + emscripten_set_click_callback("#gl_canvas_stop", NULL, false, onStopClicked); + return 0; +} + +void load() { + renderer.load(&context); + + pill.body.position = Vector2 { context.width / 2.f, context.height / 2.f }; + pill.load(&renderer, 64, 100.f, 50.f); + pill.body.rotationalVelocity = 0.3f; + + segmentList[0].load(&renderer, Vector4().fromColor(191, 251, 146, 255.f), Vector2 { 50.f, 0.f }, Vector2 { 50.f, static_cast(context.height) }); + segmentList[1].load(&renderer, Vector4().fromColor(159, 224, 210, 255.f), Vector2 { context.width - 50.f, 0.f }, Vector2 { context.width - 50.f, static_cast(context.height) }); + segmentList[2].load(&renderer, Vector4().fromColor(248, 255, 156, 255.f), Vector2 { 50.f, 50.f }, Vector2 { context.width - 50.f, 150.f }); + segmentList[3].load(&renderer, Vector4().fromColor(205, 178, 214, 255.f), Vector2 { 50.f, 150.f }, Vector2 { context.width - 50.f, 50.f }); + + mainLoop.run(update); +} + +float32 areaOfTriangle(Vector2 a, Vector2 b, Vector2 c) { + // Refernce for this for the formula: https://www.onlinemath4all.com/area-of-triangle-using-determinant-formula.html + return ABS(0.5 * (a.x * b.y - b.x * a.y + b.x * c.y - c.x * b.y + c.x * a.y - a.x * c.y)); +} + +IntersectionResult getIntersection(Pill* pill, LineSegment* segment) { + IntersectionResult result; + Mat4x4 inverseModel = pill->shape.model.inverse(); + Vector2 start = inverseModel * segment->start; + Vector2 end = inverseModel * segment->end; + Vector2 diff = end - start; + float32 A = (diff.x * diff.x) / (pill->a * pill->a) + (diff.y * diff.y) / (pill->b * pill->b); + float32 B = ((2 * start.x) * (end.x - start.x)) / (pill->a * pill->a) + ((2 * start.y) * (end.y - start.y)) / (pill->b * pill->b); + float32 C = (start.x * start.x) / (pill->a * pill->a) + (start.y * start.y) / (pill->b * pill->b) - 1.f; + + float32 determinant = B * B - 4 * A * C; + if (determinant < 0.f) { + result.intersect = false; + return result; + } + + float32 t1 = (-B + sqrtf(determinant)) / (2 * A); + float32 t2 = (-B - sqrtf(determinant)) / (2 * A); + + Vector2 pointOnLine = segment->getPointOnLine(t1); // This point is in world space and line space, since the line is in world space + Vector2 pointOnEllipse = pointOnLine - pill->body.position; // This point is in ellipse space + + //float32 parametricTEllipse = atan2f( pointOnEllipse.y / pill->b , pointOnEllipse.x / pill->a ); + //Vector2 tangent = { -pill->a * sinf(parametricTEllipse), pill->b * cosf(parametricTEllipse) }; + //Vector2 normal = tangent.getPerp(); + // Or, simply: Vector2 normal = { pointOnEllipse.x * (pill->b / pill->a), pointOnEllipse.y * (pill->a / pill->b) }; + Vector2 normal = segment->getNormal(); + + result.intersect = true; + result.relativeVelocity = pill->body.velocity - segment->body.velocity;; + result.collisionNormal = normal; + result.firstPointOfApplication = pointOnEllipse; + result.secondPointOfApplication = pointOnLine; + + return result; +} + +void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* ir) { + Vector2 relativeVelocity = ir->relativeVelocity; + Vector2 collisionNormal = ir->collisionNormal; + Vector2 firstPerp = ir->firstPointOfApplication.getPerp(); + Vector2 secondPerp = ir->secondPointOfApplication.getPerp(); + + float32 cofOfRestition = (first->cofOfRestition + second->cofOfRestition) / 2.f; + float32 lNumerator = (relativeVelocity * -(1.f + cofOfRestition)).dot(collisionNormal); + float32 lLinearDenomPart = collisionNormal.dot(collisionNormal * (1.f / first->mass + 1.f / second->mass)); + float32 lRotationalDenomPart = powf(firstPerp.dot(collisionNormal), 2.f) / first->momentOfInertia + powf(secondPerp.dot(collisionNormal), 2) / second->momentOfInertia; + + float32 lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart); + + first->velocity = first->velocity + (collisionNormal * (lImpulseMagnitude / first->mass)); + second->velocity = second->velocity - (collisionNormal * (lImpulseMagnitude / second->mass)); + + printf("%f\n", firstPerp.dot(collisionNormal * lImpulseMagnitude) / first->momentOfInertia); + + first->rotationalVelocity = first->rotationalVelocity + firstPerp.dot(collisionNormal * lImpulseMagnitude) / first->momentOfInertia; + second->rotationalVelocity = second->rotationalVelocity - secondPerp.dot(collisionNormal * lImpulseMagnitude) / second->momentOfInertia; +} + +void update(float32 deltaTimeSeconds, void* userData) { + + // Input + pill.body.applyGravity(); + + // Update + Pill copyPill = pill.copy(); + pill.update(deltaTimeSeconds); + + // Intersections + for (int32 lineIdx = 0; lineIdx < 4; lineIdx++) { + IntersectionResult ir = getIntersection(&pill, &segmentList[lineIdx]); + if (ir.intersect) { + + // Find the exact moment that the intersection happens by rewinding the simulation until we're not intersecting + IntersectionResult subIr = ir; + float32 subdividedTimeSeconds = deltaTimeSeconds; + do { + ir = subIr; + + pill = copyPill.copy(); + subdividedTimeSeconds /= 2.f; + pill.update(subdividedTimeSeconds); + + subIr = getIntersection(&pill, &segmentList[lineIdx]); + if (subdividedTimeSeconds == 0.f) { + printf("Error: Should not be happening.\n"); + break; + } + } while (subIr.intersect); + + printf("Found intersection at timestamp: %f\n", subdividedTimeSeconds); + resolveCollision(&pill.body, &segmentList[lineIdx].body, &ir); + pill.update(deltaTimeSeconds - subdividedTimeSeconds); + } + } + + // Render + renderer.render(); + pill.shape.render(&renderer); + + for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) { + segmentList[segmentIndex].render(&renderer); + } +} + +void unload() { + mainLoop.stop(); + pill.unload(); + renderer.unload(); + for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) { + segmentList[segmentIndex].unload(); + } +} + +// +// Interactions with DOM handled below +// +EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Play clicked\n"); + + load(); + return true; +} + +EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Stop clicked\n"); + unload(); + return true; +} diff --git a/2d/_collisions/pill_line/build.sh b/2d/_collisions/pill_line/build.sh new file mode 100755 index 0000000..892dddd --- /dev/null +++ b/2d/_collisions/pill_line/build.sh @@ -0,0 +1,16 @@ +filepaths="" + +for file in ../../../shared_cpp/*; do + if [ -f "$file" ]; then + if [[ "$file" == *.cpp ]]; then + filepaths+=" $file" + fi + fi +done + +if [ ! -d ./dist ]; then + mkdir ./dist +fi + +echo "$filepaths" +emcc -o dist/output.js main.cpp $filepaths -s ALLOW_MEMORY_GROWTH=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s WASM=1 -s NO_EXIT_RUNTIME=1 \ No newline at end of file diff --git a/2d/_collisions/pill_line/dist/output.js b/2d/_collisions/pill_line/dist/output.js new file mode 100644 index 0000000..e1dfee3 --- /dev/null +++ b/2d/_collisions/pill_line/dist/output.js @@ -0,0 +1,3377 @@ + + +// The Module object: Our interface to the outside world. We import +// and export values on it. There are various ways Module can be used: +// 1. Not defined. We create it here +// 2. A function parameter, function(Module) { ..generated code.. } +// 3. pre-run appended it, var Module = {}; ..generated code.. +// 4. External script tag defines var Module. +// We need to check if Module already exists (e.g. case 3 above). +// Substitution will be replaced with actual code on later stage of the build, +// this way Closure Compiler will not mangle it (e.g. case 4. above). +// Note that if you want to run closure, and also to use Module +// after the generated code, you will need to define var Module = {}; +// before the code. Then that object will be used in the code, and you +// can continue to use Module afterwards as well. +var Module = typeof Module !== 'undefined' ? Module : {}; + +// --pre-jses are emitted after the Module integration code, so that they can +// refer to Module (if they choose; they can also define Module) +// {{PRE_JSES}} + +// Sometimes an existing Module object exists with properties +// meant to overwrite the default module functionality. Here +// we collect those properties and reapply _after_ we configure +// the current environment's defaults to avoid having to be so +// defensive during initialization. +var moduleOverrides = {}; +var key; +for (key in Module) { + if (Module.hasOwnProperty(key)) { + moduleOverrides[key] = Module[key]; + } +} + +var arguments_ = []; +var thisProgram = './this.program'; +var quit_ = function(status, toThrow) { + throw toThrow; +}; + +// Determine the runtime environment we are in. You can customize this by +// setting the ENVIRONMENT setting at compile time (see settings.js). + +var ENVIRONMENT_IS_WEB = false; +var ENVIRONMENT_IS_WORKER = false; +var ENVIRONMENT_IS_NODE = false; +var ENVIRONMENT_IS_SHELL = false; +ENVIRONMENT_IS_WEB = typeof window === 'object'; +ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; +// N.b. Electron.js environment is simultaneously a NODE-environment, but +// also a web environment. +ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string'; +ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + +if (Module['ENVIRONMENT']) { + throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)'); +} + +// `/` 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 read_, + readAsync, + readBinary, + setWindowTitle; + +var nodeFS; +var nodePath; + +if (ENVIRONMENT_IS_NODE) { + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = require('path').dirname(scriptDirectory) + '/'; + } else { + scriptDirectory = __dirname + '/'; + } + +// include: node_shell_read.js + + +read_ = function shell_read(filename, binary) { + if (!nodeFS) nodeFS = require('fs'); + if (!nodePath) nodePath = require('path'); + filename = nodePath['normalize'](filename); + return nodeFS['readFileSync'](filename, binary ? null : 'utf8'); +}; + +readBinary = function readBinary(filename) { + var ret = read_(filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; +}; + +// end include: node_shell_read.js + if (process['argv'].length > 1) { + thisProgram = process['argv'][1].replace(/\\/g, '/'); + } + + arguments_ = process['argv'].slice(2); + + if (typeof module !== 'undefined') { + module['exports'] = Module; + } + + process['on']('uncaughtException', function(ex) { + // suppress ExitStatus exceptions from showing an error + if (!(ex instanceof ExitStatus)) { + throw ex; + } + }); + + process['on']('unhandledRejection', abort); + + quit_ = function(status) { + process['exit'](status); + }; + + Module['inspect'] = function () { return '[Emscripten Module object]'; }; + +} else +if (ENVIRONMENT_IS_SHELL) { + + if (typeof read != 'undefined') { + read_ = function shell_read(f) { + return read(f); + }; + } + + readBinary = function readBinary(f) { + var data; + if (typeof readbuffer === 'function') { + return new Uint8Array(readbuffer(f)); + } + data = read(f, 'binary'); + assert(typeof data === 'object'); + return data; + }; + + if (typeof scriptArgs != 'undefined') { + arguments_ = scriptArgs; + } else if (typeof arguments != 'undefined') { + arguments_ = arguments; + } + + if (typeof quit === 'function') { + quit_ = function(status) { + quit(status); + }; + } + + if (typeof print !== 'undefined') { + // Prefer to use print/printErr where they exist, as they usually work better. + if (typeof console === 'undefined') console = /** @type{!Console} */({}); + console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); + console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr !== 'undefined' ? printErr : print); + } + +} 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) { + if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled + scriptDirectory = self.location.href; + } else if (typeof document !== 'undefined' && document.currentScript) { // web + scriptDirectory = document.currentScript.src; + } + // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. + // otherwise, slice off the final part of the url to find the script directory. + // if scriptDirectory does not contain a slash, lastIndexOf will return -1, + // and scriptDirectory will correctly be replaced with an empty string. + if (scriptDirectory.indexOf('blob:') !== 0) { + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf('/')+1); + } else { + scriptDirectory = ''; + } + + // Differentiate the Web Worker from the Node Worker case, as reading must + // be done differently. + { + +// include: web_or_worker_shell_read.js + + + read_ = function(url) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.send(null); + return xhr.responseText; + }; + + if (ENVIRONMENT_IS_WORKER) { + readBinary = function(url) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response)); + }; + } + + readAsync = function(url, onload, onerror) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = function() { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 + onload(xhr.response); + return; + } + onerror(); + }; + xhr.onerror = onerror; + xhr.send(null); + }; + +// end include: web_or_worker_shell_read.js + } + + setWindowTitle = function(title) { document.title = title }; +} else +{ + throw new Error('environment detection error'); +} + +// Set up the out() and err() hooks, which are how we can print to stdout or +// stderr, respectively. +var out = Module['print'] || console.log.bind(console); +var err = Module['printErr'] || console.warn.bind(console); + +// Merge back in the overrides +for (key in moduleOverrides) { + if (moduleOverrides.hasOwnProperty(key)) { + Module[key] = moduleOverrides[key]; + } +} +// Free the object hierarchy contained in the overrides, this lets the GC +// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. +moduleOverrides = null; + +// Emit code to handle expected values on the Module object. This applies Module.x +// to the proper local x. This has two benefits: first, we only emit it if it is +// expected to arrive, and second, by using a local everywhere else that can be +// minified. + +if (Module['arguments']) arguments_ = Module['arguments']; +if (!Object.getOwnPropertyDescriptor(Module, 'arguments')) { + Object.defineProperty(Module, 'arguments', { + configurable: true, + get: function() { + abort('Module.arguments has been replaced with plain arguments_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} + +if (Module['thisProgram']) thisProgram = Module['thisProgram']; +if (!Object.getOwnPropertyDescriptor(Module, 'thisProgram')) { + Object.defineProperty(Module, 'thisProgram', { + configurable: true, + get: function() { + abort('Module.thisProgram has been replaced with plain thisProgram (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} + +if (Module['quit']) quit_ = Module['quit']; +if (!Object.getOwnPropertyDescriptor(Module, 'quit')) { + Object.defineProperty(Module, 'quit', { + configurable: true, + get: function() { + abort('Module.quit has been replaced with plain quit_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} + +// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message +// Assertions on removed incoming Module JS APIs. +assert(typeof Module['memoryInitializerPrefixURL'] === 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['pthreadMainPrefixURL'] === 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['cdInitializerPrefixURL'] === 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['filePackagePrefixURL'] === 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['read'] === 'undefined', 'Module.read option was removed (modify read_ in JS)'); +assert(typeof Module['readAsync'] === 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); +assert(typeof Module['readBinary'] === 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); +assert(typeof Module['setWindowTitle'] === 'undefined', 'Module.setWindowTitle option was removed (modify setWindowTitle in JS)'); +assert(typeof Module['TOTAL_MEMORY'] === 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); + +if (!Object.getOwnPropertyDescriptor(Module, 'read')) { + Object.defineProperty(Module, 'read', { + configurable: true, + get: function() { + abort('Module.read has been replaced with plain read_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} + +if (!Object.getOwnPropertyDescriptor(Module, 'readAsync')) { + Object.defineProperty(Module, 'readAsync', { + configurable: true, + get: function() { + abort('Module.readAsync has been replaced with plain readAsync (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} + +if (!Object.getOwnPropertyDescriptor(Module, 'readBinary')) { + Object.defineProperty(Module, 'readBinary', { + configurable: true, + get: function() { + abort('Module.readBinary has been replaced with plain readBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} + +if (!Object.getOwnPropertyDescriptor(Module, 'setWindowTitle')) { + Object.defineProperty(Module, 'setWindowTitle', { + configurable: true, + get: function() { + abort('Module.setWindowTitle has been replaced with plain setWindowTitle (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} +var IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js'; +var PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js'; +var WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js'; +var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js'; + + + + +var STACK_ALIGN = 16; + +function alignMemory(size, factor) { + if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default + return Math.ceil(size / factor) * factor; +} + +function getNativeTypeSize(type) { + switch (type) { + case 'i1': case 'i8': return 1; + case 'i16': return 2; + case 'i32': return 4; + case 'i64': return 8; + case 'float': return 4; + case 'double': return 8; + default: { + if (type[type.length-1] === '*') { + return 4; // A pointer + } else if (type[0] === 'i') { + var bits = Number(type.substr(1)); + assert(bits % 8 === 0, 'getNativeTypeSize invalid bits ' + bits + ', type ' + type); + return bits / 8; + } else { + return 0; + } + } + } +} + +function warnOnce(text) { + if (!warnOnce.shown) warnOnce.shown = {}; + if (!warnOnce.shown[text]) { + warnOnce.shown[text] = 1; + err(text); + } +} + +// include: runtime_functions.js + + +// Wraps a JS function as a wasm function with a given signature. +function convertJsFunctionToWasm(func, sig) { + + // If the type reflection proposal is available, use the new + // "WebAssembly.Function" constructor. + // Otherwise, construct a minimal wasm module importing the JS function and + // re-exporting it. + if (typeof WebAssembly.Function === "function") { + var typeNames = { + 'i': 'i32', + 'j': 'i64', + 'f': 'f32', + 'd': 'f64' + }; + var type = { + parameters: [], + results: sig[0] == 'v' ? [] : [typeNames[sig[0]]] + }; + for (var i = 1; i < sig.length; ++i) { + type.parameters.push(typeNames[sig[i]]); + } + return new WebAssembly.Function(type, func); + } + + // The module is static, with the exception of the type section, which is + // generated based on the signature passed in. + var typeSection = [ + 0x01, // id: section, + 0x00, // length: 0 (placeholder) + 0x01, // count: 1 + 0x60, // form: func + ]; + var sigRet = sig.slice(0, 1); + var sigParam = sig.slice(1); + var typeCodes = { + 'i': 0x7f, // i32 + 'j': 0x7e, // i64 + 'f': 0x7d, // f32 + 'd': 0x7c, // f64 + }; + + // Parameters, length + signatures + typeSection.push(sigParam.length); + for (var i = 0; i < sigParam.length; ++i) { + typeSection.push(typeCodes[sigParam[i]]); + } + + // Return values, length + signatures + // With no multi-return in MVP, either 0 (void) or 1 (anything else) + if (sigRet == 'v') { + typeSection.push(0x00); + } else { + typeSection = typeSection.concat([0x01, typeCodes[sigRet]]); + } + + // Write the overall length of the type section back into the section header + // (excepting the 2 bytes for the section id and length) + typeSection[1] = typeSection.length - 2; + + // Rest of the module is static + var bytes = new Uint8Array([ + 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") + 0x01, 0x00, 0x00, 0x00, // version: 1 + ].concat(typeSection, [ + 0x02, 0x07, // import section + // (import "e" "f" (func 0 (type 0))) + 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00, + 0x07, 0x05, // export section + // (export "f" (func 0 (type 0))) + 0x01, 0x01, 0x66, 0x00, 0x00, + ])); + + // We can compile this wasm module synchronously because it is very small. + // This accepts an import (at "e.f"), that it reroutes to an export (at "f") + var module = new WebAssembly.Module(bytes); + var instance = new WebAssembly.Instance(module, { + 'e': { + 'f': func + } + }); + var wrappedFunc = instance.exports['f']; + return wrappedFunc; +} + +var freeTableIndexes = []; + +// Weak map of functions in the table to their indexes, created on first use. +var functionsInTableMap; + +function getEmptyTableSlot() { + // Reuse a free index if there is one, otherwise grow. + if (freeTableIndexes.length) { + return freeTableIndexes.pop(); + } + // Grow the table + try { + wasmTable.grow(1); + } catch (err) { + if (!(err instanceof RangeError)) { + throw err; + } + throw 'Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'; + } + return wasmTable.length - 1; +} + +// Add a wasm function to the table. +function addFunctionWasm(func, sig) { + // Check if the function is already in the table, to ensure each function + // gets a unique index. First, create the map if this is the first use. + if (!functionsInTableMap) { + functionsInTableMap = new WeakMap(); + for (var i = 0; i < wasmTable.length; i++) { + var item = wasmTable.get(i); + // Ignore null values. + if (item) { + functionsInTableMap.set(item, i); + } + } + } + if (functionsInTableMap.has(func)) { + return functionsInTableMap.get(func); + } + + // It's not in the table, add it now. + + var ret = getEmptyTableSlot(); + + // Set the new value. + try { + // Attempting to call this with JS function will cause of table.set() to fail + wasmTable.set(ret, func); + } catch (err) { + if (!(err instanceof TypeError)) { + throw err; + } + assert(typeof sig !== 'undefined', 'Missing signature argument to addFunction: ' + func); + var wrapped = convertJsFunctionToWasm(func, sig); + wasmTable.set(ret, wrapped); + } + + functionsInTableMap.set(func, ret); + + return ret; +} + +function removeFunction(index) { + functionsInTableMap.delete(wasmTable.get(index)); + freeTableIndexes.push(index); +} + +// 'sig' parameter is required for the llvm backend but only when func is not +// already a WebAssembly function. +function addFunction(func, sig) { + assert(typeof func !== 'undefined'); + + return addFunctionWasm(func, sig); +} + +// end include: runtime_functions.js +// include: runtime_debug.js + + +// end include: runtime_debug.js +function makeBigInt(low, high, unsigned) { + return unsigned ? ((+((low>>>0)))+((+((high>>>0)))*4294967296.0)) : ((+((low>>>0)))+((+((high|0)))*4294967296.0)); +} + +var tempRet0 = 0; + +var setTempRet0 = function(value) { + tempRet0 = value; +}; + +var getTempRet0 = function() { + return tempRet0; +}; + +function getCompilerSetting(name) { + throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for getCompilerSetting or emscripten_get_compiler_setting to work'; +} + + + +// === 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; +if (Module['wasmBinary']) wasmBinary = Module['wasmBinary']; +if (!Object.getOwnPropertyDescriptor(Module, 'wasmBinary')) { + Object.defineProperty(Module, 'wasmBinary', { + configurable: true, + get: function() { + abort('Module.wasmBinary has been replaced with plain wasmBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} +var noExitRuntime = Module['noExitRuntime'] || true; +if (!Object.getOwnPropertyDescriptor(Module, 'noExitRuntime')) { + Object.defineProperty(Module, 'noExitRuntime', { + configurable: true, + get: function() { + abort('Module.noExitRuntime has been replaced with plain noExitRuntime (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} + +if (typeof WebAssembly !== 'object') { + abort('no native wasm support detected'); +} + +// include: runtime_safe_heap.js + + +// In MINIMAL_RUNTIME, setValue() and getValue() are only available when building with safe heap enabled, for heap safety checking. +// In traditional runtime, setValue() and getValue() are always available (although their use is highly discouraged due to perf penalties) + +/** @param {number} ptr + @param {number} value + @param {string} type + @param {number|boolean=} noSafe */ +function setValue(ptr, value, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit + switch(type) { + case 'i1': HEAP8[((ptr)>>0)] = value; break; + case 'i8': HEAP8[((ptr)>>0)] = value; break; + case 'i16': HEAP16[((ptr)>>1)] = value; break; + case 'i32': HEAP32[((ptr)>>2)] = value; break; + case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)] = tempI64[0],HEAP32[(((ptr)+(4))>>2)] = tempI64[1]); break; + case 'float': HEAPF32[((ptr)>>2)] = value; break; + case 'double': HEAPF64[((ptr)>>3)] = value; break; + default: abort('invalid type for setValue: ' + type); + } +} + +/** @param {number} ptr + @param {string} type + @param {number|boolean=} noSafe */ +function getValue(ptr, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit + switch(type) { + case 'i1': return HEAP8[((ptr)>>0)]; + case 'i8': return HEAP8[((ptr)>>0)]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': return HEAP32[((ptr)>>2)]; + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + default: abort('invalid type for getValue: ' + type); + } + return null; +} + +// end include: runtime_safe_heap.js +// Wasm globals + +var wasmMemory; + +//======================================== +// 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; + +/** @type {function(*, string=)} */ +function assert(condition, text) { + if (!condition) { + abort('Assertion failed: ' + text); + } +} + +// Returns the C function with a specified identifier (for C++, you need to do manual name mangling) +function getCFunc(ident) { + var func = Module['_' + ident]; // closure exported function + assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported'); + return func; +} + +// C calling interface. +/** @param {string|null=} returnType + @param {Array=} argTypes + @param {Arguments|Array=} args + @param {Object=} opts */ +function ccall(ident, returnType, argTypes, args, opts) { + // For fast lookup of conversion functions + var toC = { + 'string': function(str) { + var ret = 0; + if (str !== null && str !== undefined && str !== 0) { // null string + // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' + var len = (str.length << 2) + 1; + ret = stackAlloc(len); + stringToUTF8(str, ret, len); + } + return ret; + }, + 'array': function(arr) { + var ret = stackAlloc(arr.length); + writeArrayToMemory(arr, ret); + return ret; + } + }; + + function convertReturnValue(ret) { + if (returnType === 'string') return UTF8ToString(ret); + if (returnType === 'boolean') return Boolean(ret); + return ret; + } + + var func = getCFunc(ident); + var cArgs = []; + var stack = 0; + assert(returnType !== 'array', 'Return type should not be "array".'); + if (args) { + for (var i = 0; i < args.length; i++) { + var converter = toC[argTypes[i]]; + if (converter) { + if (stack === 0) stack = stackSave(); + cArgs[i] = converter(args[i]); + } else { + cArgs[i] = args[i]; + } + } + } + var ret = func.apply(null, cArgs); + + ret = convertReturnValue(ret); + if (stack !== 0) stackRestore(stack); + return ret; +} + +/** @param {string=} returnType + @param {Array=} argTypes + @param {Object=} opts */ +function cwrap(ident, returnType, argTypes, opts) { + return function() { + return ccall(ident, returnType, argTypes, arguments, opts); + } +} + +// We used to include malloc/free by default in the past. Show a helpful error in +// builds with assertions. + +var ALLOC_NORMAL = 0; // Tries to use _malloc() +var ALLOC_STACK = 1; // Lives for the duration of the current function call + +// allocate(): This is for internal use. You can use it yourself as well, but the interface +// is a little tricky (see docs right below). The reason is that it is optimized +// for multiple syntaxes to save space in generated code. So you should +// normally not use allocate(), and instead allocate memory using _malloc(), +// initialize it with setValue(), and so forth. +// @slab: An array of data. +// @allocator: How to allocate memory, see ALLOC_* +/** @type {function((Uint8Array|Array), number)} */ +function allocate(slab, allocator) { + var ret; + assert(typeof allocator === 'number', 'allocate no longer takes a type argument') + assert(typeof slab !== 'number', 'allocate no longer takes a number as arg0') + + if (allocator == ALLOC_STACK) { + ret = stackAlloc(slab.length); + } else { + ret = _malloc(slab.length); + } + + if (slab.subarray || slab.slice) { + HEAPU8.set(/** @type {!Uint8Array} */(slab), ret); + } else { + HEAPU8.set(new Uint8Array(slab), ret); + } + return ret; +} + +// include: runtime_strings.js + + +// runtime_strings.js: Strings related runtime functions that are part of both MINIMAL_RUNTIME and regular runtime. + +// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns +// a copy of that string as a Javascript String object. + +var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined; + +/** + * @param {number} idx + * @param {number=} maxBytesToRead + * @return {string} + */ +function UTF8ArrayToString(heap, idx, maxBytesToRead) { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. + // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. + // (As a tiny code save trick, compare endPtr against endIdx using a negation, so that undefined means Infinity) + while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr; + + if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) { + return UTF8Decoder.decode(heap.subarray(idx, endPtr)); + } else { + var str = ''; + // If building with TextDecoder, we have already computed the string length above, so test loop end condition against that + while (idx < endPtr) { + // For UTF8 byte structure, see: + // http://en.wikipedia.org/wiki/UTF-8#Description + // https://www.ietf.org/rfc/rfc2279.txt + // https://tools.ietf.org/html/rfc3629 + var u0 = heap[idx++]; + if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } + var u1 = heap[idx++] & 63; + if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } + var u2 = heap[idx++] & 63; + if ((u0 & 0xF0) == 0xE0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte 0x' + u0.toString(16) + ' encountered when deserializing a UTF-8 string on the asm.js/wasm heap to a JS string!'); + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63); + } + + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } + } + } + return str; +} + +// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns a +// copy of that string as a Javascript String object. +// maxBytesToRead: an optional length that specifies the maximum number of bytes to read. You can omit +// this parameter to scan the string until the first \0 byte. If maxBytesToRead is +// passed, and the string at [ptr, ptr+maxBytesToReadr[ contains a null byte in the +// middle, then the string will cut short at that byte index (i.e. maxBytesToRead will +// not produce a string of exact length [ptr, ptr+maxBytesToRead[) +// N.B. mixing frequent uses of UTF8ToString() with and without maxBytesToRead may +// throw JS JIT optimizations off, so it is worth to consider consistently using one +// style or the other. +/** + * @param {number} ptr + * @param {number=} maxBytesToRead + * @return {string} + */ +function UTF8ToString(ptr, maxBytesToRead) { + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; +} + +// Copies the given Javascript String object 'str' to the given byte array at address 'outIdx', +// encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP. +// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// heap: the array to copy to. Each index in this array is assumed to be one 8-byte element. +// outIdx: The starting offset in the array to begin the copying. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. +// This count should include the null terminator, +// i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else. +// maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { + if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes. + return 0; + + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) { + var u1 = str.charCodeAt(++i); + u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF); + } + if (u <= 0x7F) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 0x7FF) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 0xC0 | (u >> 6); + heap[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xFFFF) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 0xE0 | (u >> 12); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + if (u >= 0x200000) warnOnce('Invalid Unicode code point 0x' + u.toString(16) + ' encountered when serializing a JS string to an UTF-8 string on the asm.js/wasm heap! (Valid unicode code points should be in range 0-0x1FFFFF).'); + heap[outIdx++] = 0xF0 | (u >> 18); + heap[outIdx++] = 0x80 | ((u >> 12) & 63); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } + } + // Null-terminate the pointer to the buffer. + heap[outIdx] = 0; + return outIdx - startIdx; +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP. +// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF8(str, outPtr, maxBytesToWrite) { + assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); + return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite); +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. +function lengthBytesUTF8(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); + if (u <= 0x7F) ++len; + else if (u <= 0x7FF) len += 2; + else if (u <= 0xFFFF) len += 3; + else len += 4; + } + return len; +} + +// end include: runtime_strings.js +// include: runtime_strings_extra.js + + +// runtime_strings_extra.js: Strings related runtime functions that are available only in regular runtime. + +// Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +function AsciiToString(ptr) { + var str = ''; + while (1) { + var ch = HEAPU8[((ptr++)>>0)]; + if (!ch) return str; + str += String.fromCharCode(ch); + } +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP. + +function stringToAscii(str, outPtr) { + return writeAsciiToMemory(str, outPtr, false); +} + +// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined; + +function UTF16ToString(ptr, maxBytesToRead) { + assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!'); + var endPtr = ptr; + // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. + // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. + var idx = endPtr >> 1; + var maxIdx = idx + maxBytesToRead / 2; + // If maxBytesToRead is not passed explicitly, it will be undefined, and this + // will always evaluate to true. This saves on code size. + while (!(idx >= maxIdx) && HEAPU16[idx]) ++idx; + endPtr = idx << 1; + + if (endPtr - ptr > 32 && UTF16Decoder) { + return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr)); + } else { + var str = ''; + + // If maxBytesToRead is not passed explicitly, it will be undefined, and the for-loop's condition + // will always evaluate to true. The loop is then terminated on the first null char. + for (var i = 0; !(i >= maxBytesToRead / 2); ++i) { + var codeUnit = HEAP16[(((ptr)+(i*2))>>1)]; + if (codeUnit == 0) break; + // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through. + str += String.fromCharCode(codeUnit); + } + + return str; + } +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP. +// Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outPtr: Byte address in Emscripten HEAP where to write the string to. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else. +// maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF16(str, outPtr, maxBytesToWrite) { + assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!'); + assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + if (maxBytesToWrite === undefined) { + maxBytesToWrite = 0x7FFFFFFF; + } + if (maxBytesToWrite < 2) return 0; + maxBytesToWrite -= 2; // Null terminator. + var startPtr = outPtr; + var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length; + for (var i = 0; i < numCharsToWrite; ++i) { + // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. + var codeUnit = str.charCodeAt(i); // possibly a lead surrogate + HEAP16[((outPtr)>>1)] = codeUnit; + outPtr += 2; + } + // Null-terminate the pointer to the HEAP. + HEAP16[((outPtr)>>1)] = 0; + return outPtr - startPtr; +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF16(str) { + return str.length*2; +} + +function UTF32ToString(ptr, maxBytesToRead) { + assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!'); + var i = 0; + + var str = ''; + // If maxBytesToRead is not passed explicitly, it will be undefined, and this + // will always evaluate to true. This saves on code size. + while (!(i >= maxBytesToRead / 4)) { + var utf32 = HEAP32[(((ptr)+(i*4))>>2)]; + if (utf32 == 0) break; + ++i; + // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + if (utf32 >= 0x10000) { + var ch = utf32 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } else { + str += String.fromCharCode(utf32); + } + } + return str; +} + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP. +// Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outPtr: Byte address in Emscripten HEAP where to write the string to. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else. +// maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF32(str, outPtr, maxBytesToWrite) { + assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!'); + assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + if (maxBytesToWrite === undefined) { + maxBytesToWrite = 0x7FFFFFFF; + } + if (maxBytesToWrite < 4) return 0; + var startPtr = outPtr; + var endPtr = startPtr + maxBytesToWrite - 4; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var codeUnit = str.charCodeAt(i); // possibly a lead surrogate + if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) { + var trailSurrogate = str.charCodeAt(++i); + codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF); + } + HEAP32[((outPtr)>>2)] = codeUnit; + outPtr += 4; + if (outPtr + 4 > endPtr) break; + } + // Null-terminate the pointer to the HEAP. + HEAP32[((outPtr)>>2)] = 0; + return outPtr - startPtr; +} + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF32(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var codeUnit = str.charCodeAt(i); + if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate. + len += 4; + } + + return len; +} + +// Allocate heap space for a JS string, and write it there. +// It is the responsibility of the caller to free() that memory. +function allocateUTF8(str) { + var size = lengthBytesUTF8(str) + 1; + var ret = _malloc(size); + if (ret) stringToUTF8Array(str, HEAP8, ret, size); + return ret; +} + +// Allocate stack space for a JS string, and write it there. +function allocateUTF8OnStack(str) { + var size = lengthBytesUTF8(str) + 1; + var ret = stackAlloc(size); + stringToUTF8Array(str, HEAP8, ret, size); + return ret; +} + +// Deprecated: This function should not be called because it is unsafe and does not provide +// a maximum length limit of how many bytes it is allowed to write. Prefer calling the +// function stringToUTF8Array() instead, which takes in a maximum length that can be used +// to be secure from out of bounds writes. +/** @deprecated + @param {boolean=} dontAddNull */ +function writeStringToMemory(string, buffer, dontAddNull) { + warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); + + var /** @type {number} */ lastChar, /** @type {number} */ end; + if (dontAddNull) { + // stringToUTF8Array always appends null. If we don't want to do that, remember the + // character that existed at the location where the null will be placed, and restore + // that after the write (below). + end = buffer + lengthBytesUTF8(string); + lastChar = HEAP8[end]; + } + stringToUTF8(string, buffer, Infinity); + if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. +} + +function writeArrayToMemory(array, buffer) { + assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') + HEAP8.set(array, buffer); +} + +/** @param {boolean=} dontAddNull */ +function writeAsciiToMemory(str, buffer, dontAddNull) { + for (var i = 0; i < str.length; ++i) { + assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff); + HEAP8[((buffer++)>>0)] = str.charCodeAt(i); + } + // Null-terminate the pointer to the HEAP. + if (!dontAddNull) HEAP8[((buffer)>>0)] = 0; +} + +// end include: runtime_strings_extra.js +// Memory management + +function alignUp(x, multiple) { + if (x % multiple > 0) { + x += multiple - (x % multiple); + } + return x; +} + +var HEAP, +/** @type {ArrayBuffer} */ + buffer, +/** @type {Int8Array} */ + HEAP8, +/** @type {Uint8Array} */ + HEAPU8, +/** @type {Int16Array} */ + HEAP16, +/** @type {Uint16Array} */ + HEAPU16, +/** @type {Int32Array} */ + HEAP32, +/** @type {Uint32Array} */ + HEAPU32, +/** @type {Float32Array} */ + HEAPF32, +/** @type {Float64Array} */ + HEAPF64; + +function updateGlobalBufferAndViews(buf) { + buffer = buf; + Module['HEAP8'] = HEAP8 = new Int8Array(buf); + Module['HEAP16'] = HEAP16 = new Int16Array(buf); + Module['HEAP32'] = HEAP32 = new Int32Array(buf); + Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf); + Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf); + Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf); + Module['HEAPF32'] = HEAPF32 = new Float32Array(buf); + Module['HEAPF64'] = HEAPF64 = new Float64Array(buf); +} + +var TOTAL_STACK = 5242880; +if (Module['TOTAL_STACK']) assert(TOTAL_STACK === Module['TOTAL_STACK'], 'the stack size can no longer be determined at runtime') + +var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216; +if (!Object.getOwnPropertyDescriptor(Module, 'INITIAL_MEMORY')) { + Object.defineProperty(Module, 'INITIAL_MEMORY', { + configurable: true, + get: function() { + abort('Module.INITIAL_MEMORY has been replaced with plain INITIAL_MEMORY (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') + } + }); +} + +assert(INITIAL_MEMORY >= TOTAL_STACK, 'INITIAL_MEMORY should be larger than TOTAL_STACK, was ' + INITIAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); + +// check for full engine support (use string 'subarray' to avoid closure compiler confusion) +assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray !== undefined && Int32Array.prototype.set !== undefined, + 'JS engine does not provide full typed array support'); + +// If memory is defined in wasm, the user can't provide it. +assert(!Module['wasmMemory'], 'Use of `wasmMemory` detected. Use -s IMPORTED_MEMORY to define wasmMemory externally'); +assert(INITIAL_MEMORY == 16777216, 'Detected runtime INITIAL_MEMORY setting. Use -s IMPORTED_MEMORY to define wasmMemory dynamically'); + +// include: runtime_init_table.js +// In regular non-RELOCATABLE mode the table is exported +// from the wasm module and this will be assigned once +// the exports are available. +var wasmTable; + +// end include: runtime_init_table.js +// include: runtime_stack_check.js + + +// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. +function writeStackCookie() { + var max = _emscripten_stack_get_end(); + assert((max & 3) == 0); + // The stack grows downwards + HEAPU32[(max >> 2)+1] = 0x2135467; + HEAPU32[(max >> 2)+2] = 0x89BACDFE; + // Also test the global address 0 for integrity. + HEAP32[0] = 0x63736d65; /* 'emsc' */ +} + +function checkStackCookie() { + if (ABORT) return; + var max = _emscripten_stack_get_end(); + var cookie1 = HEAPU32[(max >> 2)+1]; + var cookie2 = HEAPU32[(max >> 2)+2]; + if (cookie1 != 0x2135467 || cookie2 != 0x89BACDFE) { + abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x2135467, but received 0x' + cookie2.toString(16) + ' ' + cookie1.toString(16)); + } + // Also test the global address 0 for integrity. + if (HEAP32[0] !== 0x63736d65 /* 'emsc' */) abort('Runtime error: The application has corrupted its heap memory area (address zero)!'); +} + +// end include: runtime_stack_check.js +// include: runtime_assertions.js + + +// Endianness check (note: assumes compiler arch was little-endian) +(function() { + var h16 = new Int16Array(1); + var h8 = new Int8Array(h16.buffer); + h16[0] = 0x6373; + if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian!'; +})(); + +function abortFnPtrError(ptr, sig) { + abort("Invalid function pointer " + ptr + " called with signature '" + sig + "'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this). Build with ASSERTIONS=2 for more info."); +} + +// end include: runtime_assertions.js +var __ATPRERUN__ = []; // functions called before the runtime is initialized +var __ATINIT__ = []; // functions called during startup +var __ATMAIN__ = []; // functions called when main() is to be run +var __ATEXIT__ = []; // functions called during shutdown +var __ATPOSTRUN__ = []; // functions called after the main() is called + +var runtimeInitialized = false; +var runtimeExited = false; + +__ATINIT__.push({ func: function() { ___wasm_call_ctors() } }); + +function preRun() { + + if (Module['preRun']) { + if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length) { + addOnPreRun(Module['preRun'].shift()); + } + } + + callRuntimeCallbacks(__ATPRERUN__); +} + +function initRuntime() { + checkStackCookie(); + assert(!runtimeInitialized); + runtimeInitialized = true; + + callRuntimeCallbacks(__ATINIT__); +} + +function preMain() { + checkStackCookie(); + + callRuntimeCallbacks(__ATMAIN__); +} + +function exitRuntime() { + checkStackCookie(); + runtimeExited = true; +} + +function postRun() { + checkStackCookie(); + + if (Module['postRun']) { + if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length) { + addOnPostRun(Module['postRun'].shift()); + } + } + + callRuntimeCallbacks(__ATPOSTRUN__); +} + +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} + +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} + +function addOnPreMain(cb) { + __ATMAIN__.unshift(cb); +} + +function addOnExit(cb) { +} + +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} + +// include: runtime_math.js + + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc + +assert(Math.imul, 'This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.fround, 'This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.clz32, 'This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.trunc, 'This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); + +// end include: runtime_math.js +// A counter of dependencies for calling run(). If we need to +// do asynchronous work before running, increment this and +// decrement it. Incrementing must happen in a place like +// Module.preRun (used by emcc to add file preloading). +// Note that you can add dependencies in preRun, even though +// it happens right before run - run will be postponed until +// the dependencies are met. +var runDependencies = 0; +var runDependencyWatcher = null; +var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled +var runDependencyTracking = {}; + +function getUniqueRunDependency(id) { + var orig = id; + while (1) { + if (!runDependencyTracking[id]) return id; + id = orig + Math.random(); + } +} + +function addRunDependency(id) { + runDependencies++; + + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + + if (id) { + assert(!runDependencyTracking[id]); + runDependencyTracking[id] = 1; + if (runDependencyWatcher === null && typeof setInterval !== 'undefined') { + // Check for missing dependencies every few seconds + runDependencyWatcher = setInterval(function() { + if (ABORT) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + return; + } + var shown = false; + for (var dep in runDependencyTracking) { + if (!shown) { + shown = true; + err('still waiting on run dependencies:'); + } + err('dependency: ' + dep); + } + if (shown) { + err('(end of list)'); + } + }, 10000); + } + } else { + err('warning: run dependency added without ID'); + } +} + +function removeRunDependency(id) { + runDependencies--; + + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + + if (id) { + assert(runDependencyTracking[id]); + delete runDependencyTracking[id]; + } else { + err('warning: run dependency removed without ID'); + } + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); // can add another dependenciesFulfilled + } + } +} + +Module["preloadedImages"] = {}; // maps url to image data +Module["preloadedAudios"] = {}; // maps url to audio data + +/** @param {string|number=} what */ +function abort(what) { + if (Module['onAbort']) { + Module['onAbort'](what); + } + + what += ''; + err(what); + + ABORT = true; + EXITSTATUS = 1; + + var output = 'abort(' + what + ') at ' + stackTrace(); + what = output; + + // 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. + 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; +} + +// {{MEM_INITIALIZER}} + +// include: memoryprofiler.js + + +// end include: memoryprofiler.js +// show errors on likely calls to FS when it was not included +var FS = { + error: function() { + abort('Filesystem support (FS) was not included. The problem is that you are using files from JS, but files were not used from C/C++, so filesystem support was not auto-included. You can force-include filesystem support with -s FORCE_FILESYSTEM=1'); + }, + init: function() { FS.error() }, + createDataFile: function() { FS.error() }, + createPreloadedFile: function() { FS.error() }, + createLazyFile: function() { FS.error() }, + open: function() { FS.error() }, + mkdev: function() { FS.error() }, + registerDevice: function() { FS.error() }, + analyzePath: function() { FS.error() }, + loadFilesFromDB: function() { FS.error() }, + + ErrnoError: function ErrnoError() { FS.error() }, +}; +Module['FS_createDataFile'] = FS.createDataFile; +Module['FS_createPreloadedFile'] = FS.createPreloadedFile; + +// include: URIUtils.js + + +function hasPrefix(str, prefix) { + return String.prototype.startsWith ? + str.startsWith(prefix) : + str.indexOf(prefix) === 0; +} + +// Prefix of data URIs emitted by SINGLE_FILE and related options. +var dataURIPrefix = 'data:application/octet-stream;base64,'; + +// Indicates whether filename is a base64 data URI. +function isDataURI(filename) { + return hasPrefix(filename, dataURIPrefix); +} + +var fileURIPrefix = "file://"; + +// Indicates whether filename is delivered via file protocol (as opposed to http/https) +function isFileURI(filename) { + return hasPrefix(filename, fileURIPrefix); +} + +// end include: URIUtils.js +function createExportWrapper(name, fixedasm) { + return function() { + var displayName = name; + var asm = fixedasm; + if (!fixedasm) { + asm = Module['asm']; + } + assert(runtimeInitialized, 'native function `' + displayName + '` called before runtime initialization'); + assert(!runtimeExited, 'native function `' + displayName + '` called after runtime exit (use NO_EXIT_RUNTIME to keep it alive after main() exits)'); + if (!asm[name]) { + assert(asm[name], 'exported native function `' + displayName + '` not found'); + } + return asm[name].apply(null, arguments); + }; +} + +var wasmBinaryFile = 'output.wasm'; +if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile); +} + +function getBinary(file) { + try { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary); + } + if (readBinary) { + return readBinary(file); + } else { + throw "both async and sync fetching of the wasm failed"; + } + } + catch (err) { + abort(err); + } +} + +function getBinaryPromise() { + // If we don't have the binary yet, try to to load it asynchronously. + // 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 fetch if it is available and the url is not a file, otherwise fall back to XHR. + if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { + if (typeof fetch === 'function' + && !isFileURI(wasmBinaryFile) + ) { + return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { + if (!response['ok']) { + throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; + } + return response['arrayBuffer'](); + }).catch(function () { + return getBinary(wasmBinaryFile); + }); + } + else { + if (readAsync) { + // fetch is not available or url is file => try XHR (readAsync uses XHR internally) + return new Promise(function(resolve, reject) { + readAsync(wasmBinaryFile, function(response) { resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))) }, reject) + }); + } + } + } + + // Otherwise, getBinary should be able to get it synchronously + return Promise.resolve().then(function() { return getBinary(wasmBinaryFile); }); +} + +// Create the wasm instance. +// Receives the wasm imports, returns the exports. +function createWasm() { + // prepare imports + var info = { + 'env': asmLibraryArg, + 'wasi_snapshot_preview1': asmLibraryArg, + }; + // Load the wasm module and create an instance of using native support in the JS engine. + // handle a generated wasm instance, receiving its exports and + // performing other necessary setup + /** @param {WebAssembly.Module=} module*/ + function receiveInstance(instance, module) { + var exports = instance.exports; + + Module['asm'] = exports; + + wasmMemory = Module['asm']['memory']; + assert(wasmMemory, "memory not found in wasm exports"); + // This assertion doesn't hold when emscripten is run in --post-link + // mode. + // TODO(sbc): Read INITIAL_MEMORY out of the wasm file in post-link mode. + //assert(wasmMemory.buffer.byteLength === 16777216); + updateGlobalBufferAndViews(wasmMemory.buffer); + + wasmTable = Module['asm']['__indirect_function_table']; + assert(wasmTable, "table not found in wasm exports"); + + removeRunDependency('wasm-instantiate'); + } + // we can't run yet (except in a pthread, where we have a custom sync instantiator) + addRunDependency('wasm-instantiate'); + + // Async compilation can be confusing when an error on the page overwrites Module + // (for example, if the order of elements is wrong, and the one defining Module is + // later), so we save Module and check it later. + var trueModule = Module; + function receiveInstantiatedSource(output) { + // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance. + // receiveInstance() will swap in the exports (to Module.asm) so they can be called + assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); + trueModule = null; + // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. + // When the regression is fixed, can restore the above USE_PTHREADS-enabled path. + receiveInstance(output['instance']); + } + + function instantiateArrayBuffer(receiver) { + return getBinaryPromise().then(function(binary) { + return WebAssembly.instantiate(binary, info); + }).then(receiver, function(reason) { + err('failed to asynchronously prepare wasm: ' + reason); + + // Warn on some common problems. + if (isFileURI(wasmBinaryFile)) { + err('warning: Loading from a file URI (' + wasmBinaryFile + ') is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing'); + } + abort(reason); + }); + } + + // Prefer streaming instantiation if available. + function instantiateAsync() { + if (!wasmBinary && + typeof WebAssembly.instantiateStreaming === 'function' && + !isDataURI(wasmBinaryFile) && + // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. + !isFileURI(wasmBinaryFile) && + typeof fetch === 'function') { + return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) { + var result = WebAssembly.instantiateStreaming(response, info); + return result.then(receiveInstantiatedSource, function(reason) { + // We expect the most common failure cause to be a bad MIME type for the binary, + // in which case falling back to ArrayBuffer instantiation should work. + err('wasm streaming compile failed: ' + reason); + err('falling back to ArrayBuffer instantiation'); + return instantiateArrayBuffer(receiveInstantiatedSource); + }); + }); + } else { + return instantiateArrayBuffer(receiveInstantiatedSource); + } + } + + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback + // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel + // to any other async startup actions they are performing. + if (Module['instantiateWasm']) { + try { + var exports = Module['instantiateWasm'](info, receiveInstance); + return exports; + } catch(e) { + err('Module.instantiateWasm callback failed with error: ' + e); + return false; + } + } + + instantiateAsync(); + return {}; // no exports yet; we'll fill them in later +} + +// Globals used by JS i64 conversions (see makeSetValue) +var tempDouble; +var tempI64; + +// === Body === + +var ASM_CONSTS = { + +}; + + + + + + + function abortStackOverflow(allocSize) { + abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (_emscripten_stack_get_free() + allocSize) + ' bytes available!'); + } + + function callRuntimeCallbacks(callbacks) { + while(callbacks.length > 0) { + var callback = callbacks.shift(); + if (typeof callback == 'function') { + callback(Module); // Pass the module as the first argument. + continue; + } + var func = callback.func; + if (typeof func === 'number') { + if (callback.arg === undefined) { + wasmTable.get(func)(); + } else { + wasmTable.get(func)(callback.arg); + } + } else { + func(callback.arg === undefined ? null : callback.arg); + } + } + } + + function demangle(func) { + warnOnce('warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling'); + return func; + } + + function demangleAll(text) { + var regex = + /\b_Z[\w\d_]+/g; + return text.replace(regex, + function(x) { + var y = demangle(x); + return x === y ? x : (y + ' [' + x + ']'); + }); + } + + function jsStackTrace() { + var error = new Error(); + if (!error.stack) { + // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown, + // so try that as a special-case. + try { + throw new Error(); + } catch(e) { + error = e; + } + if (!error.stack) { + return '(no stack trace available)'; + } + } + return error.stack.toString(); + } + + function stackTrace() { + var js = jsStackTrace(); + if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); + return demangleAll(js); + } + + function _abort() { + abort(); + } + + function _emscripten_memcpy_big(dest, src, num) { + HEAPU8.copyWithin(dest, src, src + num); + } + + function _emscripten_request_animation_frame_loop(cb, userData) { + function tick(timeStamp) { + if (wasmTable.get(cb)(timeStamp, userData)) { + requestAnimationFrame(tick); + } + } + return requestAnimationFrame(tick); + } + + function _emscripten_get_heap_size() { + return HEAPU8.length; + } + + function emscripten_realloc_buffer(size) { + try { + // round size grow request up to wasm page size (fixed 64KB per spec) + wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16); // .grow() takes a delta compared to the previous size + updateGlobalBufferAndViews(wasmMemory.buffer); + return 1 /*success*/; + } catch(e) { + console.error('emscripten_realloc_buffer: Attempted to grow heap from ' + buffer.byteLength + ' bytes to ' + size + ' bytes, but got error: ' + e); + } + // implicit 0 return to save code size (caller will cast "undefined" into 0 + // anyhow) + } + function _emscripten_resize_heap(requestedSize) { + var oldSize = _emscripten_get_heap_size(); + // With pthreads, races can happen (another thread might increase the size in between), so return a failure, and let the caller retry. + assert(requestedSize > oldSize); + + // Memory resize rules: + // 1. Always increase heap size to at least the requested size, rounded up to next page multiple. + // 2a. If MEMORY_GROWTH_LINEAR_STEP == -1, excessively resize the heap geometrically: increase the heap size according to + // MEMORY_GROWTH_GEOMETRIC_STEP factor (default +20%), + // At most overreserve by MEMORY_GROWTH_GEOMETRIC_CAP bytes (default 96MB). + // 2b. If MEMORY_GROWTH_LINEAR_STEP != -1, excessively resize the heap linearly: increase the heap size by at least MEMORY_GROWTH_LINEAR_STEP bytes. + // 3. Max size for the heap is capped at 2048MB-WASM_PAGE_SIZE, or by MAXIMUM_MEMORY, or by ASAN limit, depending on which is smallest + // 4. If we were unable to allocate as much memory, it may be due to over-eager decision to excessively reserve due to (3) above. + // Hence if an allocation fails, cut down on the amount of excess growth, in an attempt to succeed to perform a smaller allocation. + + // A limit was set for how much we can grow. We should not exceed that + // (the wasm binary specifies it, so if we tried, we'd fail anyhow). + // In CAN_ADDRESS_2GB mode, stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate full 4GB Wasm memories, the size will wrap + // back to 0 bytes in Wasm side for any code that deals with heap sizes, which would require special casing all heap size related code to treat + // 0 specially. + var maxHeapSize = 2147483648; + if (requestedSize > maxHeapSize) { + err('Cannot enlarge memory, asked to go up to ' + requestedSize + ' bytes, but the limit is ' + maxHeapSize + ' bytes!'); + return false; + } + + // Loop through potential heap size increases. If we attempt a too eager reservation that fails, cut down on the + // attempted size and reserve a smaller bump instead. (max 3 times, chosen somewhat arbitrarily) + for(var cutDown = 1; cutDown <= 4; cutDown *= 2) { + var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown); // ensure geometric growth + // but limit overreserving (default to capping at +96MB overgrowth at most) + overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296 ); + + var newSize = Math.min(maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536)); + + var replacement = emscripten_realloc_buffer(newSize); + if (replacement) { + + return true; + } + } + err('Failed to grow the heap from ' + oldSize + ' bytes to ' + newSize + ' bytes, not enough memory!'); + return false; + } + + var JSEvents={inEventHandler:0,removeAllEventListeners:function() { + for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) { + JSEvents._removeHandler(i); + } + JSEvents.eventHandlers = []; + JSEvents.deferredCalls = []; + },registerRemoveEventListeners:function() { + if (!JSEvents.removeEventListenersRegistered) { + __ATEXIT__.push(JSEvents.removeAllEventListeners); + JSEvents.removeEventListenersRegistered = true; + } + },deferredCalls:[],deferCall:function(targetFunction, precedence, argsList) { + function arraysHaveEqualContent(arrA, arrB) { + if (arrA.length != arrB.length) return false; + + for(var i in arrA) { + if (arrA[i] != arrB[i]) return false; + } + return true; + } + // Test if the given call was already queued, and if so, don't add it again. + for(var i in JSEvents.deferredCalls) { + var call = JSEvents.deferredCalls[i]; + if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { + return; + } + } + JSEvents.deferredCalls.push({ + targetFunction: targetFunction, + precedence: precedence, + argsList: argsList + }); + + JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; }); + },removeDeferredCalls:function(targetFunction) { + for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { + if (JSEvents.deferredCalls[i].targetFunction == targetFunction) { + JSEvents.deferredCalls.splice(i, 1); + --i; + } + } + },canPerformEventHandlerRequests:function() { + return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; + },runDeferredCalls:function() { + if (!JSEvents.canPerformEventHandlerRequests()) { + return; + } + for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { + var call = JSEvents.deferredCalls[i]; + JSEvents.deferredCalls.splice(i, 1); + --i; + call.targetFunction.apply(null, call.argsList); + } + },eventHandlers:[],removeAllHandlersOnTarget:function(target, eventTypeString) { + for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == target && + (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) { + JSEvents._removeHandler(i--); + } + } + },_removeHandler:function(i) { + var h = JSEvents.eventHandlers[i]; + h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); + JSEvents.eventHandlers.splice(i, 1); + },registerOrRemoveHandler:function(eventHandler) { + var jsEventHandler = function jsEventHandler(event) { + // Increment nesting count for the event handler. + ++JSEvents.inEventHandler; + JSEvents.currentEventHandler = eventHandler; + // Process any old deferred calls the user has placed. + JSEvents.runDeferredCalls(); + // Process the actual event, calls back to user C code handler. + eventHandler.handlerFunc(event); + // Process any new deferred calls that were placed right now from this event handler. + JSEvents.runDeferredCalls(); + // Out of event handler - restore nesting count. + --JSEvents.inEventHandler; + }; + + if (eventHandler.callbackfunc) { + eventHandler.eventListenerFunc = jsEventHandler; + eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture); + JSEvents.eventHandlers.push(eventHandler); + JSEvents.registerRemoveEventListeners(); + } else { + for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == eventHandler.target + && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { + JSEvents._removeHandler(i--); + } + } + } + },getNodeNameForTarget:function(target) { + if (!target) return ''; + if (target == window) return '#window'; + if (target == screen) return '#screen'; + return (target && target.nodeName) ? target.nodeName : ''; + },fullscreenEnabled:function() { + return document.fullscreenEnabled + // Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitFullscreenEnabled. + // TODO: If Safari at some point ships with unprefixed version, update the version check above. + || document.webkitFullscreenEnabled + ; + }}; + + function maybeCStringToJsString(cString) { + // "cString > 2" checks if the input is a number, and isn't of the special + // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). + // In other words, if cString > 2 then it's a pointer to a valid place in + // memory, and points to a C string. + return cString > 2 ? UTF8ToString(cString) : cString; + } + + var specialHTMLTargets=[0, typeof document !== 'undefined' ? document : 0, typeof window !== 'undefined' ? window : 0]; + function findEventTarget(target) { + target = maybeCStringToJsString(target); + var domElement = specialHTMLTargets[target] || (typeof document !== 'undefined' ? document.querySelector(target) : undefined); + return domElement; + } + function findCanvasEventTarget(target) { return findEventTarget(target); } + function _emscripten_set_canvas_element_size(target, width, height) { + var canvas = findCanvasEventTarget(target); + if (!canvas) return -4; + canvas.width = width; + canvas.height = height; + return 0; + } + + function getBoundingClientRect(e) { + return specialHTMLTargets.indexOf(e) < 0 ? e.getBoundingClientRect() : {'left':0,'top':0}; + } + function fillMouseEventData(eventStruct, e, target) { + assert(eventStruct % 4 == 0); + var idx = eventStruct >> 2; + HEAP32[idx + 0] = e.screenX; + HEAP32[idx + 1] = e.screenY; + HEAP32[idx + 2] = e.clientX; + HEAP32[idx + 3] = e.clientY; + HEAP32[idx + 4] = e.ctrlKey; + HEAP32[idx + 5] = e.shiftKey; + HEAP32[idx + 6] = e.altKey; + HEAP32[idx + 7] = e.metaKey; + HEAP16[idx*2 + 16] = e.button; + HEAP16[idx*2 + 17] = e.buttons; + + HEAP32[idx + 9] = e["movementX"] + ; + + HEAP32[idx + 10] = e["movementY"] + ; + + var rect = getBoundingClientRect(target); + HEAP32[idx + 11] = e.clientX - rect.left; + HEAP32[idx + 12] = e.clientY - rect.top; + + } + function registerMouseEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { + if (!JSEvents.mouseEvent) JSEvents.mouseEvent = _malloc( 64 ); + target = findEventTarget(target); + + var mouseEventHandlerFunc = function(ev) { + var e = ev || event; + + // TODO: Make this access thread safe, or this could update live while app is reading it. + fillMouseEventData(JSEvents.mouseEvent, e, target); + + if (wasmTable.get(callbackfunc)(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: eventTypeString != 'mousemove' && eventTypeString != 'mouseenter' && eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: mouseEventHandlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + } + function _emscripten_set_click_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { + registerMouseEventCallback(target, userData, useCapture, callbackfunc, 4, "click", targetThread); + return 0; + } + + function __webgl_enable_ANGLE_instanced_arrays(ctx) { + // Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2. + var ext = ctx.getExtension('ANGLE_instanced_arrays'); + if (ext) { + ctx['vertexAttribDivisor'] = function(index, divisor) { ext['vertexAttribDivisorANGLE'](index, divisor); }; + ctx['drawArraysInstanced'] = function(mode, first, count, primcount) { ext['drawArraysInstancedANGLE'](mode, first, count, primcount); }; + ctx['drawElementsInstanced'] = function(mode, count, type, indices, primcount) { ext['drawElementsInstancedANGLE'](mode, count, type, indices, primcount); }; + return 1; + } + } + + function __webgl_enable_OES_vertex_array_object(ctx) { + // Extension available in WebGL 1 from Firefox 25 and WebKit 536.28/desktop Safari 6.0.3 onwards. Core feature in WebGL 2. + var ext = ctx.getExtension('OES_vertex_array_object'); + if (ext) { + ctx['createVertexArray'] = function() { return ext['createVertexArrayOES'](); }; + ctx['deleteVertexArray'] = function(vao) { ext['deleteVertexArrayOES'](vao); }; + ctx['bindVertexArray'] = function(vao) { ext['bindVertexArrayOES'](vao); }; + ctx['isVertexArray'] = function(vao) { return ext['isVertexArrayOES'](vao); }; + return 1; + } + } + + function __webgl_enable_WEBGL_draw_buffers(ctx) { + // Extension available in WebGL 1 from Firefox 28 onwards. Core feature in WebGL 2. + var ext = ctx.getExtension('WEBGL_draw_buffers'); + if (ext) { + ctx['drawBuffers'] = function(n, bufs) { ext['drawBuffersWEBGL'](n, bufs); }; + return 1; + } + } + + function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx) { + // Closure is expected to be allowed to minify the '.dibvbi' property, so not accessing it quoted. + return !!(ctx.dibvbi = ctx.getExtension('WEBGL_draw_instanced_base_vertex_base_instance')); + } + + function __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(ctx) { + // Closure is expected to be allowed to minify the '.mdibvbi' property, so not accessing it quoted. + return !!(ctx.mdibvbi = ctx.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance')); + } + + function __webgl_enable_WEBGL_multi_draw(ctx) { + // Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted. + return !!(ctx.multiDrawWebgl = ctx.getExtension('WEBGL_multi_draw')); + } + var GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},timerQueriesEXT:[],queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],programInfos:{},stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode) { + if (!GL.lastError) { + GL.lastError = errorCode; + } + },getNewId:function(table) { + var ret = GL.counter++; + for (var i = table.length; i < ret; i++) { + table[i] = null; + } + return ret; + },MAX_TEMP_BUFFER_SIZE:2097152,numTempVertexBuffersPerSize:64,log2ceilLookup:function(i) { + return 32 - Math.clz32(i-1); + },generateTempBuffers:function(quads, context) { + var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE); + context.tempVertexBufferCounters1 = []; + context.tempVertexBufferCounters2 = []; + context.tempVertexBufferCounters1.length = context.tempVertexBufferCounters2.length = largestIndex+1; + context.tempVertexBuffers1 = []; + context.tempVertexBuffers2 = []; + context.tempVertexBuffers1.length = context.tempVertexBuffers2.length = largestIndex+1; + context.tempIndexBuffers = []; + context.tempIndexBuffers.length = largestIndex+1; + for (var i = 0; i <= largestIndex; ++i) { + context.tempIndexBuffers[i] = null; // Created on-demand + context.tempVertexBufferCounters1[i] = context.tempVertexBufferCounters2[i] = 0; + var ringbufferLength = GL.numTempVertexBuffersPerSize; + context.tempVertexBuffers1[i] = []; + context.tempVertexBuffers2[i] = []; + var ringbuffer1 = context.tempVertexBuffers1[i]; + var ringbuffer2 = context.tempVertexBuffers2[i]; + ringbuffer1.length = ringbuffer2.length = ringbufferLength; + for (var j = 0; j < ringbufferLength; ++j) { + ringbuffer1[j] = ringbuffer2[j] = null; // Created on-demand + } + } + + if (quads) { + // GL_QUAD indexes can be precalculated + context.tempQuadIndexBuffer = GLctx.createBuffer(); + context.GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, context.tempQuadIndexBuffer); + var numIndexes = GL.MAX_TEMP_BUFFER_SIZE >> 1; + var quadIndexes = new Uint16Array(numIndexes); + var i = 0, v = 0; + while (1) { + quadIndexes[i++] = v; + if (i >= numIndexes) break; + quadIndexes[i++] = v+1; + if (i >= numIndexes) break; + quadIndexes[i++] = v+2; + if (i >= numIndexes) break; + quadIndexes[i++] = v; + if (i >= numIndexes) break; + quadIndexes[i++] = v+2; + if (i >= numIndexes) break; + quadIndexes[i++] = v+3; + if (i >= numIndexes) break; + v += 4; + } + context.GLctx.bufferData(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, quadIndexes, 0x88E4 /*GL_STATIC_DRAW*/); + context.GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, null); + } + },getTempVertexBuffer:function getTempVertexBuffer(sizeBytes) { + var idx = GL.log2ceilLookup(sizeBytes); + var ringbuffer = GL.currentContext.tempVertexBuffers1[idx]; + var nextFreeBufferIndex = GL.currentContext.tempVertexBufferCounters1[idx]; + GL.currentContext.tempVertexBufferCounters1[idx] = (GL.currentContext.tempVertexBufferCounters1[idx]+1) & (GL.numTempVertexBuffersPerSize-1); + var vbo = ringbuffer[nextFreeBufferIndex]; + if (vbo) { + return vbo; + } + var prevVBO = GLctx.getParameter(0x8894 /*GL_ARRAY_BUFFER_BINDING*/); + ringbuffer[nextFreeBufferIndex] = GLctx.createBuffer(); + GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, ringbuffer[nextFreeBufferIndex]); + GLctx.bufferData(0x8892 /*GL_ARRAY_BUFFER*/, 1 << idx, 0x88E8 /*GL_DYNAMIC_DRAW*/); + GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, prevVBO); + return ringbuffer[nextFreeBufferIndex]; + },getTempIndexBuffer:function getTempIndexBuffer(sizeBytes) { + var idx = GL.log2ceilLookup(sizeBytes); + var ibo = GL.currentContext.tempIndexBuffers[idx]; + if (ibo) { + return ibo; + } + var prevIBO = GLctx.getParameter(0x8895 /*ELEMENT_ARRAY_BUFFER_BINDING*/); + GL.currentContext.tempIndexBuffers[idx] = GLctx.createBuffer(); + GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, GL.currentContext.tempIndexBuffers[idx]); + GLctx.bufferData(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, 1 << idx, 0x88E8 /*GL_DYNAMIC_DRAW*/); + GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, prevIBO); + return GL.currentContext.tempIndexBuffers[idx]; + },newRenderingFrameStarted:function newRenderingFrameStarted() { + if (!GL.currentContext) { + return; + } + var vb = GL.currentContext.tempVertexBuffers1; + GL.currentContext.tempVertexBuffers1 = GL.currentContext.tempVertexBuffers2; + GL.currentContext.tempVertexBuffers2 = vb; + vb = GL.currentContext.tempVertexBufferCounters1; + GL.currentContext.tempVertexBufferCounters1 = GL.currentContext.tempVertexBufferCounters2; + GL.currentContext.tempVertexBufferCounters2 = vb; + var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE); + for (var i = 0; i <= largestIndex; ++i) { + GL.currentContext.tempVertexBufferCounters1[i] = 0; + } + },getSource:function(shader, count, string, length) { + var source = ''; + for (var i = 0; i < count; ++i) { + var len = length ? HEAP32[(((length)+(i*4))>>2)] : -1; + source += UTF8ToString(HEAP32[(((string)+(i*4))>>2)], len < 0 ? undefined : len); + } + return source; + },calcBufLength:function calcBufLength(size, type, stride, count) { + if (stride > 0) { + return count * stride; // XXXvlad this is not exactly correct I don't think + } + var typeSize = GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; + return size * typeSize * count; + },usedTempBuffers:[],preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count) { + GL.resetBufferBinding = false; + + // TODO: initial pass to detect ranges we need to upload, might not need an upload per attrib + for (var i = 0; i < GL.currentContext.maxVertexAttribs; ++i) { + var cb = GL.currentContext.clientBuffers[i]; + if (!cb.clientside || !cb.enabled) continue; + + GL.resetBufferBinding = true; + + var size = GL.calcBufLength(cb.size, cb.type, cb.stride, count); + var buf = GL.getTempVertexBuffer(size); + GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, buf); + GLctx.bufferSubData(0x8892 /*GL_ARRAY_BUFFER*/, + 0, + HEAPU8.subarray(cb.ptr, cb.ptr + size)); + cb.vertexAttribPointerAdaptor.call(GLctx, i, cb.size, cb.type, cb.normalized, cb.stride, 0); + } + },postDrawHandleClientVertexAttribBindings:function postDrawHandleClientVertexAttribBindings() { + if (GL.resetBufferBinding) { + GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, GL.buffers[GLctx.currentArrayBufferBinding]); + } + },createContext:function(canvas, webGLContextAttributes) { + + var ctx = + (webGLContextAttributes.majorVersion > 1) + ? + canvas.getContext("webgl2", webGLContextAttributes) + : + (canvas.getContext("webgl", webGLContextAttributes) + // https://caniuse.com/#feat=webgl + ); + + if (!ctx) return 0; + + var handle = GL.registerContext(ctx, webGLContextAttributes); + + return handle; + },registerContext:function(ctx, webGLContextAttributes) { + // without pthreads a context is just an integer ID + var handle = GL.getNewId(GL.contexts); + + var context = { + handle: handle, + attributes: webGLContextAttributes, + version: webGLContextAttributes.majorVersion, + GLctx: ctx + }; + + // Store the created context object so that we can access the context given a canvas without having to pass the parameters again. + if (ctx.canvas) ctx.canvas.GLctxObject = context; + GL.contexts[handle] = context; + if (typeof webGLContextAttributes.enableExtensionsByDefault === 'undefined' || webGLContextAttributes.enableExtensionsByDefault) { + GL.initExtensions(context); + } + + context.maxVertexAttribs = context.GLctx.getParameter(0x8869 /*GL_MAX_VERTEX_ATTRIBS*/); + context.clientBuffers = []; + for (var i = 0; i < context.maxVertexAttribs; i++) { + context.clientBuffers[i] = { enabled: false, clientside: false, size: 0, type: 0, normalized: 0, stride: 0, ptr: 0, vertexAttribPointerAdaptor: null }; + } + + GL.generateTempBuffers(false, context); + + return handle; + },makeContextCurrent:function(contextHandle) { + + GL.currentContext = GL.contexts[contextHandle]; // Active Emscripten GL layer context object. + Module.ctx = GLctx = GL.currentContext && GL.currentContext.GLctx; // Active WebGL context object. + return !(contextHandle && !GLctx); + },getContext:function(contextHandle) { + return GL.contexts[contextHandle]; + },deleteContext:function(contextHandle) { + if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null; + if (typeof JSEvents === 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted. + if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises. + GL.contexts[contextHandle] = null; + },initExtensions:function(context) { + // If this function is called without a specific context object, init the extensions of the currently active context. + if (!context) context = GL.currentContext; + + if (context.initExtensionsDone) return; + context.initExtensionsDone = true; + + var GLctx = context.GLctx; + + // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist. + + // Extensions that are only available in WebGL 1 (the calls will be no-ops if called on a WebGL 2 context active) + __webgl_enable_ANGLE_instanced_arrays(GLctx); + __webgl_enable_OES_vertex_array_object(GLctx); + __webgl_enable_WEBGL_draw_buffers(GLctx); + // Extensions that are available from WebGL >= 2 (no-op if called on a WebGL 1 context active) + __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); + __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx); + + GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); + __webgl_enable_WEBGL_multi_draw(GLctx); + + // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. + var exts = GLctx.getSupportedExtensions() || []; + exts.forEach(function(ext) { + // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders are not enabled by default. + if (ext.indexOf('lose_context') < 0 && ext.indexOf('debug') < 0) { + // Call .getExtension() to enable that extension permanently. + GLctx.getExtension(ext); + } + }); + },populateUniformTable:function(program) { + var p = GL.programs[program]; + var ptable = GL.programInfos[program] = { + uniforms: {}, + maxUniformLength: 0, // This is eagerly computed below, since we already enumerate all uniforms anyway. + maxAttributeLength: -1, // This is lazily computed and cached, computed when/if first asked, "-1" meaning not computed yet. + maxUniformBlockNameLength: -1 // Lazily computed as well + }; + + var utable = ptable.uniforms; + // A program's uniform table maps the string name of an uniform to an integer location of that uniform. + // The global GL.uniforms map maps integer locations to WebGLUniformLocations. + var numUniforms = GLctx.getProgramParameter(p, 0x8B86/*GL_ACTIVE_UNIFORMS*/); + for (var i = 0; i < numUniforms; ++i) { + var u = GLctx.getActiveUniform(p, i); + + var name = u.name; + ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length+1); + + // If we are dealing with an array, e.g. vec4 foo[3], strip off the array index part to canonicalize that "foo", "foo[]", + // and "foo[0]" will mean the same. Loop below will populate foo[1] and foo[2]. + if (name.slice(-1) == ']') { + name = name.slice(0, name.lastIndexOf('[')); + } + + // Optimize memory usage slightly: If we have an array of uniforms, e.g. 'vec3 colors[3];', then + // only store the string 'colors' in utable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i. + // Note that for the GL.uniforms table, we still need to fetch the all WebGLUniformLocations for all the indices. + var loc = GLctx.getUniformLocation(p, name); + if (loc) { + var id = GL.getNewId(GL.uniforms); + utable[name] = [u.size, id]; + GL.uniforms[id] = loc; + + for (var j = 1; j < u.size; ++j) { + var n = name + '['+j+']'; + loc = GLctx.getUniformLocation(p, n); + id = GL.getNewId(GL.uniforms); + + GL.uniforms[id] = loc; + } + } + } + }}; + + var __emscripten_webgl_power_preferences=['default', 'low-power', 'high-performance']; + function _emscripten_webgl_do_create_context(target, attributes) { + assert(attributes); + var a = attributes >> 2; + var powerPreference = HEAP32[a + (24>>2)]; + var contextAttributes = { + 'alpha': !!HEAP32[a + (0>>2)], + 'depth': !!HEAP32[a + (4>>2)], + 'stencil': !!HEAP32[a + (8>>2)], + 'antialias': !!HEAP32[a + (12>>2)], + 'premultipliedAlpha': !!HEAP32[a + (16>>2)], + 'preserveDrawingBuffer': !!HEAP32[a + (20>>2)], + 'powerPreference': __emscripten_webgl_power_preferences[powerPreference], + 'failIfMajorPerformanceCaveat': !!HEAP32[a + (28>>2)], + // The following are not predefined WebGL context attributes in the WebGL specification, so the property names can be minified by Closure. + majorVersion: HEAP32[a + (32>>2)], + minorVersion: HEAP32[a + (36>>2)], + enableExtensionsByDefault: HEAP32[a + (40>>2)], + explicitSwapControl: HEAP32[a + (44>>2)], + proxyContextToMainThread: HEAP32[a + (48>>2)], + renderViaOffscreenBackBuffer: HEAP32[a + (52>>2)] + }; + + var canvas = findCanvasEventTarget(target); + + if (!canvas) { + return 0; + } + + if (contextAttributes.explicitSwapControl) { + return 0; + } + + var contextHandle = GL.createContext(canvas, contextAttributes); + return contextHandle; + } + function _emscripten_webgl_create_context(a0,a1 + ) { + return _emscripten_webgl_do_create_context(a0,a1); + } + + function _emscripten_webgl_init_context_attributes(attributes) { + assert(attributes); + var a = attributes >> 2; + for(var i = 0; i < (56>>2); ++i) { + HEAP32[a+i] = 0; + } + + HEAP32[a + (0>>2)] = + HEAP32[a + (4>>2)] = + HEAP32[a + (12>>2)] = + HEAP32[a + (16>>2)] = + HEAP32[a + (32>>2)] = + HEAP32[a + (40>>2)] = 1; + + } + + function _emscripten_webgl_make_context_current(contextHandle) { + var success = GL.makeContextCurrent(contextHandle); + return success ? 0 : -5; + } + + function flush_NO_FILESYSTEM() { + // flush anything remaining in the buffers during shutdown + if (typeof _fflush !== 'undefined') _fflush(0); + var buffers = SYSCALLS.buffers; + if (buffers[1].length) SYSCALLS.printChar(1, 10); + if (buffers[2].length) SYSCALLS.printChar(2, 10); + } + + var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream, curr) { + var buffer = SYSCALLS.buffers[stream]; + assert(buffer); + if (curr === 0 || curr === 10) { + (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); + buffer.length = 0; + } else { + buffer.push(curr); + } + },varargs:undefined,get:function() { + assert(SYSCALLS.varargs != undefined); + SYSCALLS.varargs += 4; + var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; + return ret; + },getStr:function(ptr) { + var ret = UTF8ToString(ptr); + return ret; + },get64:function(low, high) { + if (low >= 0) assert(high === 0); + else assert(high === -1); + return low; + }}; + function _fd_write(fd, iov, iovcnt, pnum) { + // hack to support printf in SYSCALLS_REQUIRE_FILESYSTEM=0 + var num = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[(((iov)+(i*8))>>2)]; + var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; + for (var j = 0; j < len; j++) { + SYSCALLS.printChar(fd, HEAPU8[ptr+j]); + } + num += len; + } + HEAP32[((pnum)>>2)] = num + return 0; + } + + function _glAttachShader(program, shader) { + GLctx.attachShader(GL.programs[program], + GL.shaders[shader]); + } + + function _glBindBuffer(target, buffer) { + if (target == 0x8892 /*GL_ARRAY_BUFFER*/) { + GLctx.currentArrayBufferBinding = buffer; + } else if (target == 0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/) { + GLctx.currentElementArrayBufferBinding = buffer; + } + + if (target == 0x88EB /*GL_PIXEL_PACK_BUFFER*/) { + // In WebGL 2 glReadPixels entry point, we need to use a different WebGL 2 API function call when a buffer is bound to + // GL_PIXEL_PACK_BUFFER_BINDING point, so must keep track whether that binding point is non-null to know what is + // the proper API function to call. + GLctx.currentPixelPackBufferBinding = buffer; + } else if (target == 0x88EC /*GL_PIXEL_UNPACK_BUFFER*/) { + // In WebGL 2 gl(Compressed)Tex(Sub)Image[23]D entry points, we need to + // use a different WebGL 2 API function call when a buffer is bound to + // GL_PIXEL_UNPACK_BUFFER_BINDING point, so must keep track whether that + // binding point is non-null to know what is the proper API function to + // call. + GLctx.currentPixelUnpackBufferBinding = buffer; + } + GLctx.bindBuffer(target, GL.buffers[buffer]); + } + + function _glBindVertexArray(vao) { + GLctx['bindVertexArray'](GL.vaos[vao]); + var ibo = GLctx.getParameter(0x8895 /*ELEMENT_ARRAY_BUFFER_BINDING*/); + GLctx.currentElementArrayBufferBinding = ibo ? (ibo.name | 0) : 0; + } + + function _glBufferData(target, size, data, usage) { + + if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. + if (data) { + GLctx.bufferData(target, HEAPU8, usage, data, size); + } else { + GLctx.bufferData(target, size, usage); + } + } else { + // N.b. here first form specifies a heap subarray, second form an integer size, so the ?: code here is polymorphic. It is advised to avoid + // randomly mixing both uses in calling code, to avoid any potential JS engine JIT issues. + GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage); + } + } + + function _glClear(x0) { GLctx['clear'](x0) } + + function _glClearColor(x0, x1, x2, x3) { GLctx['clearColor'](x0, x1, x2, x3) } + + function _glClearDepth(x0) { GLctx['clearDepth'](x0) } + + function _glCompileShader(shader) { + GLctx.compileShader(GL.shaders[shader]); + } + + function _glCreateProgram() { + var id = GL.getNewId(GL.programs); + var program = GLctx.createProgram(); + program.name = id; + GL.programs[id] = program; + return id; + } + + function _glCreateShader(shaderType) { + var id = GL.getNewId(GL.shaders); + GL.shaders[id] = GLctx.createShader(shaderType); + return id; + } + + function _glDeleteBuffers(n, buffers) { + for (var i = 0; i < n; i++) { + var id = HEAP32[(((buffers)+(i*4))>>2)]; + var buffer = GL.buffers[id]; + + // From spec: "glDeleteBuffers silently ignores 0's and names that do not + // correspond to existing buffer objects." + if (!buffer) continue; + + GLctx.deleteBuffer(buffer); + buffer.name = 0; + GL.buffers[id] = null; + + if (id == GLctx.currentArrayBufferBinding) GLctx.currentArrayBufferBinding = 0; + if (id == GLctx.currentElementArrayBufferBinding) GLctx.currentElementArrayBufferBinding = 0; + if (id == GLctx.currentPixelPackBufferBinding) GLctx.currentPixelPackBufferBinding = 0; + if (id == GLctx.currentPixelUnpackBufferBinding) GLctx.currentPixelUnpackBufferBinding = 0; + } + } + + function _glDeleteProgram(id) { + if (!id) return; + var program = GL.programs[id]; + if (!program) { // glDeleteProgram actually signals an error when deleting a nonexisting object, unlike some other GL delete functions. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + GLctx.deleteProgram(program); + program.name = 0; + GL.programs[id] = null; + GL.programInfos[id] = null; + } + + function _glDeleteShader(id) { + if (!id) return; + var shader = GL.shaders[id]; + if (!shader) { // glDeleteShader actually signals an error when deleting a nonexisting object, unlike some other GL delete functions. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + GLctx.deleteShader(shader); + GL.shaders[id] = null; + } + + function _glDeleteVertexArrays(n, vaos) { + for (var i = 0; i < n; i++) { + var id = HEAP32[(((vaos)+(i*4))>>2)]; + GLctx['deleteVertexArray'](GL.vaos[id]); + GL.vaos[id] = null; + } + } + + function _glDepthFunc(x0) { GLctx['depthFunc'](x0) } + + function _glDepthMask(flag) { + GLctx.depthMask(!!flag); + } + + function _glDrawArrays(mode, first, count) { + // bind any client-side buffers + GL.preDrawHandleClientVertexAttribBindings(first + count); + + GLctx.drawArrays(mode, first, count); + + GL.postDrawHandleClientVertexAttribBindings(); + } + + function _glEnable(x0) { GLctx['enable'](x0) } + + function _glEnableVertexAttribArray(index) { + var cb = GL.currentContext.clientBuffers[index]; + cb.enabled = true; + GLctx.enableVertexAttribArray(index); + } + + function __glGenObject(n, buffers, createFunction, objectTable + ) { + for (var i = 0; i < n; i++) { + var buffer = GLctx[createFunction](); + var id = buffer && GL.getNewId(objectTable); + if (buffer) { + buffer.name = id; + objectTable[id] = buffer; + } else { + GL.recordError(0x502 /* GL_INVALID_OPERATION */); + } + HEAP32[(((buffers)+(i*4))>>2)] = id; + } + } + function _glGenBuffers(n, buffers) { + __glGenObject(n, buffers, 'createBuffer', GL.buffers + ); + } + + function _glGenVertexArrays(n, arrays) { + __glGenObject(n, arrays, 'createVertexArray', GL.vaos + ); + } + + function _glGetAttribLocation(program, name) { + return GLctx.getAttribLocation(GL.programs[program], UTF8ToString(name)); + } + + function _glGetProgramInfoLog(program, maxLength, length, infoLog) { + var log = GLctx.getProgramInfoLog(GL.programs[program]); + if (log === null) log = '(unknown error)'; + var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; + if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; + } + + function _glGetProgramiv(program, pname, p) { + if (!p) { + // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + + if (program >= GL.counter) { + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + + var ptable = GL.programInfos[program]; + if (!ptable) { + GL.recordError(0x502 /* GL_INVALID_OPERATION */); + return; + } + + if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH + var log = GLctx.getProgramInfoLog(GL.programs[program]); + if (log === null) log = '(unknown error)'; + HEAP32[((p)>>2)] = log.length + 1; + } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) { + HEAP32[((p)>>2)] = ptable.maxUniformLength; + } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { + if (ptable.maxAttributeLength == -1) { + program = GL.programs[program]; + var numAttribs = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); + ptable.maxAttributeLength = 0; // Spec says if there are no active attribs, 0 must be returned. + for (var i = 0; i < numAttribs; ++i) { + var activeAttrib = GLctx.getActiveAttrib(program, i); + ptable.maxAttributeLength = Math.max(ptable.maxAttributeLength, activeAttrib.name.length+1); + } + } + HEAP32[((p)>>2)] = ptable.maxAttributeLength; + } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) { + if (ptable.maxUniformBlockNameLength == -1) { + program = GL.programs[program]; + var numBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); + ptable.maxUniformBlockNameLength = 0; + for (var i = 0; i < numBlocks; ++i) { + var activeBlockName = GLctx.getActiveUniformBlockName(program, i); + ptable.maxUniformBlockNameLength = Math.max(ptable.maxUniformBlockNameLength, activeBlockName.length+1); + } + } + HEAP32[((p)>>2)] = ptable.maxUniformBlockNameLength; + } else { + HEAP32[((p)>>2)] = GLctx.getProgramParameter(GL.programs[program], pname); + } + } + + function _glGetShaderInfoLog(shader, maxLength, length, infoLog) { + var log = GLctx.getShaderInfoLog(GL.shaders[shader]); + if (log === null) log = '(unknown error)'; + var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; + if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; + } + + function _glGetShaderiv(shader, pname, p) { + if (!p) { + // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH + var log = GLctx.getShaderInfoLog(GL.shaders[shader]); + if (log === null) log = '(unknown error)'; + // The GLES2 specification says that if the shader has an empty info log, + // a value of 0 is returned. Otherwise the log has a null char appended. + // (An empty string is falsey, so we can just check that instead of + // looking at log.length.) + var logLength = log ? log.length + 1 : 0; + HEAP32[((p)>>2)] = logLength; + } else if (pname == 0x8B88) { // GL_SHADER_SOURCE_LENGTH + var source = GLctx.getShaderSource(GL.shaders[shader]); + // source may be a null, or the empty string, both of which are falsey + // values that we report a 0 length for. + var sourceLength = source ? source.length + 1 : 0; + HEAP32[((p)>>2)] = sourceLength; + } else { + HEAP32[((p)>>2)] = GLctx.getShaderParameter(GL.shaders[shader], pname); + } + } + + /** @suppress {checkTypes} */ + function jstoi_q(str) { + return parseInt(str); + } + function _glGetUniformLocation(program, name) { + name = UTF8ToString(name); + + var arrayIndex = 0; + // If user passed an array accessor "[index]", parse the array index off the accessor. + if (name[name.length - 1] == ']') { + var leftBrace = name.lastIndexOf('['); + arrayIndex = name[leftBrace+1] != ']' ? jstoi_q(name.slice(leftBrace + 1)) : 0; // "index]", parseInt will ignore the ']' at the end; but treat "foo[]" as "foo[0]" + name = name.slice(0, leftBrace); + } + + var uniformInfo = GL.programInfos[program] && GL.programInfos[program].uniforms[name]; // returns pair [ dimension_of_uniform_array, uniform_location ] + if (uniformInfo && arrayIndex >= 0 && arrayIndex < uniformInfo[0]) { // Check if user asked for an out-of-bounds element, i.e. for 'vec4 colors[3];' user could ask for 'colors[10]' which should return -1. + return uniformInfo[1] + arrayIndex; + } else { + return -1; + } + } + + function _glLinkProgram(program) { + GLctx.linkProgram(GL.programs[program]); + GL.populateUniformTable(program); + } + + function _glShaderSource(shader, count, string, length) { + var source = GL.getSource(shader, count, string, length); + + GLctx.shaderSource(GL.shaders[shader], source); + } + + var miniTempWebGLFloatBuffers=[]; + function _glUniformMatrix4fv(location, count, transpose, value) { + + if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. + GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*16); + return; + } + + if (count <= 18) { + // avoid allocation when uploading few enough uniforms + var view = miniTempWebGLFloatBuffers[16*count-1]; + // hoist the heap out of the loop for size and for pthreads+growth. + var heap = HEAPF32; + value >>= 2; + for (var i = 0; i < 16 * count; i += 16) { + var dst = value + i; + view[i] = heap[dst]; + view[i + 1] = heap[dst + 1]; + view[i + 2] = heap[dst + 2]; + view[i + 3] = heap[dst + 3]; + view[i + 4] = heap[dst + 4]; + view[i + 5] = heap[dst + 5]; + view[i + 6] = heap[dst + 6]; + view[i + 7] = heap[dst + 7]; + view[i + 8] = heap[dst + 8]; + view[i + 9] = heap[dst + 9]; + view[i + 10] = heap[dst + 10]; + view[i + 11] = heap[dst + 11]; + view[i + 12] = heap[dst + 12]; + view[i + 13] = heap[dst + 13]; + view[i + 14] = heap[dst + 14]; + view[i + 15] = heap[dst + 15]; + } + } else + { + var view = HEAPF32.subarray((value)>>2, (value+count*64)>>2); + } + GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, view); + } + + function _glUseProgram(program) { + GLctx.useProgram(GL.programs[program]); + } + + function _glVertexAttribPointer(index, size, type, normalized, stride, ptr) { + var cb = GL.currentContext.clientBuffers[index]; + if (!GLctx.currentArrayBufferBinding) { + cb.size = size; + cb.type = type; + cb.normalized = normalized; + cb.stride = stride; + cb.ptr = ptr; + cb.clientside = true; + cb.vertexAttribPointerAdaptor = function(index, size, type, normalized, stride, ptr) { + this.vertexAttribPointer(index, size, type, normalized, stride, ptr); + }; + return; + } + cb.clientside = false; + GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); + } + + function _setTempRet0($i) { + setTempRet0(($i) | 0); + } +var GLctx;; +var miniTempWebGLFloatBuffersStorage = new Float32Array(288); + for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { + miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i+1); + } + ; +var ASSERTIONS = true; + + + +/** @type {function(string, boolean=, number=)} */ +function intArrayFromString(stringy, dontAddNull, length) { + var len = length > 0 ? length : lengthBytesUTF8(stringy)+1; + var u8array = new Array(len); + var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); + if (dontAddNull) u8array.length = numBytesWritten; + return u8array; +} + +function intArrayToString(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + var chr = array[i]; + if (chr > 0xFF) { + if (ASSERTIONS) { + assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.'); + } + chr &= 0xFF; + } + ret.push(String.fromCharCode(chr)); + } + return ret.join(''); +} + + +var asmLibraryArg = { + "abort": _abort, + "emscripten_memcpy_big": _emscripten_memcpy_big, + "emscripten_request_animation_frame_loop": _emscripten_request_animation_frame_loop, + "emscripten_resize_heap": _emscripten_resize_heap, + "emscripten_set_canvas_element_size": _emscripten_set_canvas_element_size, + "emscripten_set_click_callback_on_thread": _emscripten_set_click_callback_on_thread, + "emscripten_webgl_create_context": _emscripten_webgl_create_context, + "emscripten_webgl_init_context_attributes": _emscripten_webgl_init_context_attributes, + "emscripten_webgl_make_context_current": _emscripten_webgl_make_context_current, + "fd_write": _fd_write, + "glAttachShader": _glAttachShader, + "glBindBuffer": _glBindBuffer, + "glBindVertexArray": _glBindVertexArray, + "glBufferData": _glBufferData, + "glClear": _glClear, + "glClearColor": _glClearColor, + "glClearDepth": _glClearDepth, + "glCompileShader": _glCompileShader, + "glCreateProgram": _glCreateProgram, + "glCreateShader": _glCreateShader, + "glDeleteBuffers": _glDeleteBuffers, + "glDeleteProgram": _glDeleteProgram, + "glDeleteShader": _glDeleteShader, + "glDeleteVertexArrays": _glDeleteVertexArrays, + "glDepthFunc": _glDepthFunc, + "glDepthMask": _glDepthMask, + "glDrawArrays": _glDrawArrays, + "glEnable": _glEnable, + "glEnableVertexAttribArray": _glEnableVertexAttribArray, + "glGenBuffers": _glGenBuffers, + "glGenVertexArrays": _glGenVertexArrays, + "glGetAttribLocation": _glGetAttribLocation, + "glGetProgramInfoLog": _glGetProgramInfoLog, + "glGetProgramiv": _glGetProgramiv, + "glGetShaderInfoLog": _glGetShaderInfoLog, + "glGetShaderiv": _glGetShaderiv, + "glGetUniformLocation": _glGetUniformLocation, + "glLinkProgram": _glLinkProgram, + "glShaderSource": _glShaderSource, + "glUniformMatrix4fv": _glUniformMatrix4fv, + "glUseProgram": _glUseProgram, + "glVertexAttribPointer": _glVertexAttribPointer, + "setTempRet0": _setTempRet0 +}; +var asm = createWasm(); +/** @type {function(...*):?} */ +var ___wasm_call_ctors = Module["___wasm_call_ctors"] = createExportWrapper("__wasm_call_ctors"); + +/** @type {function(...*):?} */ +var _main = Module["_main"] = createExportWrapper("main"); + +/** @type {function(...*):?} */ +var ___errno_location = Module["___errno_location"] = createExportWrapper("__errno_location"); + +/** @type {function(...*):?} */ +var _fflush = Module["_fflush"] = createExportWrapper("fflush"); + +/** @type {function(...*):?} */ +var stackSave = Module["stackSave"] = createExportWrapper("stackSave"); + +/** @type {function(...*):?} */ +var stackRestore = Module["stackRestore"] = createExportWrapper("stackRestore"); + +/** @type {function(...*):?} */ +var stackAlloc = Module["stackAlloc"] = createExportWrapper("stackAlloc"); + +/** @type {function(...*):?} */ +var _emscripten_stack_init = Module["_emscripten_stack_init"] = function() { + return (_emscripten_stack_init = Module["_emscripten_stack_init"] = Module["asm"]["emscripten_stack_init"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _emscripten_stack_get_free = Module["_emscripten_stack_get_free"] = function() { + return (_emscripten_stack_get_free = Module["_emscripten_stack_get_free"] = Module["asm"]["emscripten_stack_get_free"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _emscripten_stack_get_end = Module["_emscripten_stack_get_end"] = function() { + return (_emscripten_stack_get_end = Module["_emscripten_stack_get_end"] = Module["asm"]["emscripten_stack_get_end"]).apply(null, arguments); +}; + +/** @type {function(...*):?} */ +var _malloc = Module["_malloc"] = createExportWrapper("malloc"); + +/** @type {function(...*):?} */ +var _free = Module["_free"] = createExportWrapper("free"); + +/** @type {function(...*):?} */ +var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji"); + + + + + +// === Auto-generated postamble setup entry stuff === + +if (!Object.getOwnPropertyDescriptor(Module, "intArrayFromString")) Module["intArrayFromString"] = function() { abort("'intArrayFromString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "intArrayToString")) Module["intArrayToString"] = function() { abort("'intArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "ccall")) Module["ccall"] = function() { abort("'ccall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "cwrap")) Module["cwrap"] = function() { abort("'cwrap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "setValue")) Module["setValue"] = function() { abort("'setValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getValue")) Module["getValue"] = function() { abort("'getValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "allocate")) Module["allocate"] = function() { abort("'allocate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "UTF8ArrayToString")) Module["UTF8ArrayToString"] = function() { abort("'UTF8ArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "UTF8ToString")) Module["UTF8ToString"] = function() { abort("'UTF8ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF8Array")) Module["stringToUTF8Array"] = function() { abort("'stringToUTF8Array' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF8")) Module["stringToUTF8"] = function() { abort("'stringToUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF8")) Module["lengthBytesUTF8"] = function() { abort("'lengthBytesUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stackTrace")) Module["stackTrace"] = function() { abort("'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "addOnPreRun")) Module["addOnPreRun"] = function() { abort("'addOnPreRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "addOnInit")) Module["addOnInit"] = function() { abort("'addOnInit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "addOnPreMain")) Module["addOnPreMain"] = function() { abort("'addOnPreMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "addOnExit")) Module["addOnExit"] = function() { abort("'addOnExit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "addOnPostRun")) Module["addOnPostRun"] = function() { abort("'addOnPostRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeStringToMemory")) Module["writeStringToMemory"] = function() { abort("'writeStringToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeArrayToMemory")) Module["writeArrayToMemory"] = function() { abort("'writeArrayToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeAsciiToMemory")) Module["writeAsciiToMemory"] = function() { abort("'writeAsciiToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "addRunDependency")) Module["addRunDependency"] = function() { abort("'addRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; +if (!Object.getOwnPropertyDescriptor(Module, "removeRunDependency")) Module["removeRunDependency"] = function() { abort("'removeRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS_createFolder")) Module["FS_createFolder"] = function() { abort("'FS_createFolder' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS_createPath")) Module["FS_createPath"] = function() { abort("'FS_createPath' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS_createDataFile")) Module["FS_createDataFile"] = function() { abort("'FS_createDataFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS_createPreloadedFile")) Module["FS_createPreloadedFile"] = function() { abort("'FS_createPreloadedFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS_createLazyFile")) Module["FS_createLazyFile"] = function() { abort("'FS_createLazyFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS_createLink")) Module["FS_createLink"] = function() { abort("'FS_createLink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS_createDevice")) Module["FS_createDevice"] = function() { abort("'FS_createDevice' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS_unlink")) Module["FS_unlink"] = function() { abort("'FS_unlink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; +if (!Object.getOwnPropertyDescriptor(Module, "getLEB")) Module["getLEB"] = function() { abort("'getLEB' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getFunctionTables")) Module["getFunctionTables"] = function() { abort("'getFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "alignFunctionTables")) Module["alignFunctionTables"] = function() { abort("'alignFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerFunctions")) Module["registerFunctions"] = function() { abort("'registerFunctions' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "addFunction")) Module["addFunction"] = function() { abort("'addFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "removeFunction")) Module["removeFunction"] = function() { abort("'removeFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getFuncWrapper")) Module["getFuncWrapper"] = function() { abort("'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "prettyPrint")) Module["prettyPrint"] = function() { abort("'prettyPrint' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "makeBigInt")) Module["makeBigInt"] = function() { abort("'makeBigInt' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "dynCall")) Module["dynCall"] = function() { abort("'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getCompilerSetting")) Module["getCompilerSetting"] = function() { abort("'getCompilerSetting' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "print")) Module["print"] = function() { abort("'print' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "printErr")) Module["printErr"] = function() { abort("'printErr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getTempRet0")) Module["getTempRet0"] = function() { abort("'getTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "setTempRet0")) Module["setTempRet0"] = function() { abort("'setTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "callMain")) Module["callMain"] = function() { abort("'callMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "abort")) Module["abort"] = function() { abort("'abort' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stringToNewUTF8")) Module["stringToNewUTF8"] = function() { abort("'stringToNewUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "setFileTime")) Module["setFileTime"] = function() { abort("'setFileTime' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "emscripten_realloc_buffer")) Module["emscripten_realloc_buffer"] = function() { abort("'emscripten_realloc_buffer' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "ENV")) Module["ENV"] = function() { abort("'ENV' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "ERRNO_CODES")) Module["ERRNO_CODES"] = function() { abort("'ERRNO_CODES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "ERRNO_MESSAGES")) Module["ERRNO_MESSAGES"] = function() { abort("'ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "setErrNo")) Module["setErrNo"] = function() { abort("'setErrNo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "readSockaddr")) Module["readSockaddr"] = function() { abort("'readSockaddr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeSockaddr")) Module["writeSockaddr"] = function() { abort("'writeSockaddr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "DNS")) Module["DNS"] = function() { abort("'DNS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getHostByName")) Module["getHostByName"] = function() { abort("'getHostByName' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "GAI_ERRNO_MESSAGES")) Module["GAI_ERRNO_MESSAGES"] = function() { abort("'GAI_ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "Protocols")) Module["Protocols"] = function() { abort("'Protocols' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "Sockets")) Module["Sockets"] = function() { abort("'Sockets' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getRandomDevice")) Module["getRandomDevice"] = function() { abort("'getRandomDevice' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "traverseStack")) Module["traverseStack"] = function() { abort("'traverseStack' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "UNWIND_CACHE")) Module["UNWIND_CACHE"] = function() { abort("'UNWIND_CACHE' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "withBuiltinMalloc")) Module["withBuiltinMalloc"] = function() { abort("'withBuiltinMalloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "readAsmConstArgsArray")) Module["readAsmConstArgsArray"] = function() { abort("'readAsmConstArgsArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "readAsmConstArgs")) Module["readAsmConstArgs"] = function() { abort("'readAsmConstArgs' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "mainThreadEM_ASM")) Module["mainThreadEM_ASM"] = function() { abort("'mainThreadEM_ASM' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "jstoi_q")) Module["jstoi_q"] = function() { abort("'jstoi_q' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "jstoi_s")) Module["jstoi_s"] = function() { abort("'jstoi_s' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getExecutableName")) Module["getExecutableName"] = function() { abort("'getExecutableName' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "listenOnce")) Module["listenOnce"] = function() { abort("'listenOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "autoResumeAudioContext")) Module["autoResumeAudioContext"] = function() { abort("'autoResumeAudioContext' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "dynCallLegacy")) Module["dynCallLegacy"] = function() { abort("'dynCallLegacy' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getDynCaller")) Module["getDynCaller"] = function() { abort("'getDynCaller' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "dynCall")) Module["dynCall"] = function() { abort("'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "callRuntimeCallbacks")) Module["callRuntimeCallbacks"] = function() { abort("'callRuntimeCallbacks' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "abortStackOverflow")) Module["abortStackOverflow"] = function() { abort("'abortStackOverflow' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "reallyNegative")) Module["reallyNegative"] = function() { abort("'reallyNegative' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "unSign")) Module["unSign"] = function() { abort("'unSign' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "reSign")) Module["reSign"] = function() { abort("'reSign' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "formatString")) Module["formatString"] = function() { abort("'formatString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "PATH")) Module["PATH"] = function() { abort("'PATH' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "PATH_FS")) Module["PATH_FS"] = function() { abort("'PATH_FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "SYSCALLS")) Module["SYSCALLS"] = function() { abort("'SYSCALLS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "syscallMmap2")) Module["syscallMmap2"] = function() { abort("'syscallMmap2' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "syscallMunmap")) Module["syscallMunmap"] = function() { abort("'syscallMunmap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getSocketFromFD")) Module["getSocketFromFD"] = function() { abort("'getSocketFromFD' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getSocketAddress")) Module["getSocketAddress"] = function() { abort("'getSocketAddress' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "JSEvents")) Module["JSEvents"] = function() { abort("'JSEvents' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerKeyEventCallback")) Module["registerKeyEventCallback"] = function() { abort("'registerKeyEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "specialHTMLTargets")) Module["specialHTMLTargets"] = function() { abort("'specialHTMLTargets' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "maybeCStringToJsString")) Module["maybeCStringToJsString"] = function() { abort("'maybeCStringToJsString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "findEventTarget")) Module["findEventTarget"] = function() { abort("'findEventTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "findCanvasEventTarget")) Module["findCanvasEventTarget"] = function() { abort("'findCanvasEventTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getBoundingClientRect")) Module["getBoundingClientRect"] = function() { abort("'getBoundingClientRect' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillMouseEventData")) Module["fillMouseEventData"] = function() { abort("'fillMouseEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerMouseEventCallback")) Module["registerMouseEventCallback"] = function() { abort("'registerMouseEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerWheelEventCallback")) Module["registerWheelEventCallback"] = function() { abort("'registerWheelEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerUiEventCallback")) Module["registerUiEventCallback"] = function() { abort("'registerUiEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerFocusEventCallback")) Module["registerFocusEventCallback"] = function() { abort("'registerFocusEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillDeviceOrientationEventData")) Module["fillDeviceOrientationEventData"] = function() { abort("'fillDeviceOrientationEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerDeviceOrientationEventCallback")) Module["registerDeviceOrientationEventCallback"] = function() { abort("'registerDeviceOrientationEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillDeviceMotionEventData")) Module["fillDeviceMotionEventData"] = function() { abort("'fillDeviceMotionEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerDeviceMotionEventCallback")) Module["registerDeviceMotionEventCallback"] = function() { abort("'registerDeviceMotionEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "screenOrientation")) Module["screenOrientation"] = function() { abort("'screenOrientation' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillOrientationChangeEventData")) Module["fillOrientationChangeEventData"] = function() { abort("'fillOrientationChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerOrientationChangeEventCallback")) Module["registerOrientationChangeEventCallback"] = function() { abort("'registerOrientationChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillFullscreenChangeEventData")) Module["fillFullscreenChangeEventData"] = function() { abort("'fillFullscreenChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerFullscreenChangeEventCallback")) Module["registerFullscreenChangeEventCallback"] = function() { abort("'registerFullscreenChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerRestoreOldStyle")) Module["registerRestoreOldStyle"] = function() { abort("'registerRestoreOldStyle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "hideEverythingExceptGivenElement")) Module["hideEverythingExceptGivenElement"] = function() { abort("'hideEverythingExceptGivenElement' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "restoreHiddenElements")) Module["restoreHiddenElements"] = function() { abort("'restoreHiddenElements' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "setLetterbox")) Module["setLetterbox"] = function() { abort("'setLetterbox' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "currentFullscreenStrategy")) Module["currentFullscreenStrategy"] = function() { abort("'currentFullscreenStrategy' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "restoreOldWindowedStyle")) Module["restoreOldWindowedStyle"] = function() { abort("'restoreOldWindowedStyle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "softFullscreenResizeWebGLRenderTarget")) Module["softFullscreenResizeWebGLRenderTarget"] = function() { abort("'softFullscreenResizeWebGLRenderTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "doRequestFullscreen")) Module["doRequestFullscreen"] = function() { abort("'doRequestFullscreen' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillPointerlockChangeEventData")) Module["fillPointerlockChangeEventData"] = function() { abort("'fillPointerlockChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerPointerlockChangeEventCallback")) Module["registerPointerlockChangeEventCallback"] = function() { abort("'registerPointerlockChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerPointerlockErrorEventCallback")) Module["registerPointerlockErrorEventCallback"] = function() { abort("'registerPointerlockErrorEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "requestPointerLock")) Module["requestPointerLock"] = function() { abort("'requestPointerLock' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillVisibilityChangeEventData")) Module["fillVisibilityChangeEventData"] = function() { abort("'fillVisibilityChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerVisibilityChangeEventCallback")) Module["registerVisibilityChangeEventCallback"] = function() { abort("'registerVisibilityChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerTouchEventCallback")) Module["registerTouchEventCallback"] = function() { abort("'registerTouchEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillGamepadEventData")) Module["fillGamepadEventData"] = function() { abort("'fillGamepadEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerGamepadEventCallback")) Module["registerGamepadEventCallback"] = function() { abort("'registerGamepadEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerBeforeUnloadEventCallback")) Module["registerBeforeUnloadEventCallback"] = function() { abort("'registerBeforeUnloadEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "fillBatteryEventData")) Module["fillBatteryEventData"] = function() { abort("'fillBatteryEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "battery")) Module["battery"] = function() { abort("'battery' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "registerBatteryEventCallback")) Module["registerBatteryEventCallback"] = function() { abort("'registerBatteryEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "setCanvasElementSize")) Module["setCanvasElementSize"] = function() { abort("'setCanvasElementSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getCanvasElementSize")) Module["getCanvasElementSize"] = function() { abort("'getCanvasElementSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "polyfillSetImmediate")) Module["polyfillSetImmediate"] = function() { abort("'polyfillSetImmediate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "demangle")) Module["demangle"] = function() { abort("'demangle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "demangleAll")) Module["demangleAll"] = function() { abort("'demangleAll' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "jsStackTrace")) Module["jsStackTrace"] = function() { abort("'jsStackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stackTrace")) Module["stackTrace"] = function() { abort("'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getEnvStrings")) Module["getEnvStrings"] = function() { abort("'getEnvStrings' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "checkWasiClock")) Module["checkWasiClock"] = function() { abort("'checkWasiClock' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "flush_NO_FILESYSTEM")) Module["flush_NO_FILESYSTEM"] = function() { abort("'flush_NO_FILESYSTEM' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64")) Module["writeI53ToI64"] = function() { abort("'writeI53ToI64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64Clamped")) Module["writeI53ToI64Clamped"] = function() { abort("'writeI53ToI64Clamped' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64Signaling")) Module["writeI53ToI64Signaling"] = function() { abort("'writeI53ToI64Signaling' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToU64Clamped")) Module["writeI53ToU64Clamped"] = function() { abort("'writeI53ToU64Clamped' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToU64Signaling")) Module["writeI53ToU64Signaling"] = function() { abort("'writeI53ToU64Signaling' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "readI53FromI64")) Module["readI53FromI64"] = function() { abort("'readI53FromI64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "readI53FromU64")) Module["readI53FromU64"] = function() { abort("'readI53FromU64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "convertI32PairToI53")) Module["convertI32PairToI53"] = function() { abort("'convertI32PairToI53' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "convertU32PairToI53")) Module["convertU32PairToI53"] = function() { abort("'convertU32PairToI53' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "uncaughtExceptionCount")) Module["uncaughtExceptionCount"] = function() { abort("'uncaughtExceptionCount' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "exceptionLast")) Module["exceptionLast"] = function() { abort("'exceptionLast' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "exceptionCaught")) Module["exceptionCaught"] = function() { abort("'exceptionCaught' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "ExceptionInfoAttrs")) Module["ExceptionInfoAttrs"] = function() { abort("'ExceptionInfoAttrs' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "ExceptionInfo")) Module["ExceptionInfo"] = function() { abort("'ExceptionInfo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "CatchInfo")) Module["CatchInfo"] = function() { abort("'CatchInfo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "exception_addRef")) Module["exception_addRef"] = function() { abort("'exception_addRef' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "exception_decRef")) Module["exception_decRef"] = function() { abort("'exception_decRef' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "Browser")) Module["Browser"] = function() { abort("'Browser' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "funcWrappers")) Module["funcWrappers"] = function() { abort("'funcWrappers' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "getFuncWrapper")) Module["getFuncWrapper"] = function() { abort("'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "setMainLoop")) Module["setMainLoop"] = function() { abort("'setMainLoop' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "FS")) Module["FS"] = function() { abort("'FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "mmapAlloc")) Module["mmapAlloc"] = function() { abort("'mmapAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "MEMFS")) Module["MEMFS"] = function() { abort("'MEMFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "TTY")) Module["TTY"] = function() { abort("'TTY' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "PIPEFS")) Module["PIPEFS"] = function() { abort("'PIPEFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "SOCKFS")) Module["SOCKFS"] = function() { abort("'SOCKFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "_setNetworkCallback")) Module["_setNetworkCallback"] = function() { abort("'_setNetworkCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "tempFixedLengthArray")) Module["tempFixedLengthArray"] = function() { abort("'tempFixedLengthArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "miniTempWebGLFloatBuffers")) Module["miniTempWebGLFloatBuffers"] = function() { abort("'miniTempWebGLFloatBuffers' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "heapObjectForWebGLType")) Module["heapObjectForWebGLType"] = function() { abort("'heapObjectForWebGLType' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "heapAccessShiftForWebGLHeap")) Module["heapAccessShiftForWebGLHeap"] = function() { abort("'heapAccessShiftForWebGLHeap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "GL")) Module["GL"] = function() { abort("'GL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGet")) Module["emscriptenWebGLGet"] = function() { abort("'emscriptenWebGLGet' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "computeUnpackAlignedImageSize")) Module["computeUnpackAlignedImageSize"] = function() { abort("'computeUnpackAlignedImageSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetTexPixelData")) Module["emscriptenWebGLGetTexPixelData"] = function() { abort("'emscriptenWebGLGetTexPixelData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetUniform")) Module["emscriptenWebGLGetUniform"] = function() { abort("'emscriptenWebGLGetUniform' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetVertexAttrib")) Module["emscriptenWebGLGetVertexAttrib"] = function() { abort("'emscriptenWebGLGetVertexAttrib' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetBufferBinding")) Module["emscriptenWebGLGetBufferBinding"] = function() { abort("'emscriptenWebGLGetBufferBinding' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLValidateMapBufferTarget")) Module["emscriptenWebGLValidateMapBufferTarget"] = function() { abort("'emscriptenWebGLValidateMapBufferTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "writeGLArray")) Module["writeGLArray"] = function() { abort("'writeGLArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "AL")) Module["AL"] = function() { abort("'AL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "SDL_unicode")) Module["SDL_unicode"] = function() { abort("'SDL_unicode' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "SDL_ttfContext")) Module["SDL_ttfContext"] = function() { abort("'SDL_ttfContext' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "SDL_audio")) Module["SDL_audio"] = function() { abort("'SDL_audio' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "SDL")) Module["SDL"] = function() { abort("'SDL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "SDL_gfx")) Module["SDL_gfx"] = function() { abort("'SDL_gfx' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "GLUT")) Module["GLUT"] = function() { abort("'GLUT' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "EGL")) Module["EGL"] = function() { abort("'EGL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "GLFW_Window")) Module["GLFW_Window"] = function() { abort("'GLFW_Window' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "GLFW")) Module["GLFW"] = function() { abort("'GLFW' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "GLEW")) Module["GLEW"] = function() { abort("'GLEW' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "IDBStore")) Module["IDBStore"] = function() { abort("'IDBStore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "runAndAbortIfError")) Module["runAndAbortIfError"] = function() { abort("'runAndAbortIfError' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetIndexed")) Module["emscriptenWebGLGetIndexed"] = function() { abort("'emscriptenWebGLGetIndexed' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "warnOnce")) Module["warnOnce"] = function() { abort("'warnOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stackSave")) Module["stackSave"] = function() { abort("'stackSave' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stackRestore")) Module["stackRestore"] = function() { abort("'stackRestore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stackAlloc")) Module["stackAlloc"] = function() { abort("'stackAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "AsciiToString")) Module["AsciiToString"] = function() { abort("'AsciiToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stringToAscii")) Module["stringToAscii"] = function() { abort("'stringToAscii' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "UTF16ToString")) Module["UTF16ToString"] = function() { abort("'UTF16ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF16")) Module["stringToUTF16"] = function() { abort("'stringToUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF16")) Module["lengthBytesUTF16"] = function() { abort("'lengthBytesUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "UTF32ToString")) Module["UTF32ToString"] = function() { abort("'UTF32ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF32")) Module["stringToUTF32"] = function() { abort("'stringToUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF32")) Module["lengthBytesUTF32"] = function() { abort("'lengthBytesUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "allocateUTF8")) Module["allocateUTF8"] = function() { abort("'allocateUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +if (!Object.getOwnPropertyDescriptor(Module, "allocateUTF8OnStack")) Module["allocateUTF8OnStack"] = function() { abort("'allocateUTF8OnStack' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; +Module["writeStackCookie"] = writeStackCookie; +Module["checkStackCookie"] = checkStackCookie; +if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_NORMAL")) Object.defineProperty(Module, "ALLOC_NORMAL", { configurable: true, get: function() { abort("'ALLOC_NORMAL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } }); +if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_STACK")) Object.defineProperty(Module, "ALLOC_STACK", { configurable: true, get: function() { abort("'ALLOC_STACK' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } }); + +var calledRun; + +/** + * @constructor + * @this {ExitStatus} + */ +function ExitStatus(status) { + this.name = "ExitStatus"; + this.message = "Program terminated with exit(" + status + ")"; + this.status = status; +} + +var calledMain = false; + +dependenciesFulfilled = function runCaller() { + // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled +}; + +function callMain(args) { + assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); + assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); + + var entryFunction = Module['_main']; + + args = args || []; + + var argc = args.length+1; + var argv = stackAlloc((argc + 1) * 4); + HEAP32[argv >> 2] = allocateUTF8OnStack(thisProgram); + for (var i = 1; i < argc; i++) { + HEAP32[(argv >> 2) + i] = allocateUTF8OnStack(args[i - 1]); + } + HEAP32[(argv >> 2) + argc] = 0; + + try { + + var ret = entryFunction(argc, argv); + + // In PROXY_TO_PTHREAD builds, we should never exit the runtime below, as + // execution is asynchronously handed off to a pthread. + // if we're not running an evented main loop, it's time to exit + exit(ret, /* implicit = */ true); + } + catch(e) { + if (e instanceof ExitStatus) { + // exit() throws this once it's done to make sure execution + // has been stopped completely + return; + } else if (e == 'unwind') { + // running an evented main loop, don't immediately exit + noExitRuntime = true; + return; + } else { + var toLog = e; + if (e && typeof e === 'object' && e.stack) { + toLog = [e, e.stack]; + } + err('exception thrown: ' + toLog); + quit_(1, e); + } + } finally { + calledMain = true; + + } +} + +function stackCheckInit() { + // This is normally called automatically during __wasm_call_ctors but need to + // get these values before even running any of the ctors so we call it redundantly + // here. + // TODO(sbc): Move writeStackCookie to native to to avoid this. + _emscripten_stack_init(); + writeStackCookie(); +} + +/** @type {function(Array=)} */ +function run(args) { + args = args || arguments_; + + if (runDependencies > 0) { + return; + } + + stackCheckInit(); + + preRun(); + + // a preRun added a dependency, run will be called later + if (runDependencies > 0) { + return; + } + + function doRun() { + // run may have just been called through dependencies being fulfilled just in this very frame, + // or while the async setStatus time below was happening + if (calledRun) return; + calledRun = true; + Module['calledRun'] = true; + + if (ABORT) return; + + initRuntime(); + + preMain(); + + if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); + + if (shouldRunNow) callMain(args); + + postRun(); + } + + if (Module['setStatus']) { + Module['setStatus']('Running...'); + setTimeout(function() { + setTimeout(function() { + Module['setStatus'](''); + }, 1); + doRun(); + }, 1); + } else + { + doRun(); + } + checkStackCookie(); +} +Module['run'] = run; + +function checkUnflushedContent() { + // Compiler settings do not allow exiting the runtime, so flushing + // the streams is not possible. but in ASSERTIONS mode we check + // if there was something to flush, and if so tell the user they + // should request that the runtime be exitable. + // Normally we would not even include flush() at all, but in ASSERTIONS + // builds we do so just for this check, and here we see if there is any + // content to flush, that is, we check if there would have been + // something a non-ASSERTIONS build would have not seen. + // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0 + // mode (which has its own special function for this; otherwise, all + // the code is inside libc) + var oldOut = out; + var oldErr = err; + var has = false; + out = err = function(x) { + has = true; + } + try { // it doesn't matter if it fails + var flush = flush_NO_FILESYSTEM; + if (flush) flush(); + } catch(e) {} + out = oldOut; + err = oldErr; + if (has) { + warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline when you printf etc.'); + warnOnce('(this may also be due to not including full filesystem support - try building with -s FORCE_FILESYSTEM=1)'); + } +} + +/** @param {boolean|number=} implicit */ +function exit(status, implicit) { + checkUnflushedContent(); + + // if this is just main exit-ing implicitly, and the status is 0, then we + // don't need to do anything here and can just leave. if the status is + // non-zero, though, then we need to report it. + // (we may have warned about this earlier, if a situation justifies doing so) + if (implicit && noExitRuntime && status === 0) { + return; + } + + if (noExitRuntime) { + // if exit() was called, we may warn the user if the runtime isn't actually being shut down + if (!implicit) { + var msg = 'program exited (with status: ' + status + '), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)'; + err(msg); + } + } else { + + EXITSTATUS = status; + + exitRuntime(); + + if (Module['onExit']) Module['onExit'](status); + + ABORT = true; + } + + quit_(status, new ExitStatus(status)); +} + +if (Module['preInit']) { + if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; + while (Module['preInit'].length > 0) { + Module['preInit'].pop()(); + } +} + +// shouldRunNow refers to calling main(), not run(). +var shouldRunNow = true; + +if (Module['noInitialRun']) shouldRunNow = false; + +run(); + + + + + diff --git a/2d/_collisions/pill_line/dist/output.wasm b/2d/_collisions/pill_line/dist/output.wasm new file mode 100755 index 0000000..ddf862c Binary files /dev/null and b/2d/_collisions/pill_line/dist/output.wasm differ diff --git a/2d/_collisions/pill_line/main.cpp b/2d/_collisions/pill_line/main.cpp new file mode 100644 index 0000000..b9315a8 --- /dev/null +++ b/2d/_collisions/pill_line/main.cpp @@ -0,0 +1,358 @@ +#include "../../../shared_cpp/OrthographicRenderer.h" +#include "../../../shared_cpp/types.h" +#include "../../../shared_cpp/WebglContext.h" +#include "../../../shared_cpp/mathlib.h" +#include "../../../shared_cpp/MainLoop.h" +#include +#include +#include +#include +#include +#include + +// Side note: It is Eastertime, so I chose this easter color palette. Enjoy: https://htmlcolors.com/palette/144/easter + +struct Rigidbody { + Vector2 force = { 0, 0 }; + Vector2 velocity = { 0, 0 }; + Vector2 position = { 0, 0 }; + float32 rotationalVelocity = 0.f; + float32 rotation = 0.f; + float32 mass = 1.f; + float32 cofOfRestition = 1.f; + float32 momentOfInertia = 0.f; + + void reset() { + force = { 0, 0 }; + velocity = { 0, 0 }; + rotationalVelocity = 0.f; + rotation = 0.f; + } + + void applyForce(Vector2 f) { + force += f; + } + + void applyGravity(float32 deltaTimeSeconds) { + velocity += (Vector2 { 0.f, -96.f } * deltaTimeSeconds); + } + + void update(float32 deltaTimeSeconds) { + applyGravity(deltaTimeSeconds); + + Vector2 acceleration = force / mass; + velocity += (acceleration * deltaTimeSeconds); + position += (velocity * deltaTimeSeconds); + force = Vector2 { 0.f, 0.f }; + + rotation += (rotationalVelocity * deltaTimeSeconds); + } + + void setMomentOfInertia(float32 moi) { + momentOfInertia = moi; + } +}; + +struct Pill { + OrthographicShape shape; + Rigidbody body; + float32 a = 0; + float32 b = 0; + + Pill copy() { + Pill retval; + retval.shape = shape; + retval.body = body; + retval.a = a; + retval.b = b; + return retval; + } + + void load(OrthographicRenderer* renderer, float32 numSegments, float32 width, float32 height) { + // Note that a so-called "pill" is simply an ellipse. + // Equation of an ellipse is: + // + // x^2 / a^2 + y^2 / b^2 = 1 + // + // or, in parametric form: + // + // x = a * cos(t), y = b * sin(t) + // + float32 angleIncrements = (2.f * PI) / numSegments; + uint32 numVertices = static_cast(numSegments * 3.f); + OrthographicVertex* vertices = new OrthographicVertex[numVertices]; + + a = width / 2.f; + b = height / 2.f; + + Vector4 color = Vector4().fromColor(243,166,207, 255); + + for (uint32 vertexIndex = 0; vertexIndex < numVertices; vertexIndex += 3) { + // Create a single "slice" of the ellipse (like a pizza) + float32 currAngle = (vertexIndex / 3.f) * angleIncrements; + float32 nextAngle = (vertexIndex / 3.f + 1.f) * angleIncrements; + + vertices[vertexIndex].position = Vector2 { 0.f, 0.f }; + vertices[vertexIndex].color = color; + + vertices[vertexIndex + 1].position = Vector2 { a * cosf(currAngle), b * sinf(currAngle) }; + vertices[vertexIndex + 1].color = color; + + vertices[vertexIndex + 2].position = Vector2 { a * cosf(nextAngle), b * sinf(nextAngle) }; + vertices[vertexIndex + 2].color = color; + } + + shape.load(vertices, numVertices, renderer); + body.reset(); + + body.mass = 100.f; + body.momentOfInertia = (body.mass * (a * a + b * b)) / 4.f; + + a = width / 2.f; + b = height / 2.f; + + delete[] vertices; + } + + void update(float32 deltaTimeSeconds) { + body.update(deltaTimeSeconds); + shape.model = Mat4x4().translateByVec2(body.position).rotate2D(body.rotation); + } + + void render(OrthographicRenderer* renderer) { + shape.render(renderer); + } + + void unload() { + shape.unload(); + } +}; + +struct LineSegment { + OrthographicShape shape; + Rigidbody body; + Vector2 start; + Vector2 end; + float32 length; + Vector2 normal; + OrthographicVertex vertices[2]; + + void load(OrthographicRenderer* renderer, Vector4 color, Vector2 inStart, Vector2 inEnd) { + start = inStart; + end = inEnd; + length = (start - end).length(); + vertices[0].position = start; + vertices[0].color = color; + vertices[1].position = end; + vertices[1].color = color; + normal = (end - start).getPerp().normalize(); + shape.load(vertices, 2, renderer); + + body.reset(); + body.mass = 1000000000.f; + body.cofOfRestition = 1.f; + body.rotationalVelocity = 0; + body.velocity = Vector2(); + body.momentOfInertia = body.mass * (length / 2.f); + } + + void render(OrthographicRenderer* renderer) { + shape.render(renderer, GL_LINES); + } + + void unload() { + shape.unload(); + } + + Vector2 getPointOnLine(float32 t) { + return { + start.x + (end.x - start.x) * t, + start.y + (end.y - start.y) * t, + }; + } + + Vector2 getNormal() { + return (end - start).getPerp().normalize(); + } +}; + +struct IntersectionResult { + bool intersect = false; + Vector2 collisionNormal; + Vector2 relativeVelocity; + Vector2 firstPointOfApplication; + Vector2 secondPointOfApplication; +}; + +EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); + +void load(); +void update(float32 time, void* userData); +void unload(); +IntersectionResult getIntersection(Pill* pill, LineSegment* segment); +void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* ir); + +// Global Variables +WebglContext context; +OrthographicRenderer renderer; +Pill pill; +MainLoop mainLoop; +LineSegment segmentList[4]; + +int main() { + context.init("#gl_canvas"); + emscripten_set_click_callback("#gl_canvas_play", NULL, false, onPlayClicked); + emscripten_set_click_callback("#gl_canvas_stop", NULL, false, onStopClicked); + return 0; +} + +void load() { + renderer.load(&context); + + pill.body.position = Vector2 { context.width / 4.f, context.height / 2.f }; + pill.load(&renderer, 64, 100.f, 50.f); + + segmentList[0].load(&renderer, Vector4().fromColor(191, 251, 146, 255.f), Vector2 { 50.f, 0.f }, Vector2 { 50.f, static_cast(context.height) }); + segmentList[1].load(&renderer, Vector4().fromColor(159, 224, 210, 255.f), Vector2 { context.width - 50.f, 0.f }, Vector2 { context.width - 50.f, static_cast(context.height) }); + segmentList[2].load(&renderer, Vector4().fromColor(248, 255, 156, 255.f), Vector2 { 50.f, 50.f }, Vector2 { context.width - 50.f, 150.f }); + segmentList[3].load(&renderer, Vector4().fromColor(205, 178, 214, 255.f), Vector2 { 50.f, 150.f }, Vector2 { context.width - 50.f, 50.f }); + + mainLoop.run(update); +} + +IntersectionResult getIntersection(Pill* pill, LineSegment* segment) { + IntersectionResult result; + Mat4x4 inverseModel = pill->shape.model.inverse(); + Vector2 start = inverseModel * segment->start; + Vector2 end = inverseModel * segment->end; + Vector2 diff = end - start; + float32 A = (diff.x * diff.x) / (pill->a * pill->a) + (diff.y * diff.y) / (pill->b * pill->b); + float32 B = ((2 * start.x) * (end.x - start.x)) / (pill->a * pill->a) + ((2 * start.y) * (end.y - start.y)) / (pill->b * pill->b); + float32 C = (start.x * start.x) / (pill->a * pill->a) + (start.y * start.y) / (pill->b * pill->b) - 1.f; + + float32 determinant = B * B - 4 * A * C; + if (determinant < 0.f) { + result.intersect = false; + return result; + } + + float32 t; + if (determinant < 0.001f) { + t = -B / (2.f * A); + } else { + t = (-B + sqrtf(determinant)) / (2.f * A); + // Or: t = (-B - sqrtf(determinant)) / (2 * A); + } + + Vector2 pointOnLine = segment->getPointOnLine(t); // This point is in world space and line space, since the line is in world space + + Vector2 pointOnTransformedLine = { + start.x + (end.x - start.x) * t, + start.y + (end.y - start.y) * t + }; + + Vector2 pointOnEllipse = (pointOnTransformedLine); + + Vector2 normal = segment->getNormal(); + + result.intersect = true; + result.relativeVelocity = pill->body.velocity - segment->body.velocity; + result.collisionNormal = normal; + result.firstPointOfApplication = pointOnEllipse; + result.secondPointOfApplication = pointOnLine; + + return result; +} + +void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* ir) { + Vector2 relativeVelocity = ir->relativeVelocity; + Vector2 collisionNormal = ir->collisionNormal; + Vector2 firstPerp = ir->firstPointOfApplication.getPerp(); + Vector2 secondPerp = ir->secondPointOfApplication.getPerp(); + + float32 cofOfRestition = (first->cofOfRestition + second->cofOfRestition) / 2.f; + float32 lNumerator = (relativeVelocity * -(1.f + cofOfRestition)).dot(collisionNormal); + float32 lLinearDenomPart = collisionNormal.dot(collisionNormal * (1.f / first->mass + 1.f / second->mass)); + float32 lRotationalDenomPart = powf(firstPerp.dot(collisionNormal), 2.f) / first->momentOfInertia + powf(secondPerp.dot(collisionNormal), 2) / second->momentOfInertia; + + //ir->firstPointOfApplication.printDebug("PoA"); + //firstPerp.printDebug("PoA Perp"); + //collisionNormal.printDebug("Normal"); + //printf("Dot: %f\n", firstPerp.dot(collisionNormal)); + + float32 lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart); + + first->velocity = first->velocity + (collisionNormal * (lImpulseMagnitude / first->mass)); + second->velocity = second->velocity - (collisionNormal * (lImpulseMagnitude / second->mass)); + first->rotationalVelocity = first->rotationalVelocity + firstPerp.dot(collisionNormal * lImpulseMagnitude) / first->momentOfInertia; + second->rotationalVelocity = second->rotationalVelocity - secondPerp.dot(collisionNormal * lImpulseMagnitude) / second->momentOfInertia; +} + +void update(float32 deltaTimeSeconds, void* userData) { + // Update + Pill copyPill = pill.copy(); + pill.update(deltaTimeSeconds); + + // Intersections + for (int32 lineIdx = 0; lineIdx < 4; lineIdx++) { + IntersectionResult ir = getIntersection(&pill, &segmentList[lineIdx]); + if (ir.intersect) { + + // Find the exact moment that the intersection happens by rewinding the simulation until we're not intersecting + IntersectionResult subIr = ir; + float32 subdividedTimeSeconds = deltaTimeSeconds; + do { + ir = subIr; + + pill = copyPill.copy(); + subdividedTimeSeconds /= 2.f; + pill.update(subdividedTimeSeconds); + + subIr = getIntersection(&pill, &segmentList[lineIdx]); + if (subdividedTimeSeconds <= 0.f) { + printf("Error: Should not be happening.\n"); + break; + } + } while (subIr.intersect); + + printf("Found intersection at timestamp: %f\n", subdividedTimeSeconds); + resolveCollision(&pill.body, &segmentList[lineIdx].body, &ir); + deltaTimeSeconds = deltaTimeSeconds - subdividedTimeSeconds; + pill.update(deltaTimeSeconds); + } + } + + // Render + renderer.render(); + pill.shape.render(&renderer); + + for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) { + segmentList[segmentIndex].render(&renderer); + } +} + +void unload() { + mainLoop.stop(); + pill.unload(); + renderer.unload(); + for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) { + segmentList[segmentIndex].unload(); + } +} + +// +// Interactions with DOM handled below +// +EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Play clicked\n"); + + load(); + return true; +} + +EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Stop clicked\n"); + unload(); + return true; +} -- cgit v1.2.1