From 9c071945d292362c2102ed69d2944a1a17dafc92 Mon Sep 17 00:00:00 2001 From: xnieamo Date: Wed, 29 Mar 2017 19:16:21 -0400 Subject: [PATCH] moved over from other repo --- .gitignore | 2 + index.html | 19 +++ package.json | 29 +++++ src/agent.js | 105 +++++++++++++++++ src/framework.js | 75 ++++++++++++ src/main.js | 233 +++++++++++++++++++++++++++++++++++++ src/marker.js | 33 ++++++ src/shaders/adam-frag.glsl | 16 +++ src/shaders/adam-vert.glsl | 82 +++++++++++++ webpack.config.js | 28 +++++ 10 files changed, 622 insertions(+) create mode 100644 .gitignore create mode 100644 index.html create mode 100644 package.json create mode 100644 src/agent.js create mode 100644 src/framework.js create mode 100644 src/main.js create mode 100644 src/marker.js create mode 100644 src/shaders/adam-frag.glsl create mode 100644 src/shaders/adam-vert.glsl create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8d4aaf40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +*.map \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..4d59ebb8 --- /dev/null +++ b/index.html @@ -0,0 +1,19 @@ + + + + HW7: BioCrowds + + + + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..370fadd3 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "scripts": { + "start": "webpack-dev-server --hot --inline", + "build": "webpack", + "deploy": "gh-pages-deploy" + }, + "gh-pages-deploy": { + "prep": [ + "build" + ], + "noprompt": true + }, + "dependencies": { + "dat-gui": "^0.5.0", + "gl-matrix": "^2.3.2", + "stats-js": "^1.0.0-alpha1", + "three": "^0.82.1", + "three-orbit-controls": "^82.1.0" + }, + "devDependencies": { + "babel-core": "^6.18.2", + "babel-loader": "^6.2.8", + "babel-preset-es2015": "^6.18.0", + "gh-pages-deploy": "^0.4.2", + "webpack": "^1.13.3", + "webpack-dev-server": "^1.16.2", + "webpack-glsl-loader": "^1.0.1" + } +} diff --git a/src/agent.js b/src/agent.js new file mode 100644 index 00000000..94783eb5 --- /dev/null +++ b/src/agent.js @@ -0,0 +1,105 @@ +const THREE = require('three'); + +export default class Agent { + + // Position, velocity, goal, orientation, size, markers + constructor(name, pos, vel, goal, size, markers) { + this.init(name, pos, vel, goal, size, markers); + } + + init(name, pos, vel, goal, size, markers) { + this.name = name; + this.pos = pos; + this.vel = vel; + this.goal = goal; + this.size = size; + this.markers = markers; + } + + clearMarkers() { + this.markers = []; + } + + addMarker(marker) { + this.markers.push(marker); + // console.log(this.markers); + } + + removeMarker(marker) { + var idx = this.markers.indexOf(marker); + if (idx > -1) { + this.markers = this.markers.splice(idx, 1); + } + } + + setSize(size) { + this.size = size; + } + + getMarkers() { + this.markers; + } + + getName() { + return this.name; + } + + getPos() { + return this.pos; + } + + getSize() { + return this.size; + } + + getDistToGoal() { + return Math.pow(this.pos.x - this.goal.x, 2) + Math.pow(this.pos.y - this.goal.y, 2) + } + + getGoalDir() { + return new THREE.Vector2(this.goal.x - this.pos.x, this.goal.y - this.pos.y); + } + + update() { + this.vel = new THREE.Vector2(0,0); + + // console.log(this.getDistToGoal()) + // if (this.getDistToGoal() < 1) {return;} + + var goalDir = this.getGoalDir(); + var cumWeight = 0; + + // console.log(this.name + " " + this.markers.length) + + for (var x = 0; x < this.markers.length; x++) { + var marker = this.markers[x]; + var mPos = marker.getPos(); + var markerDir = new THREE.Vector2(mPos.x - this.pos.x, mPos.y - this.pos.y); + + // Get weight for this marker + var numerator = goalDir.normalize().dot(markerDir.normalize()) + 1; + var denominator = Math.sqrt(Math.pow(this.pos.x - mPos.x, 2) + Math.pow(this.pos.y - mPos.y, 2)) + 1; + var weight = numerator / denominator; + + // Update velocity + this.vel.x += markerDir.x * weight; + this.vel.y += markerDir.y * weight; + cumWeight += weight; + + } + // console.log(this.vel); + + this.vel.x /= cumWeight; + this.vel.y /= cumWeight; + + this.vel.x = this.vel.x ? this.vel.x : 0; + this.vel.y = this.vel.y ? this.vel.y : 0; + // console.log(this.vel); + } + + updatePos(delta) { + this.pos.x += this.vel.x * delta; + this.pos.y += this.vel.y * delta; + // console.log(this.vel); + } +} \ No newline at end of file diff --git a/src/framework.js b/src/framework.js new file mode 100644 index 00000000..9cfcd1b4 --- /dev/null +++ b/src/framework.js @@ -0,0 +1,75 @@ + +const THREE = require('three'); +const OrbitControls = require('three-orbit-controls')(THREE) +import Stats from 'stats-js' +import DAT from 'dat-gui' + +// when the scene is done initializing, the function passed as `callback` will be executed +// then, every frame, the function passed as `update` will be executed +function init(callback, update) { + var stats = new Stats(); + stats.setMode(1); + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.top = '0px'; + document.body.appendChild(stats.domElement); + + var gui = new DAT.GUI(); + + var framework = { + gui: gui, + stats: stats + }; + + // run this function after the window loads + window.addEventListener('load', function() { + + var scene = new THREE.Scene(); + var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 ); + var renderer = new THREE.WebGLRenderer( { antialias: true } ); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setClearColor(0x020202, 0); + + var controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.enableZoom = true; + controls.target.set(0, 0, 0); + controls.rotateSpeed = 0.3; + controls.zoomSpeed = 1.0; + controls.panSpeed = 2.0; + + document.body.appendChild(renderer.domElement); + + // resize the canvas when the window changes + window.addEventListener('resize', function() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + }); + + // assign THREE.js objects to the object we will return + framework.scene = scene; + framework.camera = camera; + framework.renderer = renderer; + + // begin the animation loop + (function tick() { + stats.begin(); + update(framework); // perform any requested updates + renderer.render(scene, camera); // render the scene + stats.end(); + requestAnimationFrame(tick); // register to call this again when the browser renders a new frame + })(); + + // we will pass the scene, gui, renderer, camera, etc... to the callback function + return callback(framework); + }); +} + +export default { + init: init +} + +export const PI = 3.14159265 +export const e = 2.7181718 \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 00000000..8e4c7d30 --- /dev/null +++ b/src/main.js @@ -0,0 +1,233 @@ + +const THREE = require('three'); // older modules are imported like this. You shouldn't have to worry about this much +import Framework from './framework' +import Agent from './agent' +import Marker from './marker' + + +var goals = { + options : ['corner', 'center'], + corner : function(i) { + var d = Math.pow(-1, i) * (fieldSize / 3) + return new THREE.Vector2(d, d); + }, + center : function(i) { + return new THREE.Vector2(0, 0); + } +} + +var start = { + options : ['random', 'circle'], + random : function() { + return new THREE.Vector2((Math.random() - 0.5) * fieldSize, (Math.random() - 0.5) * fieldSize); + }, + circle : function() { + var angle = Math.random() * Math.PI * 2; + var x = Math.cos(angle) * fieldSize/3; + var y = Math.sin(angle) * fieldSize/3; + return new THREE.Vector2(x,y); + } +} + +var fieldSize = 100; +var numMarkers = 5000; +var numAgents = 50; +var maxAgentSize = 5; +var distBuffer = 30; +var speed = 0.5; + +var markers = []; +var agents = []; +var grid = {}; + +var config = { + goal : "corner", + start : "circle" +} + +function createMarkers() { + for (var i = 0; i < numMarkers; i++) { + var pos = new THREE.Vector2((Math.random() - 0.5) * fieldSize, (Math.random() - 0.5) * fieldSize); + var m = new Marker(pos); + + markers.push(m); + + var xG = Math.floor(pos.x / maxAgentSize); + var yG = Math.floor(pos.y / maxAgentSize); + var gridIdx = xG + '-' + yG; + if (!grid[gridIdx]) { + grid[gridIdx] = []; + } + grid[gridIdx].push(m); + } +} + +function createAgents() { + agents = []; + for (var i = 0; i < numAgents; i++) { + var name = "agent" + i; + + var posFunction = start[config.start]; + var pos = posFunction(); + var vel = new THREE.Vector2(0, 0); + + + var goalFunction = goals[config.goal]; + var goal = goalFunction(i); + var size = 1; + + var agent = new Agent(name, pos, vel, goal, size, []); + agents.push(agent); + } +} + +// called after the scene loads +function onLoad(framework) { + var scene = framework.scene; + var camera = framework.camera; + var renderer = framework.renderer; + var gui = framework.gui; + var stats = framework.stats; + + // set camera position + camera.position.set(1, 1, 100); + camera.lookAt(new THREE.Vector3(0,0,0)); + + // Create plane + var squareGeometry = new THREE.PlaneGeometry(fieldSize, fieldSize); + var squareMaterial = new THREE.MeshBasicMaterial({ + color:0xFFFFFF, + side:THREE.DoubleSide + }); + + var squareMesh = new THREE.Mesh(squareGeometry, squareMaterial); + squareMesh.position.set(0,0,0); + scene.add(squareMesh); + + // Create markers and agents + createMarkers(); + createAgents(); + + console.log(grid) + + // // Add markers to scene + // for (var i = 0; i < numMarkers; i++) { + // var markerGeom = new THREE.CircleGeometry(1,8); + // var markerMaterial = new THREE.MeshBasicMaterial({ + // color:0x000000, + // side:THREE.DoubleSide + // }); + + // var markerMesh = new THREE.Mesh(markerGeom, markerMaterial); + // var markerPos = markers[i].getPos(); + // markerMesh.position.set(markerPos.x, markerPos.y, 1); + // scene.add(markerMesh); + // } + + // Add agents to scene + for (var i = 0; i < numAgents; i++) { + var agent = agents[i]; + + + var agentGeom = new THREE.CircleGeometry(agent.getSize(),8); + var agentMaterial = new THREE.MeshBasicMaterial({ + color:0x00FF00, + side:THREE.DoubleSide + }); + + var agentMesh = new THREE.Mesh(agentGeom, agentMaterial); + var agentPos = agents[i].getPos(); + agentMesh.position.set(agentPos.x, agentPos.y, 1); + agentMesh.name = agent.getName(); + scene.add(agentMesh); + } + + // edit params and listen to changes like this + // more information here: https://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage + gui.add(camera, 'fov', 0, 180).onChange(function(newVal) { + camera.updateProjectionMatrix(); + }); + + gui.add(config, 'goal', goals.options).onChange(function(newVal){ + config.goal = newVal; + createAgents(); + }); + + gui.add(config, 'start', start.options).onChange(function(newVal){ + config.start = newVal; + createAgents(); + }); +} + +// called on frame updates +function onUpdate(framework) { + if (markers.length == 0 || agents.length == 0) { + return; + } + + for (var x = 0; x < markers.length; x++) { + markers[x].reset(); + } + + // Assign markers to agents + for (var x = 0; x < agents.length; x++) { + var agent = agents[x]; + agent.clearMarkers(); + + var pos = agent.getPos(); + var xG = Math.max(Math.floor(pos.x / maxAgentSize) - 1, -Math.floor(fieldSize / 2 / maxAgentSize)); + var yG = Math.max(Math.floor(pos.y / maxAgentSize) - 1, -Math.floor(fieldSize / 2 / maxAgentSize)); + + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + var gridIdx = (xG + i) + '-' + (yG + j); + if (!grid[gridIdx]) {break;} + + if (grid[gridIdx]) { + for (var y = 0; y < grid[gridIdx].length; y++) { + var marker = grid[gridIdx][y]; + var mPos = marker.getPos(); + + var dist = (Math.pow(pos.x - mPos.x, 2) + Math.pow(pos.y - mPos.y, 2)); + // console.log(dist); + if (dist < distBuffer) { + + // Check case where marker is already assigned + if (!marker.getAgent()) { + agent.addMarker(marker); + marker.setAgent(agent); + marker.setDist(dist); + } else if (marker.getDist() > dist) { + // var oldAgent = marker.getAgent(); + // oldAgent.removeMarker(marker); + agent.addMarker(marker); + marker.setAgent(agent); + marker.setDist(dist); + } + + + + } + } + } + } + } + agents[x] = agent; + } + + // Compute velocities and update scene + for (var x = 0; x < agents.length; x++) { + agents[x].update(); + agents[x].updatePos(speed); + + var mesh = framework.scene.getObjectByName(agents[x].getName()); + if (mesh) { + var pos = agents[x].getPos(); + mesh.position.set(pos.x, pos.y, 1); + mesh.needsUpdate = true; + } + } +} + +// when the scene is done initializing, it will call onLoad, then on frame updates, call onUpdate +Framework.init(onLoad, onUpdate); \ No newline at end of file diff --git a/src/marker.js b/src/marker.js new file mode 100644 index 00000000..bb3d6417 --- /dev/null +++ b/src/marker.js @@ -0,0 +1,33 @@ +export default class Marker { + + // Position, velocity, goal, orientation, size, markers + constructor(pos) { + this.pos = pos; + } + + setAgent(agent) { + this.agent = agent; + } + + setDist(dist) { + this.dist = dist; + } + + getDist() { + return this.dist; + } + + getAgent() { + return this.agent; + } + + getPos() { + return this.pos; + } + + reset() { + this.agent = null; + this.dist = null; + } + +} \ No newline at end of file diff --git a/src/shaders/adam-frag.glsl b/src/shaders/adam-frag.glsl new file mode 100644 index 00000000..dd579bf4 --- /dev/null +++ b/src/shaders/adam-frag.glsl @@ -0,0 +1,16 @@ +varying vec2 vUv; +varying vec3 nor; +varying float noise; +varying float mus; + +uniform sampler2D image; +uniform vec3 colorMult; + + +void main() { + + vec4 color = texture2D( image, vUv ); + + gl_FragColor = vec4( (1. - noise) * nor.rgb + noise * colorMult, 1.0 ); + // gl_FragColor = vec4( mus * color.rgb, 1.0 ); +} \ No newline at end of file diff --git a/src/shaders/adam-vert.glsl b/src/shaders/adam-vert.glsl new file mode 100644 index 00000000..b821a35c --- /dev/null +++ b/src/shaders/adam-vert.glsl @@ -0,0 +1,82 @@ + +varying vec2 vUv; +varying vec3 nor; +varying float noise; +varying float mus; + +uniform float inv_persistence; +uniform float time; +uniform float music; +uniform float music2; + +#define M_PI 3.14159265 +const int N_OCTAVES = 5; + +float sampleNoise(vec3 pos) { + float x = fract(sin(dot(pos, vec3(134.9235, 63.5879, 218.9542))) * 27495.2467); + return x; +} + +float interpolate(float a, float b, float t) { + float cos_t = (1. - cos(t * M_PI)) * 0.5; + return a * (1. - cos_t) + b * cos_t; +} + +float interpNoise(vec3 pos, float f) { + + // Calculate the min/max positions of cube + vec3 p0 = floor(pos * f) / f; + vec3 p1 = p0 + 1. / f; + vec3 t = (pos - p0) * f; + + // Find noise values at corners of cube + float A = sampleNoise(vec3(p0.x, p0.y, p0.z)); + float E = sampleNoise(vec3(p1.x, p0.y, p0.z)); + + float B = sampleNoise(vec3(p0.x, p1.y, p0.z)); + float F = sampleNoise(vec3(p1.x, p1.y, p0.z)); + + float C = sampleNoise(vec3(p0.x, p0.y, p1.z)); + float G = sampleNoise(vec3(p1.x, p0.y, p1.z)); + + float D = sampleNoise(vec3(p0.x, p1.y, p1.z)); + float H = sampleNoise(vec3(p1.x, p1.y, p1.z)); + + // First pass of interpolation + float interpLi_AE = interpolate(A, E, t.x); + float interpLi_BF = interpolate(B, F, t.x); + float interpLi_CG = interpolate(C, G, t.x); + float interpLi_DH = interpolate(D, H, t.x); + + // Second pass of interpolation + float interpBi_12 = interpolate(interpLi_AE, interpLi_BF, t.y); + float interpBi_34 = interpolate(interpLi_CG, interpLi_DH, t.y); + + // Third pass + return interpolate(interpBi_12, interpBi_34, t.z); +} + +float multiOctaveNoise(float offset) { + + float total = 0.; + float persistence = 1. / inv_persistence; + + for (int i = 0; i < N_OCTAVES; i++) { + + float frequency = pow(2., float(i)); + float amplitude = pow(persistence, float(i)); + total += interpNoise(position + offset, frequency) * amplitude; + } + + return total; +} + +void main() { + vUv = uv; + mus = music; + + noise = multiOctaveNoise(time); + nor = vec3(projectionMatrix * modelViewMatrix * vec4(normal, 0.)); + gl_Position = projectionMatrix * modelViewMatrix * vec4(position + noise * nor * music, 1.); +} + diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..57dce485 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,28 @@ +const path = require('path'); + +module.exports = { + entry: path.join(__dirname, "src/main"), + output: { + filename: "./bundle.js" + }, + module: { + loaders: [ + { + test: /\.js$/, + exclude: /(node_modules|bower_components)/, + loader: 'babel', + query: { + presets: ['es2015'] + } + }, + { + test: /\.glsl$/, + loader: "webpack-glsl" + }, + ] + }, + devtool: 'source-map', + devServer: { + port: 7000 + } +} \ No newline at end of file