summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rwxr-xr-x2d/rigidbody/rigidbody_2/dist/output.wasmbin48126 -> 50226 bytes
-rw-r--r--2d/rigidbody/rigidbody_2/main.cpp80
-rw-r--r--2d/rigidbody/rigidbody_3.html2
-rw-r--r--2d/rigidbody/rigidbody_3.html.content2
-rwxr-xr-x2d/rigidbody/rigidbody_3/dist/output.wasmbin52832 -> 49458 bytes
-rw-r--r--2d/rigidbody/rigidbody_3/main.cpp366
11 files changed, 513 insertions, 373 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];
diff --git a/2d/rigidbody/rigidbody_2/dist/output.wasm b/2d/rigidbody/rigidbody_2/dist/output.wasm
index f7dcbd9..29c319a 100755
--- a/2d/rigidbody/rigidbody_2/dist/output.wasm
+++ b/2d/rigidbody/rigidbody_2/dist/output.wasm
Binary files differ
diff --git a/2d/rigidbody/rigidbody_2/main.cpp b/2d/rigidbody/rigidbody_2/main.cpp
index 5f875b8..bad6949 100644
--- a/2d/rigidbody/rigidbody_2/main.cpp
+++ b/2d/rigidbody/rigidbody_2/main.cpp
@@ -11,46 +11,90 @@
#include <cmath>
#include <cfloat>
+struct Impulse {
+ Vector2 force = { 0, 0 };
+ Vector2 pointOfApplication = { 0, 0 };
+ float32 timeOfApplicationSeconds = 0.25f;
+ float32 timeAppliedSeconds = 0.f;
+ bool isDead = false;
+};
+
+const int32 NUM_IMPULSES = 4;
+
struct Rigidbody {
- Vector2 linearForce = { 0, 0 };
+ int32 numImpulses = 0;
+ Impulse activeImpulses[NUM_IMPULSES];
Vector2 velocity = { 0, 0 };
Vector2 position = { 0, 0 };
float32 mass = 1.f;
- float32 torque = 0.f;
float32 rotationalVelocity = 0.f;
float32 rotation = 0.f;
float32 momentOfInertia = 1.f;
void reset() {
- linearForce = { 0, 0 };
+ numImpulses = 0;
velocity = { 0, 0 };
rotationalVelocity = 0.f;
rotation = 0.f;
}
- void applyForce(Vector2 force, Vector2 pointOfApplication) {
- linearForce += force;
- torque += pointOfApplication.getPerp().dot(force);
+ void applyImpulse(Impulse i) {
+ if (numImpulses > NUM_IMPULSES) {
+ printf("Unable to apply impulse. Buffer full.\n");
+ return;
+ }
+
+ activeImpulses[numImpulses] = i;
+ numImpulses++;
}
- void applyGravity() {
- applyForce(Vector2 { 0.f, -100.f }, Vector2 { 0.f, 0.f });
+ void applyGravity(float32 deltaTimeSeconds) {
+ velocity += (Vector2 { 0.f, -9.8f } * deltaTimeSeconds);
}
void update(float32 deltaTimeSeconds) {
- applyGravity();
+ applyGravity(deltaTimeSeconds);
+
+ Vector2 force;
+ float32 torque = 0.f;
+ 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;
+ torque += i.pointOfApplication.getPerp().dot(forceToApply);
+
+ i.timeAppliedSeconds = nextTimeAppliedSeconds;
+ }
- Vector2 acceleration = linearForce / mass;
+ Vector2 acceleration = force / mass;
velocity += (acceleration * deltaTimeSeconds);
position += (velocity * deltaTimeSeconds);
- linearForce = Vector2 { 0.f, 0.f };
- // New: Update the rotational velocity as well
+ // New: Update the rotational velocity as well
float32 rotationalAcceleration = torque / momentOfInertia;
rotationalVelocity += (rotationalAcceleration * deltaTimeSeconds);
rotation += (rotationalVelocity * deltaTimeSeconds);
- torque = 0.f;
+
+ // 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--;
+ }
+ }
}
};
@@ -112,6 +156,7 @@ struct Rectangle {
struct Circle {
OrthographicShape shape;
Rigidbody body;
+ Vector2 force;
float32 radius = 5.f;
@@ -211,7 +256,10 @@ void update(float32 deltaTimeSeconds, void* userData) {
if (!isIntersectingPointer) {
isIntersectingPointer = true;
Vector2 pointOfApplication = pointer.body.position - rectangle.body.position;
- rectangle.body.applyForce(pointer.body.linearForce, pointOfApplication);
+ Impulse i;
+ i.force = pointer.force;
+ i.pointOfApplication = pointOfApplication;
+ rectangle.body.applyImpulse(i);
}
} else if (isIntersectingPointer) {
isIntersectingPointer = false;
@@ -270,8 +318,8 @@ EM_BOOL onMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void
return true;
}
- pointer.body.linearForce.x = static_cast<float32>(mouseEvent->movementX) * 1000.f;
- pointer.body.linearForce.y = static_cast<float32>(-mouseEvent->movementY) * 1000.f;
+ pointer.force.x = static_cast<float32>(mouseEvent->movementX) * 1000.f;
+ pointer.force.y = static_cast<float32>(-mouseEvent->movementY) * 1000.f;
pointer.body.position.x = static_cast<float32>(mouseEvent->targetX);
pointer.body.position.y = static_cast<float32>(600.f - mouseEvent->targetY);
diff --git a/2d/rigidbody/rigidbody_3.html b/2d/rigidbody/rigidbody_3.html
index 9cb1041..60ff03f 100644
--- a/2d/rigidbody/rigidbody_3.html
+++ b/2d/rigidbody/rigidbody_3.html
@@ -50,7 +50,7 @@
</li>
</ul>
</nav>
-3<script src="./rigidbody_3/dist/output.js"></script>
+<script src="./rigidbody_3/dist/output.js"></script>
<script>
window.onload = function() {
var lPlayElement = document.getElementById('gl_canvas_play'),
diff --git a/2d/rigidbody/rigidbody_3.html.content b/2d/rigidbody/rigidbody_3.html.content
index 8985532..f79db4f 100644
--- a/2d/rigidbody/rigidbody_3.html.content
+++ b/2d/rigidbody/rigidbody_3.html.content
@@ -1,4 +1,4 @@
-3<script src="./rigidbody_3/dist/output.js"></script>
+<script src="./rigidbody_3/dist/output.js"></script>
<script>
window.onload = function() {
var lPlayElement = document.getElementById('gl_canvas_play'),
diff --git a/2d/rigidbody/rigidbody_3/dist/output.wasm b/2d/rigidbody/rigidbody_3/dist/output.wasm
index 1a6e153..b89435a 100755
--- a/2d/rigidbody/rigidbody_3/dist/output.wasm
+++ b/2d/rigidbody/rigidbody_3/dist/output.wasm
Binary files differ
diff --git a/2d/rigidbody/rigidbody_3/main.cpp b/2d/rigidbody/rigidbody_3/main.cpp
index 75379c4..e34f444 100644
--- a/2d/rigidbody/rigidbody_3/main.cpp
+++ b/2d/rigidbody/rigidbody_3/main.cpp
@@ -11,114 +11,139 @@
#include <cmath>
#include <cfloat>
+
+struct Impulse {
+ Vector2 force = { 0, 0 };
+ Vector2 pointOfApplication = { 0, 0 };
+ float32 timeOfApplicationSeconds = 0.25f;
+ float32 timeAppliedSeconds = 0.f;
+ bool isDead = false;
+};
+
+const int32 NUM_IMPULSES = 4;
+
struct Rigidbody {
- Vector2 linearForce = { 0, 0 };
+ int32 numImpulses = 0;
+ Impulse activeImpulses[NUM_IMPULSES];
Vector2 velocity = { 0, 0 };
Vector2 position = { 0, 0 };
float32 mass = 1.f;
- float32 torque = 0.f;
float32 rotationalVelocity = 0.f;
float32 rotation = 0.f;
float32 momentOfInertia = 1.f;
- float32 cofOfRestitution = 1.f;
+
+ float32 cofOfRestitution = 1.f;
void reset() {
- linearForce = { 0, 0 };
+ numImpulses = 0;
velocity = { 0, 0 };
rotationalVelocity = 0.f;
rotation = 0.f;
}
- void applyForce(Vector2 force, Vector2 pointOfApplication) {
- linearForce += force;
- torque += pointOfApplication.getPerp().dot(force);
+ void applyImpulse(Impulse i) {
+ if (numImpulses > NUM_IMPULSES) {
+ printf("Unable to apply impulse. Buffer full.\n");
+ return;
+ }
+
+ activeImpulses[numImpulses] = i;
+ numImpulses++;
}
- void applyGravity() {
- applyForce(Vector2 { 0.f, -100.f }, Vector2 { 0.f, 0.f });
+ void applyGravity(float32 deltaTimeSeconds) {
+ velocity += (Vector2 { 0.f, -9.8f } * deltaTimeSeconds);
}
void update(float32 deltaTimeSeconds) {
- applyGravity();
+ applyGravity(deltaTimeSeconds);
+
+ Vector2 force;
+ float32 torque = 0.f;
+ 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;
+ torque += i.pointOfApplication.getPerp().dot(forceToApply);
+
+ i.timeAppliedSeconds = nextTimeAppliedSeconds;
+ }
- Vector2 acceleration = linearForce / mass;
+ Vector2 acceleration = force / mass;
velocity += (acceleration * deltaTimeSeconds);
position += (velocity * deltaTimeSeconds);
- linearForce = Vector2 { 0.f, 0.f };
- // New: Update the rotational velocity as well
float32 rotationalAcceleration = torque / momentOfInertia;
rotationalVelocity += (rotationalAcceleration * deltaTimeSeconds);
rotation += (rotationalVelocity * deltaTimeSeconds);
- torque = 0.f;
- }
-};
-struct Edge {
- Vector2 normal;
- Vector2 start;
- Vector2 end;
+ 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--;
+ }
+ }
+ }
};
-struct Rectangle {
+struct Circle {
OrthographicShape shape;
Rigidbody body;
- Rigidbody previousBody;
- Vector2 originalPoints[4];
- Vector2 transformedPoints[4];
- Edge edges[4];
-
- void load(OrthographicRenderer* renderer, Vector4 color, float32 width, float32 height) {
- color = color.toNormalizedColor();
-
- float32 halfWidth = width / 2.f;
- float32 halfHeight = height / 2.f;
-
- OrthographicVertex vertices[6];
- vertices[0].position = Vector2 { -halfWidth, -halfHeight };
- vertices[1].position = Vector2 { -halfWidth, halfHeight };
- vertices[2].position = Vector2 { halfWidth, halfHeight };
- vertices[3].position = Vector2 { -halfWidth, -halfHeight };
- vertices[4].position = Vector2 { halfWidth, -halfHeight };
- vertices[5].position = Vector2 { halfWidth, halfHeight };
+ Rigidbody previousBody;
+ Vector2 force;
+ float32 radius;
+
+ void load(OrthographicRenderer* renderer, float32 inRadius, Vector4 startColor, Vector4 endColor) {
+ radius = inRadius;
+ const int32 numSegments = 36;
+ const float32 radiansPerSegment = (2.f * PI) / static_cast<float>(numSegments);
+ const int32 numVertices = numSegments * 3;
- for (int32 i = 0; i < 6; i++) {
- vertices[i].color = color;
+ startColor = startColor.toNormalizedColor();
+ endColor = endColor.toNormalizedColor();
+
+ OrthographicVertex vertices[numSegments * 3];
+ for (int idx = 0; idx < numSegments; idx++) {
+ int vIdx = idx * 3;
+
+ Vector4 color;
+ if (idx >= numSegments / 2) {
+ color = endColor;
+ } else {
+ color = startColor;
+ }
+
+ 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)) };
}
- 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);
+ shape.load(vertices, numVertices, renderer);
body.reset();
-
- body.momentOfInertia = (width * width + height * height) * (body.mass / 12.f);
+ body.momentOfInertia = (PI * (radius * radius)) / 4.f;
}
void update(float32 dtSeconds) {
- previousBody = body;
- body.update(dtSeconds);
- shape.model = Mat4x4().translateByVec2(body.position).rotate2D(body.rotation);
-
- // Note: This helps us check rectangle collisions using SAT later on.
- // This is probably a slightly slow way of doing this, but we will ignore
- // that for now.
- for (int idx = 0; idx < 4; idx++) {
- transformedPoints[idx] = shape.model * originalPoints[idx];
- }
-
- for (int eidx = 0; eidx < 4; eidx++) {
- edges[eidx].start = transformedPoints[eidx];
- edges[eidx].end = transformedPoints[eidx == 3 ? 0 : eidx + 1];
- edges[eidx].normal = (edges[eidx].end - edges[eidx].start).getPerp().normalize();
- }
- }
+ previousBody = body;
- void restorePreviousBody() {
- body = previousBody;
+ shape.model = Mat4x4().translateByVec2(body.position).rotate2D(body.rotation);
+ body.update(dtSeconds);
}
void render(OrthographicRenderer* renderer) {
@@ -128,6 +153,10 @@ struct Rectangle {
void unload() {
shape.unload();
}
+
+ void restorePreviousBody() {
+ body = previousBody;
+ }
};
struct IntersectionResult {
@@ -148,8 +177,8 @@ void unload();
WebglContext context;
OrthographicRenderer renderer;
MainLoop mainLoop;
-Rectangle r1;
-Rectangle r2;
+Circle c1;
+Circle c2;
int main() {
context.init("#gl_canvas");
@@ -161,145 +190,60 @@ int main() {
void load() {
renderer.load(&context);
- r1.load(&renderer, Vector4 { 55.f, 235.f, 35.f, 255.f }, 128.f, 64.f);
- r1.body.mass = 3.f;
- r1.body.position = Vector2 { context.width / 4.f, context.height / 4.f };
- r1.body.velocity = Vector2 { 100.f, 250.f };
+ c1.load(&renderer, 32.f, Vector4 { 55.f, 235.f, 35.f, 255.f }, Vector4 { 235.f, 5.f, 235.f, 255.f });
+ c1.body.mass = 3.f;
+ c1.body.position = Vector2 { context.width / 4.f, context.height / 4.f };
+ c1.body.velocity = Vector2 { 100.f, 250.f };
- r2.load(&renderer, Vector4 { 235.f, 5.f, 35.f, 255.f }, 96.f, 64.f);
- r2.body.mass = 1.f;
- r2.body.position = Vector2 { context.width * (3.f / 4.f), context.height * (3.f / 4.f) };
- r2.body.velocity = Vector2 { -300.f, -150.f };
- r2.body.rotationalVelocity = 0.9f;
+ c2.load(&renderer, 64.f, Vector4 { 235.f, 5.f, 35.f, 255.f }, Vector4 { 5.f, 35.f, 235.f, 255.f });
+ c2.body.mass = 1.f;
+ c2.body.position = Vector2 { context.width * (3.f / 4.f), context.height * (3.f / 4.f) };
+ c2.body.velocity = Vector2 { -300.f, -150.f };
mainLoop.run(update);
}
-void handleCollisionWithWall(Rectangle* r) {
- if (r->body.position.x <= 0.f) {
- r->body.position.x = 0.f;
- r->body.velocity = r->body.velocity - Vector2 { 1.f, 0.f } * (2 * (r->body.velocity.dot(Vector2 { 1.f, 0.f })));
+void handleCollisionWithWall(Circle* c) {
+ if (c->body.position.x <= 0.f) {
+ c->body.position.x = 0.f;
+ c->body.velocity = c->body.velocity - Vector2 { 1.f, 0.f } * (2 * (c->body.velocity.dot(Vector2 { 1.f, 0.f })));
}
- if (r->body.position.y <= 0.f) {
- r->body.position.y = 0.f;
- r->body.velocity = r->body.velocity - Vector2 { 0.f, 1.f } * (2 * (r->body.velocity.dot(Vector2 { 0.f, 1.f })));
+ if (c->body.position.y <= 0.f) {
+ c->body.position.y = 0.f;
+ c->body.velocity = c->body.velocity - Vector2 { 0.f, 1.f } * (2 * (c->body.velocity.dot(Vector2 { 0.f, 1.f })));
}
- if (r->body.position.x >= 800.f) {
- r->body.position.x = 800.f;
- r->body.velocity = r->body.velocity - Vector2 { -1.f, 0.f } * (2 * (r->body.velocity.dot(Vector2{ -1.f, 0.f })));
+ if (c->body.position.x >= 800.f) {
+ c->body.position.x = 800.f;
+ c->body.velocity = c->body.velocity - Vector2 { -1.f, 0.f } * (2 * (c->body.velocity.dot(Vector2{ -1.f, 0.f })));
}
- if (r->body.position.y >= 600.f) {
- r->body.position.y = 600.f;
- r->body.velocity = r->body.velocity - Vector2 { 0.f, -1.f } * (2 * (r->body.velocity.dot(Vector2 { 0.f, -1.f }))) ;
+ if (c->body.position.y >= 600.f) {
+ c->body.position.y = 600.f;
+ c->body.velocity = c->body.velocity - Vector2 { 0.f, -1.f } * (2 * (c->body.velocity.dot(Vector2 { 0.f, -1.f }))) ;
}
}
-/*
- Do not worry about how w are exactly finding the intersection here, for now.
- We are using the Separating Axis Theorem to do so here. In the 2D -> Collisions
- section of the website, we describe this method at length.
-*/
-Vector2 getProjection(Vector2* vertices, Vector2 axis) {
- float32 min = axis.dot(vertices[0]);
- float32 max = min;
-
- for (int v = 1; v < 4; v++) {
- float32 d = axis.dot(vertices[v]);
-
- if (d < min) {
- min = d;
- } else if (d > max) {
- max = d;
- }
- }
-
- return Vector2 { min, max };
-}
-
-inline bool projectionsOverlap(Vector2 first, Vector2 second) {
- return first.x <= second.y && second.x <= first.y;
-}
-
-inline float32 getProjectionOverlap(Vector2 first, Vector2 second) {
- float32 firstOverlap = (first.x - second.y); // TODO: Does this need to be absolute value?
- float32 secondOverlap = (second.x - first.y);
- return firstOverlap > secondOverlap ? secondOverlap : firstOverlap;
-}
-
-struct IntermediateIntersectionResult {
- float32 minOverlap = FLT_MAX;
- Edge* minOverlapEdge;
- bool isOverlapOnFirstEdge = true;
-};
-
-bool checkEdgeOverlap(Edge* edges, Rectangle* first, Rectangle* second, IntermediateIntersectionResult* iir, bool isFirstEdge) {
- // Returns true if SAT passes for the provided set of edges.
- for (int i = 0; i < 4; i++) {
- Vector2 normal = edges[i].normal;
-
- Vector2 firstProj = getProjection(first->transformedPoints, normal);
- Vector2 secondProj = getProjection(second->transformedPoints, normal);
-
- if (!projectionsOverlap(firstProj, secondProj)) {
- return false;
- }
-
- float32 overlap = getProjectionOverlap(firstProj, secondProj);
- if (overlap < iir->minOverlap) {
- iir->minOverlap = overlap;
- iir->minOverlapEdge = &edges[i];
- iir->isOverlapOnFirstEdge = isFirstEdge;
- }
- }
-
- return true;
-}
-
-const float32 EPSILON = 1.f;
-IntersectionResult getIntersection(Rectangle* first, Rectangle* second) {
- IntersectionResult ir;
-
- IntermediateIntersectionResult iir;
- if (!checkEdgeOverlap(first->edges, first, second, &iir, true)) {
- return ir;
- }
-
- if (!checkEdgeOverlap(second->edges, first, second, &iir, false)) {
- return ir;
- }
-
- ir.intersect = true;
- ir.relativeVelocity = first->body.velocity - second->body.velocity;
- ir.collisionNormal = iir.minOverlapEdge->normal;
-
- float32 minDistanceFromEdge = FLT_MAX;
- Vector2 pointOfContact;
- Vector2* pointsToCheck = iir.isOverlapOnFirstEdge ? second->transformedPoints : first->transformedPoints;
- for (int p = 0; p < 4; p++) {
- Vector2 point = pointsToCheck[p];
-
- float32 distanceFromEdge = MIN((iir.minOverlapEdge->start - point).length(), (iir.minOverlapEdge->end - point).length());
-
- if (distanceFromEdge < minDistanceFromEdge) {
- minDistanceFromEdge = distanceFromEdge;
- pointOfContact = point;
- }
- }
-
-
- ir.firstPointOfApplication = pointOfContact - first->body.position;
- ir.secondPointOfApplication = pointOfContact - second->body.position;;
+IntersectionResult getIntersection(Circle* first, Circle* second) {
+ IntersectionResult ir;
+ Vector2 positionDiff = (first->body.position - second->body.position);
+ if (positionDiff.length() > first->radius + second->radius) {
+ return ir; // Not intersecting
+ }
+ Vector2 positionDirection = positionDiff.normalize();
+ ir.relativeVelocity = first->body.velocity - second->body.velocity;
+
+ // The positionDirection could represent the normal at which our circles intersect, but then we would
+ // never get any rotation on them. At the same time, this is not an entirely great selection because, in the real
+ // world, two circles wouldn't hit one another at exactly the normal. To fix this, we offset the positionDirection
+ // by the relative velocity. This gives a normal that is slightly more believable, and allows our spin to take place.
+ ir.collisionNormal = (positionDirection + ir.relativeVelocity.negate().normalize()).normalize();
+ ir.firstPointOfApplication = positionDirection * first->radius;
+ ir.secondPointOfApplication = positionDirection * second->radius;
+ ir.intersect = true;
+
return ir;
}
-/**
-In this method, we resolve the collision of two rigidbodies using the IntersectionResult
-that we gathered from the collision information. Note that this particular tutorial
-is not about how we find this collision, but rather how we use this collision. To see the
-variety of ways of how this IntersectionResult can be calculated go to the 2D->Collision
-section of the website.
-***/
void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* ir) {
Vector2 relativeVelocity = ir->relativeVelocity;
Vector2 collisionNormal = ir->collisionNormal;
@@ -322,29 +266,29 @@ void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* i
}
void update(float32 deltaTimeSeconds, void* userData) {
- r1.update(deltaTimeSeconds);
- r2.update(deltaTimeSeconds);
+ c1.update(deltaTimeSeconds);
+ c2.update(deltaTimeSeconds);
// Let's backtrack the simulation to find the precise point at which we collided.
// There exists many ways to find this precise point. This is by far the most
// expensive, but it gets the job done.
- IntersectionResult ir = getIntersection(&r1, &r2);
+ IntersectionResult ir = getIntersection(&c1, &c2);
if (ir.intersect) {
IntersectionResult irCopy = ir;
float32 copyDt = deltaTimeSeconds;
float32 subdivisionAmountSeconds = deltaTimeSeconds / 16.f;
do {
- r1.restorePreviousBody();
- r2.restorePreviousBody();
+ c1.restorePreviousBody();
+ c2.restorePreviousBody();
ir = irCopy;
copyDt = copyDt - subdivisionAmountSeconds;
- r1.update(copyDt);
- r2.update(copyDt);
+ c1.update(copyDt);
+ c2.update(copyDt);
- irCopy = getIntersection(&r1, &r2);
+ irCopy = getIntersection(&c1, &c2);
if (copyDt <= 0.f) {
printf("Error: Should not be happening.\n");
@@ -358,28 +302,28 @@ void update(float32 deltaTimeSeconds, void* userData) {
// The following function is the main one that we're talking about in this tutorial.
// This function will take the collision data, and repel the objects away from one
// another using what we know from physics.
- resolveCollision(&r1.body, &r2.body, &ir);
+ resolveCollision(&c1.body, &c2.body, &ir);
float32 frameTimeRemaining = deltaTimeSeconds - copyDt;
- r1.update(frameTimeRemaining);
- r2.update(frameTimeRemaining);
+ c1.update(frameTimeRemaining);
+ c2.update(frameTimeRemaining);
}
// Keep within the bounds
- handleCollisionWithWall(&r1);
- handleCollisionWithWall(&r2);
+ handleCollisionWithWall(&c1);
+ handleCollisionWithWall(&c2);
// Renderer
renderer.render();
- r1.render(&renderer);
- r2.render(&renderer);
+ c1.render(&renderer);
+ c2.render(&renderer);
}
void unload() {
mainLoop.stop();
renderer.unload();
- r1.unload();
- r2.unload();
+ c1.unload();
+ c2.unload();
}
//