/// /// /// /// /// /// /// /// (function() { var programContext, rectangleObject, lineObjectList, programInfo, exitRequestFunc; function main() { programContext = getContext('#rectangle_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 Rectangle-Line Collisions'); programContext.load().then(function(pProgramInfo) { programInfo = pProgramInfo; rectangleObject = rectangle(programContext.gl, { width: 50, height: 50, mass: 1, color: { x: 0.3, y: 0.5, z: 0.1, w: 0.7 }, position: vec2(programContext.width / 2.0 - 100, programContext.height / 2.0 + 100) }); rectangleObject.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 }, 10000000.0)); lineObjectList.push(line2({ x: 100, y: 200 }, { x: programContext.width - 100, y: 100 }, programContext.gl, { x: 1, y: 1, z: 0, w: 1 }, 10000000.0)); lineObjectList.push(line2({ x: 100, y: 0 }, { x: 100, y: programContext.height }, programContext.gl, { x: 0, y: 1, z: 0, w: 1 }, 10000000.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 }, 10000000.0)); exitRequestFunc = requestUpdateLoop(update, cleanup); programContext.stopButton.on('click', reset); }); } function update(pDeltaTimeSeconds) { pDeltaTimeSeconds = pDeltaTimeSeconds; updateRigidBody2(rectangleObject, pDeltaTimeSeconds); collision(pDeltaTimeSeconds); render(); } function collision(pDeltaTimeSeconds) { for (var lineIdx = 0; lineIdx < lineObjectList.length; lineIdx++) { var lLine = lineObjectList[lineIdx]; if (areIntersecting(rectangleObject, lLine, pDeltaTimeSeconds)) { break; } } } function doesPointIntersect(pPoint, pLine) { const EPSILON = 0.98; return distanceFromPoint2ToLine2(pPoint, pLine) <= EPSILON; } function getIntersectionInformation(pRectangle, pCollisionPoint, pLine) { var lCollisionNormal = pLine.normal, lRectCollisionPoint_WorldCoords = multMat4ByVec2(pRectangle.model, pCollisionPoint), lRectPosition = pRectangle.position, lTrueRectCollisionPoint = normalize2(subVec2(lRectPosition, lRectCollisionPoint_WorldCoords)); console.log(lTrueRectCollisionPoint); return { relativeVelocity: subVec2(pRectangle.velocity, pLine.velocity), collisionNormal: lCollisionNormal, rectangleCollisionPoint: lTrueRectCollisionPoint } } function areIntersecting(pRectangle, pLine, pDeltaTimeSeconds) { var lCollisionPoint = undefined, lCheckCollision = function(pRectangle) { var lLowerLeft = multMat4ByVec2(pRectangle.model, vec2(-pRectangle.width / 2.0, -pRectangle.height / 2.0)), lUpperLeft = multMat4ByVec2(pRectangle.model, vec2(-pRectangle.width / 2.0, pRectangle.height / 2.0)), lUpperRight = multMat4ByVec2(pRectangle.model, vec2(pRectangle.width / 2.0, pRectangle.height / 2.0)), lLowerRight = multMat4ByVec2(pRectangle.model, vec2(pRectangle.width / 2.0, -pRectangle.height / 2.0)); if (doesPointIntersect(lLowerLeft, pLine)) { console.log('Lwer left'); lCollisionPoint = vec2(-pRectangle.width / 2.0, -pRectangle.height / 2.0); return true; } else if (doesPointIntersect(lUpperLeft, pLine)) { console.log('Upper left'); lCollisionPoint = vec2(-pRectangle.width / 2.0, pRectangle.height / 2.0); return true; } else if (doesPointIntersect(lUpperRight, pLine)) { console.log('Upepr right'); lCollisionPoint = vec2(pRectangle.width / 2.0, pRectangle.height / 2.0); return true; } else if (doesPointIntersect(lLowerRight, pLine)) { console.log('Lower right'); lCollisionPoint = vec2(pRectangle.width / 2.0, -pRectangle.height / 2.0); return true; } return false; }; if (!lCheckCollision(pRectangle)) { return; } // Subdivide until we find the exact moment where we intersected var lSubdividedDeltaTime = pDeltaTimeSeconds, lSubRectangle = undefined; do { lSubRectangle = JSON.parse(JSON.stringify(pRectangle)); lSubRectangle.position = {...pRectangle.prevPos}; lSubRectangle.velocity = {...pRectangle.prevVelocity}; lSubRectangle.getMomentOfInertia = pRectangle.getMomentOfInertia; lSubdividedDeltaTime = lSubdividedDeltaTime / 2.0; updateRigidBody2(lSubRectangle, lSubdividedDeltaTime); if (lSubdividedDeltaTime === 0) { console.error('This should NOT be happening'); break; } } while (lCheckCollision(lSubRectangle)); var lIntersectionResult = getIntersectionInformation(lSubRectangle, lCollisionPoint, pLine), lRelativeVelocity = lIntersectionResult.relativeVelocity, lCollisionNormal = lIntersectionResult.collisionNormal, lRectPerp = getPerp2(lIntersectionResult.rectangleCollisionPoint); const lNumerator = dot2(scaleVec2(lRelativeVelocity, -(1.0 + 1.0)), lCollisionNormal); const lLinearDenomPart = dot2(lCollisionNormal, (scaleVec2(lCollisionNormal, 1.0 / pRectangle.mass))); const lRotationalDenomPart = (Math.pow(dot2(lRectPerp, lCollisionNormal), 2) / pRectangle.getMomentOfInertia()); const lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart); pRectangle.position = lSubRectangle.position; pRectangle.velocity = addVec2(lSubRectangle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / pRectangle.mass)); pRectangle.rotationVelocity = lSubRectangle.rotationVelocity + dot2(lRectPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / pRectangle.getMomentOfInertia(); updateRigidBody2(pRectangle, pDeltaTimeSeconds - lSubdividedDeltaTime); return true; } 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); programInfo.renderShape(rectangleObject); lineObjectList.forEach(function(lineObject) { renderLine2(programContext.gl, programInfo, lineObject); }); } function cleanup() { programContext.gl.deleteBuffer(rectangleObject.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); })()