/// /// /// /// /// /// /// /// (function() { // Define Constants const CIRCLE_RADIUS = 16; const GRAVITY = 20.0; const COF_OF_RESTITUITION = 0.75; const TORQUE_MULTIPLIER = 100.0; // TODO: This may be unncessary var programContext, circleObject, lineObjectList, programInfo, exitRequestFunc; function main() { programContext = getContext('#circle_line_collision'); if (programContext.gl === null) { console.error('Unable to initialize WebGL. Your browser or machine may not support it.'); return; } programContext.gl.clearColor(0.1, 0.15, 0.2, 1.0); programContext.gl.clear(programContext.gl.COLOR_BUFFER_BIT); programContext.playButton.on('click', run); } function run() { console.log('Running Circle-Line Collisions'); programContext.load().then(function(pProgramInfo) { programInfo = pProgramInfo; circleObject = circle(programContext.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(programContext.width * (3.0 / 4.0), programContext.height / 2.0 + 100)); circleObject.velocity = vec2(0, -50); lineObjectList = []; lineObjectList.push(line2({ x: 100, y: 100 }, { x: programContext.width - 100, y: 200 }, programContext.gl, { x: 1, y: 0, z: 0, w: 1 }, 2.0)); lineObjectList.push(line2({ x: 100, y: 200 }, { x: programContext.width - 100, y: 100 }, programContext.gl, { x: 1, y: 1, z: 0, w: 1 }, 2.0)); lineObjectList.push(line2({ x: 100, y: 0 }, { x: 100, y: programContext.height }, programContext.gl, { x: 0, y: 1, z: 0, w: 1 }, 2.0)); lineObjectList.push(line2({ x: programContext.width - 100, y: 0 }, { x: programContext.width - 100, y: programContext.height }, programContext.gl, { x: 0, y: 1, z: 0, w: 1 }, 2.0)); exitRequestFunc = requestUpdateLoop(update, cleanup); programContext.stopButton.on('click', reset); }); } function update(pDeltaTimeSeconds) { pDeltaTimeSeconds = pDeltaTimeSeconds; updateCircle(circleObject, pDeltaTimeSeconds); collision(pDeltaTimeSeconds); render(); } function updateCircle(pCircle, pDeltaTimeSeconds) { 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(); 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 collision(pDeltaTimeSeconds) { lineObjectList.forEach(function(lineObject) { if (!lineCircleCollision2(circleObject, lineObject)) { return; } var lSubdividedDeltaTime = pDeltaTimeSeconds, lSubdividedCircle = undefined; do { lSubdividedCircle = JSON.parse(JSON.stringify(circleObject)); lSubdividedCircle.position = {...circleObject.prevPos}; lSubdividedCircle.velocity = {...circleObject.prevVelocity}; lSubdividedDeltaTime = lSubdividedDeltaTime / 2.0; updateCircle(lSubdividedCircle, lSubdividedDeltaTime); if (lSubdividedDeltaTime === 0) { console.error('This should NOT be happening'); break; } } while (lineCircleCollision2(lSubdividedCircle, lineObject)) const lIntersectionResult = getLineCircleCollison2Data(lSubdividedCircle, lineObject), 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 / circleObject.mass))); const lRotationalDenomPart = (Math.pow(dot2(lFirstPerp, lCollisionNormal), 2) / getCircleMomentOfInertia(circleObject)); const lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart); circleObject.position = lSubdividedCircle.position; circleObject.velocity = addVec2(lSubdividedCircle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / circleObject.mass)); circleObject.rotationVelocity = lSubdividedCircle.rotationVelocity + dot2(lFirstPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getCircleMomentOfInertia(circleObject); updateCircle(circleObject, pDeltaTimeSeconds - lSubdividedDeltaTime); return; }) } 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 render() { programContext.gl.clearColor(0.1, 0.15, 0.2, 1.0); programContext.gl.clearDepth(1.0); programContext.gl.enable(programContext.gl.DEPTH_TEST); programContext.gl.depthFunc(programContext.gl.LEQUAL); programContext.gl.clear(programContext.gl.COLOR_BUFFER_BIT | programContext.gl.DEPTH_BUFFER_BIT); programContext.gl.useProgram(programInfo.program); programContext.gl.uniformMatrix4fv(programInfo.uniformLocations.projection, false, programContext.perspective); renderCircle(programContext.gl, programInfo, circleObject); lineObjectList.forEach(function(lineObject) { renderLine2(programContext.gl, programInfo, lineObject); }); } function cleanup() { programContext.gl.deleteBuffer(circleObject.buffer); lineObjectList.forEach(function(lineObject) { programContext.gl.deleteBuffer(lineObject.buffer); }); programContext.gl.deleteProgram(programInfo.program); programContext.gl.clearColor(0.1, 0.15, 0.2, 1.0); programContext.gl.clear(programContext.gl.COLOR_BUFFER_BIT); } function reset() { exitRequestFunc(); programContext.reset(); } $(document).ready(main); })()