diff --git a/.glitch-assets b/.glitch-assets new file mode 100644 index 00000000..9c62638b --- /dev/null +++ b/.glitch-assets @@ -0,0 +1,13 @@ +{"name":"drag-in-files.svg","date":"2016-10-22T16:17:49.954Z","url":"https://cdn.hyperdev.com/drag-in-files.svg","type":"image/svg","size":7646,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/drag-in-files.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(102, 153, 205)","uuid":"adSBq97hhhpFNUna"} +{"name":"click-me.svg","date":"2016-10-23T16:17:49.954Z","url":"https://cdn.hyperdev.com/click-me.svg","type":"image/svg","size":7116,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/click-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(243, 185, 186)","uuid":"adSBq97hhhpFNUnb"} +{"name":"paste-me.svg","date":"2016-10-24T16:17:49.954Z","url":"https://cdn.hyperdev.com/paste-me.svg","type":"image/svg","size":7242,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/paste-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(42, 179, 185)","uuid":"adSBq97hhhpFNUnc"} +{"uuid":"adSBq97hhhpFNUna","deleted":true} +{"uuid":"adSBq97hhhpFNUnb","deleted":true} +{"uuid":"adSBq97hhhpFNUnc","deleted":true} +{"name":"b737.glb","date":"2020-10-08T19:15:20.408Z","url":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fb737.glb","type":"","size":857042,"thumbnail":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fthumbnails%2Fb737.glb","thumbnailWidth":210,"thumbnailHeight":210,"uuid":"yfRUm40za1VPZ7m4"} +{"uuid":"yfRUm40za1VPZ7m4","deleted":true} +{"name":"b737.glb","date":"2020-10-08T19:18:54.351Z","url":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fb737.glb","type":"","size":857042,"thumbnail":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fthumbnails%2Fb737.glb","thumbnailWidth":210,"thumbnailHeight":210,"uuid":"YIwdAeySYmSmUYs1"} +{"name":"material_baseColor.png","date":"2020-10-08T20:04:24.215Z","url":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fmaterial_baseColor.png","type":"image/png","size":2552559,"imageWidth":2048,"imageHeight":2048,"thumbnail":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fthumbnails%2Fmaterial_baseColor.png","thumbnailWidth":330,"thumbnailHeight":330,"uuid":"24rYqTHVZFJEpMCw"} +{"name":"scene.bin","date":"2020-10-08T20:17:34.518Z","url":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fscene.bin","type":"application/octet-stream","size":2036060,"thumbnail":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fthumbnails%2Fscene.bin","thumbnailWidth":210,"thumbnailHeight":210,"uuid":"XyeHrD6NuepCjB3n"} +{"name":"scene.gltf","date":"2020-10-08T20:17:38.072Z","url":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fscene.gltf","type":"","size":4481,"thumbnail":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2Fthumbnails%2Fscene.gltf","thumbnailWidth":210,"thumbnailHeight":210,"uuid":"qDQn8CT4wcBR78xg"} +{"name":"infoButtonImage.png","date":"2020-10-12T00:40:25.843Z","url":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2FinfoButtonImage.png","type":"image/png","size":2144,"imageWidth":50,"imageHeight":50,"thumbnail":"https://cdn.glitch.com/2655b22a-16eb-431c-a433-a3bf6500f396%2FinfoButtonImage.png","thumbnailWidth":50,"thumbnailHeight":50,"uuid":"5T2mR9G124fbQj8W"} diff --git a/README.md b/README.md index b61d3e2b..ad539c74 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,14 @@ -Assignment 4 - Creative Coding: Interactive Multimedia Experiences -=== +## ThreeJS 3d game +http://a4-garrett-smith.glitch.me/ -Due: October 11th, by 11:59 PM. +Has a ball within confines of a box controlled by wasd, camera changeable with mouse drag and scroll. Incomplete, future additions were to add a collectable coin that increases maximum speed and obstacles that 'kill' the player. -For this assignment we will focus on client-side development using popular audio/graphics/visualization technologies; the server requirements are minimal. The goal of this assignment is to refine our JavaScript knowledge while exploring the multimedia capabilities of the browser. +# Challenges +- Implementing collision physics +- Working with external models, eventually gave up on this +- Controlling movement with vectors -Baseline Requirements ---- +# Technical Achievements +Implements believable collision physics with wall and friction deceleration. -Your application is required to implement the following functionalities: -- A server created using Express. This server can be as simple as needed. -- A client-side interactive experience using at least one of the following web technologies frameworks. - - [Three.js](https://threejs.org/): A library for 3D graphics / VR experiences - - [D3.js](https://d3js.org): A library that is primarily used for interactive data visualizations - - [Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API): A 2D raster drawing API included in all modern browsers - - [SVG](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API): A 2D vector drawing framework that enables shapes to be defined via XML. - - [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API): An API for audio synthesis, analysis, processing, and file playback. -- A user interface for interaction with your project, which must expose at least six parameters for user control. [dat.gui](https://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage) is highly recommended for this. You might also explore interaction by tracking mouse movement via the `window.onmousemove` event handler in tandem with the `event.clientX` and `event.clientY` properties. Consider using the [Pointer Events API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) to ensure that that mouse and touch events will both be supported in your app. -- Your application should display basic documentation for the user interface when the application first loads. This documentation should be dismissable, however, users should be able to redisplay it via either a help buton (this could, for example, be inside a dat.gui interface) or via a keyboard shortcut (commonly the question mark). -- Your HTML and CSS should validate. There are options/plugins for most IDEs to check validation. - -The interactive experience should possess a reasonable level of complexity. Some examples: -### Three.js -- A generative algorithm creates simple agents that move through a virtual world. Your interface controls the behavior / appearance of these agents. -- A simple 3D game -- An 3D audio visualization of a song of your choosing. User interaction should control aspects of the visualization. -### Canvas -- Implement a generative algorithm such as [Conway's Game of Life](https://bitstorm.org/gameoflife/) (or 1D cellular automata) and provide interactive controls. Note that the Game of Life has been created by 100s of people using ; we'll be checking to ensure that your implementation is not a copy of these. -- Design a 2D audio visualizer of a song of your choosing. User interaction should control visual aspects of the experience. -### Web Audio API -- Create a screen-based musical instrument using the Web Audio API. You can use projects such as [Interface.js](http://charlie-roberts.com/interface/) or [Nexus UI](https://nexus-js.github.io/ui/api/#Piano) to provide common musical interface elements, or use dat.GUI in combination with mouse/touch events (use the Pointer Events API). Your GUI should enable users to control aspects of sound synthesis. -### D3.js -- Create visualizations using the datasets found at [Awesome JSON Datasets](https://github.com/jdorfman/Awesome-JSON-Datasets). Experiment with providing different visualizations of the same data set, and providing users interactive control over visualization parameters and/or data filtering. Alternatively, create a single visualization with using one of the more complicated techniques shown at [d3js.org](d3js.org) and provide meaningful points of interaction for users. - -Deliverables ---- - -Do the following to complete this assignment: - -1. Implement your project with the above requirements. -3. Test your project to make sure that when someone goes to your main page on Glitch/Heroku/etc., it displays correctly. -4. Ensure that your project has the proper naming scheme `a4-firstname-lastname` so we can find it. -5. Fork this repository and modify the README to the specifications below. *NOTE: If you don't use Glitch for hosting (where we can see the files) then you must include all project files that you author in your repo for this assignment*. -6. Create and submit a Pull Request to the original repo. Name the pull request using the following template: `a4-firstname-lastname`. - -Sample Readme (delete the above when you're ready to submit, and modify the below so with your links and descriptions) ---- - -## Your Web Application Title - -your hosting link e.g. http://a4-charlieroberts.glitch.me - -Include a very brief summary of your project here. Images are encouraged when needed, along with concise, high-level text. Be sure to include: - -- the goal of the application -- challenges you faced in realizing the application -- the instructions you present in the website should be clear enough to use the application, but if you feel any need to provide additional instructions please do so here. diff --git a/models/yak/scene.bin b/models/yak/scene.bin new file mode 100644 index 00000000..e5f2f11c Binary files /dev/null and b/models/yak/scene.bin differ diff --git a/models/yak/scene.gltf b/models/yak/scene.gltf new file mode 100644 index 00000000..6128ea37 --- /dev/null +++ b/models/yak/scene.gltf @@ -0,0 +1,253 @@ +{ + "accessors": [ + { + "bufferView": 2, + "componentType": 5126, + "count": 39373, + "max": [ + 263.07180786132812, + 214.05734252929688, + 147.8524169921875 + ], + "min": [ + -262.70651245117188, + -206.63665771484375, + -2.0941925048828125 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 472476, + "componentType": 5126, + "count": 39373, + "max": [ + 0.99999672174453735, + 0.99989336729049683, + 0.99999821186065674 + ], + "min": [ + -0.99999076128005981, + -0.99998182058334351, + -0.99999982118606567 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 39373, + "max": [ + 0.98252224922180176, + 0.99188995361328125 + ], + "min": [ + 0.0098431305959820747, + 0.010617733001708984 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "componentType": 5125, + "count": 194031, + "max": [ + 39372 + ], + "min": [ + 0 + ], + "type": "SCALAR" + } + ], + "asset": { + "extras": { + "author": "Ashkelon (https://sketchfab.com/Ashkelon)", + "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", + "source": "https://sketchfab.com/3d-models/yak-cdd952b6a19a4975aa00feb5b58e4fa9", + "title": "YAK" + }, + "generator": "Sketchfab-9.37.0", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 776124, + "byteOffset": 0, + "name": "floatBufferViews", + "target": 34963 + }, + { + "buffer": 0, + "byteLength": 314984, + "byteOffset": 776124, + "byteStride": 8, + "name": "floatBufferViews", + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 944952, + "byteOffset": 1091108, + "byteStride": 12, + "name": "floatBufferViews", + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 2036060, + "uri": "scene.bin" + } + ], + "images": [ + { + "uri": "textures/material_baseColor.png" + } + ], + "materials": [ + { + "doubleSided": true, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "name": "material", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "baseColorTexture": { + "index": 0, + "texCoord": 0 + }, + "metallicFactor": 0.62796788452535135, + "roughnessFactor": 0.20786744006469304 + } + } + ], + "meshes": [ + { + "name": "YAK_Yak_0", + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 0, + "TEXCOORD_0": 2 + }, + "indices": 3, + "material": 0, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "name": "RootNode (gltf orientation matrix)", + "rotation": [ + -0.70710678118654746, + -0, + -0, + 0.70710678118654757 + ] + }, + { + "children": [ + 2 + ], + "name": "RootNode (model correction matrix)" + }, + { + "children": [ + 3 + ], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + -1, + 0, + 0, + 0, + 0, + 0, + 1 + ], + "name": "5b88abda07614cccb2bb00b1dd52d41d.fbx" + }, + { + "children": [ + 4 + ], + "name": "RootNode" + }, + { + "children": [ + 5 + ], + "matrix": [ + -7.3159570948604751, + -9.1489929777258649e-09, + -0.56638749288422541, + 0, + -0.5663874928842163, + 1.4280235895042443e-06, + 7.3159570948603365, + 0, + 1.0110334704754538e-07, + 7.3378486633299378, + -1.4244694397415929e-06, + 0, + 1.862645149230957e-07, + 0, + 0, + 1 + ], + "name": "YAK" + }, + { + "mesh": 0, + "name": "YAK_Yak_0" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "scene": 0, + "scenes": [ + { + "name": "OSG_Scene", + "nodes": [ + 0 + ] + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ] +} + diff --git a/models/yak/textures/material_baseColor.png b/models/yak/textures/material_baseColor.png new file mode 100644 index 00000000..0e1be1bb Binary files /dev/null and b/models/yak/textures/material_baseColor.png differ diff --git a/package.json b/package.json new file mode 100644 index 00000000..ae721d87 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "//1": "describes your app and its dependencies", + "//2": "https://docs.npmjs.com/files/package.json", + "//3": "updating this file will download and update your packages", + "name": "hello-express", + "version": "0.0.1", + "description": "A simple Node app built on Express, instantly up and running.", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "^4.17.1", + "three": "^1.58.1" + }, + "engines": { + "node": "12.x" + }, + "repository": { + "url": "https://glitch.com/edit/#!/hello-express" + }, + "license": "MIT", + "keywords": [ + "node", + "glitch", + "express" + ] +} \ No newline at end of file diff --git a/public/game.js b/public/game.js new file mode 100644 index 00000000..8d2b8823 --- /dev/null +++ b/public/game.js @@ -0,0 +1,294 @@ +// client-side js +// run by the browser each time your view template is loaded + +// Extract globals, otherwise linting gets angry +const { THREE } = window; + +// CONSTANT VARIABLES +let groundLevel = 0; +let arenaSize = 60; +let borderHeight = 8; +let charSize = 2; +let collisionSpeedFactor = 0.5; +let coinRadius = 3; +let coinHeight = 5; +let coinElevation = 2; + +let pressedKeys = {}; +// Create a scene +const scene = new THREE.Scene(); +const camera = new THREE.PerspectiveCamera( + 50, window.innerWidth / window.innerHeight, 0.1, 1000 +); +camera.position.set(-(arenaSize*4)/4, arenaSize, (arenaSize*3)/4); +camera.lookAt(new THREE.Vector3(27,0,0)); +const renderer = new THREE.WebGLRenderer({alpha:true, antialias: true}); + +renderer.shadowMap.enabled = true;//enable shadow +renderer.shadowMap.type = THREE.PCFSoftShadowMap; +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +document.body.appendChild(renderer.domElement); + + + +const sun = new THREE.DirectionalLight( 0xffffff, .2); +sun.position.set( -2*arenaSize,arenaSize,0 ); +sun.castShadow = true; +scene.add(sun); +var controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.update(); + +// CREATE ARENA +var groundGeom = new THREE.PlaneGeometry( arenaSize, arenaSize, 32 ); +rotateObject(groundGeom, 90, 0, 0); + +var groundMat = new THREE.MeshBasicMaterial( {color: 0x444444, side: THREE.DoubleSide} ); +var ground = new THREE.Mesh( groundGeom, groundMat ); +//rotateObject(ground, 90, 0, 0); +printBoundData(ground); +scene.add( ground ); + +createArenaBorders(); +// Add a cube to the scene + +const charGeom = new THREE.SphereGeometry( charSize, 32, 32); +const charMat = new THREE.MeshBasicMaterial( {color: 0x39ff14} ); +const character = new THREE.Mesh( charGeom, charMat ); +character.translateY(charSize); +//placeProper(character); +scene.add( character ); + +// Position our camera so we can see the cube + + +// Add a directional light to the scene +var helper = new THREE.CameraHelper( sun.shadow.camera ); +//scene.add( helper ); + +// add skyBox +var skyBoxGeometry = new THREE.CubeGeometry( arenaSize*5, arenaSize*5, arenaSize*5 ); + + var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: 0x25AEFF, side: THREE.BackSide, transparent:true, opacity: 0.9 } ); + var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial ); + scene.add(skyBox); + +// Add an ambient light to the scene +scene.add( new THREE.AmbientLight( 0x111122 ) ); +//scene.add(ambientLight); + +let zAcc = 0.01; +let xAcc = Math.PI/64; +let zDecel = zAcc/8; +let zSpeed = 0; +let xRotate = 0; +let zLim = 0.5; +let xLim = 2*Math.PI; +let charMotion = new THREE.Vector3(0,0,0); + +let boundCollisionFlag = false; + +createCoin(); + +// Start the render loop +function render() { + onDocumentKeyDown(); + requestAnimationFrame(render); + controls.update(); + // Rotate our cube + applyMotion(); + //character.position.x += xRotate; + //character.position.z += zSpeed; + renderer.render(scene, camera); +} +render(); + +function rotateObject(object, degreeX=0, degreeY=0, degreeZ=0) { + object.rotateX(THREE.Math.degToRad(degreeX)); + object.rotateY(THREE.Math.degToRad(degreeY)); + object.rotateZ(THREE.Math.degToRad(degreeZ)); +} + +function placeProper(object) { + var box = new THREE.Box3().setFromObject( object ); + var low = box.min.z; + // object.translateZ(groundLevel - low); + //console.log( box.min, box.max, box.getSize() ); + printBoundData(object); +} + +function printBoundData(obj) { + var box = new THREE.Box3().setFromObject( obj ); + //console.log( box.min, box.max, box.getSize() ); +} + + +window.onkeyup = function(e) { pressedKeys[e.keyCode] = false; } +window.onkeydown = function(e) { pressedKeys[e.keyCode] = true; } + +//document.addEventListener("keydown", onDocumentKeyDown, false); +function onDocumentKeyDown() { + //var keyCode = event.which; + var vector = new THREE.Vector3( 1, 0, 0 ); + +var axis = new THREE.Vector3( 0, 1, 0 ); +var angle = Math.PI / 2; + +vector.applyAxisAngle( axis, angle ); + //console.log(charMotion); + if (pressedKeys[87]) { //w + zSpeed = changeSpeed(zSpeed, zAcc, zLim); + } else if (pressedKeys[83]) { //s + zSpeed = changeSpeed(zSpeed, -zAcc, zLim); + + } else { + if(zSpeed > 0) zSpeed -= zDecel; + else if(zSpeed < 0) zSpeed += zDecel; + } + if (pressedKeys[68]) { + xRotate = changeAngularDisplacement(xRotate, -xAcc); + + } else if (pressedKeys[65]) { + xRotate = changeAngularDisplacement(xRotate, xAcc); + } + if (pressedKeys[32]) { + character.position.set(0, charSize, 0); + xRotate = 0; + zSpeed = 0; + } +}; +function changeSpeed(speed, inter, limit) { + speed += inter; + if(speed > limit) speed = limit; + if(speed < -limit) speed = -limit; + return speed; +} +function changeVectorSpeed(vector, increase) { + let vec2 = vector.clone(); + vec2.clampLength(xLim, xLim); + if(increase){ + vector.add(vec2); + } else { + vector.sub(vec2); + } + +} +function rotateVector(vector, val) { + var axis = new THREE.Vector3( 0, 1, 0 ); + + vector.applyAxisAngle( axis, val ); +} +function changeAngularDisplacement(ang, inter) { + if(ang >= 2*Math.PI) { + ang -= 2*Math.PI; + } else if(ang <= -2*Math.PI) { + ang += 2*Math.PI; + } + ang += inter; + return ang; +} +function applyMotion() { + var motion = new THREE.Vector3(0, 0, zSpeed); + charMotion.clampLength(-zSpeed, zSpeed); + var rotAxis = new THREE.Vector3(0, 1, 0); + motion.applyAxisAngle(rotAxis, xRotate); + let prevPos = character.position; + character.position = character.position.add(motion); + let negMotion = outOfBoundsCheck(motion); + + if(boundCollisionFlag) { + let newDir = Math.atan2(negMotion.x, negMotion.z); + xRotate = newDir; + character.position = prevPos; + character.position = character.position.add(negMotion); + adjustCollisionSpeed(); + boundCollisionFlag = false; + } +} + +function createArenaBorders() { + var borderGeom = new THREE.PlaneGeometry( arenaSize, borderHeight, 32 ); + + var borderMat = new THREE.MeshBasicMaterial( {color: 0x800000 , side: THREE.DoubleSide, transparent: true, opacity: 0.5} ); + var borderL = new THREE.Mesh( borderGeom, borderMat ); + var borderR = new THREE.Mesh( borderGeom, borderMat ); + var borderF = new THREE.Mesh( borderGeom, borderMat ); + var borderB = new THREE.Mesh( borderGeom, borderMat ); + borderL.position.y += borderHeight/2; + borderL.position.z -= arenaSize/2; + borderR.position.y += borderHeight/2; + borderR.position.z += arenaSize/2; + borderF.position.y += borderHeight/2; + borderF.position.x -= arenaSize/2; + borderB.position.y += borderHeight/2; + borderB.position.x += arenaSize/2; + rotateObject(borderB, 0, 90, 0); + rotateObject(borderF, 0, 90, 0); + + scene.add(borderL); + scene.add(borderR); + scene.add(borderF); + scene.add(borderB); +} +function createCoin() { + + const radialSegments = 16; + + const coinGeom = new THREE.ConeBufferGeometry(coinRadius, coinHeight, radialSegments); + var coinMat = new THREE.MeshBasicMaterial( {color: 0xFFFFFF, side: THREE.DoubleSide} ); + var coin = new THREE.Mesh( coinGeom, coinMat ); + coin.translateY(coinHeight/2 + coinElevation); + scene.add(coin); +} + +function collision(a, b) { + for (var vertexIndex = 0; vertexIndex < a.geometry.vertices.length; vertexIndex++) +{ + var localVertex = a.geometry.vertices[vertexIndex].clone(); + var globalVertex = a.matrix.multiplyVector3(localVertex); + var directionVector = globalVertex.subSelf( a.position ); + + var ray = new THREE.Ray( a.position, directionVector.clone().normalize() ); + var collisionResults = ray.intersectObjects( collidableMeshList ); + if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) + { + // a collision occurred... do something... + } +} +} +function outOfBoundsCheck(vec) { + + if(outOfBoundsHelper(Math.abs(character.position.x)+charSize, arenaSize/2)) { + vec.x = -vec.x; + + boundCollisionFlag = true; + } else if(outOfBoundsHelper(Math.abs(character.position.z)+charSize, arenaSize/2)) { + vec.z = -vec.z; + boundCollisionFlag = true; + + } + return vec; + +} +function outOfBounds() { + return outOfBoundsHelper(character.position.x+charSize, arenaSize/2) || + outOfBoundsHelper(character.position.z+charSize/2, arenaSize/2); +} +function outOfBoundsHelper(val, lim) { + if(val < -lim) { + + return true; + } + if(val > lim) { + + return true; + } + return false; +} +//} + +function adjustCollisionSpeed() { + zSpeed *= collisionSpeedFactor; + //xRotate = + //xRotate = changeAngularDisplacement(xRotate, Math.PI); +} \ No newline at end of file diff --git a/public/style.css b/public/style.css new file mode 100644 index 00000000..baa63bd9 --- /dev/null +++ b/public/style.css @@ -0,0 +1,79 @@ +/* this file is loaded by index.html and styles the page */ + +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 2em 1em; + line-height: 1.5em; +} + +h1 { + font-style: italic; + color: #373fff; + max-width: calc(100% - 5rem); + line-height: 1.1; +} +#info { + background-color: gray; + opacity: 0.5; + position: absolute; + top: 300px; + left: 200px; + width: 1000px; + height: 600px; + font-size: 40px; + text-align: left; + z-index: 100; + display:block; + visiblity: visible; +} +#infoButton { + position: absolute; + top: 100px; + left: 10px; + width: 100px; + height: 100px; + text-align: center; + z-index: 100; + display:block; +} +form { + background-color: #eee; + display: grid; + grid-gap: 1em; + padding: 1em; + max-width: 40ch; +} + +input { + border: 1px solid silver; + display: block; + font-size: 16px; + margin-bottom: 10px; + padding: 5px; + width: 100%; +} + +form button { + background-color: #bbbbf2; + border: 2px solid currentColor; + border-radius: .25em; + cursor: pointer; + font-size: inherit; + line-height: 1.4em; + padding: 0.25em 1em; + max-width: 20ch; +} + +form button:hover { + background-color: lavender; +} + +footer { + margin-top: 3em; + padding-top: 1.5em; + border-top: 1px solid lightgrey; +} diff --git a/server.js b/server.js new file mode 100644 index 00000000..ec6b0008 --- /dev/null +++ b/server.js @@ -0,0 +1,30 @@ +// server.js +// where your node app starts + +// we've started you off with Express (https://expressjs.com/) +// but feel free to use whatever libraries or frameworks you'd like through `package.json`. +const express = require("express"); +const three = require("three"); +const app = express(); + +// our default array of dreams +app.use(express.static('views')); +app.use(express.static('node_modules/three/build')); +app.use(express.static('node_modules/three/examples/js/renderers')); +app.use(express.static('node_modules/three/examples/js/loaders')); +app.use(express.static('node_modules/three/examples/js/controls')); +app.use(express.static('node_modules/three/examples/js/*')); + +// make all the files in 'public' available +// https://expressjs.com/en/starter/static-files.html +app.use(express.static("public")); + +// https://expressjs.com/en/starter/basic-routing.html +app.get("/", (request, response) => { + response.sendFile(__dirname + "/views/index.html"); +}); + +// listen for requests :) +const listener = app.listen(process.env.PORT, () => { + console.log("Your app is listening on port " + listener.address().port); +}); diff --git a/shrinkwrap.yaml b/shrinkwrap.yaml new file mode 100644 index 00000000..93dc765b --- /dev/null +++ b/shrinkwrap.yaml @@ -0,0 +1,402 @@ +dependencies: + express: 4.17.1 + three: 0.121.1 +packages: + /accepts/1.3.7: + dependencies: + mime-types: 2.1.25 + negotiator: 0.6.2 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + /array-flatten/1.1.1: + dev: false + resolution: + integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + /body-parser/1.19.0: + dependencies: + bytes: 3.1.0 + content-type: 1.0.4 + debug: 2.6.9 + depd: 1.1.2 + http-errors: 1.7.2 + iconv-lite: 0.4.24 + on-finished: 2.3.0 + qs: 6.7.0 + raw-body: 2.4.0 + type-is: 1.6.18 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + /bytes/3.1.0: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + /content-disposition/0.5.3: + dependencies: + safe-buffer: 5.1.2 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + /content-type/1.0.4: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + /cookie-signature/1.0.6: + dev: false + resolution: + integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + /cookie/0.4.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + /debug/2.6.9: + dependencies: + ms: 2.0.0 + dev: false + resolution: + integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + /depd/1.1.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + /destroy/1.0.4: + dev: false + resolution: + integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + /ee-first/1.1.1: + dev: false + resolution: + integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + /encodeurl/1.0.2: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + /escape-html/1.0.3: + dev: false + resolution: + integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + /etag/1.8.1: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + /express/4.17.1: + dependencies: + accepts: 1.3.7 + array-flatten: 1.1.1 + body-parser: 1.19.0 + content-disposition: 0.5.3 + content-type: 1.0.4 + cookie: 0.4.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 1.1.2 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.1.2 + fresh: 0.5.2 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.3.0 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.5 + qs: 6.7.0 + range-parser: 1.2.1 + safe-buffer: 5.1.2 + send: 0.17.1 + serve-static: 1.14.1 + setprototypeof: 1.1.1 + statuses: 1.5.0 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + dev: false + engines: + node: '>= 0.10.0' + resolution: + integrity: sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + /finalhandler/1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + /forwarded/0.1.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + /fresh/0.5.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + /http-errors/1.7.2: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.1 + statuses: 1.5.0 + toidentifier: 1.0.0 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + /http-errors/1.7.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.1.1 + statuses: 1.5.0 + toidentifier: 1.0.0 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + /iconv-lite/0.4.24: + dependencies: + safer-buffer: 2.1.2 + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + /inherits/2.0.3: + dev: false + resolution: + integrity: sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + /inherits/2.0.4: + dev: false + resolution: + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + /ipaddr.js/1.9.0: + dev: false + engines: + node: '>= 0.10' + resolution: + integrity: sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== + /media-typer/0.3.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + /merge-descriptors/1.0.1: + dev: false + resolution: + integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + /methods/1.1.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + /mime-db/1.42.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ== + /mime-types/2.1.25: + dependencies: + mime-db: 1.42.0 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg== + /mime/1.6.0: + dev: false + engines: + node: '>=4' + hasBin: true + resolution: + integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + /ms/2.0.0: + dev: false + resolution: + integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + /ms/2.1.1: + dev: false + resolution: + integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + /negotiator/0.6.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + /on-finished/2.3.0: + dependencies: + ee-first: 1.1.1 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + /parseurl/1.3.3: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + /path-to-regexp/0.1.7: + dev: false + resolution: + integrity: sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + /proxy-addr/2.0.5: + dependencies: + forwarded: 0.1.2 + ipaddr.js: 1.9.0 + dev: false + engines: + node: '>= 0.10' + resolution: + integrity: sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== + /qs/6.7.0: + dev: false + engines: + node: '>=0.6' + resolution: + integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + /range-parser/1.2.1: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + /raw-body/2.4.0: + dependencies: + bytes: 3.1.0 + http-errors: 1.7.2 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + /safe-buffer/5.1.2: + dev: false + resolution: + integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + /safer-buffer/2.1.2: + dev: false + resolution: + integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + /send/0.17.1: + dependencies: + debug: 2.6.9 + depd: 1.1.2 + destroy: 1.0.4 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 1.7.3 + mime: 1.6.0 + ms: 2.1.1 + on-finished: 2.3.0 + range-parser: 1.2.1 + statuses: 1.5.0 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + /serve-static/1.14.1: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.17.1 + dev: false + engines: + node: '>= 0.8.0' + resolution: + integrity: sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + /setprototypeof/1.1.1: + dev: false + resolution: + integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + /statuses/1.5.0: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + /three/0.121.1: + dev: false + resolution: + integrity: sha512-+rYhNpW5W1aBdLkPQDld/GA4Sj/uOKJJKVpCAZUbpk/HZEiYRUlPt8+s+9nmxRglWmRHdBa3ERvUwqg4nmoA5w== + /toidentifier/1.0.0: + dev: false + engines: + node: '>=0.6' + resolution: + integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + /type-is/1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.25 + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + /unpipe/1.0.0: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + /utils-merge/1.0.1: + dev: false + engines: + node: '>= 0.4.0' + resolution: + integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + /vary/1.1.2: + dev: false + engines: + node: '>= 0.8' + resolution: + integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +registry: 'https://registry.npmjs.org/' +shrinkwrapMinorVersion: 9 +shrinkwrapVersion: 3 +specifiers: + express: ^4.17.1 + three: ^0.121.1 diff --git a/views/index.html b/views/index.html new file mode 100644 index 00000000..4afe10aa --- /dev/null +++ b/views/index.html @@ -0,0 +1,41 @@ + + + + + + + ThreeJS + + + + + + + + +

+ W - Accelerate forward


+ S - Accelerate backward


+ A - Turn left


+ D - Turn right


+ Spacebar - Restart


+ Mouse drag & scroll - Control camera


+ Implemented collision physics, friction


+ + +
+ + + + + +