From d1b528b01796601c2bfea7b1a9813e5907e1c728 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Sat, 27 Feb 2021 17:32:32 -0500 Subject: Close to being done on line-circle collisions, but it appears we're running into a rotation problem. Going to work on square-line collisions in the meantime --- frontend/2d/_collisions/circle_line.js | 178 +++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 frontend/2d/_collisions/circle_line.js (limited to 'frontend/2d/_collisions/circle_line.js') diff --git a/frontend/2d/_collisions/circle_line.js b/frontend/2d/_collisions/circle_line.js new file mode 100644 index 0000000..e5898f0 --- /dev/null +++ b/frontend/2d/_collisions/circle_line.js @@ -0,0 +1,178 @@ +/// +/// +/// +/// +/// +/// +/// +/// + +(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)); + + 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); +})() \ No newline at end of file -- cgit v1.2.1