Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Updated WebGLOpenCloth.js (https://code.google.com/p/opencloth/) to newest version of Three.js (r.67)
<html>
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r67/three.min.js"></script>
<script src="js/Stats.js"></script>
<title>OpenCloth WebGL Demo by Movania Muhammad Mobeen</title>
</head>
<body style="background-color:#eee">
<div align=right><p><b>OpenCloth WebGL Demo</b><br>
Integrator: Explicit Euler <br>
Step size: 1/60</p></div>
</body>
<script type="text/javascript">
var camera, scene, renderer,
geometry, material, mesh, plane;
var stats;
var oldX, oldY, rX, rY, dist, state, D2R;
var simulate = true;
var EPSILON = 0.0001;
var EPSILON2 = EPSILON*EPSILON;
springs=new Array();
F=new Array();
V=new Array();
var numX = 20;
var numY = 20;
var pickedPoint = -1;
DEFAULT_DAMPING= 60 ;
GRAVITY=new THREE.Vector3(0,-9.8,0);
mass = 1;
dt = 1.0/60;
init();
animate();
function Spring () {
this.p1=-1;
this.p2=-1;
this.rest_length=0;
this.Ks=0.5;
this.Kd=0.5;
}
function AddSpring(a, b, ks, kd) {
var spring=new Spring();
spring.p1=a;
spring.p2=b;
spring.Ks=ks;
spring.Kd=kd;
var deltaP = new THREE.Vector3();
deltaP.subVectors(mesh.geometry.vertices[a],mesh.geometry.vertices[b]);
spring.rest_length = Math.sqrt(deltaP.dot(deltaP));
springs.push(spring);
}
function init() {
var info = document.createElement('div');
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
document.body.appendChild(info);
rX = 75;
rY = 90;
dist = 10;
oldX = 0;
oldY = 0;
state = -1;
D2R = 0.0174532925199433;
size = 4; //world space size of cloth
hsize = size/2;
u = numX +1;
v = numY +1;
KsStruct = 1000;
KdStruct = 0.5;
KsShear = 1000;
KdShear = 0.5;
KsBend = 1000;
KdBend = 0.5;
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );
scene = new THREE.Scene();
scene.add(camera);
//draw axes
var geoX = new THREE.Geometry();
var geoY = new THREE.Geometry();
var geoZ = new THREE.Geometry();
geoX.vertices.push(new THREE.Vector3(0, 0, 0));
geoX.vertices.push(new THREE.Vector3(1, 0, 0));
geoY.vertices.push(new THREE.Vector3(0, 0, 0));
geoY.vertices.push(new THREE.Vector3(0, 1, 0));
geoZ.vertices.push(new THREE.Vector3(0, 0, 0));
geoZ.vertices.push(new THREE.Vector3(0, 0, 1));
var lineX = new THREE.Line(geoX, new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 3 }));
var lineY = new THREE.Line(geoY, new THREE.LineBasicMaterial({ color: 0x00ff00, linewidth: 3 }));
var lineZ = new THREE.Line(geoZ, new THREE.LineBasicMaterial({ color: 0x0000ff, linewidth: 3 }));
scene.add(lineX);
scene.add(lineY);
scene.add(lineZ);
geometry = new THREE.PlaneGeometry( -20, 20, 10, 10);
plane = new THREE.Mesh( geometry,new THREE.MeshBasicMaterial( { color: 0x0000ff, wireframe:true } ));
plane.doubleSided = true;
plane.rotation.x = 90 * D2R;
scene.add( plane );
geometry = new THREE.Geometry();
//Fill in vertices of cloth mesh
for(var j=0;j<v;j++) {
for(var i=0;i<u;i++) {
var tmp = new THREE.Vector3( ((i/(u-1)) *2.0-1.)* hsize,size+1, ((j/(v-1.0) )* size));
F.push(new THREE.Vector3(0,0,0));
V.push(new THREE.Vector3(0,0,0));
geometry.vertices.push(tmp);
}
}
//Now fill in face information
for (var i = 0; i < numY; i++) {
for (var j = 0; j < numX; j++) {
i0 = i * (numX+1) + j;
i1 = i0 + 1;
i2 = i0 + (numX+1);
i3 = i2 + 1;
/*
if ((j+i)%2) {
var face1 = new THREE.Face3(i0, i2, i1);
var face2 = new THREE.Face3(i1, i2, i3);
geometry.faces.push(face1);
geometry.faces.push(face2);
} else {
var face1 = new THREE.Face3(i0, i2, i3);
var face2 = new THREE.Face3(i0, i3, i1);
geometry.faces.push(face1);
geometry.faces.push(face2);
}
*/
//using Face4 instead of Face3 based on suggestion of Mr.Doob
var face = new THREE.Face3(i0, i2, i3);
geometry.faces.push(face);
var face2 = new THREE.Face3(i2, i3, i1);
geometry.faces.push(face2);
}
}
geometry.computeFaceNormals();
// geometry.computeCentroids();
mesh = new THREE.Mesh( geometry,new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe:true } )); //new THREE.MeshNormalMaterial() );
mesh.doubleSided = true;
mesh.geometry.dynamic = true; //for WebGL renderer
scene.add( mesh );
//setup springs
// Horizontal
for (l1 = 0; l1 < v; l1++) // v
for (l2 = 0; l2 < (u - 1); l2++) {
AddSpring((l1 * u) + l2,(l1 * u) + l2 + 1,KsStruct,KdStruct);
}
// Vertical
for (l1 = 0; l1 < (u); l1++)
for (l2 = 0; l2 < (v - 1); l2++) {
AddSpring((l2 * u) + l1,((l2 + 1) * u) + l1,KsStruct,KdStruct);
}
// Shearing Springs
for (l1 = 0; l1 < (v - 1); l1++)
for (l2 = 0; l2 < (u - 1); l2++) {
AddSpring((l1 * u) + l2,((l1 + 1) * u) + l2 + 1,KsShear,KdShear);
AddSpring(((l1 + 1) * u) + l2,(l1 * u) + l2 + 1,KsShear,KdShear);
}
// Bend Springs
for (l1 = 0; l1 < (v); l1++) {
for (l2 = 0; l2 < (u - 2); l2++) {
AddSpring((l1 * u) + l2,(l1 * u) + l2 + 2,KsBend,KdBend);
}
AddSpring((l1 * u) + (u - 3),(l1 * u) + (u - 1),KsBend,KdBend);
}
for (l1 = 0; l1 < (u); l1++) {
for (l2 = 0; l2 < (v - 2); l2++) {
AddSpring((l2 * u) + l1,((l2 + 2) * u) + l1,KsBend,KdBend);
}
AddSpring(((v - 3) * u) + l1,((v - 1) * u) + l1,KsBend,KdBend);
}
console.log("Total springs: "+springs.length);
console.log("Total forces: "+F.length);
console.log("Total velocities: "+V.length);
// renderer = new THREE.CanvasRenderer();
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
document.body.appendChild(stats.domElement);
document.addEventListener( 'mousedown', onMouseDown, false );
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
}
function onMouseDown( event ) {
event.preventDefault();
//0 left mouse button
//1 middle mouse button
//2 right mouse button
state = event.button;
oldX = event.clientX;
oldY = event.clientY;
if (state == 0) {
}
}
function onMouseMove(event) {
if(state == 1) {
dist = dist * (1 + (event.clientY - oldY)/60.0);
} else if(state == 0){
rY += (event.clientX - oldX)/5.0;
rX += (event.clientY - oldY)/5.0;
}
oldX = event.clientX;
oldY = event.clientY;
render();
}
function onMouseUp( event ) {
event.preventDefault();
//console.log("Rx: "+rX+",Ry: "+rY);
state = -1;
}
function computeForces() {
for(var i=0;i<mesh.geometry.vertices.length;i++) {
F[i].x=0;
F[i].y=0;
F[i].z=0;
if(i!=0 && i!= numX)
F[i].add(GRAVITY);
F[i].sub( V[i].multiplyScalar(DEFAULT_DAMPING));
}
//add spring forces
var deltaP = new THREE.Vector3();
var deltaV = new THREE.Vector3();
for(var i=0;i<springs.length;i++) {
var p1 = mesh.geometry.vertices[springs[i].p1];
var p2 = mesh.geometry.vertices[springs[i].p2];
var v1 = V[springs[i].p1];
var v2 = V[springs[i].p2];
deltaP.subVectors(p1,p2);
deltaV.subVectors(v1,v2);
var dist = deltaP.length();
//console.log("p1: "+p1+", p2: "+p2+" Dis: "+dist);
var leftTerm = -springs[i].Ks * (dist-springs[i].rest_length);
var rightTerm = -springs[i].Kd * ((deltaV.dot(deltaP))/dist);
var springForce = (deltaP.normalize()).multiplyScalar(leftTerm + rightTerm);
if(springs[i].p1 != 0 && springs[i].p1 != numX)
F[springs[i].p1].add(springForce);
if(springs[i].p2 != 0 && springs[i].p2 != numX )
F[springs[i].p2].sub(springForce);
}
}
function integrateEuler() {
var deltaTimeMass = dt/ mass;
var N = mesh.geometry.vertices.length;
var oldP = mesh.geometry.vertices[N-1].clone();
geometry.verticesNeedUpdate = true;
for(var i=0;i<N;i++) {
var oldV = V[i];
V[i].add(F[i].multiplyScalar(deltaTimeMass));
mesh.geometry.vertices[i].add(oldV.multiplyScalar(dt));
if(mesh.geometry.vertices[i].y <0) {
mesh.geometry.vertices[i].y = 0;
}
}
var diff = new THREE.Vector3(0,0,0);
diff.subVectors(oldP, mesh.geometry.vertices[N - 1]);
simulate = (diff.lengthSq()> EPSILON2);
//
//console.log("Diff: " + diff.length());
}
function doPhysics() {
computeForces();
//console.log("Force: "+F[1].z+" Vel: "+V[1].z);
integrateEuler();
}
function animate() {
requestAnimationFrame(animate);
if (simulate)
doPhysics();
else {
if (mesh.material.color.r == 1) {
mesh.material.color.r = 0;
mesh.material.color.g = 1;
console.log("Cloth passive");
}
}
render();
stats.update();
}
function render() {
// console.log("rendering");
/*
mesh.position.z = dist;
mesh.rotation.x = rX*D2R;
mesh.rotation.z = rY*D2R;
plane.position.z = dist;
plane.rotation.x = rX*D2R;
plane.rotation.z = rY*D2R;
*/
var theta = rY * D2R;
var phi = rX * D2R;
var radius = dist;
camera.position.x = radius * Math.sin(phi) * Math.cos(theta);
camera.position.y = radius * Math.cos(phi);
camera.position.z = radius * Math.sin(phi) * Math.sin(theta);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// camera.update();
renderer.render( scene, camera );
}
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.