From c5e5d7c939a2640763775b0688ed698c51ec28c1 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Mon, 24 May 2021 13:32:27 -0400 Subject: Applying force to the object using the mouse cursor --- 2d/rigidbody/rigidbody_1/dist/output.js | 319 ++++++++++++------------------ 2d/rigidbody/rigidbody_1/dist/output.wasm | Bin 33767 -> 47023 bytes 2d/rigidbody/rigidbody_1/main.cpp | 113 ++++++++++- 3 files changed, 236 insertions(+), 196 deletions(-) (limited to '2d/rigidbody/rigidbody_1') diff --git a/2d/rigidbody/rigidbody_1/dist/output.js b/2d/rigidbody/rigidbody_1/dist/output.js index 42a4ed5..9ba383a 100644 --- a/2d/rigidbody/rigidbody_1/dist/output.js +++ b/2d/rigidbody/rigidbody_1/dist/output.js @@ -609,7 +609,7 @@ if (typeof WebAssembly !== 'object') { function setValue(ptr, value, type, noSafe) { type = type || 'i8'; if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit - switch (type) { + switch(type) { case 'i1': HEAP8[((ptr)>>0)] = value; break; case 'i8': HEAP8[((ptr)>>0)] = value; break; case 'i16': HEAP16[((ptr)>>1)] = value; break; @@ -627,7 +627,7 @@ function setValue(ptr, value, type, noSafe) { function getValue(ptr, type, noSafe) { type = type || 'i8'; if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit - switch (type) { + switch(type) { case 'i1': return HEAP8[((ptr)>>0)]; case 'i8': return HEAP8[((ptr)>>0)]; case 'i16': return HEAP16[((ptr)>>1)]; @@ -808,7 +808,7 @@ function UTF8ArrayToString(heap, idx, maxBytesToRead) { 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 in wasm memory to a JS string!'); + 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); } @@ -884,7 +884,7 @@ function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { 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 a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x1FFFFF).'); + 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); @@ -1252,12 +1252,12 @@ function checkStackCookie() { // include: runtime_assertions.js -// Endianness check +// 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! (Run with -s SUPPORT_BIG_ENDIAN=1 to bypass)'; + if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian!'; })(); function abortFnPtrError(ptr, sig) { @@ -1274,6 +1274,8 @@ 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']) { @@ -1290,7 +1292,6 @@ function initRuntime() { checkStackCookie(); assert(!runtimeInitialized); runtimeInitialized = true; - callRuntimeCallbacks(__ATINIT__); } @@ -1615,8 +1616,6 @@ function createWasm() { wasmTable = Module['asm']['__indirect_function_table']; assert(wasmTable, "table not found in wasm exports"); - addOnInit(Module['asm']['__wasm_call_ctors']); - removeRunDependency('wasm-instantiate'); } // we can't run yet (except in a pthread, where we have a custom sync instantiator) @@ -1638,8 +1637,7 @@ function createWasm() { function instantiateArrayBuffer(receiver) { return getBinaryPromise().then(function(binary) { - var result = WebAssembly.instantiate(binary, info); - return result; + return WebAssembly.instantiate(binary, info); }).then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason); @@ -1706,8 +1704,12 @@ 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) { + while(callbacks.length > 0) { var callback = callbacks.shift(); if (typeof callback == 'function') { callback(Module); // Pass the module as the first argument. @@ -1758,11 +1760,6 @@ var ASM_CONSTS = { return error.stack.toString(); } - var runtimeKeepaliveCounter=0; - function keepRuntimeAlive() { - return noExitRuntime || runtimeKeepaliveCounter > 0; - } - function stackTrace() { var js = jsStackTrace(); if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); @@ -1786,6 +1783,10 @@ var ASM_CONSTS = { 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) @@ -1799,8 +1800,7 @@ var ASM_CONSTS = { // anyhow) } function _emscripten_resize_heap(requestedSize) { - var oldSize = HEAPU8.length; - requestedSize = requestedSize >>> 0; + 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); @@ -1827,7 +1827,7 @@ var ASM_CONSTS = { // 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) { + 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 ); @@ -1845,7 +1845,7 @@ var ASM_CONSTS = { } var JSEvents={inEventHandler:0,removeAllEventListeners:function() { - for (var i = JSEvents.eventHandlers.length-1; i >= 0; --i) { + for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) { JSEvents._removeHandler(i); } JSEvents.eventHandlers = []; @@ -1859,13 +1859,13 @@ var ASM_CONSTS = { function arraysHaveEqualContent(arrA, arrB) { if (arrA.length != arrB.length) return false; - for (var i in arrA) { + 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) { + for(var i in JSEvents.deferredCalls) { var call = JSEvents.deferredCalls[i]; if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { return; @@ -1879,7 +1879,7 @@ var ASM_CONSTS = { JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; }); },removeDeferredCalls:function(targetFunction) { - for (var i = 0; i < JSEvents.deferredCalls.length; ++i) { + for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { if (JSEvents.deferredCalls[i].targetFunction == targetFunction) { JSEvents.deferredCalls.splice(i, 1); --i; @@ -1891,14 +1891,14 @@ var ASM_CONSTS = { if (!JSEvents.canPerformEventHandlerRequests()) { return; } - for (var i = 0; i < JSEvents.deferredCalls.length; ++i) { + 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) { + for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { if (JSEvents.eventHandlers[i].target == target && (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) { JSEvents._removeHandler(i--); @@ -1929,7 +1929,7 @@ var ASM_CONSTS = { JSEvents.eventHandlers.push(eventHandler); JSEvents.registerRemoveEventListeners(); } else { - for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { + 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--); @@ -2028,6 +2028,11 @@ var ASM_CONSTS = { return 0; } + function _emscripten_set_mousemove_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { + registerMouseEventCallback(target, userData, useCapture, callbackfunc, 8, "mousemove", 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'); @@ -2074,7 +2079,7 @@ var ASM_CONSTS = { // 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:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode) { + 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; } @@ -2085,7 +2090,7 @@ var ASM_CONSTS = { } return ret; },MAX_TEMP_BUFFER_SIZE:2097152,numTempVertexBuffersPerSize:64,log2ceilLookup:function(i) { - return 32 - Math.clz32(i === 0 ? 0 : i - 1); + return 32 - Math.clz32(i-1); },generateTempBuffers:function(quads, context) { var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE); context.tempVertexBufferCounters1 = []; @@ -2213,19 +2218,6 @@ var ASM_CONSTS = { } },createContext:function(canvas, webGLContextAttributes) { - // BUG: Workaround Safari WebGL issue: After successfully acquiring WebGL context on a canvas, - // calling .getContext() will always return that context independent of which 'webgl' or 'webgl2' - // context version was passed. See https://bugs.webkit.org/show_bug.cgi?id=222758 and - // https://github.com/emscripten-core/emscripten/issues/13295. - // TODO: Once the bug is fixed and shipped in Safari, adjust the Safari version field in above check. - if (!canvas.getContextSafariWebGL2Fixed) { - canvas.getContextSafariWebGL2Fixed = canvas.getContext; - canvas.getContext = function(ver, attrs) { - var gl = canvas.getContextSafariWebGL2Fixed(ver, attrs); - return ((ver == 'webgl') == (gl instanceof WebGLRenderingContext)) ? gl : null; - } - } - var ctx = (webGLContextAttributes.majorVersion > 1) ? @@ -2298,21 +2290,7 @@ var ASM_CONSTS = { __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx); - // On WebGL 2, EXT_disjoint_timer_query is replaced with an alternative - // that's based on core APIs, and exposes only the queryCounterEXT() - // entrypoint. - if (context.version >= 2) { - GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query_webgl2"); - } - - // However, Firefox exposes the WebGL 1 version on WebGL 2 as well and - // thus we look for the WebGL 1 version again if the WebGL 2 version - // isn't present. https://bugzilla.mozilla.org/show_bug.cgi?id=1328882 - if (context.version < 2 || !GLctx.disjointTimerQueryExt) - { - GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); - } - + 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. @@ -2324,6 +2302,49 @@ var ASM_CONSTS = { 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']; @@ -2370,7 +2391,7 @@ var ASM_CONSTS = { function _emscripten_webgl_init_context_attributes(attributes) { assert(attributes); var a = attributes >> 2; - for (var i = 0; i < (56>>2); ++i) { + for(var i = 0; i < (56>>2); ++i) { HEAP32[a+i] = 0; } @@ -2434,7 +2455,8 @@ var ASM_CONSTS = { } function _glAttachShader(program, shader) { - GLctx.attachShader(GL.programs[program], GL.shaders[shader]); + GLctx.attachShader(GL.programs[program], + GL.shaders[shader]); } function _glBindBuffer(target, buffer) { @@ -2494,11 +2516,7 @@ var ASM_CONSTS = { function _glCreateProgram() { var id = GL.getNewId(GL.programs); var program = GLctx.createProgram(); - // Store additional information needed for each shader program: program.name = id; - // Lazy cache results of glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH/GL_ACTIVE_ATTRIBUTE_MAX_LENGTH/GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH) - program.maxUniformLength = program.maxAttributeLength = program.maxUniformBlockNameLength = 0; - program.uniformIdCounter = 1; GL.programs[id] = program; return id; } @@ -2506,7 +2524,6 @@ var ASM_CONSTS = { function _glCreateShader(shaderType) { var id = GL.getNewId(GL.shaders); GL.shaders[id] = GLctx.createShader(shaderType); - return id; } @@ -2540,6 +2557,7 @@ var ASM_CONSTS = { GLctx.deleteProgram(program); program.name = 0; GL.programs[id] = null; + GL.programInfos[id] = null; } function _glDeleteShader(id) { @@ -2632,35 +2650,42 @@ var ASM_CONSTS = { return; } - program = GL.programs[program]; + 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(program); + 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 */) { - if (!program.maxUniformLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) { - program.maxUniformLength = Math.max(program.maxUniformLength, GLctx.getActiveUniform(program, i).name.length+1); - } - } - HEAP32[((p)>>2)] = program.maxUniformLength; + HEAP32[((p)>>2)] = ptable.maxUniformLength; } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { - if (!program.maxAttributeLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); ++i) { - program.maxAttributeLength = Math.max(program.maxAttributeLength, GLctx.getActiveAttrib(program, i).name.length+1); + 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)] = program.maxAttributeLength; + HEAP32[((p)>>2)] = ptable.maxAttributeLength; } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) { - if (!program.maxUniformBlockNameLength) { - for (var i = 0; i < GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); ++i) { - program.maxUniformBlockNameLength = Math.max(program.maxUniformBlockNameLength, GLctx.getActiveUniformBlockName(program, i).length+1); + 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)] = program.maxUniformBlockNameLength; + HEAP32[((p)>>2)] = ptable.maxUniformBlockNameLength; } else { - HEAP32[((p)>>2)] = GLctx.getProgramParameter(program, pname); + HEAP32[((p)>>2)] = GLctx.getProgramParameter(GL.programs[program], pname); } } @@ -2703,91 +2728,27 @@ var ASM_CONSTS = { return parseInt(str); } function _glGetUniformLocation(program, name) { - // Returns the index of '[' character in an uniform that represents an array of uniforms (e.g. colors[10]) - // Closure does counterproductive inlining: https://github.com/google/closure-compiler/issues/3203, so prevent - // inlining manually. - /** @noinline */ - function getLeftBracePos(name) { - return name.slice(-1) == ']' && name.lastIndexOf('['); - } - name = UTF8ToString(name); - program = GL.programs[program]; - var uniformLocsById = program.uniformLocsById; // Maps GLuint -> WebGLUniformLocation - var uniformSizeAndIdsByName = program.uniformSizeAndIdsByName; // Maps name -> [uniform array length, GLuint] - var i, j; var arrayIndex = 0; - var uniformBaseName = name; - - // Invariant: when populating integer IDs for uniform locations, we must maintain the precondition that - // arrays reside in contiguous addresses, i.e. for a 'vec4 colors[10];', colors[4] must be at location colors[0]+4. - // However, user might call glGetUniformLocation(program, "colors") for an array, so we cannot discover based on the user - // input arguments whether the uniform we are dealing with is an array. The only way to discover which uniforms are arrays - // is to enumerate over all the active uniforms in the program. - var leftBrace = getLeftBracePos(name); - - // On the first time invocation of glGetUniformLocation on this shader program: - // initialize cache data structures and discover which uniforms are arrays. - if (!uniformLocsById) { - // maps GLint integer locations to WebGLUniformLocations - program.uniformLocsById = uniformLocsById = {}; - // maps integer locations back to uniform name strings, so that we can lazily fetch uniform array locations - program.uniformArrayNamesById = {}; - - for (i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) { - var u = GLctx.getActiveUniform(program, i); - var nm = u.name; - var sz = u.size; - var lb = getLeftBracePos(nm); - var arrayName = lb > 0 ? nm.slice(0, lb) : nm; - - // Assign a new location. - var id = program.uniformIdCounter; - program.uniformIdCounter += sz; - // Eagerly get the location of the uniformArray[0] base element. - // The remaining indices >0 will be left for lazy evaluation to - // improve performance. Those may never be needed to fetch, if the - // application fills arrays always in full starting from the first - // element of the array. - uniformSizeAndIdsByName[arrayName] = [sz, id]; - - // Store placeholder integers in place that highlight that these - // >0 index locations are array indices pending population. - for(j = 0; j < sz; ++j) { - uniformLocsById[id] = j; - program.uniformArrayNamesById[id++] = arrayName; - } - } - } - // If user passed an array accessor "[index]", parse the array index off the accessor. - if (leftBrace > 0) { - arrayIndex = jstoi_q(name.slice(leftBrace + 1)) >>> 0; // "index]", coerce parseInt(']') with >>>0 to treat "foo[]" as "foo[0]" and foo[-1] as unsigned out-of-bounds. - uniformBaseName = name.slice(0, leftBrace); + 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); } - // Have we cached the location of this uniform before? - var sizeAndId = uniformSizeAndIdsByName[uniformBaseName]; // A pair [array length, GLint of the uniform location] - - // If an uniform with this name exists, and if its index is within the array limits (if it's even an array), - // query the WebGLlocation, or return an existing cached location. - if (sizeAndId && arrayIndex < sizeAndId[0]) { - arrayIndex += sizeAndId[1]; // Add the base location of the uniform to the array index offset. - if ((uniformLocsById[arrayIndex] = uniformLocsById[arrayIndex] || GLctx.getUniformLocation(program, name))) { - return arrayIndex; - } + 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; } - return -1; } function _glLinkProgram(program) { - program = GL.programs[program]; - GLctx.linkProgram(program); - // Invalidate earlier computed uniform->ID mappings, those have now become stale - program.uniformLocsById = 0; // Mark as null-like so that glGetUniformLocation() knows to populate this again. - program.uniformSizeAndIdsByName = {}; - + GLctx.linkProgram(GL.programs[program]); + GL.populateUniformTable(program); } function _glShaderSource(shader, count, string, length) { @@ -2796,25 +2757,11 @@ var ASM_CONSTS = { GLctx.shaderSource(GL.shaders[shader], source); } - function webglGetUniformLocation(location) { - var p = GLctx.currentProgram; - var webglLoc = p.uniformLocsById[location]; - // p.uniformLocsById[location] stores either an integer, or a WebGLUniformLocation. - - // If an integer, we have not yet bound the location, so do it now. The integer value specifies the array index - // we should bind to. - if (webglLoc >= 0) { - p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? '[' + webglLoc + ']' : '')); - } - // Else an already cached WebGLUniformLocation, return it. - return webglLoc; - } - 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(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*16); + GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*16); return; } @@ -2847,15 +2794,11 @@ var ASM_CONSTS = { { var view = HEAPF32.subarray((value)>>2, (value+count*64)>>2); } - GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view); + GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, view); } function _glUseProgram(program) { - program = GL.programs[program]; - GLctx.useProgram(program); - // Record the currently active program so that we can access the uniform - // mapping table of that program. - GLctx.currentProgram = program; + GLctx.useProgram(GL.programs[program]); } function _glVertexAttribPointer(index, size, type, normalized, stride, ptr) { @@ -2921,6 +2864,7 @@ var asmLibraryArg = { "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_set_mousemove_callback_on_thread": _emscripten_set_mousemove_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, @@ -3066,10 +3010,6 @@ if (!Object.getOwnPropertyDescriptor(Module, "ENV")) Module["ENV"] = function() 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, "inetPton4")) Module["inetPton4"] = function() { abort("'inetPton4' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "inetNtop4")) Module["inetNtop4"] = function() { abort("'inetNtop4' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "inetPton6")) Module["inetPton6"] = function() { abort("'inetPton6' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "inetNtop6")) Module["inetNtop6"] = function() { abort("'inetNtop6' 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)") }; @@ -3093,12 +3033,7 @@ if (!Object.getOwnPropertyDescriptor(Module, "dynCallLegacy")) Module["dynCallLe 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, "runtimeKeepaliveCounter")) Module["runtimeKeepaliveCounter"] = function() { abort("'runtimeKeepaliveCounter' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "keepRuntimeAlive")) Module["keepRuntimeAlive"] = function() { abort("'keepRuntimeAlive' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "runtimeKeepalivePush")) Module["runtimeKeepalivePush"] = function() { abort("'runtimeKeepalivePush' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "runtimeKeepalivePop")) Module["runtimeKeepalivePop"] = function() { abort("'runtimeKeepalivePop' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "callUserCallback")) Module["callUserCallback"] = function() { abort("'callUserCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; -if (!Object.getOwnPropertyDescriptor(Module, "maybeExit")) Module["maybeExit"] = function() { abort("'maybeExit' 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)") }; @@ -3199,7 +3134,6 @@ if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGet")) Module["emsc 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, "webglGetUniformLocation")) Module["webglGetUniformLocation"] = function() { abort("'webglGetUniformLocation' 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)") }; @@ -3289,6 +3223,7 @@ function callMain(args) { return; } else if (e == 'unwind') { // running an evented main loop, don't immediately exit + noExitRuntime = true; return; } else { var toLog = e; @@ -3398,19 +3333,17 @@ function checkUnflushedContent() { /** @param {boolean|number=} implicit */ function exit(status, implicit) { - EXITSTATUS = status; - 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 && keepRuntimeAlive() && status === 0) { + if (implicit && noExitRuntime && status === 0) { return; } - if (keepRuntimeAlive()) { + 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)'; @@ -3418,6 +3351,8 @@ function exit(status, implicit) { } } else { + EXITSTATUS = status; + exitRuntime(); if (Module['onExit']) Module['onExit'](status); diff --git a/2d/rigidbody/rigidbody_1/dist/output.wasm b/2d/rigidbody/rigidbody_1/dist/output.wasm index 2a9924f..c091140 100755 Binary files a/2d/rigidbody/rigidbody_1/dist/output.wasm and b/2d/rigidbody/rigidbody_1/dist/output.wasm differ diff --git a/2d/rigidbody/rigidbody_1/main.cpp b/2d/rigidbody/rigidbody_1/main.cpp index 26d24e2..8d4ab0e 100644 --- a/2d/rigidbody/rigidbody_1/main.cpp +++ b/2d/rigidbody/rigidbody_1/main.cpp @@ -43,6 +43,8 @@ struct Rigidbody { struct Rectangle { OrthographicShape shape; Rigidbody body; + Vector2 originalPoints[4]; + Vector2 transformedPoints[4]; void load(OrthographicRenderer* renderer, Vector4 color, float32 width, float32 height) { color = color.toNormalizedColor(); @@ -62,6 +64,11 @@ struct Rectangle { vertices[i].color = color; } + originalPoints[0] = vertices[0].position; + originalPoints[1] = vertices[1].position; + originalPoints[2] = vertices[2].position; + originalPoints[3] = vertices[4].position; + shape.load(vertices, 6, renderer); body.reset(); } @@ -69,6 +76,54 @@ struct Rectangle { void update(float32 dtSeconds) { body.update(dtSeconds); shape.model = Mat4x4().translateByVec2(body.position); + + // Note: This helps us check if the cursor is within the bounds of the + // rectangle later on. + for (int idx = 0; idx < 4; idx++) { + transformedPoints[idx] = shape.model * originalPoints[idx]; + } + } + + void render(OrthographicRenderer* renderer) { + shape.render(renderer); + } + + void unload() { + shape.unload(); + } +}; + +struct Circle { + OrthographicShape shape; + Rigidbody body; + + float32 radius = 5.f; + + void load(OrthographicRenderer* renderer, Vector4 color) { + const int32 numSegments = 36; + const float32 radiansPerSegment = (2.f * PI) / static_cast(numSegments); + const int32 numVertices = numSegments * 3; + + color = color.toNormalizedColor(); + + OrthographicVertex vertices[numSegments * 3]; + for (int idx = 0; idx < numSegments; idx++) { + int vIdx = idx * 3; + + vertices[vIdx].color = color; + vertices[vIdx].position = Vector2 { radius * cosf(radiansPerSegment * idx), radius * sinf(radiansPerSegment * idx) }; + vertices[vIdx + 1].color = color; + vertices[vIdx + 1].position = Vector2 { 0.f, 0.f }; + vertices[vIdx + 2].color = color; + vertices[vIdx + 2].position = Vector2 { radius * cosf(radiansPerSegment * (idx + 1)), radius * sinf(radiansPerSegment * (idx + 1)) }; + } + + shape.load(vertices, numVertices, renderer); + body.reset(); + } + + void update(float32 dtSeconds) { + shape.model = Mat4x4().translateByVec2(body.position); } void render(OrthographicRenderer* renderer) { @@ -82,6 +137,7 @@ struct Rectangle { EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL onMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); void load(); void update(float32 time, void* userData); @@ -91,11 +147,15 @@ WebglContext context; OrthographicRenderer renderer; MainLoop mainLoop; Rectangle rectangle; +Circle pointer; +bool isIntersectingPointer = false; 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); + emscripten_set_mousemove_callback("#gl_canvas", NULL, false, onMouseMove); + return 0; } @@ -106,11 +166,40 @@ void load() { rectangle.body.position = Vector2 { context.width / 3.f, context.height / 3.f }; rectangle.body.velocity = Vector2 { 100.f, 250.f }; + pointer.load(&renderer, Vector4 { 25.f, 235.f, 235.f, 255.f }); + mainLoop.run(update); } +bool isPointInRectangle(Vector2 p, Rectangle r) { + // https://math.stackexchange.com/a/190373 + Vector2 A = r.transformedPoints[0]; + Vector2 B = r.transformedPoints[1]; + Vector2 D = r.transformedPoints[3]; + + float32 amDotAb = (p - A).dot(B - A); + float32 abDotAb = (B - A).dot(B - A); + + float32 amDotAd = (p - A).dot(D - A); + float32 aDDotAd = (D - A).dot(D - A); + + return amDotAb > 0 && amDotAb < abDotAb && amDotAd > 0 && amDotAd < aDDotAd; + +} + void update(float32 deltaTimeSeconds, void* userData) { rectangle.update(deltaTimeSeconds); + pointer.update(deltaTimeSeconds); + + if (isPointInRectangle(pointer.body.position, rectangle)) { + if (!isIntersectingPointer) { + isIntersectingPointer = true; + rectangle.body.force += pointer.body.force; + } + } else if (isIntersectingPointer) { + isIntersectingPointer = false; + } + // Check collisions with walls so we don't go out of the scene. Simply reflect here. if (rectangle.body.position.x <= 0.f) { @@ -121,24 +210,26 @@ void update(float32 deltaTimeSeconds, void* userData) { rectangle.body.position.y = 0.f; rectangle.body.velocity = rectangle.body.velocity - Vector2 { 0.f, 1.f } * (2 * (rectangle.body.velocity.dot(Vector2 { 0.f, 1.f }))); } - if (rectangle.body.position.x >= 640.f) { - rectangle.body.position.x = 640.f; + if (rectangle.body.position.x >= 800.f) { + rectangle.body.position.x = 800.f; rectangle.body.velocity = rectangle.body.velocity - Vector2 { -1.f, 0.f } * (2 * (rectangle.body.velocity.dot(Vector2{ -1.f, 0.f }))); } - if (rectangle.body.position.y >= 480.f) { - rectangle.body.position.y = 480.f; + if (rectangle.body.position.y >= 600.f) { + rectangle.body.position.y = 600.f; rectangle.body.velocity = rectangle.body.velocity - Vector2 { 0.f, -1.f } * (2 * (rectangle.body.velocity.dot(Vector2 { 0.f, -1.f }))) ; } // Renderer renderer.render(); rectangle.render(&renderer); + pointer.render(&renderer); } void unload() { mainLoop.stop(); renderer.unload(); rectangle.unload(); + pointer.unload(); } // @@ -156,3 +247,17 @@ EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, voi unload(); return true; } + +EM_BOOL onMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) { + if (!mainLoop.isRunning) { + return true; + } + + pointer.body.force.x = static_cast(mouseEvent->movementX) * 1000.f; + pointer.body.force.y = static_cast(-mouseEvent->movementY) * 1000.f; + + pointer.body.position.x = static_cast(mouseEvent->targetX); + pointer.body.position.y = static_cast(600.f - mouseEvent->targetY); + + return true; +} -- cgit v1.2.1