Cloth.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /*
  2. * Aug 3 2012
  3. *
  4. * Since I started working for a new startup not too long ago,
  5. * I commute between home and work for over 2 hours a day.
  6. * Although this means less time on three.js,
  7. * I try getting a little coding on the train.
  8. *
  9. * This set of experiments started from a simple hook's law doodle,
  10. * to spring simulation, string simulation, and I realized
  11. * I once again stepped onto physics and particle simulation,
  12. * this time, more specifically soft body physics.
  13. *
  14. * Based on the "Advanced Character Physics" article,
  15. * this experiment attempts to use a "massless"
  16. * cloth simulation model. It's somewhat similiar
  17. * but simplier to most cloth simulations I found.
  18. *
  19. * This was coded out fairly quickly, so expect more to come
  20. * meanwhile feel free to experiment yourself and share
  21. *
  22. * Cheers,
  23. * Graphics Noob (aka @Blurspline, zz85)
  24. */
  25. // Suggested Readings
  26. // Advanced Character Physics by Thomas Jakobsen Character
  27. // http://freespace.virgin.net/hugo.elias/models/m_cloth.htm
  28. // http://en.wikipedia.org/wiki/Cloth_modeling
  29. // http://cg.alexandra.dk/tag/spring-mass-system/
  30. // Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf
  31. var DAMPING = 0.01;
  32. var DRAG = 1 - DAMPING;
  33. var MASS = .1;
  34. var restDistance = 25; //
  35. function Particle(x, y, z, mass) {
  36. this.position = new THREE.Vector3(x, y, z); // position
  37. this.previous = new THREE.Vector3(x, y, z); // previous
  38. this.a = new THREE.Vector3(0, 0, 0); // acceleration
  39. this.mass = mass;
  40. this.invMass = 1 / mass;
  41. this.tmp = new THREE.Vector3();
  42. this.tmp2 = new THREE.Vector3();
  43. }
  44. // Force -> Acceleration
  45. Particle.prototype.addForce = function(force) {
  46. this.a.addSelf(
  47. this.tmp2.copy(force).multiplyScalar(this.invMass)
  48. );
  49. };
  50. // Performs verlet integration
  51. Particle.prototype.integrate = function(timesq) {
  52. var newPos = this.tmp.sub(this.position, this.previous);
  53. newPos.multiplyScalar(DRAG).addSelf(this.position);
  54. newPos.addSelf(this.a.multiplyScalar(timesq));
  55. this.tmp = this.previous;
  56. this.previous = this.position;
  57. this.position = newPos;
  58. this.a.set(0, 0, 0);
  59. }
  60. var diff = new THREE.Vector3();
  61. function satisifyConstrains(p1, p2, distance) {
  62. diff.sub(p2.position, p1.position);
  63. var currentDist = diff.length();
  64. if (currentDist==0) return; // prevents division by 0
  65. var correction = diff.multiplyScalar(1 - distance/currentDist);
  66. var correctionHalf = correction.multiplyScalar(0.5);
  67. p1.position.addSelf(correctionHalf);
  68. p2.position.subSelf(correctionHalf);
  69. // float difference = (restingDistance - d) / d
  70. // im1 = 1 / p1.mass // inverse mass quantities
  71. // im2 = 1 / p2.mass
  72. // p1.position += delta * (im1 / (im1 + im2)) * stiffness * difference
  73. }
  74. function Cloth(w, h) {
  75. w = w || 10;
  76. h = h || 10;
  77. this.w = w;
  78. this.h = h;
  79. var particles = [];
  80. var constrains = [];
  81. var u, v;
  82. // Create particles
  83. for (v=0;v<=h;v++) {
  84. for (u=0;u<=w;u++) {
  85. particles.push(
  86. new Particle((u - w/2) * restDistance, (v - h/2) * -restDistance, 0, MASS)
  87. );
  88. }
  89. }
  90. // Structural
  91. for (v=0;v<h;v++) {
  92. for (u=0;u<w;u++) {
  93. constrains.push([
  94. particles[index(u, v)],
  95. particles[index(u, v+1)],
  96. restDistance
  97. ]);
  98. constrains.push([
  99. particles[index(u, v)],
  100. particles[index(u+1, v)],
  101. restDistance
  102. ]);
  103. }
  104. }
  105. for (u=w, v=0;v<h;v++) {
  106. constrains.push([
  107. particles[index(u, v)],
  108. particles[index(u, v+1)],
  109. restDistance
  110. ]);
  111. }
  112. for (v=h, u=0;u<w;u++) {
  113. constrains.push([
  114. particles[index(u, v)],
  115. particles[index(u+1, v)],
  116. restDistance
  117. ]);
  118. }
  119. // While many system uses shear and bend springs,
  120. // the relax constrains model seem to be just fine
  121. // using structural springs.
  122. // // Shear
  123. // var diagonalDist = Math.sqrt(restDistance * restDistance * 2);
  124. // for (v=0;v<h;v++) {
  125. // for (u=0;u<w;u++) {
  126. // constrains.push([
  127. // particles[index(u, v)],
  128. // particles[index(u+1, v+1)],
  129. // diagonalDist
  130. // ]);
  131. // constrains.push([
  132. // particles[index(u+1, v)],
  133. // particles[index(u, v+1)],
  134. // diagonalDist
  135. // ]);
  136. // }
  137. // }
  138. // // Bend
  139. // var wlen = restDistance * 2;
  140. // var hlen = restDistance * 2;
  141. // diagonalDist = Math.sqrt(wlen * wlen + hlen * hlen);
  142. // for (v=0;v<h-1;v++) {
  143. // for (u=0;u<w-1;u++) {
  144. // constrains.push([
  145. // particles[index(u, v)],
  146. // particles[index(u+2, v)],
  147. // wlen
  148. // ]);
  149. // constrains.push([
  150. // particles[index(u, v)],
  151. // particles[index(u, v+2)],
  152. // hlen
  153. // ]);
  154. // constrains.push([
  155. // particles[index(u, v)],
  156. // particles[index(u+2, v+2)],
  157. // diagonalDist
  158. // ]);
  159. // constrains.push([
  160. // particles[index(u, v+2)],
  161. // particles[index(u+2, v+2)],
  162. // wlen
  163. // ]);
  164. // constrains.push([
  165. // particles[index(u+2, v+2)],
  166. // particles[index(u+2, v+2)],
  167. // hlen
  168. // ]);
  169. // constrains.push([
  170. // particles[index(u+2, v)],
  171. // particles[index(u, v+2)],
  172. // diagonalDist
  173. // ]);
  174. // }
  175. // }
  176. this.particles = particles;
  177. this.constrains = constrains;
  178. function index(u, v) {
  179. return u + v * (w + 1);
  180. }
  181. }
  182. var cloth = new Cloth();
  183. var GRAVITY = 981; //
  184. var gravity = new THREE.Vector3( 0, -GRAVITY, 0 ).multiplyScalar(MASS);
  185. var TIMESTEP = 14 / 1000;
  186. var TIMESTEP_SQ = TIMESTEP * TIMESTEP;
  187. var pins = [true];
  188. pins[cloth.w] = true;
  189. var wind = true;
  190. var windStrength = 2;
  191. var windForce = new THREE.Vector3(0,0,0);
  192. var ballPosition = new THREE.Vector3(0, -45, 0);
  193. var ballSize = 60; //40
  194. var tmpForce = new THREE.Vector3();
  195. function simulate() {
  196. var i, il, particles, particle, pt, constrains, constrain;
  197. // Aerodynamics forces
  198. if (wind) {
  199. var face, faces = clothGeometry.faces, normal;
  200. particles = cloth.particles;
  201. for (i=0,il=faces.length;i<il;i++) {
  202. face = faces[i];
  203. normal = face.normal;
  204. tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(windForce));
  205. particles[face.a].addForce(tmpForce);
  206. particles[face.b].addForce(tmpForce);
  207. particles[face.c].addForce(tmpForce);
  208. }
  209. }
  210. for (particles = cloth.particles, i=0, il = particles.length
  211. ;i<il;i++) {
  212. particle = particles[i];
  213. particle.addForce(gravity);
  214. // particle.addForce(windForce);
  215. particle.integrate(TIMESTEP_SQ);
  216. }
  217. // Start Constrains
  218. constrains = cloth.constrains,
  219. il = constrains.length;
  220. for (i=0;i<il;i++) {
  221. constrain = constrains[i];
  222. satisifyConstrains(constrain[0], constrain[1], constrain[2]);
  223. }
  224. // Ball Constrains
  225. ballPosition.z = -Math.sin(Date.now()/300) * 90 ; //+ 40;
  226. ballPosition.x = Math.cos(Date.now()/200) * 70
  227. if (sphere.visible)
  228. for (particles = cloth.particles, i=0, il = particles.length
  229. ;i<il;i++) {
  230. particle = particles[i];
  231. pos = particle.position;
  232. diff.sub(pos, ballPosition);
  233. if (diff.length() < ballSize) {
  234. // collided
  235. diff.normalize().multiplyScalar(ballSize);
  236. pos.copy(ballPosition).addSelf(diff);
  237. }
  238. }
  239. // Pin Constrains
  240. for (i=0, il=cloth.w;i<=il;i++) {
  241. if (pins[i]) {
  242. particle = particles[i];
  243. particle.previous.set((i - cloth.w/2) * restDistance, -cloth.h/2 * -restDistance, 0);
  244. particle.position.copy(particle.previous);
  245. }
  246. }
  247. }