|
@@ -1,7 +1,9 @@
|
|
|
+(function() {
|
|
|
class WasmMemoryInterface {
|
|
|
constructor() {
|
|
|
this.memory = null;
|
|
|
this.exports = null;
|
|
|
+ this.listenerMap = {};
|
|
|
}
|
|
|
|
|
|
setMemory(memory) {
|
|
@@ -10,7 +12,6 @@ class WasmMemoryInterface {
|
|
|
|
|
|
setExports(exports) {
|
|
|
this.exports = exports;
|
|
|
- this.listenerMap = {};
|
|
|
}
|
|
|
|
|
|
get mem() {
|
|
@@ -90,477 +91,191 @@ class WasmMemoryInterface {
|
|
|
storeUint(addr, value) { this.mem.setUint32 (addr, value, true); }
|
|
|
};
|
|
|
|
|
|
-function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
|
|
|
- const MAX_INFO_CONSOLE_LINES = 512;
|
|
|
- let infoConsoleLines = new Array();
|
|
|
- const addConsoleLine = (line) => {
|
|
|
- if (!line) {
|
|
|
- return;
|
|
|
+class WebGLInterface {
|
|
|
+ constructor(wasmMemoryInterface) {
|
|
|
+ this.wasmMemoryInterface = wasmMemoryInterface;
|
|
|
+ this.ctxElement = null;
|
|
|
+ this.ctx = null;
|
|
|
+ this.ctxVersion = 1.0;
|
|
|
+ this.counter = 1;
|
|
|
+ this.lastError = 0;
|
|
|
+ this.buffers = [];
|
|
|
+ this.mappedBuffers = {};
|
|
|
+ this.programs = [];
|
|
|
+ this.framebuffers = [];
|
|
|
+ this.renderbuffers = [];
|
|
|
+ this.textures = [];
|
|
|
+ this.uniforms = [];
|
|
|
+ this.shaders = [];
|
|
|
+ this.vaos = [];
|
|
|
+ this.contexts = [];
|
|
|
+ this.currentContext = null;
|
|
|
+ this.offscreenCanvases = {};
|
|
|
+ this.timerQueriesEXT = [];
|
|
|
+ this.queries = [];
|
|
|
+ this.samplers = [];
|
|
|
+ this.transformFeedbacks = [];
|
|
|
+ this.syncs = [];
|
|
|
+ this.programInfos = {};
|
|
|
+ }
|
|
|
+
|
|
|
+ get mem() {
|
|
|
+ return this.wasmMemoryInterface
|
|
|
+ }
|
|
|
+
|
|
|
+ setCurrentContext(element, contextSettings) {
|
|
|
+ if (!element) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- if (line.endsWith("\n")) {
|
|
|
- line = line.substring(0, line.length-1);
|
|
|
- } else if (infoConsoleLines.length > 0) {
|
|
|
- let prev_line = infoConsoleLines.pop();
|
|
|
- line = prev_line.concat(line);
|
|
|
+ if (this.ctxElement == element) {
|
|
|
+ return true;
|
|
|
}
|
|
|
- infoConsoleLines.push(line);
|
|
|
|
|
|
- if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) {
|
|
|
- infoConsoleLines.shift();
|
|
|
+ contextSettings = contextSettings ?? {};
|
|
|
+ this.ctx = element.getContext("webgl2", contextSettings) || element.getContext("webgl", contextSettings);
|
|
|
+ if (!this.ctx) {
|
|
|
+ return false;
|
|
|
}
|
|
|
+ this.ctxElement = element;
|
|
|
+ if (this.ctx.getParameter(0x1F02).indexOf("WebGL 2.0") !== -1) {
|
|
|
+ this.ctxVersion = 2.0;
|
|
|
+ } else {
|
|
|
+ this.ctxVersion = 1.0;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- let data = "";
|
|
|
- for (let i = 0; i < infoConsoleLines.length; i++) {
|
|
|
- if (i != 0) {
|
|
|
- data = data.concat("\n");
|
|
|
+ assertWebGL2() {
|
|
|
+ if (this.ctxVersion < 2) {
|
|
|
+ throw new Error("WebGL2 procedure called in a canvas without a WebGL2 context");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ getNewId(table) {
|
|
|
+ for (var ret = this.counter++, i = table.length; i < ret; i++) {
|
|
|
+ table[i] = null;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ recordError(errorCode) {
|
|
|
+ this.lastError || (this.lastError = errorCode);
|
|
|
+ }
|
|
|
+ populateUniformTable(program) {
|
|
|
+ let p = this.programs[program];
|
|
|
+ this.programInfos[program] = {
|
|
|
+ uniforms: {},
|
|
|
+ maxUniformLength: 0,
|
|
|
+ maxAttributeLength: -1,
|
|
|
+ maxUniformBlockNameLength: -1,
|
|
|
+ };
|
|
|
+ for (let ptable = this.programInfos[program], utable = ptable.uniforms, numUniforms = this.ctx.getProgramParameter(p, this.ctx.ACTIVE_UNIFORMS), i = 0; i < numUniforms; ++i) {
|
|
|
+ let u = this.ctx.getActiveUniform(p, i);
|
|
|
+ let name = u.name;
|
|
|
+ if (ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length + 1), name.indexOf("]", name.length - 1) !== -1) {
|
|
|
+ name = name.slice(0, name.lastIndexOf("["));
|
|
|
+ }
|
|
|
+ let loc = this.ctx.getUniformLocation(p, name);
|
|
|
+ if (loc !== null) {
|
|
|
+ let id = this.getNewId(this.uniforms);
|
|
|
+ utable[name] = [u.size, id], this.uniforms[id] = loc;
|
|
|
+ for (let j = 1; j < u.size; ++j) {
|
|
|
+ let n = name + "[" + j + "]";
|
|
|
+ let loc = this.ctx.getUniformLocation(p, n);
|
|
|
+ let id = this.getNewId(this.uniforms);
|
|
|
+ this.uniforms[id] = loc;
|
|
|
+ }
|
|
|
}
|
|
|
- data = data.concat(infoConsoleLines[i]);
|
|
|
}
|
|
|
-
|
|
|
- if (consoleElement) {
|
|
|
- let info = consoleElement;
|
|
|
- info.innerHTML = data;
|
|
|
- info.scrollTop = info.scrollHeight;
|
|
|
+ }
|
|
|
+ getSource(shader, strings_ptr, strings_length) {
|
|
|
+ const STRING_SIZE = 2*4;
|
|
|
+ let source = "";
|
|
|
+ for (let i = 0; i < strings_length; i++) {
|
|
|
+ let ptr = this.mem.loadPtr(strings_ptr + i*STRING_SIZE);
|
|
|
+ let len = this.mem.loadPtr(strings_ptr + i*STRING_SIZE + 4);
|
|
|
+ let str = this.mem.loadString(ptr, len);
|
|
|
+ source += str;
|
|
|
}
|
|
|
- };
|
|
|
-
|
|
|
- let event_temp_data = {};
|
|
|
+ return source;
|
|
|
+ }
|
|
|
|
|
|
- return {
|
|
|
- "env": {},
|
|
|
- "odin_env": {
|
|
|
- write: (fd, ptr, len) => {
|
|
|
- const str = wasmMemoryInterface.loadString(ptr, len);
|
|
|
- if (fd == 1) {
|
|
|
- addConsoleLine(str);
|
|
|
- return;
|
|
|
- } else if (fd == 2) {
|
|
|
- addConsoleLine(str);
|
|
|
- return;
|
|
|
- } else {
|
|
|
- throw new Error("Invalid fd to 'write'" + stripNewline(str));
|
|
|
- }
|
|
|
+ getWebGL1Interface() {
|
|
|
+ return {
|
|
|
+ SetCurrentContextById: (name_ptr, name_len) => {
|
|
|
+ let name = this.mem.loadString(name_ptr, name_len);
|
|
|
+ let element = document.getElementById(name);
|
|
|
+ return this.setCurrentContext(element, {alpha: true, antialias: true, depth: true, premultipliedAlpha: true});
|
|
|
},
|
|
|
- trap: () => { throw new Error() },
|
|
|
- alert: (ptr, len) => { alert(wasmMemoryInterface.loadString(ptr, len)) },
|
|
|
- abort: () => { Module.abort() },
|
|
|
- evaluate: (str_ptr, str_len) => { eval.call(null, wasmMemoryInterface.loadString(str_ptr, str_len)); },
|
|
|
+ CreateCurrentContextById: (name_ptr, name_len, attributes) => {
|
|
|
+ let name = this.mem.loadString(name_ptr, name_len);
|
|
|
+ let element = document.getElementById(name);
|
|
|
|
|
|
- time_now: () => {
|
|
|
- return performance.now() * 1e6;
|
|
|
+ let contextSettings = {
|
|
|
+ alpha: !(attributes & (1<<0)),
|
|
|
+ antialias: !(attributes & (1<<1)),
|
|
|
+ depth: !(attributes & (1<<2)),
|
|
|
+ failIfMajorPerformanceCaveat: !!(attributes & (1<<3)),
|
|
|
+ premultipliedAlpha: !(attributes & (1<<4)),
|
|
|
+ preserveDrawingBuffer: !!(attributes & (1<<5)),
|
|
|
+ stencil: !!(attributes & (1<<6)),
|
|
|
+ desynchronized: !!(attributes & (1<<7)),
|
|
|
+ };
|
|
|
+
|
|
|
+ return this.setCurrentContext(element, contextSettings);
|
|
|
+ },
|
|
|
+ GetCurrentContextAttributes: () => {
|
|
|
+ if (!this.ctx) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ let attrs = this.ctx.getContextAttributes();
|
|
|
+ let res = 0;
|
|
|
+ if (!attrs.alpha) res |= 1<<0;
|
|
|
+ if (!attrs.antialias) res |= 1<<1;
|
|
|
+ if (!attrs.depth) res |= 1<<2;
|
|
|
+ if (attrs.failIfMajorPerformanceCaveat) res |= 1<<3;
|
|
|
+ if (!attrs.premultipliedAlpha) res |= 1<<4;
|
|
|
+ if (attrs.preserveDrawingBuffer) res |= 1<<5;
|
|
|
+ if (attrs.stencil) res |= 1<<6;
|
|
|
+ if (attrs.desynchronized) res |= 1<<7;
|
|
|
+ return res;
|
|
|
},
|
|
|
|
|
|
- sqrt: (x) => Math.sqrt(x),
|
|
|
- sin: (x) => Math.sin(x),
|
|
|
- cos: (x) => Math.cos(x),
|
|
|
- pow: (x) => Math.pow(x),
|
|
|
- fmuladd: (x, y, z) => x*y + z,
|
|
|
- ln: (x) => Math.log(x),
|
|
|
- exp: (x) => Math.exp(x),
|
|
|
- ldexp: (x) => Math.ldexp(x),
|
|
|
- },
|
|
|
- "odin_dom": {
|
|
|
- init_event_raw: (ep) => {
|
|
|
- const W = 4;
|
|
|
- let offset = ep;
|
|
|
- let off = (amount, alignment) => {
|
|
|
- if (alignment === undefined) {
|
|
|
- alignment = Math.min(amount, W);
|
|
|
- }
|
|
|
- if (offset % alignment != 0) {
|
|
|
- offset += alignment - (offset%alignment);
|
|
|
- }
|
|
|
- let x = offset;
|
|
|
- offset += amount;
|
|
|
- return x;
|
|
|
- };
|
|
|
+ DrawingBufferWidth: () => this.ctx.drawingBufferWidth,
|
|
|
+ DrawingBufferHeight: () => this.ctx.drawingBufferHeight,
|
|
|
|
|
|
- let wmi = wasmMemoryInterface;
|
|
|
+ IsExtensionSupported: (name_ptr, name_len) => {
|
|
|
+ let name = this.mem.loadString(name_ptr, name_len);
|
|
|
+ let extensions = this.ctx.getSupportedExtensions();
|
|
|
+ return extensions.indexOf(name) !== -1
|
|
|
+ },
|
|
|
|
|
|
- let e = event_temp_data.event;
|
|
|
|
|
|
- wmi.storeU32(off(4), event_temp_data.name_code);
|
|
|
- if (e.target == document) {
|
|
|
- wmi.storeU32(off(4), 1);
|
|
|
- } else if (e.target == window) {
|
|
|
- wmi.storeU32(off(4), 2);
|
|
|
- } else {
|
|
|
- wmi.storeU32(off(4), 0);
|
|
|
+ GetError: () => {
|
|
|
+ let err = this.lastError;
|
|
|
+ this.recordError(0);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
}
|
|
|
- if (e.currentTarget == document) {
|
|
|
- wmi.storeU32(off(4), 1);
|
|
|
- } else if (e.currentTarget == window) {
|
|
|
- wmi.storeU32(off(4), 2);
|
|
|
- } else {
|
|
|
- wmi.storeU32(off(4), 0);
|
|
|
+ return this.ctx.getError();
|
|
|
+ },
|
|
|
+
|
|
|
+ GetWebGLVersion: (major_ptr, minor_ptr) => {
|
|
|
+ let version = this.ctx.getParameter(0x1F02);
|
|
|
+ if (version.indexOf("WebGL 2.0") !== -1) {
|
|
|
+ this.mem.storeI32(major_ptr, 2);
|
|
|
+ this.mem.storeI32(minor_ptr, 0);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- wmi.storeUint(off(W), event_temp_data.id_ptr);
|
|
|
- wmi.storeUint(off(W), event_temp_data.id_len);
|
|
|
-
|
|
|
- wmi.storeF64(off(8), e.timeStamp*1e-3);
|
|
|
-
|
|
|
- wmi.storeU8(off(1), e.eventPhase);
|
|
|
- wmi.storeU8(off(1), !!e.bubbles);
|
|
|
- wmi.storeU8(off(1), !!e.cancelable);
|
|
|
- wmi.storeU8(off(1), !!e.composed);
|
|
|
- wmi.storeU8(off(1), !!e.isComposing);
|
|
|
- wmi.storeU8(off(1), !!e.isTrusted);
|
|
|
-
|
|
|
- let base = off(0, 8);
|
|
|
- if (e instanceof MouseEvent) {
|
|
|
- wmi.storeI64(off(8), e.screenX);
|
|
|
- wmi.storeI64(off(8), e.screenY);
|
|
|
- wmi.storeI64(off(8), e.clientX);
|
|
|
- wmi.storeI64(off(8), e.clientY);
|
|
|
- wmi.storeI64(off(8), e.offsetX);
|
|
|
- wmi.storeI64(off(8), e.offsetY);
|
|
|
- wmi.storeI64(off(8), e.pageX);
|
|
|
- wmi.storeI64(off(8), e.pageY);
|
|
|
- wmi.storeI64(off(8), e.movementX);
|
|
|
- wmi.storeI64(off(8), e.movementY);
|
|
|
-
|
|
|
- wmi.storeU8(off(1), !!e.ctrlKey);
|
|
|
- wmi.storeU8(off(1), !!e.shiftKey);
|
|
|
- wmi.storeU8(off(1), !!e.altKey);
|
|
|
- wmi.storeU8(off(1), !!e.metaKey);
|
|
|
-
|
|
|
- wmi.storeI16(off(2), e.button);
|
|
|
- wmi.storeU16(off(2), e.buttons);
|
|
|
- } else if (e instanceof KeyboardEvent) {
|
|
|
- let keyOffset = off(W*2, W);
|
|
|
- let codeOffet = off(W*2, W);
|
|
|
- wmi.storeU8(off(1), e.location);
|
|
|
-
|
|
|
- wmi.storeU8(off(1), !!e.ctrlKey);
|
|
|
- wmi.storeU8(off(1), !!e.shiftKey);
|
|
|
- wmi.storeU8(off(1), !!e.altKey);
|
|
|
- wmi.storeU8(off(1), !!e.metaKey);
|
|
|
-
|
|
|
- wmi.storeU8(off(1), !!e.repeat);
|
|
|
- } else if (e instanceof WheelEvent) {
|
|
|
- wmi.storeF64(off(8), e.deltaX);
|
|
|
- wmi.storeF64(off(8), e.deltaY);
|
|
|
- wmi.storeF64(off(8), e.deltaZ);
|
|
|
- wmi.storeU32(off(4), e.deltaMode);
|
|
|
- } else if (e instanceof Event) {
|
|
|
- if ('scrollX' in e) {
|
|
|
- wmi.storeF64(off(8), e.scrollX);
|
|
|
- wmi.storeF64(off(8), e.scrollY);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- add_event_listener: (id_ptr, id_len, name_ptr, name_len, name_code, data, callback, use_capture) => {
|
|
|
- let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
- let name = wasmMemoryInterface.loadString(name_ptr, name_len);
|
|
|
- let element = document.getElementById(id);
|
|
|
- if (element == undefined) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- let listener = (e) => {
|
|
|
- const odin_ctx = wasmMemoryInterface.exports.default_context_ptr();
|
|
|
- event_temp_data.id_ptr = id_ptr;
|
|
|
- event_temp_data.id_len = id_len;
|
|
|
- event_temp_data.event = e;
|
|
|
- event_temp_data.name_code = name_code;
|
|
|
- // console.log(e);
|
|
|
- wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx);
|
|
|
- };
|
|
|
- wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
|
|
|
- element.addEventListener(name, listener, !!use_capture);
|
|
|
- return true;
|
|
|
- },
|
|
|
-
|
|
|
- remove_event_listener: (id_ptr, id_len, name_ptr, name_len, data, callback) => {
|
|
|
- let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
- let name = wasmMemoryInterface.loadString(name_ptr, name_len);
|
|
|
- let element = document.getElementById(id);
|
|
|
- if (element == undefined) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
|
|
|
- if (listener == undefined) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- element.removeEventListener(name, listener);
|
|
|
- return true;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- add_window_event_listener: (name_ptr, name_len, name_code, data, callback, use_capture) => {
|
|
|
- let name = wasmMemoryInterface.loadString(name_ptr, name_len);
|
|
|
- let element = window;
|
|
|
- let listener = (e) => {
|
|
|
- const odin_ctx = wasmMemoryInterface.exports.default_context_ptr();
|
|
|
- event_temp_data.id_ptr = 0;
|
|
|
- event_temp_data.id_len = 0;
|
|
|
- event_temp_data.event = e;
|
|
|
- event_temp_data.name_code = name_code;
|
|
|
- // console.log(e);
|
|
|
- wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx);
|
|
|
- };
|
|
|
- wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
|
|
|
- element.addEventListener(name, listener, !!use_capture);
|
|
|
- return true;
|
|
|
- },
|
|
|
-
|
|
|
- remove_window_event_listener: (name_ptr, name_len, data, callback) => {
|
|
|
- let name = wasmMemoryInterface.loadString(name_ptr, name_len);
|
|
|
- let element = window;
|
|
|
- let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
|
|
|
- if (listener == undefined) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- element.removeEventListener(name, listener);
|
|
|
- return true;
|
|
|
- },
|
|
|
-
|
|
|
- event_stop_propagation: () => {
|
|
|
- if (event_temp_data && event_temp_data.event) {
|
|
|
- event_temp_data.event.eventStopPropagation();
|
|
|
- }
|
|
|
- },
|
|
|
- event_stop_immediate_propagation: () => {
|
|
|
- if (event_temp_data && event_temp_data.event) {
|
|
|
- event_temp_data.event.eventStopImmediatePropagation();
|
|
|
- }
|
|
|
- },
|
|
|
- event_prevent_default: () => {
|
|
|
- if (event_temp_data && event_temp_data.event) {
|
|
|
- event_temp_data.event.eventPreventDefault();
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- get_element_value_f64: (id_ptr, id_len) => {
|
|
|
- let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
- let element = document.getElementById(id);
|
|
|
- return element ? element.value : 0;
|
|
|
- },
|
|
|
- get_element_value_string: (id_ptr, id_len, buf_ptr, buf_len) => {
|
|
|
- let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
- let element = document.getElementById(id);
|
|
|
- if (element) {
|
|
|
- let str = element.value;
|
|
|
- if (buf_len > 0 && buf_ptr) {
|
|
|
- let n = Math.min(buf_len, str.length);
|
|
|
- str = str.substring(0, n);
|
|
|
- this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(str))
|
|
|
- return n;
|
|
|
- }
|
|
|
- }
|
|
|
- return 0;
|
|
|
- },
|
|
|
- get_element_min_max: (ptr_array2_f64, id_ptr, id_len) => {
|
|
|
- let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
- let element = document.getElementById(id);
|
|
|
- if (element) {
|
|
|
- let values = wasmMemoryInterface.loadF64Array(ptr_array2_f64, 2);
|
|
|
- values[0] = element.min;
|
|
|
- values[1] = element.max;
|
|
|
- }
|
|
|
- },
|
|
|
- set_element_value: (id_ptr, id_len, value) => {
|
|
|
- let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
- let element = document.getElementById(id);
|
|
|
- if (element) {
|
|
|
- element.value = value;
|
|
|
- }
|
|
|
- },
|
|
|
- },
|
|
|
- };
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-class WebGLInterface {
|
|
|
- constructor(wasmMemoryInterface, canvasElement, contextSettings) {
|
|
|
- this.wasmMemoryInterface = wasmMemoryInterface;
|
|
|
- this.ctxElement = null;
|
|
|
- this.ctx = null;
|
|
|
- this.ctxVersion = 1.0;
|
|
|
- this.counter = 1;
|
|
|
- this.lastError = 0;
|
|
|
- this.buffers = [];
|
|
|
- this.mappedBuffers = {};
|
|
|
- this.programs = [];
|
|
|
- this.framebuffers = [];
|
|
|
- this.renderbuffers = [];
|
|
|
- this.textures = [];
|
|
|
- this.uniforms = [];
|
|
|
- this.shaders = [];
|
|
|
- this.vaos = [];
|
|
|
- this.contexts = [];
|
|
|
- this.currentContext = null;
|
|
|
- this.offscreenCanvases = {};
|
|
|
- this.timerQueriesEXT = [];
|
|
|
- this.queries = [];
|
|
|
- this.samplers = [];
|
|
|
- this.transformFeedbacks = [];
|
|
|
- this.syncs = [];
|
|
|
- this.programInfos = {};
|
|
|
-
|
|
|
- this.setCurrentContext(canvasElement, contextSettings);
|
|
|
- }
|
|
|
-
|
|
|
- get mem() {
|
|
|
- return this.wasmMemoryInterface
|
|
|
- }
|
|
|
-
|
|
|
- setCurrentContext(element, contextSettings) {
|
|
|
- if (!element) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (this.ctxElement == element) {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- contextSettings = contextSettings ?? {};
|
|
|
- this.ctx = element.getContext("webgl2", contextSettings) || element.getContext("webgl", contextSettings);
|
|
|
- if (!this.ctx) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- this.ctxElement = element;
|
|
|
- if (this.ctx.getParameter(0x1F02).indexOf("WebGL 2.0") !== -1) {
|
|
|
- this.ctxVersion = 2.0;
|
|
|
- } else {
|
|
|
- this.ctxVersion = 1.0;
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- assertWebGL2() {
|
|
|
- if (this.ctxVersion < 2) {
|
|
|
- throw new Error("WebGL2 procedure called in a canvas without a WebGL2 context");
|
|
|
- }
|
|
|
- }
|
|
|
- getNewId(table) {
|
|
|
- for (var ret = this.counter++, i = table.length; i < ret; i++) {
|
|
|
- table[i] = null;
|
|
|
- }
|
|
|
- return ret;
|
|
|
- }
|
|
|
- recordError(errorCode) {
|
|
|
- this.lastError || (this.lastError = errorCode);
|
|
|
- }
|
|
|
- populateUniformTable(program) {
|
|
|
- let p = this.programs[program];
|
|
|
- this.programInfos[program] = {
|
|
|
- uniforms: {},
|
|
|
- maxUniformLength: 0,
|
|
|
- maxAttributeLength: -1,
|
|
|
- maxUniformBlockNameLength: -1,
|
|
|
- };
|
|
|
- for (let ptable = this.programInfos[program], utable = ptable.uniforms, numUniforms = this.ctx.getProgramParameter(p, this.ctx.ACTIVE_UNIFORMS), i = 0; i < numUniforms; ++i) {
|
|
|
- let u = this.ctx.getActiveUniform(p, i);
|
|
|
- let name = u.name;
|
|
|
- if (ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length + 1), name.indexOf("]", name.length - 1) !== -1) {
|
|
|
- name = name.slice(0, name.lastIndexOf("["));
|
|
|
- }
|
|
|
- let loc = this.ctx.getUniformLocation(p, name);
|
|
|
- if (loc !== null) {
|
|
|
- let id = this.getNewId(this.uniforms);
|
|
|
- utable[name] = [u.size, id], this.uniforms[id] = loc;
|
|
|
- for (let j = 1; j < u.size; ++j) {
|
|
|
- let n = name + "[" + j + "]";
|
|
|
- let loc = this.ctx.getUniformLocation(p, n);
|
|
|
- let id = this.getNewId(this.uniforms);
|
|
|
- this.uniforms[id] = loc;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- getSource(shader, strings_ptr, strings_length) {
|
|
|
- const STRING_SIZE = 2*4;
|
|
|
- let source = "";
|
|
|
- for (let i = 0; i < strings_length; i++) {
|
|
|
- let ptr = this.mem.loadPtr(strings_ptr + i*STRING_SIZE);
|
|
|
- let len = this.mem.loadPtr(strings_ptr + i*STRING_SIZE + 4);
|
|
|
- let str = this.mem.loadString(ptr, len);
|
|
|
- source += str;
|
|
|
- }
|
|
|
- return source;
|
|
|
- }
|
|
|
-
|
|
|
- getWebGL1Interface() {
|
|
|
- return {
|
|
|
- SetCurrentContextById: (name_ptr, name_len) => {
|
|
|
- let name = this.mem.loadString(name_ptr, name_len);
|
|
|
- let element = document.getElementById(name);
|
|
|
- return this.setCurrentContext(element, {alpha: true, antialias: true, depth: true, premultipliedAlpha: true});
|
|
|
- },
|
|
|
- CreateCurrentContextById: (name_ptr, name_len, attributes) => {
|
|
|
- let name = this.mem.loadString(name_ptr, name_len);
|
|
|
- let element = document.getElementById(name);
|
|
|
-
|
|
|
- let contextSettings = {
|
|
|
- alpha: !(attributes & (1<<0)),
|
|
|
- antialias: !(attributes & (1<<1)),
|
|
|
- depth: !(attributes & (1<<2)),
|
|
|
- failIfMajorPerformanceCaveat: !!(attributes & (1<<3)),
|
|
|
- premultipliedAlpha: !(attributes & (1<<4)),
|
|
|
- preserveDrawingBuffer: !!(attributes & (1<<5)),
|
|
|
- stencil: !!(attributes & (1<<6)),
|
|
|
- desynchronized: !!(attributes & (1<<7)),
|
|
|
- };
|
|
|
-
|
|
|
- return this.setCurrentContext(element, contextSettings);
|
|
|
- },
|
|
|
- GetCurrentContextAttributes: () => {
|
|
|
- if (!this.ctx) {
|
|
|
- return 0;
|
|
|
- }
|
|
|
- let attrs = this.ctx.getContextAttributes();
|
|
|
- let res = 0;
|
|
|
- if (!attrs.alpha) res |= 1<<0;
|
|
|
- if (!attrs.antialias) res |= 1<<1;
|
|
|
- if (!attrs.depth) res |= 1<<2;
|
|
|
- if (attrs.failIfMajorPerformanceCaveat) res |= 1<<3;
|
|
|
- if (!attrs.premultipliedAlpha) res |= 1<<4;
|
|
|
- if (attrs.preserveDrawingBuffer) res |= 1<<5;
|
|
|
- if (attrs.stencil) res |= 1<<6;
|
|
|
- if (attrs.desynchronized) res |= 1<<7;
|
|
|
- return res;
|
|
|
- },
|
|
|
-
|
|
|
- DrawingBufferWidth: () => this.ctx.drawingBufferWidth,
|
|
|
- DrawingBufferHeight: () => this.ctx.drawingBufferHeight,
|
|
|
-
|
|
|
- IsExtensionSupported: (name_ptr, name_len) => {
|
|
|
- let name = this.mem.loadString(name_ptr, name_len);
|
|
|
- let extensions = this.ctx.getSupportedExtensions();
|
|
|
- return extensions.indexOf(name) !== -1
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- GetError: () => {
|
|
|
- let err = this.lastError;
|
|
|
- this.recordError(0);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
- return this.ctx.getError();
|
|
|
- },
|
|
|
-
|
|
|
- GetWebGLVersion: (major_ptr, minor_ptr) => {
|
|
|
- let version = this.ctx.getParameter(0x1F02);
|
|
|
- if (version.indexOf("WebGL 2.0") !== -1) {
|
|
|
- this.mem.storeI32(major_ptr, 2);
|
|
|
- this.mem.storeI32(minor_ptr, 0);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- this.mem.storeI32(major_ptr, 1);
|
|
|
- this.mem.storeI32(minor_ptr, 0);
|
|
|
- },
|
|
|
- GetESVersion: (major_ptr, minor_ptr) => {
|
|
|
- let version = this.ctx.getParameter(0x1F02);
|
|
|
- if (version.indexOf("OpenGL ES 3.0") !== -1) {
|
|
|
- this.mem.storeI32(major_ptr, 3);
|
|
|
- this.mem.storeI32(minor_ptr, 0);
|
|
|
- return;
|
|
|
- }
|
|
|
+ this.mem.storeI32(major_ptr, 1);
|
|
|
+ this.mem.storeI32(minor_ptr, 0);
|
|
|
+ },
|
|
|
+ GetESVersion: (major_ptr, minor_ptr) => {
|
|
|
+ let version = this.ctx.getParameter(0x1F02);
|
|
|
+ if (version.indexOf("OpenGL ES 3.0") !== -1) {
|
|
|
+ this.mem.storeI32(major_ptr, 3);
|
|
|
+ this.mem.storeI32(minor_ptr, 0);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
this.mem.storeI32(major_ptr, 2);
|
|
|
this.mem.storeI32(minor_ptr, 0);
|
|
@@ -1206,255 +921,597 @@ class WebGLInterface {
|
|
|
this.assertWebGL2();
|
|
|
this.ctx.drawElementsInstanced(mode, count, type, offset, instanceCount);
|
|
|
},
|
|
|
- DrawRangeElements: (mode, start, end, count, type, offset) => {
|
|
|
+ DrawRangeElements: (mode, start, end, count, type, offset) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.drawRangeElements(mode, start, end, count, type, offset);
|
|
|
+ },
|
|
|
+
|
|
|
+ /* Multiple Render Targets */
|
|
|
+ DrawBuffers: (buffers_ptr, buffers_len) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let array = this.mem.loadU32Array(buffers_ptr, buffers_len);
|
|
|
+ this.ctx.drawBuffers(array);
|
|
|
+ },
|
|
|
+ ClearBufferfv: (buffer, drawbuffer, values_ptr, values_len) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let array = this.mem.loadF32Array(values_ptr, values_len);
|
|
|
+ this.ctx.clearBufferfv(buffer, drawbuffer, array);
|
|
|
+ },
|
|
|
+ ClearBufferiv: (buffer, drawbuffer, values_ptr, values_len) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let array = this.mem.loadI32Array(values_ptr, values_len);
|
|
|
+ this.ctx.clearBufferiv(buffer, drawbuffer, array);
|
|
|
+ },
|
|
|
+ ClearBufferuiv: (buffer, drawbuffer, values_ptr, values_len) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let array = this.mem.loadU32Array(values_ptr, values_len);
|
|
|
+ this.ctx.clearBufferuiv(buffer, drawbuffer, array);
|
|
|
+ },
|
|
|
+ ClearBufferfi: (buffer, drawbuffer, depth, stencil) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.clearBufferfi(buffer, drawbuffer, depth, stencil);
|
|
|
+ },
|
|
|
+
|
|
|
+ /* Query Objects */
|
|
|
+ CreateQuery: () => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let query = this.ctx.createQuery();
|
|
|
+ let id = this.getNewId(this.queries);
|
|
|
+ query.name = id;
|
|
|
+ this.queries[id] = query;
|
|
|
+ return id;
|
|
|
+ },
|
|
|
+ DeleteQuery: (id) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let obj = this.querys[id];
|
|
|
+ if (obj && id != 0) {
|
|
|
+ this.ctx.deleteQuery(obj);
|
|
|
+ this.querys[id] = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ IsQuery: (query) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ return this.ctx.isQuery(this.queries[query]);
|
|
|
+ },
|
|
|
+ BeginQuery: (target, query) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.beginQuery(target, this.queries[query])
|
|
|
+ },
|
|
|
+ EndQuery: (target) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.endQuery(target);
|
|
|
+ },
|
|
|
+ GetQuery: (target, pname) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let query = this.ctx.getQuery(target, pname);
|
|
|
+ if (!query) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (this.queries.indexOf(query) !== -1) {
|
|
|
+ return query.name;
|
|
|
+ }
|
|
|
+ let id = this.getNewId(this.queries);
|
|
|
+ query.name = id;
|
|
|
+ this.queries[id] = query;
|
|
|
+ return id;
|
|
|
+ },
|
|
|
+
|
|
|
+ /* Sampler Objects */
|
|
|
+ CreateSampler: () => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let sampler = this.ctx.createSampler();
|
|
|
+ let id = this.getNewId(this.samplers);
|
|
|
+ sampler.name = id;
|
|
|
+ this.samplers[id] = sampler;
|
|
|
+ return id;
|
|
|
+ },
|
|
|
+ DeleteSampler: (id) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let obj = this.samplers[id];
|
|
|
+ if (obj && id != 0) {
|
|
|
+ this.ctx.deleteSampler(obj);
|
|
|
+ this.samplers[id] = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ IsSampler: (sampler) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ return this.ctx.isSampler(this.samplers[sampler]);
|
|
|
+ },
|
|
|
+ BindSampler: (unit, sampler) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.bindSampler(unit, this.samplers[Sampler]);
|
|
|
+ },
|
|
|
+ SamplerParameteri: (sampler, pname, param) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.samplerParameteri(this.samplers[sampler], pname, param);
|
|
|
+ },
|
|
|
+ SamplerParameterf: (sampler, pname, param) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.samplerParameterf(this.samplers[sampler], pname, param);
|
|
|
+ },
|
|
|
+
|
|
|
+ /* Sync objects */
|
|
|
+ FenceSync: (condition, flags) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let sync = this.ctx.fenceSync(condition, flags);
|
|
|
+ let id = this.getNewId(this.syncs);
|
|
|
+ sync.name = id;
|
|
|
+ this.syncs[id] = sync;
|
|
|
+ return id;
|
|
|
+ },
|
|
|
+ IsSync: (sync) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ return this.ctx.isSync(this.syncs[sync]);
|
|
|
+ },
|
|
|
+ DeleteSync: (id) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let obj = this.syncs[id];
|
|
|
+ if (obj && id != 0) {
|
|
|
+ this.ctx.deleteSampler(obj);
|
|
|
+ this.syncs[id] = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ ClientWaitSync: (sync, flags, timeout) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ return this.ctx.clientWaitSync(this.syncs[sync], flags, timeout);
|
|
|
+ },
|
|
|
+ WaitSync: (sync, flags, timeout) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.waitSync(this.syncs[sync], flags, timeout) ;
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ /* Transform Feedback */
|
|
|
+ CreateTransformFeedback: () => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let transformFeedback = this.ctx.createtransformFeedback();
|
|
|
+ let id = this.getNewId(this.transformFeedbacks);
|
|
|
+ transformFeedback.name = id;
|
|
|
+ this.transformFeedbacks[id] = transformFeedback;
|
|
|
+ return id;
|
|
|
+ },
|
|
|
+ DeleteTransformFeedback: (id) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let obj = this.transformFeedbacks[id];
|
|
|
+ if (obj && id != 0) {
|
|
|
+ this.ctx.deleteTransformFeedback(obj);
|
|
|
+ this.transformFeedbacks[id] = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ IsTransformFeedback: (tf) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ return this.ctx.isTransformFeedback(this.transformFeedbacks[tf]);
|
|
|
+ },
|
|
|
+ BindTransformFeedback: (target, tf) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.bindTransformFeedback(target, this.transformFeedbacks[tf]);
|
|
|
+ },
|
|
|
+ BeginTransformFeedback: (primitiveMode) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.beginTransformFeedback(primitiveMode);
|
|
|
+ },
|
|
|
+ EndTransformFeedback: () => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.endTransformFeedback();
|
|
|
+ },
|
|
|
+ TransformFeedbackVaryings: (program, varyings_ptr, varyings_len, bufferMode) => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ let varyings = [];
|
|
|
+ for (let i = 0; i < varyings_len; i++) {
|
|
|
+ let ptr = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 0*4);
|
|
|
+ let len = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 1*4);
|
|
|
+ varyings.push(this.mem.loadString(ptr, len));
|
|
|
+ }
|
|
|
+ this.ctx.transformFeedbackVaryings(this.programs[program], varyings, bufferMode);
|
|
|
+ },
|
|
|
+ PauseTransformFeedback: () => {
|
|
|
+ this.assertWebGL2();
|
|
|
+ this.ctx.pauseTransformFeedback();
|
|
|
+ },
|
|
|
+ ResumeTransformFeedback: () => {
|
|
|
this.assertWebGL2();
|
|
|
- this.ctx.drawRangeElements(mode, start, end, count, type, offset);
|
|
|
+ this.ctx.resumeTransformFeedback();
|
|
|
},
|
|
|
|
|
|
- /* Multiple Render Targets */
|
|
|
- DrawBuffers: (buffers_ptr, buffers_len) => {
|
|
|
+
|
|
|
+ /* Uniform Buffer Objects and Transform Feedback Buffers */
|
|
|
+ BindBufferBase: (target, index, buffer) => {
|
|
|
this.assertWebGL2();
|
|
|
- let array = this.mem.loadU32Array(buffers_ptr, buffers_len);
|
|
|
- this.ctx.drawBuffers(array);
|
|
|
+ this.ctx.bindBufferBase(target, index, this.buffers[buffer]);
|
|
|
},
|
|
|
- ClearBufferfv: (buffer, drawbuffer, values_ptr, values_len) => {
|
|
|
+ BindBufferRange: (target, index, buffer, offset, size) => {
|
|
|
this.assertWebGL2();
|
|
|
- let array = this.mem.loadF32Array(values_ptr, values_len);
|
|
|
- this.ctx.clearBufferfv(buffer, drawbuffer, array);
|
|
|
+ this.ctx.bindBufferRange(target, index, this.buffers[buffer], offset, size);
|
|
|
},
|
|
|
- ClearBufferiv: (buffer, drawbuffer, values_ptr, values_len) => {
|
|
|
+ GetUniformBlockIndex: (program, uniformBlockName_ptr, uniformBlockName_len) => {
|
|
|
this.assertWebGL2();
|
|
|
- let array = this.mem.loadI32Array(values_ptr, values_len);
|
|
|
- this.ctx.clearBufferiv(buffer, drawbuffer, array);
|
|
|
+ return this.ctx.getUniformBlockIndex(this.programs[program], this.mem.loadString(uniformBlockName_ptr, uniformBlockName_len));
|
|
|
},
|
|
|
- ClearBufferuiv: (buffer, drawbuffer, values_ptr, values_len) => {
|
|
|
+ // any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname);
|
|
|
+ GetActiveUniformBlockName: (program, uniformBlockIndex, buf_ptr, buf_len, length_ptr) => {
|
|
|
this.assertWebGL2();
|
|
|
- let array = this.mem.loadU32Array(values_ptr, values_len);
|
|
|
- this.ctx.clearBufferuiv(buffer, drawbuffer, array);
|
|
|
+ let name = this.ctx.getActiveUniformBlockName(this.programs[program], uniformBlockIndex);
|
|
|
+
|
|
|
+ let n = Math.min(buf_len, name.length);
|
|
|
+ name = name.substring(0, n);
|
|
|
+ this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(name))
|
|
|
+ this.mem.storeInt(length_ptr, n);
|
|
|
},
|
|
|
- ClearBufferfi: (buffer, drawbuffer, depth, stencil) => {
|
|
|
+ UniformBlockBinding: (program, uniformBlockIndex, uniformBlockBinding) => {
|
|
|
this.assertWebGL2();
|
|
|
- this.ctx.clearBufferfi(buffer, drawbuffer, depth, stencil);
|
|
|
+ this.ctx.uniformBlockBinding(this.programs[program], uniformBlockIndex, uniformBlockBinding);
|
|
|
},
|
|
|
|
|
|
- /* Query Objects */
|
|
|
- CreateQuery: () => {
|
|
|
+ /* Vertex Array Objects */
|
|
|
+ CreateVertexArray: () => {
|
|
|
this.assertWebGL2();
|
|
|
- let query = this.ctx.createQuery();
|
|
|
- let id = this.getNewId(this.queries);
|
|
|
- query.name = id;
|
|
|
- this.queries[id] = query;
|
|
|
+ let vao = this.ctx.createVertexArray();
|
|
|
+ let id = this.getNewId(this.vaos);
|
|
|
+ vao.name = id;
|
|
|
+ this.vaos[id] = vao;
|
|
|
return id;
|
|
|
},
|
|
|
- DeleteQuery: (id) => {
|
|
|
+ DeleteVertexArray: (id) => {
|
|
|
this.assertWebGL2();
|
|
|
- let obj = this.querys[id];
|
|
|
+ let obj = this.vaos[id];
|
|
|
if (obj && id != 0) {
|
|
|
- this.ctx.deleteQuery(obj);
|
|
|
- this.querys[id] = null;
|
|
|
+ this.ctx.deleteVertexArray(obj);
|
|
|
+ this.vaos[id] = null;
|
|
|
}
|
|
|
},
|
|
|
- IsQuery: (query) => {
|
|
|
+ IsVertexArray: (vertexArray) => {
|
|
|
this.assertWebGL2();
|
|
|
- return this.ctx.isQuery(this.queries[query]);
|
|
|
+ return this.ctx.isVertexArray(this.vaos[vertexArray]);
|
|
|
},
|
|
|
- BeginQuery: (target, query) => {
|
|
|
+ BindVertexArray: (vertexArray) => {
|
|
|
this.assertWebGL2();
|
|
|
- this.ctx.beginQuery(target, this.queries[query])
|
|
|
+ this.ctx.bindVertexArray(this.vaos[vertexArray]);
|
|
|
},
|
|
|
- EndQuery: (target) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.endQuery(target);
|
|
|
+ };
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
|
|
|
+ const MAX_INFO_CONSOLE_LINES = 512;
|
|
|
+ let infoConsoleLines = new Array();
|
|
|
+ const addConsoleLine = (line) => {
|
|
|
+ if (!line) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (line.endsWith("\n")) {
|
|
|
+ line = line.substring(0, line.length-1);
|
|
|
+ } else if (infoConsoleLines.length > 0) {
|
|
|
+ let prev_line = infoConsoleLines.pop();
|
|
|
+ line = prev_line.concat(line);
|
|
|
+ }
|
|
|
+ infoConsoleLines.push(line);
|
|
|
+
|
|
|
+ if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) {
|
|
|
+ infoConsoleLines.shift();
|
|
|
+ }
|
|
|
+
|
|
|
+ let data = "";
|
|
|
+ for (let i = 0; i < infoConsoleLines.length; i++) {
|
|
|
+ if (i != 0) {
|
|
|
+ data = data.concat("\n");
|
|
|
+ }
|
|
|
+ data = data.concat(infoConsoleLines[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (consoleElement) {
|
|
|
+ let info = consoleElement;
|
|
|
+ info.innerHTML = data;
|
|
|
+ info.scrollTop = info.scrollHeight;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let event_temp_data = {};
|
|
|
+
|
|
|
+ let webglContext = new WebGLInterface(wasmMemoryInterface);
|
|
|
+ return {
|
|
|
+ "env": {},
|
|
|
+ "odin_env": {
|
|
|
+ write: (fd, ptr, len) => {
|
|
|
+ const str = wasmMemoryInterface.loadString(ptr, len);
|
|
|
+ if (fd == 1) {
|
|
|
+ addConsoleLine(str);
|
|
|
+ return;
|
|
|
+ } else if (fd == 2) {
|
|
|
+ addConsoleLine(str);
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ throw new Error("Invalid fd to 'write'" + stripNewline(str));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trap: () => { throw new Error() },
|
|
|
+ alert: (ptr, len) => { alert(wasmMemoryInterface.loadString(ptr, len)) },
|
|
|
+ abort: () => { Module.abort() },
|
|
|
+ evaluate: (str_ptr, str_len) => { eval.call(null, wasmMemoryInterface.loadString(str_ptr, str_len)); },
|
|
|
+
|
|
|
+ time_now: () => {
|
|
|
+ return performance.now() * 1e6;
|
|
|
+ },
|
|
|
+
|
|
|
+ sqrt: (x) => Math.sqrt(x),
|
|
|
+ sin: (x) => Math.sin(x),
|
|
|
+ cos: (x) => Math.cos(x),
|
|
|
+ pow: (x) => Math.pow(x),
|
|
|
+ fmuladd: (x, y, z) => x*y + z,
|
|
|
+ ln: (x) => Math.log(x),
|
|
|
+ exp: (x) => Math.exp(x),
|
|
|
+ ldexp: (x) => Math.ldexp(x),
|
|
|
+ },
|
|
|
+ "odin_dom": {
|
|
|
+ init_event_raw: (ep) => {
|
|
|
+ const W = 4;
|
|
|
+ let offset = ep;
|
|
|
+ let off = (amount, alignment) => {
|
|
|
+ if (alignment === undefined) {
|
|
|
+ alignment = Math.min(amount, W);
|
|
|
+ }
|
|
|
+ if (offset % alignment != 0) {
|
|
|
+ offset += alignment - (offset%alignment);
|
|
|
+ }
|
|
|
+ let x = offset;
|
|
|
+ offset += amount;
|
|
|
+ return x;
|
|
|
+ };
|
|
|
+
|
|
|
+ let wmi = wasmMemoryInterface;
|
|
|
+
|
|
|
+ let e = event_temp_data.event;
|
|
|
+
|
|
|
+ wmi.storeU32(off(4), event_temp_data.name_code);
|
|
|
+ if (e.target == document) {
|
|
|
+ wmi.storeU32(off(4), 1);
|
|
|
+ } else if (e.target == window) {
|
|
|
+ wmi.storeU32(off(4), 2);
|
|
|
+ } else {
|
|
|
+ wmi.storeU32(off(4), 0);
|
|
|
+ }
|
|
|
+ if (e.currentTarget == document) {
|
|
|
+ wmi.storeU32(off(4), 1);
|
|
|
+ } else if (e.currentTarget == window) {
|
|
|
+ wmi.storeU32(off(4), 2);
|
|
|
+ } else {
|
|
|
+ wmi.storeU32(off(4), 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ wmi.storeUint(off(W), event_temp_data.id_ptr);
|
|
|
+ wmi.storeUint(off(W), event_temp_data.id_len);
|
|
|
+
|
|
|
+ wmi.storeF64(off(8), e.timeStamp*1e-3);
|
|
|
+
|
|
|
+ wmi.storeU8(off(1), e.eventPhase);
|
|
|
+ wmi.storeU8(off(1), !!e.bubbles);
|
|
|
+ wmi.storeU8(off(1), !!e.cancelable);
|
|
|
+ wmi.storeU8(off(1), !!e.composed);
|
|
|
+ wmi.storeU8(off(1), !!e.isComposing);
|
|
|
+ wmi.storeU8(off(1), !!e.isTrusted);
|
|
|
+
|
|
|
+ let base = off(0, 8);
|
|
|
+ if (e instanceof MouseEvent) {
|
|
|
+ wmi.storeI64(off(8), e.screenX);
|
|
|
+ wmi.storeI64(off(8), e.screenY);
|
|
|
+ wmi.storeI64(off(8), e.clientX);
|
|
|
+ wmi.storeI64(off(8), e.clientY);
|
|
|
+ wmi.storeI64(off(8), e.offsetX);
|
|
|
+ wmi.storeI64(off(8), e.offsetY);
|
|
|
+ wmi.storeI64(off(8), e.pageX);
|
|
|
+ wmi.storeI64(off(8), e.pageY);
|
|
|
+ wmi.storeI64(off(8), e.movementX);
|
|
|
+ wmi.storeI64(off(8), e.movementY);
|
|
|
+
|
|
|
+ wmi.storeU8(off(1), !!e.ctrlKey);
|
|
|
+ wmi.storeU8(off(1), !!e.shiftKey);
|
|
|
+ wmi.storeU8(off(1), !!e.altKey);
|
|
|
+ wmi.storeU8(off(1), !!e.metaKey);
|
|
|
+
|
|
|
+ wmi.storeI16(off(2), e.button);
|
|
|
+ wmi.storeU16(off(2), e.buttons);
|
|
|
+ } else if (e instanceof KeyboardEvent) {
|
|
|
+ let keyOffset = off(W*2, W);
|
|
|
+ let codeOffet = off(W*2, W);
|
|
|
+ wmi.storeU8(off(1), e.location);
|
|
|
+
|
|
|
+ wmi.storeU8(off(1), !!e.ctrlKey);
|
|
|
+ wmi.storeU8(off(1), !!e.shiftKey);
|
|
|
+ wmi.storeU8(off(1), !!e.altKey);
|
|
|
+ wmi.storeU8(off(1), !!e.metaKey);
|
|
|
+
|
|
|
+ wmi.storeU8(off(1), !!e.repeat);
|
|
|
+ } else if (e instanceof WheelEvent) {
|
|
|
+ wmi.storeF64(off(8), e.deltaX);
|
|
|
+ wmi.storeF64(off(8), e.deltaY);
|
|
|
+ wmi.storeF64(off(8), e.deltaZ);
|
|
|
+ wmi.storeU32(off(4), e.deltaMode);
|
|
|
+ } else if (e instanceof Event) {
|
|
|
+ if ('scrollX' in e) {
|
|
|
+ wmi.storeF64(off(8), e.scrollX);
|
|
|
+ wmi.storeF64(off(8), e.scrollY);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ add_event_listener: (id_ptr, id_len, name_ptr, name_len, name_code, data, callback, use_capture) => {
|
|
|
+ let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
+ let name = wasmMemoryInterface.loadString(name_ptr, name_len);
|
|
|
+ let element = document.getElementById(id);
|
|
|
+ if (element == undefined) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ let listener = (e) => {
|
|
|
+ const odin_ctx = wasmMemoryInterface.exports.default_context_ptr();
|
|
|
+ event_temp_data.id_ptr = id_ptr;
|
|
|
+ event_temp_data.id_len = id_len;
|
|
|
+ event_temp_data.event = e;
|
|
|
+ event_temp_data.name_code = name_code;
|
|
|
+ // console.log(e);
|
|
|
+ wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx);
|
|
|
+ };
|
|
|
+ wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
|
|
|
+ element.addEventListener(name, listener, !!use_capture);
|
|
|
+ return true;
|
|
|
},
|
|
|
- GetQuery: (target, pname) => {
|
|
|
- this.assertWebGL2();
|
|
|
- let query = this.ctx.getQuery(target, pname);
|
|
|
- if (!query) {
|
|
|
- return 0;
|
|
|
+
|
|
|
+ remove_event_listener: (id_ptr, id_len, name_ptr, name_len, data, callback) => {
|
|
|
+ let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
+ let name = wasmMemoryInterface.loadString(name_ptr, name_len);
|
|
|
+ let element = document.getElementById(id);
|
|
|
+ if (element == undefined) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- if (this.queries.indexOf(query) !== -1) {
|
|
|
- return query.name;
|
|
|
+
|
|
|
+ let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
|
|
|
+ if (listener == undefined) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- let id = this.getNewId(this.queries);
|
|
|
- query.name = id;
|
|
|
- this.queries[id] = query;
|
|
|
- return id;
|
|
|
+ element.removeEventListener(name, listener);
|
|
|
+ return true;
|
|
|
},
|
|
|
|
|
|
- /* Sampler Objects */
|
|
|
- CreateSampler: () => {
|
|
|
- this.assertWebGL2();
|
|
|
- let sampler = this.ctx.createSampler();
|
|
|
- let id = this.getNewId(this.samplers);
|
|
|
- sampler.name = id;
|
|
|
- this.samplers[id] = sampler;
|
|
|
- return id;
|
|
|
+
|
|
|
+ add_window_event_listener: (name_ptr, name_len, name_code, data, callback, use_capture) => {
|
|
|
+ let name = wasmMemoryInterface.loadString(name_ptr, name_len);
|
|
|
+ let element = window;
|
|
|
+ let listener = (e) => {
|
|
|
+ const odin_ctx = wasmMemoryInterface.exports.default_context_ptr();
|
|
|
+ event_temp_data.id_ptr = 0;
|
|
|
+ event_temp_data.id_len = 0;
|
|
|
+ event_temp_data.event = e;
|
|
|
+ event_temp_data.name_code = name_code;
|
|
|
+ // console.log(e);
|
|
|
+ wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx);
|
|
|
+ };
|
|
|
+ wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
|
|
|
+ element.addEventListener(name, listener, !!use_capture);
|
|
|
+ return true;
|
|
|
},
|
|
|
- DeleteSampler: (id) => {
|
|
|
- this.assertWebGL2();
|
|
|
- let obj = this.samplers[id];
|
|
|
- if (obj && id != 0) {
|
|
|
- this.ctx.deleteSampler(obj);
|
|
|
- this.samplers[id] = null;
|
|
|
+
|
|
|
+ remove_window_event_listener: (name_ptr, name_len, data, callback) => {
|
|
|
+ let name = wasmMemoryInterface.loadString(name_ptr, name_len);
|
|
|
+ let element = window;
|
|
|
+ let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
|
|
|
+ if (listener == undefined) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- },
|
|
|
- IsSampler: (sampler) => {
|
|
|
- this.assertWebGL2();
|
|
|
- return this.ctx.isSampler(this.samplers[sampler]);
|
|
|
- },
|
|
|
- BindSampler: (unit, sampler) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.bindSampler(unit, this.samplers[Sampler]);
|
|
|
- },
|
|
|
- SamplerParameteri: (sampler, pname, param) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.samplerParameteri(this.samplers[sampler], pname, param);
|
|
|
- },
|
|
|
- SamplerParameterf: (sampler, pname, param) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.samplerParameterf(this.samplers[sampler], pname, param);
|
|
|
+ element.removeEventListener(name, listener);
|
|
|
+ return true;
|
|
|
},
|
|
|
|
|
|
- /* Sync objects */
|
|
|
- FenceSync: (condition, flags) => {
|
|
|
- this.assertWebGL2();
|
|
|
- let sync = this.ctx.fenceSync(condition, flags);
|
|
|
- let id = this.getNewId(this.syncs);
|
|
|
- sync.name = id;
|
|
|
- this.syncs[id] = sync;
|
|
|
- return id;
|
|
|
- },
|
|
|
- IsSync: (sync) => {
|
|
|
- this.assertWebGL2();
|
|
|
- return this.ctx.isSync(this.syncs[sync]);
|
|
|
- },
|
|
|
- DeleteSync: (id) => {
|
|
|
- this.assertWebGL2();
|
|
|
- let obj = this.syncs[id];
|
|
|
- if (obj && id != 0) {
|
|
|
- this.ctx.deleteSampler(obj);
|
|
|
- this.syncs[id] = null;
|
|
|
+ event_stop_propagation: () => {
|
|
|
+ if (event_temp_data && event_temp_data.event) {
|
|
|
+ event_temp_data.event.eventStopPropagation();
|
|
|
}
|
|
|
},
|
|
|
- ClientWaitSync: (sync, flags, timeout) => {
|
|
|
- this.assertWebGL2();
|
|
|
- return this.ctx.clientWaitSync(this.syncs[sync], flags, timeout);
|
|
|
+ event_stop_immediate_propagation: () => {
|
|
|
+ if (event_temp_data && event_temp_data.event) {
|
|
|
+ event_temp_data.event.eventStopImmediatePropagation();
|
|
|
+ }
|
|
|
},
|
|
|
- WaitSync: (sync, flags, timeout) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.waitSync(this.syncs[sync], flags, timeout) ;
|
|
|
+ event_prevent_default: () => {
|
|
|
+ if (event_temp_data && event_temp_data.event) {
|
|
|
+ event_temp_data.event.eventPreventDefault();
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
-
|
|
|
- /* Transform Feedback */
|
|
|
- CreateTransformFeedback: () => {
|
|
|
- this.assertWebGL2();
|
|
|
- let transformFeedback = this.ctx.createtransformFeedback();
|
|
|
- let id = this.getNewId(this.transformFeedbacks);
|
|
|
- transformFeedback.name = id;
|
|
|
- this.transformFeedbacks[id] = transformFeedback;
|
|
|
- return id;
|
|
|
+ get_element_value_f64: (id_ptr, id_len) => {
|
|
|
+ let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
+ let element = document.getElementById(id);
|
|
|
+ return element ? element.value : 0;
|
|
|
},
|
|
|
- DeleteTransformFeedback: (id) => {
|
|
|
- this.assertWebGL2();
|
|
|
- let obj = this.transformFeedbacks[id];
|
|
|
- if (obj && id != 0) {
|
|
|
- this.ctx.deleteTransformFeedback(obj);
|
|
|
- this.transformFeedbacks[id] = null;
|
|
|
+ get_element_value_string: (id_ptr, id_len, buf_ptr, buf_len) => {
|
|
|
+ let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
+ let element = document.getElementById(id);
|
|
|
+ if (element) {
|
|
|
+ let str = element.value;
|
|
|
+ if (buf_len > 0 && buf_ptr) {
|
|
|
+ let n = Math.min(buf_len, str.length);
|
|
|
+ str = str.substring(0, n);
|
|
|
+ this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(str))
|
|
|
+ return n;
|
|
|
+ }
|
|
|
}
|
|
|
+ return 0;
|
|
|
},
|
|
|
- IsTransformFeedback: (tf) => {
|
|
|
- this.assertWebGL2();
|
|
|
- return this.ctx.isTransformFeedback(this.transformFeedbacks[tf]);
|
|
|
- },
|
|
|
- BindTransformFeedback: (target, tf) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.bindTransformFeedback(target, this.transformFeedbacks[tf]);
|
|
|
- },
|
|
|
- BeginTransformFeedback: (primitiveMode) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.beginTransformFeedback(primitiveMode);
|
|
|
- },
|
|
|
- EndTransformFeedback: () => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.endTransformFeedback();
|
|
|
- },
|
|
|
- TransformFeedbackVaryings: (program, varyings_ptr, varyings_len, bufferMode) => {
|
|
|
- this.assertWebGL2();
|
|
|
- let varyings = [];
|
|
|
- for (let i = 0; i < varyings_len; i++) {
|
|
|
- let ptr = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 0*4);
|
|
|
- let len = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 1*4);
|
|
|
- varyings.push(this.mem.loadString(ptr, len));
|
|
|
+ get_element_min_max: (ptr_array2_f64, id_ptr, id_len) => {
|
|
|
+ let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
+ let element = document.getElementById(id);
|
|
|
+ if (element) {
|
|
|
+ let values = wasmMemoryInterface.loadF64Array(ptr_array2_f64, 2);
|
|
|
+ values[0] = element.min;
|
|
|
+ values[1] = element.max;
|
|
|
}
|
|
|
- this.ctx.transformFeedbackVaryings(this.programs[program], varyings, bufferMode);
|
|
|
- },
|
|
|
- PauseTransformFeedback: () => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.pauseTransformFeedback();
|
|
|
},
|
|
|
- ResumeTransformFeedback: () => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.resumeTransformFeedback();
|
|
|
+ set_element_value: (id_ptr, id_len, value) => {
|
|
|
+ let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
|
|
+ let element = document.getElementById(id);
|
|
|
+ if (element) {
|
|
|
+ element.value = value;
|
|
|
+ }
|
|
|
},
|
|
|
+ },
|
|
|
+
|
|
|
+ "webgl": webglContext.getWebGL1Interface(),
|
|
|
+ "webgl2": webglContext.getWebGL2Interface(),
|
|
|
+ };
|
|
|
+};
|
|
|
|
|
|
+async function runWasmCanvas(wasmPath, consoleElement, extraForeignImports) {
|
|
|
+ let wasmMemoryInterface = new WasmMemoryInterface();
|
|
|
|
|
|
- /* Uniform Buffer Objects and Transform Feedback Buffers */
|
|
|
- BindBufferBase: (target, index, buffer) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.bindBufferBase(target, index, this.buffers[buffer]);
|
|
|
- },
|
|
|
- BindBufferRange: (target, index, buffer, offset, size) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.bindBufferRange(target, index, this.buffers[buffer], offset, size);
|
|
|
- },
|
|
|
- GetUniformBlockIndex: (program, uniformBlockName_ptr, uniformBlockName_len) => {
|
|
|
- this.assertWebGL2();
|
|
|
- return this.ctx.getUniformBlockIndex(this.programs[program], this.mem.loadString(uniformBlockName_ptr, uniformBlockName_len));
|
|
|
- },
|
|
|
- // any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname);
|
|
|
- GetActiveUniformBlockName: (program, uniformBlockIndex, buf_ptr, buf_len, length_ptr) => {
|
|
|
- this.assertWebGL2();
|
|
|
- let name = this.ctx.getActiveUniformBlockName(this.programs[program], uniformBlockIndex);
|
|
|
+ let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement);
|
|
|
+ let exports = {};
|
|
|
|
|
|
- let n = Math.min(buf_len, name.length);
|
|
|
- name = name.substring(0, n);
|
|
|
- this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(name))
|
|
|
- this.mem.storeInt(length_ptr, n);
|
|
|
- },
|
|
|
- UniformBlockBinding: (program, uniformBlockIndex, uniformBlockBinding) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.uniformBlockBinding(this.programs[program], uniformBlockIndex, uniformBlockBinding);
|
|
|
- },
|
|
|
+ if (extraForeignImports !== undefined) {
|
|
|
+ imports = {
|
|
|
+ ...imports,
|
|
|
+ ...extraForeignImports,
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
- /* Vertex Array Objects */
|
|
|
- CreateVertexArray: () => {
|
|
|
- this.assertWebGL2();
|
|
|
- let vao = this.ctx.createVertexArray();
|
|
|
- let id = this.getNewId(this.vaos);
|
|
|
- vao.name = id;
|
|
|
- this.vaos[id] = vao;
|
|
|
- return id;
|
|
|
- },
|
|
|
- DeleteVertexArray: (id) => {
|
|
|
- this.assertWebGL2();
|
|
|
- let obj = this.vaos[id];
|
|
|
- if (obj && id != 0) {
|
|
|
- this.ctx.deleteVertexArray(obj);
|
|
|
- this.vaos[id] = null;
|
|
|
- }
|
|
|
- },
|
|
|
- IsVertexArray: (vertexArray) => {
|
|
|
- this.assertWebGL2();
|
|
|
- return this.ctx.isVertexArray(this.vaos[vertexArray]);
|
|
|
- },
|
|
|
- BindVertexArray: (vertexArray) => {
|
|
|
- this.assertWebGL2();
|
|
|
- this.ctx.bindVertexArray(this.vaos[vertexArray]);
|
|
|
- },
|
|
|
+ const response = await fetch(wasmPath);
|
|
|
+ const file = await response.arrayBuffer();
|
|
|
+ const wasm = await WebAssembly.instantiate(file, imports);
|
|
|
+ exports = wasm.instance.exports;
|
|
|
+ wasmMemoryInterface.setExports(exports);
|
|
|
+ wasmMemoryInterface.setMemory(exports.memory);
|
|
|
+
|
|
|
+ exports._start();
|
|
|
+
|
|
|
+ if (exports.step) {
|
|
|
+ const odin_ctx = exports.default_context_ptr();
|
|
|
+
|
|
|
+ let prevTimeStamp = undefined;
|
|
|
+ const step = (currTimeStamp) => {
|
|
|
+ if (prevTimeStamp == undefined) {
|
|
|
+ prevTimeStamp = currTimeStamp;
|
|
|
+ }
|
|
|
+
|
|
|
+ const dt = (currTimeStamp - prevTimeStamp)*0.001;
|
|
|
+ prevTimeStamp = currTimeStamp;
|
|
|
+ exports.step(dt, odin_ctx);
|
|
|
+ window.requestAnimationFrame(step);
|
|
|
};
|
|
|
+
|
|
|
+ window.requestAnimationFrame(step);
|
|
|
}
|
|
|
+
|
|
|
+ exports._end();
|
|
|
+
|
|
|
+ return;
|
|
|
};
|
|
|
|
|
|
+window.odin = {
|
|
|
+ // Interface Types
|
|
|
+ WasmMemoryInterface: WasmMemoryInterface,
|
|
|
+ WebGLInterface: WebGLInterface,
|
|
|
|
|
|
-export {WasmMemoryInterface, odinSetupDefaultImports, WebGLInterface};
|
|
|
+ // Functions
|
|
|
+ setupDefaultImports: odinSetupDefaultImports,
|
|
|
+ runWasmCanvas: runWasmCanvas,
|
|
|
+};
|
|
|
+})();
|