summaryrefslogtreecommitdiff
path: root/2d/_collisions
diff options
context:
space:
mode:
Diffstat (limited to '2d/_collisions')
-rw-r--r--2d/_collisions/polygon_polygon.html23
-rw-r--r--2d/_collisions/polygon_polygon.html.content23
-rw-r--r--2d/_collisions/polygon_polygon/dist/output.js313
-rwxr-xr-x2d/_collisions/polygon_polygon/dist/output.wasmbin56585 -> 57485 bytes
-rw-r--r--2d/_collisions/polygon_polygon/main.cpp77
5 files changed, 292 insertions, 144 deletions
diff --git a/2d/_collisions/polygon_polygon.html b/2d/_collisions/polygon_polygon.html
index ce26f62..e8fe7ea 100644
--- a/2d/_collisions/polygon_polygon.html
+++ b/2d/_collisions/polygon_polygon.html
@@ -93,18 +93,35 @@
<p>
Given two polygons <b>A</b> and <b>B</b>:
<ol>
- <li>For each edge on A, get the normal <i>n</i> of that edge.</li>
+ <li>For each edge on <b>A</b>, get the normal <i>n</i> of that edge.</li>
<li>Project each vertex <i>v</i> of <b>A</b> onto <i>n</i>. Return the minimum and maximum projection of all vertices.</li>
<li>Repeat Step 2 for polygon <b>B</b>.</li>
<li>If the min and max projections found in Steps 2 and 3 do <b>NOT</b> overlap, the polygons are not intersecting. Return false.</li>
<li>If the projections overlap for each edge of both shapes, the shapes are intersecting. Return true.</li>
</ol>
+
+ And that is all there is to <i>finding</i> the intersection between two convex polygons.
+ </p>
+ </section>
+ <section>
+ <h2>SAT Collision Resolution</h2>
+ <p>
+ Now that we know our objects have intersecting, we want to be able to send them tumbling away from each other to simulate a collision. To do this, we will need to find the following things:
+ <ul>
+ <li><b>Collision Normal</b>: in what direction, point towards object <b>A</b>, did the polygons intersect</li>
+ <li><b>Point of Application</b>: at what point on each object did the objects first intersect</li>
+ <li><b>Relative Velocity</b>: easily found by taking the difference between the two velocities.
+ </ul>
+
+ <h3>Collision Normal</h3>
+ <p>
+
+ </p>
</p>
-
</section>
<section>
<h2>
- Live Example
+ Live Example of Intersection Detection
</h2>
<div class="opengl_canvas_container">
<canvas id="gl_canvas" width="800" height="600"></canvas>
diff --git a/2d/_collisions/polygon_polygon.html.content b/2d/_collisions/polygon_polygon.html.content
index 2d6f6b7..ffa9b15 100644
--- a/2d/_collisions/polygon_polygon.html.content
+++ b/2d/_collisions/polygon_polygon.html.content
@@ -41,18 +41,35 @@
<p>
Given two polygons <b>A</b> and <b>B</b>:
<ol>
- <li>For each edge on A, get the normal <i>n</i> of that edge.</li>
+ <li>For each edge on <b>A</b>, get the normal <i>n</i> of that edge.</li>
<li>Project each vertex <i>v</i> of <b>A</b> onto <i>n</i>. Return the minimum and maximum projection of all vertices.</li>
<li>Repeat Step 2 for polygon <b>B</b>.</li>
<li>If the min and max projections found in Steps 2 and 3 do <b>NOT</b> overlap, the polygons are not intersecting. Return false.</li>
<li>If the projections overlap for each edge of both shapes, the shapes are intersecting. Return true.</li>
</ol>
+
+ And that is all there is to <i>finding</i> the intersection between two convex polygons.
+ </p>
+ </section>
+ <section>
+ <h2>SAT Collision Resolution</h2>
+ <p>
+ Now that we know our objects have intersecting, we want to be able to send them tumbling away from each other to simulate a collision. To do this, we will need to find the following things:
+ <ul>
+ <li><b>Collision Normal</b>: in what direction, point towards object <b>A</b>, did the polygons intersect</li>
+ <li><b>Point of Application</b>: at what point on each object did the objects first intersect</li>
+ <li><b>Relative Velocity</b>: easily found by taking the difference between the two velocities.
+ </ul>
+
+ <h3>Collision Normal</h3>
+ <p>
+
+ </p>
</p>
-
</section>
<section>
<h2>
- Live Example
+ Live Example of Intersection Detection
</h2>
<div class="opengl_canvas_container">
<canvas id="gl_canvas" width="800" height="600"></canvas>
diff --git a/2d/_collisions/polygon_polygon/dist/output.js b/2d/_collisions/polygon_polygon/dist/output.js
index e1dfee3..42a4ed5 100644
--- a/2d/_collisions/polygon_polygon/dist/output.js
+++ b/2d/_collisions/polygon_polygon/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 on the asm.js/wasm heap 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 in wasm memory 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 an UTF-8 string on the asm.js/wasm heap! (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 a UTF-8 string in wasm memory! (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 (note: assumes compiler arch was little-endian)
+// Endianness check
(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!';
+ 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)';
})();
function abortFnPtrError(ptr, sig) {
@@ -1274,8 +1274,6 @@ 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']) {
@@ -1292,6 +1290,7 @@ function initRuntime() {
checkStackCookie();
assert(!runtimeInitialized);
runtimeInitialized = true;
+
callRuntimeCallbacks(__ATINIT__);
}
@@ -1616,6 +1615,8 @@ 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)
@@ -1637,7 +1638,8 @@ function createWasm() {
function instantiateArrayBuffer(receiver) {
return getBinaryPromise().then(function(binary) {
- return WebAssembly.instantiate(binary, info);
+ var result = WebAssembly.instantiate(binary, info);
+ return result;
}).then(receiver, function(reason) {
err('failed to asynchronously prepare wasm: ' + reason);
@@ -1704,12 +1706,8 @@ 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.
@@ -1760,6 +1758,11 @@ 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']();
@@ -1783,10 +1786,6 @@ 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)
@@ -1800,7 +1799,8 @@ var ASM_CONSTS = {
// anyhow)
}
function _emscripten_resize_heap(requestedSize) {
- var oldSize = _emscripten_get_heap_size();
+ var oldSize = HEAPU8.length;
+ requestedSize = requestedSize >>> 0;
// 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--);
@@ -2074,7 +2074,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:[],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) {
+ 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) {
if (!GL.lastError) {
GL.lastError = errorCode;
}
@@ -2085,7 +2085,7 @@ var ASM_CONSTS = {
}
return ret;
},MAX_TEMP_BUFFER_SIZE:2097152,numTempVertexBuffersPerSize:64,log2ceilLookup:function(i) {
- return 32 - Math.clz32(i-1);
+ return 32 - Math.clz32(i === 0 ? 0 : i - 1);
},generateTempBuffers:function(quads, context) {
var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE);
context.tempVertexBufferCounters1 = [];
@@ -2213,6 +2213,19 @@ 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)
?
@@ -2285,7 +2298,21 @@ var ASM_CONSTS = {
__webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx);
__webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx);
- GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query");
+ // 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");
+ }
+
__webgl_enable_WEBGL_multi_draw(GLctx);
// .getSupportedExtensions() can return null if context is lost, so coerce to empty array.
@@ -2297,49 +2324,6 @@ 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'];
@@ -2386,7 +2370,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;
}
@@ -2450,8 +2434,7 @@ 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) {
@@ -2511,7 +2494,11 @@ 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;
}
@@ -2519,6 +2506,7 @@ var ASM_CONSTS = {
function _glCreateShader(shaderType) {
var id = GL.getNewId(GL.shaders);
GL.shaders[id] = GLctx.createShader(shaderType);
+
return id;
}
@@ -2552,7 +2540,6 @@ var ASM_CONSTS = {
GLctx.deleteProgram(program);
program.name = 0;
GL.programs[id] = null;
- GL.programInfos[id] = null;
}
function _glDeleteShader(id) {
@@ -2645,42 +2632,35 @@ var ASM_CONSTS = {
return;
}
- var ptable = GL.programInfos[program];
- if (!ptable) {
- GL.recordError(0x502 /* GL_INVALID_OPERATION */);
- return;
- }
+ program = GL.programs[program];
if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH
- var log = GLctx.getProgramInfoLog(GL.programs[program]);
+ var log = GLctx.getProgramInfoLog(program);
if (log === null) log = '(unknown error)';
HEAP32[((p)>>2)] = log.length + 1;
} else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) {
- HEAP32[((p)>>2)] = ptable.maxUniformLength;
+ 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;
} else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) {
- if (ptable.maxAttributeLength == -1) {
- program = GL.programs[program];
- var numAttribs = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/);
- ptable.maxAttributeLength = 0; // Spec says if there are no active attribs, 0 must be returned.
- for (var i = 0; i < numAttribs; ++i) {
- var activeAttrib = GLctx.getActiveAttrib(program, i);
- ptable.maxAttributeLength = Math.max(ptable.maxAttributeLength, activeAttrib.name.length+1);
+ 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);
}
}
- HEAP32[((p)>>2)] = ptable.maxAttributeLength;
+ HEAP32[((p)>>2)] = program.maxAttributeLength;
} else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) {
- if (ptable.maxUniformBlockNameLength == -1) {
- program = GL.programs[program];
- var numBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/);
- ptable.maxUniformBlockNameLength = 0;
- for (var i = 0; i < numBlocks; ++i) {
- var activeBlockName = GLctx.getActiveUniformBlockName(program, i);
- ptable.maxUniformBlockNameLength = Math.max(ptable.maxUniformBlockNameLength, activeBlockName.length+1);
+ 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);
}
}
- HEAP32[((p)>>2)] = ptable.maxUniformBlockNameLength;
+ HEAP32[((p)>>2)] = program.maxUniformBlockNameLength;
} else {
- HEAP32[((p)>>2)] = GLctx.getProgramParameter(GL.programs[program], pname);
+ HEAP32[((p)>>2)] = GLctx.getProgramParameter(program, pname);
}
}
@@ -2723,27 +2703,91 @@ 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 (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);
+ 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);
}
- 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;
+ // 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;
+ }
}
+ return -1;
}
function _glLinkProgram(program) {
- GLctx.linkProgram(GL.programs[program]);
- GL.populateUniformTable(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 = {};
+
}
function _glShaderSource(shader, count, string, length) {
@@ -2752,11 +2796,25 @@ 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(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*16);
+ GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*16);
return;
}
@@ -2789,11 +2847,15 @@ var ASM_CONSTS = {
{
var view = HEAPF32.subarray((value)>>2, (value+count*64)>>2);
}
- GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, view);
+ GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view);
}
function _glUseProgram(program) {
- GLctx.useProgram(GL.programs[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;
}
function _glVertexAttribPointer(index, size, type, normalized, stride, ptr) {
@@ -3004,6 +3066,10 @@ 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)") };
@@ -3027,7 +3093,12 @@ 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, "abortStackOverflow")) Module["abortStackOverflow"] = function() { abort("'abortStackOverflow' 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, "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)") };
@@ -3128,6 +3199,7 @@ 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)") };
@@ -3217,7 +3289,6 @@ function callMain(args) {
return;
} else if (e == 'unwind') {
// running an evented main loop, don't immediately exit
- noExitRuntime = true;
return;
} else {
var toLog = e;
@@ -3327,17 +3398,19 @@ 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 && noExitRuntime && status === 0) {
+ if (implicit && keepRuntimeAlive() && status === 0) {
return;
}
- if (noExitRuntime) {
+ if (keepRuntimeAlive()) {
// 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)';
@@ -3345,8 +3418,6 @@ function exit(status, implicit) {
}
} else {
- EXITSTATUS = status;
-
exitRuntime();
if (Module['onExit']) Module['onExit'](status);
diff --git a/2d/_collisions/polygon_polygon/dist/output.wasm b/2d/_collisions/polygon_polygon/dist/output.wasm
index fd9538c..a30f0c4 100755
--- a/2d/_collisions/polygon_polygon/dist/output.wasm
+++ b/2d/_collisions/polygon_polygon/dist/output.wasm
Binary files differ
diff --git a/2d/_collisions/polygon_polygon/main.cpp b/2d/_collisions/polygon_polygon/main.cpp
index 1633530..c458299 100644
--- a/2d/_collisions/polygon_polygon/main.cpp
+++ b/2d/_collisions/polygon_polygon/main.cpp
@@ -11,44 +11,86 @@
#include <cmath>
#include <cfloat>
-struct Rigidbody {
+struct Impulse {
Vector2 force = { 0, 0 };
+ float32 timeOfApplicationSeconds = 0.25f;
+ float32 timeAppliedSeconds = 0.f;
+ bool isDead = false;
+};
+
+const int32 NUM_IMPULSES = 4;
+
+struct Rigidbody {
+ int32 numImpulses = 0;
+ Impulse activeImpulses[NUM_IMPULSES];
Vector2 velocity = { 0, 0 };
Vector2 position = { 0, 0 };
+ float32 mass = 1.f;
float32 rotationalVelocity = 0.f;
float32 rotation = 0.f;
- float32 mass = 1.f;
float32 cofOfRestition = 1.f;
float32 momentOfInertia = 1.f;
void reset() {
- force = { 0, 0 };
+ numImpulses = 0;
velocity = { 0, 0 };
- rotationalVelocity = 0.f;
- rotation = 0.f;
}
- void applyForce(Vector2 f) {
- force += f;
+ void setMomentOfInertia(float32 moi) {
+ momentOfInertia = moi;
+ }
+
+ void applyImpulse(Impulse i) {
+ if (numImpulses > NUM_IMPULSES) {
+ printf("Unable to apply impulse. Buffer full.\n");
+ return;
+ }
+
+ activeImpulses[numImpulses] = i;
+ numImpulses++;
}
void applyGravity(float32 deltaTimeSeconds) {
- velocity += (Vector2 { 0.f, -50.f } * deltaTimeSeconds);
+ velocity += (Vector2 { 0.f, -9.8f } * deltaTimeSeconds);
}
void update(float32 deltaTimeSeconds) {
applyGravity(deltaTimeSeconds);
-
+
+ Vector2 force;
+ for (int32 idx = 0; idx < numImpulses; idx++) {
+ Impulse& i = activeImpulses[idx];
+
+ float32 nextTimeAppliedSeconds = i.timeAppliedSeconds + deltaTimeSeconds;
+ if (nextTimeAppliedSeconds >= i.timeOfApplicationSeconds) {
+ nextTimeAppliedSeconds = i.timeOfApplicationSeconds; // Do the remainder of the time
+ i.isDead = true;
+ }
+
+ float32 impulseDtSeconds = nextTimeAppliedSeconds - i.timeAppliedSeconds;
+ Vector2 forceToApply = i.force * (impulseDtSeconds / i.timeOfApplicationSeconds);
+ force += forceToApply * impulseDtSeconds;
+
+
+ i.timeAppliedSeconds = nextTimeAppliedSeconds;
+ }
+
Vector2 acceleration = force / mass;
velocity += (acceleration * deltaTimeSeconds);
position += (velocity * deltaTimeSeconds);
- force = Vector2 { 0.f, 0.f };
-
rotation += (rotationalVelocity * deltaTimeSeconds);
- }
- void setMomentOfInertia(float32 moi) {
- momentOfInertia = moi;
+ // Cleanup any impulses that have expired in the mean time
+ for (int32 idx = 0; idx < numImpulses; idx++) {
+ if (activeImpulses[idx].isDead) {
+ for (int j = idx + 1; j < numImpulses; j++) {
+ activeImpulses[j - 1] = activeImpulses[j];
+ }
+
+ idx = idx - 1;
+ numImpulses--;
+ }
+ }
}
};
@@ -125,13 +167,14 @@ struct ConvexPolygon {
body.update(dtSeconds);
shape.model = Mat4x4().translateByVec2(body.position).rotate2D(body.rotation);
- // Populate the current position of our edges
+ // Populate the current position of our edges. Note that this might be slow depending
+ // on how many edges your shaped have.
for (int vidx = 0; vidx < numVertices; vidx++) {
Vector2 start = shape.model * originalVertices[vidx];
transformedVertices[vidx] = start;
Vector2 end = shape.model * originalVertices[vidx == numVertices - 1 ? 0 : vidx + 1];
- edges[vidx] = { (end - start).getPerp().normalize(), start, end };
+ edges[vidx] = { (end - start).getPerp(), start, end };
}
}
@@ -287,6 +330,7 @@ IntersectionResult getIntersection(ConvexPolygon* first, ConvexPolygon* second)
if (overlap < minOverlap) {
minOverlap = overlap;
minOverlapEdge = &second->edges[i];
+ minOverlapWasFirst = false;
}
}
@@ -297,7 +341,6 @@ IntersectionResult getIntersection(ConvexPolygon* first, ConvexPolygon* second)
// Time to find just where we intersected
Vector2 closestPoint;
float32 minDistance = FLT_MAX;
-
for (int p = 0; p < (minOverlapWasFirst ? second->numVertices : first->numVertices); p++) {
Vector2 point = minOverlapWasFirst ? second->transformedVertices[p] : first->transformedVertices[p];