///
///
///
///
///
///
(function() {
function main() {
// Define Constants
const CIRCLE_RADIUS = 16;
const GRAVITY = 9.8;
const COF_OF_RESTITUITION = 0.7;
// Retrieve context
const lProgramContext = getContext('#rigidbody_3a');
if (lProgramContext.gl === null) {
console.error('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
function run() {
console.log('Running Rigid Body 3a');
lProgramContext.load().then(function(pProgramInfo) {
// Circile initialization
const horizontalCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [
{ x: 1, y: 1, z: 0, w: 1 },
{ x: 1, y: 0, z: 1, w: 1 },
{ x: 0, y: 1, z: 1, w: 1 },
{ x: 0, y: 1, z: 0, w: 1 }
], vec2(400, lProgramContext.height / 2.0));
const verticalCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [
{ x: 1, y: 0, z: 0, w: 1 },
{ x: 0, y: 1, z: 0, w: 1 },
{ x: 0, y: 0, z: 1, w: 1 }
], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0 + 100));
horizontalCircle.velocity = vec2(-100, 0);
verticalCircle.velocity = vec2(0, -100);
lTimeStepScale = $('#time_step_slider').val();
/**
* Run the update method of a single circle
*
* @param {circle} pCircle
* @param {number} pDeltaTimeSeconds
*/
function updateCircle(pCircle, pDeltaTimeSeconds) {
// Same physics updates from part 1
applyForce(pCircle, vec2(0, -1.0 * (pCircle.mass * GRAVITY)));
const lCurrentAcceleration = scaleVec2(pCircle.force, 1.0 / pCircle.mass);
pCircle.prevVelocity = pCircle.velocity;
pCircle.velocity = addVec2(pCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds));
pCircle.prevPos = { ...pCircle.position };
pCircle.position = addVec2(pCircle.position, scaleVec2(pCircle.velocity, pDeltaTimeSeconds));
pCircle.force = vec2();
// Same physics updates from part 2
const lMomentOfInertia = getCircleMomentOfInertia(pCircle);
const lAngularAcceleration = pCircle.torque / lMomentOfInertia;
pCircle.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds;
pCircle.rotationRadians += pCircle.rotationVelocity * pDeltaTimeSeconds;
pCircle.torque = 0;
pCircle.model = rotateMatrix2d(translateMatrix(mat4(), pCircle.position.x, pCircle.position.y, 0), pCircle.rotationRadians);
}
function update(pDeltaTimeSeconds) {
pDeltaTimeSeconds = pDeltaTimeSeconds * lTimeStepScale;
updateCircle(horizontalCircle, pDeltaTimeSeconds);
updateCircle(verticalCircle, pDeltaTimeSeconds);
collision(pDeltaTimeSeconds);
render();
}
function collision(pDeltaTimeSeconds) {
if (!doCirclesIntersect(horizontalCircle, verticalCircle)) {
return false;
}
// We have an intersection! Let's try and figure out precisely when that happened.
let lSubdividedDeltaTime = pDeltaTimeSeconds;
let lSubdivideHorizontalBall = undefined,
lSubdivideVerticalBall = undefined;
do {
lSubdivideHorizontalBall = JSON.parse(JSON.stringify(horizontalCircle));
lSubdivideHorizontalBall.position = {...horizontalCircle.prevPos};
lSubdivideHorizontalBall.velocity = {...horizontalCircle.prevVelocity};
lSubdivideVerticalBall = JSON.parse(JSON.stringify(verticalCircle));
lSubdivideVerticalBall.position = {...verticalCircle.prevPos};
lSubdivideVerticalBall.velocity = {...verticalCircle.prevVelocity};
lSubdividedDeltaTime = lSubdividedDeltaTime / 2.0;
updateCircle(lSubdivideHorizontalBall, lSubdividedDeltaTime);
updateCircle(lSubdivideVerticalBall, lSubdividedDeltaTime);
if (lSubdividedDeltaTime === 0) {
console.error('This should NOT be happening');
break;
}
} while (doCirclesIntersect(lSubdivideHorizontalBall, lSubdivideVerticalBall))
const lIntersectionResult = getIntersectionDataForCircles(lSubdivideHorizontalBall, lSubdivideVerticalBall);
console.log('We have a collision');
const lRelativeVelocity = lIntersectionResult.relativeVelocity,
lCollisionNormal = lIntersectionResult.collisionNormal,
lFirstPerp = getPerp2(lIntersectionResult.firstPointOfApplication),
lSecondPerp = getPerp2(lIntersectionResult.secondPointOfApplication);
const lNumerator = dot2(scaleVec2(lRelativeVelocity, -(1.0 + COF_OF_RESTITUITION)), lCollisionNormal);
const lLinearDenomPart = dot2(lCollisionNormal, (scaleVec2(lCollisionNormal, 1 / horizontalCircle.mass + 1 / verticalCircle.mass)));
const lRotationalDenomPart = (Math.pow(dot2(lFirstPerp, lCollisionNormal), 2) / getCircleMomentOfInertia(horizontalCircle))
+ (Math.pow(dot2(lSecondPerp, lCollisionNormal), 2) / getCircleMomentOfInertia(verticalCircle))
const lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart);
horizontalCircle.position = lSubdivideHorizontalBall.position;
verticalCircle.position = lSubdivideVerticalBall.position;
horizontalCircle.velocity = addVec2(horizontalCircle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / horizontalCircle.mass));
verticalCircle.velocity = subVec2(verticalCircle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / verticalCircle.mass));
horizontalCircle.rotationVelocity = horizontalCircle.rotationVelocity + dot2(lFirstPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getCircleMomentOfInertia(horizontalCircle);
verticalCircle.rotationVelocity = verticalCircle.rotationVelocity - dot2(lSecondPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getCircleMomentOfInertia(verticalCircle);
updateCircle(horizontalCircle, pDeltaTimeSeconds - lSubdividedDeltaTime);
updateCircle(verticalCircle, pDeltaTimeSeconds - lSubdividedDeltaTime);
return true;
}
function render() {
lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
lProgramContext.gl.clearDepth(1.0);
lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST);
lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL);
lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT);
lProgramContext.gl.useProgram(pProgramInfo.program);
lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective);
renderCircle(lProgramContext.gl, pProgramInfo, horizontalCircle);
renderCircle(lProgramContext.gl, pProgramInfo, verticalCircle);
}
const TORQUE_MULTIPLIER = 100.0; // TODO: This may be unncessary
function applyForce(pCircle, pForceVector, pPointOfApplication) {
if (pPointOfApplication !== undefined) {
const lOriginToPointOfApp = subVec2(vec2(), pPointOfApplication),
lPerpVec = vec2(-lOriginToPointOfApp.y, lOriginToPointOfApp.x);
pCircle.torque += TORQUE_MULTIPLIER * dot2(lPerpVec, pForceVector);
}
pCircle.force = addVec2(pCircle.force, pForceVector);
}
function cleanup() {
lProgramContext.gl.deleteBuffer(horizontalCircle.buffer);
lProgramContext.gl.deleteBuffer(verticalCircle.buffer);
lProgramContext.gl.deleteProgram(pProgramInfo.program);
lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
}
function reset() {
lExitRequestFunc();
lProgramContext.reset();
$('#time_step_slider').unbind('change');
}
const lExitRequestFunc = requestUpdateLoop(update, cleanup);
lProgramContext.stopButton.on('click', reset);
$('#time_step_slider').on('change', function() { lTimeStepScale = $(this).val(); });
});
}
lProgramContext.playButton.on('click', run);
}
$(document).ready(main);
})()