/// /// /// /// /// /// (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); })()