Commit d237a10b authored by amandaghassaei's avatar amandaghassaei

starting sim

parent e686ee26
......@@ -9,6 +9,145 @@
<link rel="stylesheet" type="text/css" href="dependencies/jquery-ui.min.css"/>
<link rel="stylesheet" type="text/css" href="main.css"/>
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
</script>
<script id="packToBytesShader" type="x-shader/x-fragment">
precision mediump float;
uniform vec2 u_floatTextureDim;
uniform sampler2D u_floatTexture;
uniform float u_vectorLength;
float shift_right (float v, float amt) {
v = floor(v) + 0.5;
return floor(v / exp2(amt));
}
float shift_left (float v, float amt) {
return floor(v * exp2(amt) + 0.5);
}
float mask_last (float v, float bits) {
return mod(v, shift_left(1.0, bits));
}
float extract_bits (float num, float from, float to) {
from = floor(from + 0.5); to = floor(to + 0.5);
return mask_last(shift_right(num, from), to - from);
}
vec4 encode_float (float val) {
if (val == 0.0) return vec4(0, 0, 0, 0);
float sign = val > 0.0 ? 0.0 : 1.0;
val = abs(val);
float exponent = floor(log2(val));
float biased_exponent = exponent + 127.0;
float fraction = ((val / exp2(exponent)) - 1.0) * 8388608.0;
float t = biased_exponent / 2.0;
float last_bit_of_biased_exponent = fract(t) * 2.0;
float remaining_bits_of_biased_exponent = floor(t);
float byte4 = extract_bits(fraction, 0.0, 8.0) / 255.0;
float byte3 = extract_bits(fraction, 8.0, 16.0) / 255.0;
float byte2 = (last_bit_of_biased_exponent * 128.0 + extract_bits(fraction, 16.0, 23.0)) / 255.0;
float byte1 = (sign * 128.0 + remaining_bits_of_biased_exponent) / 255.0;
return vec4(byte4, byte3, byte2, byte1);
}
void main(){
vec2 fragCoord = gl_FragCoord.xy;
float textureXcoord = floor((fragCoord.x - 0.5)/u_vectorLength+0.0001) + 0.5;
vec4 data = texture2D(u_floatTexture, vec2(textureXcoord, fragCoord.y)/u_floatTextureDim);
int textureIndex = int(floor(mod(fragCoord.x-0.5+0.0001, u_vectorLength)));
if (textureIndex == 0) gl_FragColor = encode_float(data[0]);
else if (textureIndex == 1) gl_FragColor = encode_float(data[1]);
else if (textureIndex == 2) gl_FragColor = encode_float(data[2]);
else if (textureIndex == 3) gl_FragColor = encode_float(data[3]);
}
</script>
<script id="zeroTexture" type="x-shader/x-fragment">
void main(){
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
</script>
<script id="positionCalcShader" type="x-shader/x-fragment">
precision mediump float;
uniform vec2 u_textureDim;
uniform float u_dt;
uniform sampler2D u_lastPosition;
uniform sampler2D u_velocity;
uniform sampler2D u_mass;
void main(){
vec2 fragCoord = gl_FragCoord.xy;
vec2 scaledFragCoord = fragCoord/u_textureDim;
float isFixed = texture2D(u_mass, scaledFragCoord).y;
if (isFixed == 1.0){
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
}
vec3 lastPosition = texture2D(u_lastPosition, scaledFragCoord).xyz;
vec3 velocity = texture2D(u_velocity, scaledFragCoord).xyz;
vec3 position = velocity*u_dt + lastPosition;
gl_FragColor = vec4(position,0.0);
}
</script>
<script id="velocityCalcShader" type="x-shader/x-fragment">
precision mediump float;
uniform vec2 u_textureDim;
uniform float u_dt;
uniform sampler2D u_lastPosition;
uniform sampler2D u_lastVelocity;
uniform sampler2D u_originalPosition;
uniform sampler2D u_externalForces;
uniform sampler2D u_mass;
uniform sampler2D u_meta;
uniform sampler2D u_beamK;
uniform sampler2D u_beamD;
void main(){
vec2 fragCoord = gl_FragCoord.xy;
vec2 scaledFragCoord = fragCoord/u_textureDim;
vec2 mass = texture2D(u_mass, scaledFragCoord).xy;
if (mass.y == 1.0){
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
}
vec3 force = texture2D(u_externalForces, scaledFragCoord).xyz;
force.y = 1.0;
vec3 lastPosition = texture2D(u_lastPosition, scaledFragCoord).xyz;
vec3 lastVelocity = texture2D(u_lastVelocity, scaledFragCoord).xyz;
vec3 originalPosition = texture2D(u_originalPosition, scaledFragCoord).xyz;
vec4 neighborIndices = texture2D(u_meta, scaledFragCoord);
vec4 beamKs = texture2D(u_beamK, scaledFragCoord);
vec4 beamDs = texture2D(u_beamD, scaledFragCoord);
for (int j=0;j<4;j++){
float neighborIndex1D = neighborIndices[j];
if (neighborIndex1D<0.0) continue;//no beam
vec2 neighborIndex = vec2(mod(neighborIndex1D, u_textureDim.x)+0.5, floor(neighborIndex1D/u_textureDim.x)+0.5);
vec2 scaledNeighborIndex = neighborIndex/u_textureDim;
vec3 neighborLastPosition = texture2D(u_lastPosition, scaledNeighborIndex).xyz;
vec3 neighborLastVelocity = texture2D(u_lastVelocity, scaledNeighborIndex).xyz;
vec3 neighborOriginalPosition = texture2D(u_originalPosition, scaledNeighborIndex).xyz;
vec3 nominalDist = neighborOriginalPosition-originalPosition;
vec3 deltaP = neighborLastPosition-lastPosition+nominalDist;
// deltaP -= normalize(deltaP)*length(nominalDist);
vec3 deltaV = neighborLastVelocity-lastVelocity;
vec3 _force = deltaP*beamKs[j] + deltaV*beamDs[j];
force += _force;
}
vec3 velocity = force*u_dt/mass.x + lastVelocity;
gl_FragColor = vec4(velocity,0.0);
}
</script>
<script type="text/javascript" src="dependencies/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="dependencies/jquery-ui.min.js"></script>
<script type="text/javascript" src="dependencies/flat-ui.min.js"></script>
......@@ -23,11 +162,16 @@
<script type="text/javascript" src="js/controls.js"></script>
<script type="text/javascript" src="js/threeView.js"></script>
<script type="text/javascript" src="js/globals.js"></script>
<script type="text/javascript" src="js/node.js"></script>
<script type="text/javascript" src="js/beam.js"></script>
<script type="text/javascript" src="js/model.js"></script>
<script type="text/javascript" src="js/dynamicModel.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</head>
<body>
<div id="threeContainer"></div>
<canvas id="gpuMathCanvas"></canvas>
<div id="controlsLeft" class="flipped">
<div>
<a id="logo" target="_blank" href="http://cba.mit.edu/">
......
/**
* Created by ghassaei on 9/16/16.
*/
var beamMaterialHighlight = new THREE.LineBasicMaterial({color: 0xffffff, linewidth: 4});
var beamMaterial = new THREE.LineBasicMaterial({color: 0x000000, linewidth: 4});
function Beam(nodes){
this.type = "beam";
nodes[0].addBeam(this);
nodes[1].addBeam(this);
this.vertices = [nodes[0].getPosition(), nodes[1].getPosition()];
this.nodes = nodes;
var lineGeometry = new THREE.Geometry();
lineGeometry.dynamic = true;
lineGeometry.vertices = this.vertices;
this.object3D = new THREE.Line(lineGeometry, beamMaterial);
this.object3D._myBeam = this;
}
Beam.prototype.highlight = function(){
this.object3D.material = beamMaterialHighlight;
};
Beam.prototype.unhighlight = function(){
this.object3D.material = beamMaterial;
};
Beam.prototype.getLength = function(){
return this.getVector().length();
};
Beam.prototype.isFixed = function(){
return this.nodes[0].fixed && this.nodes[1].fixed;
};
Beam.prototype.getVector = function(){
return this.vertices[0].clone().sub(this.vertices[1]);
};
//dynamic solve
Beam.prototype.getK = function(){
return globals.axialStiffness/this.getLength();
};
Beam.prototype.getD = function(){
return globals.percentDamping*2*Math.sqrt(this.getK()*this.getMinMass());
};
Beam.prototype.getNaturalFrequency = function(){
return Math.sqrt(this.getK()/this.getMinMass());
};
Beam.prototype.getMinMass = function(){
var minMass = this.nodes[0].getSimMass();
if (this.nodes[1].getSimMass()<minMass) minMass = this.nodes[1].getSimMass();
return minMass;
};
Beam.prototype.getOtherNode = function(node){
if (this.nodes[0] == node) return this.nodes[1];
return this.nodes[0];
};
//render
Beam.prototype.getObject3D = function(){
return this.object3D;
};
Beam.prototype.render = function(shouldComputeLineDistance){
this.object3D.geometry.verticesNeedUpdate = true;
this.object3D.geometry.computeBoundingSphere();
if (shouldComputeLineDistance) this.object3D.geometry.computeLineDistances();//for dashed lines
};
//deallocate
Beam.prototype.destroy = function(){
var self = this;
_.each(this.nodes, function(node){
node.removeBeam(self);
});
this.vertices = null;
this.object3D._myBeam = null;
this.object3D = null;
this.nodes = null;
};
\ No newline at end of file
/**
* Created by ghassaei on 10/7/16.
*/
function initDynamicModel(globals){
var object3D = new THREE.Object3D();
object3D.visible = globals.dynamicSimVisible;
globals.threeView.sceneAddModel(object3D);
var nodes;
var edges;
var originalPosition;
var position;
var lastPosition;
var velocity;
var lastVelocity;
var externalForces;
var mass;
var meta;
var beamK;
var beamD;
function syncNodesAndEdges(){
nodes = globals.model.getNodes();
edges = globals.model.getEdges();
//update mesh nodes
initTypedArrays();
}
var steps;
var programsInited = false;//flag for initial setup
var textureDim = 0;
syncNodesAndEdges();
initTexturesAndPrograms(globals.gpuMath);
steps = parseInt(setSolveParams());
runSolver();
function averageSubdivide(){
globals.gpuMath.step("averageSubdivide", ["u_lastPosition", "u_originalPosition", "u_meta", "u_mass"], "u_position");
globals.gpuMath.swapTextures("u_position", "u_lastPosition");
globals.gpuMath.step("averageSubdivide", ["u_lastPosition", "u_originalPosition", "u_meta", "u_mass"], "u_position");
globals.gpuMath.swapTextures("u_position", "u_lastPosition");
}
function reset(){
globals.gpuMath.step("zeroTexture", [], "u_position");
globals.gpuMath.step("zeroTexture", [], "u_lastPosition");
globals.gpuMath.step("zeroTexture", [], "u_velocity");
globals.gpuMath.step("zeroTexture", [], "u_lastVelocity");
}
function runSolver(){
globals.threeView.startAnimation(function(){
if (!globals.dynamicSimVisible) {
if (globals.selfWeightMode == "dynamic"){
globals.staticModel.setSelfWeight();
}
return;
}
for (var j=0;j<steps;j++){
solveStep();
}
render();
});
}
function setVisibility(state){
object3D.visible = state;
}
function setSelfWeight(){
// console.log(nodes[0].getSelfWeight().length());
for (var i=0;i<nodes.length;i++){
var node = nodes[i];
if (globals.applySelfWeight) globals.schematic.forces[i].setSelfWeight(node.getSelfWeight());
else globals.schematic.forces[i].setSelfWeight(new THREE.Vector3(0,0,0));
}
globals.forceArrayUpdated();
}
function solveStep(){
if (globals.forceHasChanged){
updateExternalForces();
globals.forceHasChanged = false;
}
if (globals.fixedHasChanged){
updateFixed();
globals.fixedHasChanged = false;
}
if (globals.dynamicSimMaterialsChanged){
updateMaterials();
globals.dynamicSimMaterialsChanged = false;
}
if (globals.shouldResetDynamicSim){
reset();
globals.shouldResetDynamicSim = false;
}
var gpuMath = globals.gpuMath;
gpuMath.step("velocityCalc", ["u_lastPosition", "u_lastVelocity", "u_originalPosition", "u_externalForces",
"u_mass", "u_meta", "u_beamK", "u_beamD"], "u_velocity");
gpuMath.step("positionCalc", ["u_velocity", "u_lastPosition", "u_mass"], "u_position");
gpuMath.swapTextures("u_velocity", "u_lastVelocity");
gpuMath.swapTextures("u_position", "u_lastPosition");
}
function render(){
var vectorLength = 3;
globals.gpuMath.setProgram("packToBytes");
globals.gpuMath.setUniformForProgram("packToBytes", "u_vectorLength", vectorLength, "1f");
globals.gpuMath.setSize(textureDim*vectorLength, textureDim);
globals.gpuMath.step("packToBytes", ["u_lastPosition"], "outputBytes");
var pixels = new Uint8Array(textureDim*textureDim*4*vectorLength);
if (globals.gpuMath.readyToRead()) {
var numPixels = nodes.length*vectorLength;
var height = Math.ceil(numPixels/(textureDim*vectorLength));
globals.gpuMath.readPixels(0, 0, textureDim * vectorLength, height, pixels);
var parsedPixels = new Float32Array(pixels.buffer);
for (var i = 0; i < nodes.length; i++) {
var rgbaIndex = i * vectorLength;
var nodePosition = new THREE.Vector3(parsedPixels[rgbaIndex], parsedPixels[rgbaIndex + 1], parsedPixels[rgbaIndex + 2]);
nodes[i].render(nodePosition);
}
for (var i=0;i<edges.length;i++){
edges[i].render();
}
} else {
console.log("here");
}
globals.threeView.render();
globals.gpuMath.setSize(textureDim, textureDim);
}
function setViewMode(mode){
if (mode == "material"){
_.each(edges, function(edge){
edge.setMaterialColor();
})
}
}
function setSolveParams(){
var dt = calcDt();
var numSteps = 0.5/dt;
globals.gpuMath.setProgram("velocityCalc");
globals.gpuMath.setUniformForProgram("velocityCalc", "u_dt", dt, "1f");
globals.gpuMath.setProgram("positionCalc");
globals.gpuMath.setUniformForProgram("positionCalc", "u_dt", dt, "1f");
return numSteps;
}
function calcDt(){
var maxFreqNat = 0;
_.each(edges, function(beam){
if (beam.getNaturalFrequency()>maxFreqNat) maxFreqNat = beam.getNaturalFrequency();
});
return (1/(2*Math.PI*maxFreqNat))*0.5;//half of max delta t for good measure
}
function updateTextures(gpuMath){
gpuMath.initTextureFromData("u_originalPosition", textureDim, textureDim, "FLOAT", originalPosition, true);
gpuMath.initTextureFromData("u_meta", textureDim, textureDim, "FLOAT", meta, true);
reset();
}
function initTexturesAndPrograms(gpuMath){
textureDim = calcTextureSize(nodes.length);
var vertexShader = document.getElementById("vertexShader").text;
gpuMath.initTextureFromData("u_position", textureDim, textureDim, "FLOAT", position);
gpuMath.initFrameBufferForTexture("u_position");
gpuMath.initTextureFromData("u_lastPosition", textureDim, textureDim, "FLOAT", lastPosition);
gpuMath.initFrameBufferForTexture("u_lastPosition");
gpuMath.initTextureFromData("u_velocity", textureDim, textureDim, "FLOAT", velocity);
gpuMath.initFrameBufferForTexture("u_velocity");
gpuMath.initTextureFromData("u_lastVelocity", textureDim, textureDim, "FLOAT", lastVelocity);
gpuMath.initFrameBufferForTexture("u_lastVelocity");
gpuMath.initTextureFromData("u_originalPosition", textureDim, textureDim, "FLOAT", originalPosition);
gpuMath.initTextureFromData("u_meta", textureDim, textureDim, "FLOAT", meta);
gpuMath.createProgram("positionCalc", vertexShader, document.getElementById("positionCalcShader").text);
gpuMath.setUniformForProgram("positionCalc", "u_velocity", 0, "1i");
gpuMath.setUniformForProgram("positionCalc", "u_lastPosition", 1, "1i");
gpuMath.setUniformForProgram("positionCalc", "u_mass", 2, "1i");
gpuMath.setUniformForProgram("positionCalc", "u_textureDim", [textureDim, textureDim], "2f");
gpuMath.createProgram("velocityCalc", vertexShader, document.getElementById("velocityCalcShader").text);
gpuMath.setUniformForProgram("velocityCalc", "u_lastPosition", 0, "1i");
gpuMath.setUniformForProgram("velocityCalc", "u_lastVelocity", 1, "1i");
gpuMath.setUniformForProgram("velocityCalc", "u_originalPosition", 2, "1i");
gpuMath.setUniformForProgram("velocityCalc", "u_externalForces", 3, "1i");
gpuMath.setUniformForProgram("velocityCalc", "u_mass", 4, "1i");
gpuMath.setUniformForProgram("velocityCalc", "u_meta", 5, "1i");
gpuMath.setUniformForProgram("velocityCalc", "u_beamK", 6, "1i");
gpuMath.setUniformForProgram("velocityCalc", "u_beamD", 7, "1i");
gpuMath.setUniformForProgram("velocityCalc", "u_textureDim", [textureDim, textureDim], "2f");
gpuMath.createProgram("packToBytes", vertexShader, document.getElementById("packToBytesShader").text);
gpuMath.initTextureFromData("outputBytes", textureDim*4, textureDim, "UNSIGNED_BYTE", null);
gpuMath.initFrameBufferForTexture("outputBytes");
gpuMath.setUniformForProgram("packToBytes", "u_floatTextureDim", [textureDim, textureDim], "2f");
gpuMath.createProgram("zeroTexture", vertexShader, document.getElementById("zeroTexture").text);
gpuMath.setSize(textureDim, textureDim);
programsInited = true;
}
function calcTextureSize(numNodes){
if (numNodes == 1) return 2;
for (var i=0;i<numNodes;i++){
if (Math.pow(2, 2*i) >= numNodes){
return Math.pow(2, i);
}
}
console.warn("no texture size found for " + numCells + " cells");
return 0;
}
function updateMaterials(){
for (var i=0;i<nodes.length;i++){
for (var j=0;j<nodes[i].beams.length;j++){
var beam = nodes[i].beams[j];
beamK[4*i+j] = beam.getK();
beamD[4*i+j] = beam.getD();
}
}
globals.gpuMath.initTextureFromData("u_beamK", textureDim, textureDim, "FLOAT", beamK, true);
globals.gpuMath.initTextureFromData("u_beamD", textureDim, textureDim, "FLOAT", beamD, true);
//recalc dt
if (programsInited) setSolveParams();
}
function updateExternalForces(){
for (var i=0;i<nodes.length;i++){
var externalForce = nodes[i].getExternalForce();
externalForces[4*i] = externalForce.x;
externalForces[4*i+1] = externalForce.y;
externalForces[4*i+2] = externalForce.z;
}
globals.gpuMath.initTextureFromData("u_externalForces", textureDim, textureDim, "FLOAT", externalForces, true);
}
function updateFixed(){
for (var i=0;i<nodes.length;i++){
mass[4*i+1] = (nodes[i].isFixed() ? 1 : 0);
}
globals.gpuMath.initTextureFromData("u_mass", textureDim, textureDim, "FLOAT", mass, true);
}
function updateOriginalPosition(){
for (var i=0;i<nodes.length;i++){
var origPosition = nodes[i].getOriginalPosition();
originalPosition[4*i] = origPosition.x;
originalPosition[4*i+1] = origPosition.y;
originalPosition[4*i+2] = origPosition.z;
}
globals.gpuMath.initTextureFromData("u_originalPosition", textureDim, textureDim, "FLOAT", originalPosition, true);
}
function initTypedArrays(){
textureDim = calcTextureSize(nodes.length);
originalPosition = new Float32Array(textureDim*textureDim*4);
position = new Float32Array(textureDim*textureDim*4);
lastPosition = new Float32Array(textureDim*textureDim*4);
velocity = new Float32Array(textureDim*textureDim*4);
lastVelocity = new Float32Array(textureDim*textureDim*4);
externalForces = new Float32Array(textureDim*textureDim*4);
mass = new Float32Array(textureDim*textureDim*4);
meta = new Float32Array(textureDim*textureDim*4);
beamK = new Float32Array(textureDim*textureDim*4);
beamD = new Float32Array(textureDim*textureDim*4);
for (var i=0;i<textureDim*textureDim;i++){
mass[4*i+1] = 1;//set all fixed by default
}
_.each(nodes, function(node, index){
mass[4*index] = node.getSimMass();
meta[4*index] = -1;
meta[4*index+1] = -1;
meta[4*index+2] = -1;
meta[4*index+3] = -1;
_.each(node.beams, function(beam, i){
meta[4*index+i] = beam.getOtherNode(node).getIndex();
});
});
updateOriginalPosition();
updateMaterials();
updateFixed();
updateExternalForces();
}
function getChildren(){
return object3D.children;
}
return {
setVisibility: setVisibility,
getChildren: getChildren,
setViewMode: setViewMode,
syncNodesAndEdges: syncNodesAndEdges,
updateOriginalPosition: updateOriginalPosition
}
}
\ No newline at end of file
......@@ -18,7 +18,8 @@ function initGlobals(){
panelStiffness: 1,
//dynamic sim settings
percentDamping: 1
percentDamping: 1,
density: 1
};
var isMobile = {
......@@ -44,7 +45,7 @@ function initGlobals(){
if(isMobile.any()) _globals.dynamicSimVisible = false;
_globals.threeView = initThreeView(_globals);
// _globals.gpuMath = initGPUMath();
_globals.gpuMath = initGPUMath();
_globals.controls = initControls(_globals);
return _globals;
......
......@@ -16,6 +16,7 @@ $(function() {
var isDragging = false;
var mouseDown = false;
$(document).dblclick(function() {
});
......@@ -34,13 +35,13 @@ $(function() {
}
e.preventDefault();
// globals.controls.hideMoreInfo();
mouse.x = (e.clientX/window.innerWidth)*2-1;
mouse.y = - (e.clientY/window.innerHeight)*2+1;
raycaster.setFromCamera(mouse, globals.threeView.camera);
}
globals = initGlobals();
globals.model = initModel(globals);