diff options
Diffstat (limited to 'frontend')
-rw-r--r-- | frontend/2d/_collisions/circle_line.html (renamed from frontend/2d/_collisions/part_1.html) | 22 | ||||
-rw-r--r-- | frontend/2d/_collisions/circle_line.js | 178 | ||||
-rw-r--r-- | frontend/2d/_collisions/collisions_1.js | 125 | ||||
-rw-r--r-- | frontend/2d/_collisions/rectangle_line.html | 52 | ||||
-rw-r--r-- | frontend/2d/_collisions/rectangle_line.js | 105 | ||||
-rw-r--r-- | frontend/2d/_rigidbody/rigidbody_2.js | 2 | ||||
-rw-r--r-- | frontend/2d/_rigidbody/rigidbody_3a.js | 10 | ||||
-rw-r--r-- | frontend/2d/_rigidbody/rigidbody_3b.js | 8 | ||||
-rw-r--r-- | frontend/_shared/2d/shader.js | 18 | ||||
-rw-r--r-- | frontend/_shared/math/circle.js | 2 | ||||
-rw-r--r-- | frontend/_shared/math/collision.js | 25 | ||||
-rw-r--r-- | frontend/_shared/math/line2.js | 12 | ||||
-rw-r--r-- | frontend/_shared/math/point2.js | 16 | ||||
-rw-r--r-- | frontend/_shared/math/rectangle.js | 32 | ||||
-rw-r--r-- | frontend/_shared/math/rigidbody2.js | 57 | ||||
-rw-r--r-- | frontend/index.css | 11 | ||||
-rw-r--r-- | frontend/index.js | 2 | ||||
-rw-r--r-- | frontend/navbar.html | 9 |
18 files changed, 513 insertions, 173 deletions
diff --git a/frontend/2d/_collisions/part_1.html b/frontend/2d/_collisions/circle_line.html index 06a4b3d..038c759 100644 --- a/frontend/2d/_collisions/part_1.html +++ b/frontend/2d/_collisions/circle_line.html @@ -13,10 +13,11 @@ <script src="/_shared/math/mat4.js"></script> <script src="/_shared/math/circle.js"></script> <script src="/_shared/math/line2.js"></script> + <script src="/_shared/math/point2.js"></script> <script src="/_shared/math/collision.js"></script> <script src="/_shared/2d/shader.js"></script> <script src="/_shared/2d/program_common.js"></script> - <script src="collisions_1.js"></script> + <script src="circle_line.js"></script> </head> <body> <header> @@ -26,28 +27,15 @@ <nav> </nav> <section> - <h1>Part 1: Circle-Line</h1> + <h1>Circle-Line</h1> <article> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> - <div id="collision_1" class="opengl_canvas_container"> + <div id="circle_line_collision" class="opengl_canvas_container"> <canvas width="640" height="480"></canvas> <div class="opengl_canvas_sidebar"> - <ul class="opengl_value_tracker"> - <li><b>Linear Force:</b><span id="rigidbody_1_force_field">N/A</span></li> - <li><b>Linear Acceleration:</b><span id="rigidbody_1_acceleration_field">N/A</span></li> - <li><b>Linear Velocity:</b><span id="rigidbody_1_velocity_field">N/A</span></li> - <li><b>Linear Position:</b><span id="rigidbody_1_position_field">N/A</span></li> - </ul> - <form id="rigidbody_1_force_submit_button" style="text-align: right; padding: 0.5rem;"> - <div class="vec2_input_group"> - <label>Force Vector</label> - <input class="vec2_x_input" type="number" placeholder="X (Default 0)"/> - <input class="vec2_y_input" type="number" placeholder="Y (Default 5000 N)"/> - </div> - <input type="submit" value="Apply Force"></input> - </form> + </div> <button class="play_button"> Play 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 @@ +/// <reference path="../../scripts/jquery-3.5.1.min.js"/> +/// <reference path="../../_shared/math/vec2.js" /> +/// <reference path="../../_shared/math/mat4.js" /> +/// <reference path="../../_shared/2d/shader.js" /> +/// <reference path="../../_shared/math/circle.js" /> +/// <reference path="../../_shared/math/line2.js" /> +/// <reference path="../../_shared/math/collision.js" /> +/// <reference path="../../_shared/2d/program_common.js" /> + +(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 diff --git a/frontend/2d/_collisions/collisions_1.js b/frontend/2d/_collisions/collisions_1.js deleted file mode 100644 index ac325c2..0000000 --- a/frontend/2d/_collisions/collisions_1.js +++ /dev/null @@ -1,125 +0,0 @@ -/// <reference path="../../scripts/jquery-3.5.1.min.js"/> -/// <reference path="../../_shared/math/vec2.js" /> -/// <reference path="../../_shared/math/mat4.js" /> -/// <reference path="../../_shared/2d/shader.js" /> -/// <reference path="../../_shared/math/circle.js" /> -/// <reference path="../../_shared/math/line2.js" /> -/// <reference path="../../_shared/math/collision.js" /> -/// <reference path="../../_shared/2d/program_common.js" /> - -(function() { - - function main() { - // Define Constants - const CIRCLE_RADIUS = 16; - const GRAVITY = 9.8; - const COF_OF_RESTITUITION = 0.7; - - // Retrieve context - const lProgramContext = getContext('#collision_1'); - - 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 Circle-Line Collisions'); - lProgramContext.load().then(function(pProgramInfo) { - const mCircle = 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)); - - const mline = line2({ x: 100, y: 100 }, { x: lProgramContext.width - 100, y: 200 }, lProgramContext.gl, - { x: 1, y: 0, z: 0, w: 1 }, 2.0); - - /** - * 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 = getMomentOfInertia(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; - updateCircle(mCircle, pDeltaTimeSeconds); - collision(pDeltaTimeSeconds); - render(); - } - - function collision(pDeltaTimeSeconds) { - lineCircleCollision2(mCircle, mline); - return false; - } - - 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, mCircle); - renderLine2(lProgramContext.gl, pProgramInfo, mline); - } - - 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(mCircle.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(); - } - - const lExitRequestFunc = requestUpdateLoop(update, cleanup); - lProgramContext.stopButton.on('click', reset); - }); - } - - lProgramContext.playButton.on('click', run); - } - - $(document).ready(main); -})()
\ No newline at end of file diff --git a/frontend/2d/_collisions/rectangle_line.html b/frontend/2d/_collisions/rectangle_line.html new file mode 100644 index 0000000..5db958c --- /dev/null +++ b/frontend/2d/_collisions/rectangle_line.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <script src="/scripts/jquery-3.5.1.min.js"></script> + <script src="/index.js"></script> + <link rel="stylesheet" href="/index.css"> + <link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon"> + <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,300" rel="stylesheet" type="text/css"> + <title>Physics for Games</title> + + <script src="/_shared/math/vec2.js"></script> + <script src="/_shared/math/mat4.js"></script> + <script src="/_shared/math/rectangle.js"></script> + <script src="/_shared/math/line2.js"></script> + <script src="/_shared/math/point2.js"></script> + <script src="/_shared/math/collision.js"></script> + <script src="/_shared/math/rigidbody2.js"></script> + <script src="/_shared/2d/shader.js"></script> + <script src="/_shared/2d/program_common.js"></script> + <script src="rectangle_line.js"></script> + </head> + <body> + <header> + <h1>Physics for Games</h1> + </header> + <main> + <nav> + </nav> + <section> + <h1>Rectangle-Line</h1> + <article> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + </p> + <div id="rectangle_line_collision" class="opengl_canvas_container"> + <canvas width="640" height="480"></canvas> + <div class="opengl_canvas_sidebar"> + + </div> + <button class="play_button"> + Play + </button> + <button class="stop_button"> + Stop + </button> + </div> + </article> + </section> + </main> + </body> +</html>
\ No newline at end of file diff --git a/frontend/2d/_collisions/rectangle_line.js b/frontend/2d/_collisions/rectangle_line.js new file mode 100644 index 0000000..ac73f71 --- /dev/null +++ b/frontend/2d/_collisions/rectangle_line.js @@ -0,0 +1,105 @@ +/// <reference path="../../scripts/jquery-3.5.1.min.js"/> +/// <reference path="../../_shared/math/vec2.js" /> +/// <reference path="../../_shared/math/mat4.js" /> +/// <reference path="../../_shared/2d/shader.js" /> +/// <reference path="../../_shared/math/rectangle.js" /> +/// <reference path="../../_shared/math/line2.js" /> +/// <reference path="../../_shared/math/collision.js" /> +/// <reference path="../../_shared/2d/program_common.js" /> + +(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: 25, + 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 }, 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; + updateRigidBody2(rectangleObject, pDeltaTimeSeconds); + collision(pDeltaTimeSeconds); + render(); + } + + function collision(pDeltaTimeSeconds) { + + } + + 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); +})()
\ No newline at end of file diff --git a/frontend/2d/_rigidbody/rigidbody_2.js b/frontend/2d/_rigidbody/rigidbody_2.js index 4632b11..76d8047 100644 --- a/frontend/2d/_rigidbody/rigidbody_2.js +++ b/frontend/2d/_rigidbody/rigidbody_2.js @@ -43,7 +43,7 @@ // Angular code starts here // Retrieve the moment of inertia for our shape (Ours is a circle by default) - const lMomentOfInertia = getMomentOfInertia(lCircle); + const lMomentOfInertia = getCircleMomentOfInertia(lCircle); // Calculate the angular acceperation (omega = T / I) const lAngularAcceleration = lCircle.torque / lMomentOfInertia; diff --git a/frontend/2d/_rigidbody/rigidbody_3a.js b/frontend/2d/_rigidbody/rigidbody_3a.js index d38f1da..ef9577b 100644 --- a/frontend/2d/_rigidbody/rigidbody_3a.js +++ b/frontend/2d/_rigidbody/rigidbody_3a.js @@ -62,7 +62,7 @@ pCircle.force = vec2(); // Same physics updates from part 2 - const lMomentOfInertia = getMomentOfInertia(pCircle); + const lMomentOfInertia = getCircleMomentOfInertia(pCircle); const lAngularAcceleration = pCircle.torque / lMomentOfInertia; pCircle.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds; pCircle.rotationRadians += pCircle.rotationVelocity * pDeltaTimeSeconds; @@ -118,8 +118,8 @@ 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) / getMomentOfInertia(horizontalCircle)) - + (Math.pow(dot2(lSecondPerp, lCollisionNormal), 2) / getMomentOfInertia(verticalCircle)) + const lRotationalDenomPart = (Math.pow(dot2(lFirstPerp, lCollisionNormal), 2) / getCircleMomentOfInertia(horizontalCircle)) + + (Math.pow(dot2(lSecondPerp, lCollisionNormal), 2) / getCircleMomentOfInertia(verticalCircle)) const lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart); @@ -129,8 +129,8 @@ 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)) / getMomentOfInertia(horizontalCircle); - verticalCircle.rotationVelocity = verticalCircle.rotationVelocity - dot2(lSecondPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getMomentOfInertia(verticalCircle); + 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); diff --git a/frontend/2d/_rigidbody/rigidbody_3b.js b/frontend/2d/_rigidbody/rigidbody_3b.js index 803161a..ddb4c70 100644 --- a/frontend/2d/_rigidbody/rigidbody_3b.js +++ b/frontend/2d/_rigidbody/rigidbody_3b.js @@ -62,7 +62,7 @@ // Same physics updates from part 2 - const lMomentOfInertia = getMomentOfInertia(pCircle); + const lMomentOfInertia = getCircleMomentOfInertia(pCircle); const lAngularAcceleration = pCircle.torque / lMomentOfInertia; pCircle.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds; pCircle.rotationRadians += pCircle.rotationVelocity * pDeltaTimeSeconds; @@ -119,14 +119,14 @@ const lNumerator = dot2(scaleVec2(lRelativeVelocity, -(1.0 + COF_OF_RESTITUITION)), lCollisionNormal); const lLinearDenomPart = dot2(lCollisionNormal, (scaleVec2(lCollisionNormal, 1 / lBall.mass))); - const lRotationalDenomPart = (Math.pow(dot2(lFirstPerp, lCollisionNormal), 2) / getMomentOfInertia(lPeg)) - + (Math.pow(dot2(lSecondPerp, lCollisionNormal), 2) / getMomentOfInertia(lBall)) + const lRotationalDenomPart = (Math.pow(dot2(lFirstPerp, lCollisionNormal), 2) / getCircleMomentOfInertia(lPeg)) + + (Math.pow(dot2(lSecondPerp, lCollisionNormal), 2) / getCircleMomentOfInertia(lBall)) const lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart); lBall.position = lSubdividedBall.position; // Move the ball back to its proper subdivided position lBall.velocity = subVec2(lBall.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / lBall.mass)); - lBall.rotationVelocity = lBall.rotationVelocity - dot2(lSecondPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getMomentOfInertia(lBall); + lBall.rotationVelocity = lBall.rotationVelocity - dot2(lSecondPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getCircleMomentOfInertia(lBall); // Now we update in our new direction with the remaining time that we have left. updateCircle(lBall, pDeltaTimeSeconds - lSubdividedDeltaTime); diff --git a/frontend/_shared/2d/shader.js b/frontend/_shared/2d/shader.js index 4deddb5..7e85a9e 100644 --- a/frontend/_shared/2d/shader.js +++ b/frontend/_shared/2d/shader.js @@ -28,7 +28,7 @@ function loadOrthographicShader(pGl) { return null; } - return { + var lRetval = { program: lShaderProgram, attributeLocations: { position: pGl.getAttribLocation(lShaderProgram, 'position'), @@ -38,7 +38,23 @@ function loadOrthographicShader(pGl) { projection: pGl.getUniformLocation(lShaderProgram, 'projection'), model: pGl.getUniformLocation(lShaderProgram, 'model') } + }; + + lRetval.renderShape = function(pShape) { + pGl.uniformMatrix4fv(lRetval.uniformLocations.model, false, pShape.model); + pGl.bindBuffer(pGl.ARRAY_BUFFER, pShape.buffer); + { + pGl.enableVertexAttribArray(lRetval.attributeLocations.position); + pGl.vertexAttribPointer(lRetval.attributeLocations.position, 2, pGl.FLOAT, false, BYTES_PER_FLOAT * 6, 0); + + pGl.enableVertexAttribArray(lRetval.attributeLocations.color); + pGl.vertexAttribPointer(lRetval.attributeLocations.color, 4, pGl.FLOAT, false, BYTES_PER_FLOAT * 6, BYTES_PER_FLOAT * 2); + } + + pGl.drawArrays(pGl.TRIANGLES, 0, pShape.vertexCount); } + + return lRetval; } return fetch('/_shared/2d/shaders/orthographic.vert').then(function(pResponse) { diff --git a/frontend/_shared/math/circle.js b/frontend/_shared/math/circle.js index 340d7dc..ba33021 100644 --- a/frontend/_shared/math/circle.js +++ b/frontend/_shared/math/circle.js @@ -71,7 +71,7 @@ function renderCircle(pGl, pProgramInfo, pCircle) { pGl.drawArrays(pGl.TRIANGLE_STRIP, 0, pCircle.vertexCount); } -function getMomentOfInertia(pCircle) { +function getCircleMomentOfInertia(pCircle) { return (Math.PI * Math.pow(pCircle.radius, 4)) / 4; } diff --git a/frontend/_shared/math/collision.js b/frontend/_shared/math/collision.js index 74ec5d8..8e4be7d 100644 --- a/frontend/_shared/math/collision.js +++ b/frontend/_shared/math/collision.js @@ -2,22 +2,25 @@ /// <reference path="line2.js" /> /// <reference path="circle.js" /> /// <reference path="mat4.js" /> +/// <reference path="point2.js" /> /** * - * @param {circle} pCricle + * @param {circle} pCircle * @param {line2} pLine */ -function lineCircleCollision2(pCricle, pLine) { - // We have a triangle formed by circle's position P and the two points of the line, A and B. - var lSlope = (pCricle.position.y - pLine.start.y) / (pCricle.position.x - pLine.start.x), - lAngle = Math.atan(lSlope), - lAngleDiff = lAngle - pLine.angle, - lPositionToStart = subVec2(pCricle.position, pLine.start), - lPosToStartLength = length2(lPositionToStart), - lHeight = lPosToStartLength * Math.sin(lAngleDiff); +function lineCircleCollision2(pCircle, pLine) { + return distanceFromPoint2ToLine2(pCircle.position, pLine) <= pCircle.radius; +} - if (Math.abs(lHeight - pCricle.radius) < pCricle.radius) { - console.log('Intersection'); +function getLineCircleCollison2Data(pCircle, pLine) { + const lCollisionNormal = pLine.normal, + lCollisionPoint = addVec2(pCircle.position, scaleVec2(negate2(lCollisionNormal), pCircle.radius)); + + return { + relativeVelocity: subVec2(pCircle.velocity, pLine.velocity), + collisionNormal: lCollisionNormal, + firstPointOfApplication: subVec2(lCollisionPoint, pCircle.position), + secondPointOfApplication: subVec2(lCollisionPoint, pLine.start) } }
\ No newline at end of file diff --git a/frontend/_shared/math/line2.js b/frontend/_shared/math/line2.js index d8ab096..a9e72be 100644 --- a/frontend/_shared/math/line2.js +++ b/frontend/_shared/math/line2.js @@ -7,7 +7,7 @@ * @param {vec2} pEnd * @param {WebGLRenderingContext} pGl */ -function line2(pStart, pEnd, pGl, pColor, pThickness) { +function line2(pStart, pEnd, pGl, pColor, pMass) { const lDiffVector = subVec2(pEnd, pStart); const lBuffer = pGl.createBuffer(); @@ -28,12 +28,18 @@ function line2(pStart, pEnd, pGl, pColor, pThickness) { start: pStart, end: pEnd, slope: lSlope, - angle: Math.atan(lSlope), + normal: normalize2(getPerp2(lDiffVector)), length: length2(lDiffVector), - direction: normalize2(lDiffVector) + mass: pMass, + direction: normalize2(lDiffVector), + velocity: vec2(0, 0) }; } +function getLine2MomentOfInertia(pLine) { + return (1.0 / 12.0) * pLine.mass * Math.pow(pLine.length, 2); +} + 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/point2.js b/frontend/_shared/math/point2.js new file mode 100644 index 0000000..eeacd91 --- /dev/null +++ b/frontend/_shared/math/point2.js @@ -0,0 +1,16 @@ +function point2(x = 0, y = 0) { + return { x: x, y: y }; +} + +function distanceFromPoint2ToLine2(pPoint, pLine) { + // Check out this wikapedia article for more information: + // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line + var x0 = pPoint.x, + y0 = pPoint.y, + x1 = pLine.start.x, + y1 = pLine.start.y, + x2 = pLine.end.x, + y2 = pLine.end.y; + + return Math.abs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); +}
\ No newline at end of file diff --git a/frontend/_shared/math/rectangle.js b/frontend/_shared/math/rectangle.js new file mode 100644 index 0000000..c24fa12 --- /dev/null +++ b/frontend/_shared/math/rectangle.js @@ -0,0 +1,32 @@ +/// <reference path="rigidbody2.js" /> + +function rectangle(pGl, pData) { + var lBuffer = pGl.createBuffer(), + lColor = pData.color || { x: 1, y: 0, z: 0, w: 1 }; + + 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 + ]; + + 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 makeRigidBody2({ + vertexCount: 6, + buffer: lBuffer, + width: pData.width, + height: pData.height, + model: translateMatrix(mat4(), pData.position ? pData.position.x : 0, pData.position ? pData.position.y : 0, 0), + }, pData); +}
\ No newline at end of file diff --git a/frontend/_shared/math/rigidbody2.js b/frontend/_shared/math/rigidbody2.js new file mode 100644 index 0000000..0b18d0c --- /dev/null +++ b/frontend/_shared/math/rigidbody2.js @@ -0,0 +1,57 @@ +/// <reference path="vec2.js" /> +/// <reference path="mat4.js" /> +/// <reference path="circle.js" /> +/// <reference path="line2.js" /> +/// <reference path="collision.js" /> + +const GRAVITY = 9.8; +const BYTES_PER_FLOAT = 4; + +/** + * + * @param {Shape} pObject a 2D shape like a circle, rectangle, or line + * @param {Object} pData initial data for the rigidboy + */ +function makeRigidBody2(pObject, pData) { + pObject.velocity = vec2(); + pObject.prevVelocity = pObject.velocity; + pObject.position = pData.position || vec2(); + pObject.prevPosition = pObject.position; + pObject.force = vec2(); + pObject.rotationVelocity = 0; + pObject.rotationRadians = 0; + pObject.torque = 0; + pObject.mass = pData.mass !== undefined ? pData.mass : 1.0; + pObject.getMomentOfInertia = pData.getMomentOfInertia; + + return pObject; +} + +function updateRigidBody2(pRigidbody, pDeltaTimeSeconds) { + applyForceRigidbody2(pRigidbody, vec2(0, -1.0 * (pRigidbody.mass * GRAVITY))); + const lCurrentAcceleration = scaleVec2(pRigidbody.force, 1.0 / pRigidbody.mass); + pRigidbody.prevVelocity = pRigidbody.velocity; + pRigidbody.velocity = addVec2(pRigidbody.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds)); + pRigidbody.prevPos = { ...pRigidbody.position }; + pRigidbody.position = addVec2(pRigidbody.position, scaleVec2(pRigidbody.velocity, pDeltaTimeSeconds)); + pRigidbody.force = vec2(); + + const lMomentOfInertia = pRigidbody.getMomentOfInertia(pRigidbody); + const lAngularAcceleration = pRigidbody.torque / lMomentOfInertia; + pRigidbody.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds; + pRigidbody.rotationRadians += pRigidbody.rotationVelocity * pDeltaTimeSeconds; + pRigidbody.torque = 0; + + pRigidbody.model = rotateMatrix2d(translateMatrix(mat4(), pRigidbody.position.x, pRigidbody.position.y, 0), pRigidbody.rotationRadians); +} + +function applyForceRigidbody2(pRigidbody, pForceVector, pPointOfApplication) { + if (pPointOfApplication !== undefined) { + const lOriginToPointOfApp = subVec2(vec2(), pPointOfApplication), + lPerpVec = vec2(-lOriginToPointOfApp.y, lOriginToPointOfApp.x); + + pRigidbody.torque += TORQUE_MULTIPLIER * dot2(lPerpVec, pForceVector); + } + + pRigidbody.force = addVec2(pRigidbody.force, pForceVector); +} diff --git a/frontend/index.css b/frontend/index.css index 729768c..00ea6e9 100644 --- a/frontend/index.css +++ b/frontend/index.css @@ -77,6 +77,7 @@ nav { } .outer-tree > li > span > i { + transform: rotate(0deg); transition: 100ms ease-in-out transform; } @@ -85,6 +86,10 @@ nav { transform: rotate(180deg); } +.outer-tree li > span > span { + padding-left: 1rem;; +} + .inner-tree { padding-left: 1rem; display: none; @@ -186,6 +191,7 @@ article .opengl_canvas_container { display: flex; flex-direction: row; justify-content: center; + border-radius: 3px; } article .opengl_canvas_container .play_button { @@ -206,6 +212,11 @@ article .opengl_canvas_container .play_button { justify-content: center; } +.opengl_canvas_container canvas { + border-radius: 3px 0px 0px 3px; + border: 1px solid black; +} + article .opengl_canvas_container .play_button:hover { cursor: pointer; } diff --git a/frontend/index.js b/frontend/index.js index b943463..d0f27f3 100644 --- a/frontend/index.js +++ b/frontend/index.js @@ -26,7 +26,7 @@ } if (lSplitPath[1] === '2d') { - $('nav > ul > li:first-child').addClass('expanded'); + $('nav > ul > li:nth-child(2)').addClass('expanded'); $('nav > ul > li:last-child').removeClass('expanded'); } }); diff --git a/frontend/navbar.html b/frontend/navbar.html index 73c4fd3..01cdc72 100644 --- a/frontend/navbar.html +++ b/frontend/navbar.html @@ -1,18 +1,19 @@ <ul class="outer-tree"> + <li><a href="/" class="nav-selected">Introduction</a></li> <li> - <span><i>▲</i>2D</span> + <span><i>▲</i><span>2D</span></span> <ul class="inner-tree"> <li><label>Rigidbody</label></li> - <li><a href="/" class="nav-selected">Introduction</a></li> <li><a href="/2d/_rigidbody/part_1.html">2D - Linear Forces</a></li> <li><a href="/2d/_rigidbody/part_2.html">2D - Rotational Forces</a></li> <li><a href="/2d/_rigidbody/part_3.html">2D - Collision Forces</a></li> <li><label>Collisions</label></li> - <li><a href="/2d/_collisions/part_1.html">Circle-Line</a></li> + <li><a href="/2d/_collisions/circle_line.html">Circle-Line</a></li> + <li><a href="/2d/_collisions/rectangle_line.html">Rectangle-Line</a></li> </ul> </li> <li> - <span><i>▲</i>3D</span> + <span><i>▲</i><span>3D</span></span> </li> </ul>
\ No newline at end of file |