diff options
-rw-r--r-- | frontend/2d/_collisions/circle_line.js | 3 | ||||
-rw-r--r-- | frontend/2d/_collisions/rectangle_line.js | 105 | ||||
-rw-r--r-- | frontend/_shared/math/line2.js | 10 | ||||
-rw-r--r-- | frontend/_shared/math/mat4.js | 20 | ||||
-rw-r--r-- | frontend/_shared/math/rectangle.js | 14 | ||||
-rw-r--r-- | frontend/_shared/math/vec2.js | 8 | ||||
-rw-r--r-- | frontend/index.css | 26 |
7 files changed, 162 insertions, 24 deletions
diff --git a/frontend/2d/_collisions/circle_line.js b/frontend/2d/_collisions/circle_line.js index e5898f0..0736e2c 100644 --- a/frontend/2d/_collisions/circle_line.js +++ b/frontend/2d/_collisions/circle_line.js @@ -125,7 +125,8 @@ circleObject.position = lSubdividedCircle.position; circleObject.velocity = addVec2(lSubdividedCircle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / circleObject.mass)); - circleObject.rotationVelocity = lSubdividedCircle.rotationVelocity + dot2(lFirstPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)); + circleObject.rotationVelocity = lSubdividedCircle.rotationVelocity + + dot2(lFirstPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getCircleMomentOfInertia(circleObject); updateCircle(circleObject, pDeltaTimeSeconds - lSubdividedDeltaTime); diff --git a/frontend/2d/_collisions/rectangle_line.js b/frontend/2d/_collisions/rectangle_line.js index ac73f71..dec5108 100644 --- a/frontend/2d/_collisions/rectangle_line.js +++ b/frontend/2d/_collisions/rectangle_line.js @@ -34,7 +34,8 @@ rectangleObject = rectangle(programContext.gl, { width: 50, - height: 25, + 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) }); @@ -43,16 +44,16 @@ 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)); + { 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 }, 2.0)); + { 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 }, 2.0)); + { 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 }, 2.0)); + { x: 0, y: 1, z: 0, w: 1 }, 10000000.0)); exitRequestFunc = requestUpdateLoop(update, cleanup); @@ -68,7 +69,101 @@ } 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() { diff --git a/frontend/_shared/math/line2.js b/frontend/_shared/math/line2.js index a9e72be..8b30c1c 100644 --- a/frontend/_shared/math/line2.js +++ b/frontend/_shared/math/line2.js @@ -32,7 +32,8 @@ function line2(pStart, pEnd, pGl, pColor, pMass) { length: length2(lDiffVector), mass: pMass, direction: normalize2(lDiffVector), - velocity: vec2(0, 0) + velocity: vec2(0, 0), + position: vec2() }; } @@ -40,6 +41,13 @@ function getLine2MomentOfInertia(pLine) { return (1.0 / 12.0) * pLine.mass * Math.pow(pLine.length, 2); } +function getLine2MidPoint(pLine) { + return { + x: (pLine.end.x + pLine.start.x) / 2.0, + y: (pLine.end.y + pLine.start.y) / 2.0 + } +} + function renderLine2(pGl, pProgramInfo, pLine) { pGl.uniformMatrix4fv(pProgramInfo.uniformLocations.model, false, mat4()); // Model on a line is always default matrix pGl.bindBuffer(pGl.ARRAY_BUFFER, pLine.buffer); diff --git a/frontend/_shared/math/mat4.js b/frontend/_shared/math/mat4.js index 6ab29e2..d31a20e 100644 --- a/frontend/_shared/math/mat4.js +++ b/frontend/_shared/math/mat4.js @@ -40,4 +40,24 @@ function scaleMatrix(m, x, y, z) { m[5] = m[5] * y; m[10] = m[10] * z; return m; +} + +function multMat4ByVec2(matrix, vec) { + var lInnerVec = {...vec}; + lInnerVec.z = 0.0; + lInnerVec.w = 1.0; + + return { + x: lInnerVec.x * matrix[0] + lInnerVec.y * matrix[4] + lInnerVec.z * matrix[8] + lInnerVec.w * matrix[12], + y: lInnerVec.x * matrix[1] + lInnerVec.y * matrix[5] + lInnerVec.z * matrix[9] + lInnerVec.w * matrix[13] + }; +} + +function multMat4ByVec4(matrix, vec) { + return { + x: vec.x * matrix[0] + vec.y * matrix[4] + vec.z * matrix[8] + vec.w * matrix[12], + y: vec.x * matrix[1] + vec.y * matrix[5] + vec.z * matrix[9] + vec.w * matrix[13], + z: vec.x * matrix[2] + vec.y * matrix[6] + vec.z * matrix[10] + vec.w * matrix[14], + w: vec.x * matrix[3] + vec.y * matrix[7] + vec.z * matrix[11] + vec.w * matrix[15] + }; }
\ No newline at end of file diff --git a/frontend/_shared/math/rectangle.js b/frontend/_shared/math/rectangle.js index c24fa12..012c460 100644 --- a/frontend/_shared/math/rectangle.js +++ b/frontend/_shared/math/rectangle.js @@ -7,19 +7,19 @@ function rectangle(pGl, pData) { pGl.bindBuffer(pGl.ARRAY_BUFFER, lBuffer); var lBufferedData = [ - 0, 0, lColor.x, lColor.y, lColor.z, lColor.w, - 0, pData.height, lColor.x, lColor.y, lColor.z, lColor.w, - pData.width, pData.height, lColor.x, lColor.y, lColor.z, lColor.w, - pData.width, pData.height, lColor.x, lColor.y, lColor.z, lColor.w, - pData.width, 0, lColor.x, lColor.y, lColor.z, lColor.w, - 0, 0, lColor.x, lColor.y, lColor.z, lColor.w + -pData.width / 2.0, -pData.height / 2.0, lColor.x, lColor.y, lColor.z, lColor.w, + -pData.width / 2.0, pData.height / 2.0, lColor.x, lColor.y, lColor.z, lColor.w, + pData.width / 2.0, pData.height / 2.0, lColor.x, lColor.y, lColor.z, lColor.w, + pData.width / 2.0, pData.height /2.0, lColor.x, lColor.y, lColor.z, lColor.w, + pData.width / 2.0, -pData.height / 2.0, lColor.x, lColor.y, lColor.z, lColor.w, + -pData.width / 2.0, -pData.height / 2.0, lColor.x, lColor.y, lColor.z, lColor.w ]; pGl.bufferData(pGl.ARRAY_BUFFER, new Float32Array(lBufferedData), pGl.STATIC_DRAW) pGl.bindBuffer(pGl.ARRAY_BUFFER, undefined); pData.getMomentOfInertia = function() { - return Math.pow(pData.width * pData.height, 3.0) / 12.0; + return (1.0 / 12.0) * pData.mass * (pData.height * pData.height + pData.width * pData.width); }; return makeRigidBody2({ diff --git a/frontend/_shared/math/vec2.js b/frontend/_shared/math/vec2.js index cff38ce..11e71cd 100644 --- a/frontend/_shared/math/vec2.js +++ b/frontend/_shared/math/vec2.js @@ -53,4 +53,12 @@ function negate2(v) { x: -v.x, y: -v.y }; +} + +// Algorithm plucked from: https://matthew-brett.github.io/teaching/rotation_2d.html +function rotateAboutOrigin2(v, angle) { + return { + x: v.x * Math.cos(angle) - v.y * Math.sin(angle), + y: v.x * Math.sin(angle) + v.y * Math.cos(angle), + }; }
\ No newline at end of file diff --git a/frontend/index.css b/frontend/index.css index 00ea6e9..5f9619c 100644 --- a/frontend/index.css +++ b/frontend/index.css @@ -1,19 +1,23 @@ body { font-size: 16px; - font-family: "Open Sans", sans-serif; + font-family: "Helvetica", sans-serif; + line-height: 1.3rem; width: 100%; height: 100vh; - display: flex; - flex-direction: column; - align-items: center; margin: 0rem; padding: 0rem; + overflow-x: auto; + overflow-y: auto; } header { + background-color: lemonchiffon; padding-top: 1rem; + padding-bottom: 1rem; + font-size: 20px; + margin: auto; width: 1260px; - flex: 0 1 auto; + max-width: 1260px; text-align: center; } @@ -27,10 +31,11 @@ main { display: flex; flex-direction: row; color: #1a1a1a; - width: 1200px; + width: 1260px; + margin: auto; + max-width: 1260px; background-color: white; flex: 1 1 100%; - height: 100%; margin-bottom: 1rem; overflow-y: hidden; } @@ -38,13 +43,15 @@ main { section { min-width: 74%; width: 74%; - overflow-y: auto; + overflow-y: hidden; } /* Navigation bar */ nav { + padding-top: 1rem; + position: sticky; width: 20%; min-width: 20%; max-width: 20%; @@ -55,7 +62,6 @@ nav { text-align: left; font-size: 1rem; user-select: none; - border-right: 1px solid black;; } .outer-tree { @@ -201,7 +207,7 @@ article .opengl_canvas_container .play_button { height: 128px; position: absolute; top: calc(50% - 56px); - left: calc(50% - 184px); + left: calc(50% - 214px); border: 1px solid white; color: white; background-color: transparent; |