]> gitweb.ps.run Git - cloth_sim/blobdiff - Scripts/cloth.js
render cloth
[cloth_sim] / Scripts / cloth.js
index 9389ef60c13cb8e827d6054d92012727ac4129a1..05ff6f1f30eb54da8985b99c786d3596662f214e 100644 (file)
-/**\r
- *  Convenience Function for calculating the distance between two vectors\r
- *  because THREE JS Vector functions mutate variables\r
- * @param {Vector3} a - Vector A\r
- * @param {Vector3} b - Vector B\r
- */\r
-function vectorLength(a, b) {\r
-  let v1 = new THREE.Vector3();\r
-  v1.set(a.x, a.y, a.z);\r
-  let v2 = new THREE.Vector3();\r
-  v2.set(b.x, b.y, b.z);\r
-\r
-  return v1.sub(v2).length();\r
-}\r
-\r
-/**\r
- * Class representing a quad face\r
- * Each face consists of two triangular mesh faces\r
- * containts four indices for determining vertices\r
- * and six springs, one between each of the vertices\r
- */\r
-export class Face {\r
-  a;\r
-  b;\r
-  c;\r
-  d;\r
-\r
-  springs = [];\r
-\r
-  constructor(a, b, c, d) {\r
-    this.a = a;\r
-    this.b = b;\r
-    this.c = c;\r
-    this.d = d;\r
+// cloth rendering\r
+// simulate\r
+// setup scene\r
+\r
+const MASS = 0.1;\r
+\r
+class Particle {\r
+  constructor(x, y, z, mass) {\r
+    this.position = new THREE.Vector3(x, y, z);\r
+    this.previous = new THREE.Vector3(x, y, z);\r
+    this.acceleration = new THREE.Vector3(0, 0, 0);\r
+    this.mass = mass;\r
   }\r
-}\r
-\r
-/**\r
- * Class representing a single spring\r
- * has a current and resting length\r
- * and indices to the two connected vertices\r
- */\r
-export class Spring {\r
-  restLength;\r
-  currentLength;\r
-  index1;\r
-  index2;\r
+  addForce(force) {\r
 \r
-\r
-  /**\r
-   * set vertex indices\r
-   * and calculate inital length based on the\r
-   * vertex positions\r
-   * @param {Array<Vector3>} vertices \r
-   * @param {number} index1 \r
-   * @param {number} index2 \r
-   */\r
-  constructor(vertices, index1, index2) {\r
-    this.index1 = index1;\r
-    this.index2 = index2;\r
-\r
-    let length = vectorLength(vertices[index1], vertices[index2]);\r
-    this.restLength = length;\r
-    this.currentLength = length;\r
   }\r
+  verlet(dt) {\r
 \r
-  getDirection(vertices) {\r
-    let direction = new THREE.Vector3(\r
-      vertices[this.index1].x,\r
-      vertices[this.index1].y,\r
-      vertices[this.index1].z\r
-    );\r
-\r
-    direction.sub(vertices[this.index2]);\r
-    direction.divideScalar(vectorLength(vertices[this.index1], vertices[this.index2]));\r
-\r
-    return direction;\r
   }\r
 }\r
 \r
-/**\r
- * Class representing a single piece of cloth\r
- * contains THREE JS geometry,\r
- * logically represented by an array of adjacent faces\r
- * and vertex weights which are accessed by the same\r
- * indices as the vertices in the Mesh\r
- */\r
-export class Cloth {\r
-  VertexWeight = 1;\r
-\r
-  geometry = new THREE.Geometry();\r
-\r
-  faces = [];\r
-\r
-  vertexWeights = [];\r
-\r
-\r
-\r
-  /**\r
-   * creates a rectangular piece of cloth\r
-   * takes the size of the cloth\r
-   * and the number of vertices it should be composed of\r
-   * @param {number} width - width of the cloth\r
-   * @param {number} height - height of the cloth\r
-   * @param {number} numPointsWidth - number of vertices in horizontal direction\r
-   * @param {number} numPointsHeight  - number of vertices in vertical direction\r
-   */\r
-  createBasic(width, height, numPointsWidth, numPointsHeight) {\r
-    /** resulting vertices and faces */\r
-    let vertices = [];\r
-    let faces = [];\r
+class Cloth {\r
+  constructor(width, height, numPointsWidth, numPointsHeight) {\r
+    this.width = width;\r
+    this.height = height;\r
+    this.numPointsWidth = numPointsWidth;\r
+    this.numPointsHeight = numPointsHeight;\r
 \r
     /**\r
      * distance between two vertices horizontally/vertically\r
@@ -120,15 +36,37 @@ export class Cloth {
 \r
     /**\r
      * iterate over the number of vertices in x/y axis\r
-     * and add a new Vector3 to "vertices"\r
+     * and add a new Particle to "particles"\r
      */\r
+    this.particles = [];\r
     for (let y = 0; y < numPointsHeight; y++) {\r
       for (let x = 0; x < numPointsWidth; x++) {\r
-        vertices.push(\r
-          new THREE.Vector3(x * stepWidth, height - y * stepHeight, 0)\r
+        this.particles.push(\r
+          new Particle(\r
+            (x - ((numPointsWidth-1)/2)) * stepWidth,\r
+            height - (y + ((numPointsHeight-1)/2)) * stepHeight,\r
+            0,\r
+            MASS)\r
         );\r
       }\r
     }\r
+  }\r
+  generateGeometry() {\r
+    const geometry = new THREE.BufferGeometry();\r
+\r
+    const vertices = [];\r
+    const normals = [];\r
+    const indices = [];\r
+\r
+    for (let particle of this.particles) {\r
+      vertices.push(\r
+        particle.position.x,\r
+        particle.position.y,\r
+        particle.position.z);\r
+    }\r
+\r
+    const numPointsWidth = this.numPointsWidth;\r
+    const numPointsHeight = this.numPointsHeight;\r
 \r
     /**\r
      * helper function to calculate index of vertex\r
@@ -147,289 +85,28 @@ export class Cloth {
      */\r
     for (let y = 0; y < numPointsHeight - 1; y++) {\r
       for (let x = 0; x < numPointsWidth - 1; x++) {\r
-        let newFace = new Face(\r
+        indices.push(\r
           getVertexIndex(x, y),\r
-          getVertexIndex(x, y + 1),\r
-          getVertexIndex(x + 1, y),\r
-          getVertexIndex(x + 1, y + 1),\r
+          getVertexIndex(x+1, y),\r
+          getVertexIndex(x+1, y+1)\r
+        );\r
+        indices.push(\r
+          getVertexIndex(x, y),\r
+          getVertexIndex(x+1, y+1),\r
+          getVertexIndex(x, y+1)\r
         );\r
-\r
-        newFace.springs.push(new Spring(vertices, getVertexIndex(x, y), getVertexIndex(x + 1, y)));\r
-        newFace.springs.push(new Spring(vertices, getVertexIndex(x, y), getVertexIndex(x, y + 1)));\r
-        newFace.springs.push(new Spring(vertices, getVertexIndex(x, y), getVertexIndex(x + 1, y + 1)));\r
-        newFace.springs.push(new Spring(vertices, getVertexIndex(x + 1, y), getVertexIndex(x, y + 1)));\r
-        newFace.springs.push(new Spring(vertices, getVertexIndex(x + 1, y), getVertexIndex(x + 1, y + 1)));\r
-        newFace.springs.push(new Spring(vertices, getVertexIndex(x, y + 1), getVertexIndex(x + 1, y + 1)));\r
-\r
-        faces.push(newFace);\r
       }\r
     }\r
 \r
-    /**\r
-     * call createExplicit\r
-     * with generated vertices and faces\r
-     */\r
-    this.createExplicit(vertices, faces);\r
-  }\r
-\r
-  /**\r
-   * Generate THREE JS Geometry\r
-   * (list of vertices and list of indices representing triangles)\r
-   * and calculate the weight of each face and split it between\r
-   * surrounding vertices\r
-   * @param {Array<Vector3>} vertices \r
-   * @param {Array<Face>} faces \r
-   */\r
-  createExplicit(vertices, faces) {\r
-\r
-    /**\r
-     * Copy vertices and initialize vertex weights to 0\r
-     */\r
-    for (let i in vertices) {\r
-      this.geometry.vertices.push(vertices[i]);\r
-      this.previousPositions.push(vertices[i]);\r
-      this.vertexWeights.push(0);\r
-    }\r
-    /**\r
-     * copy faces,\r
-     * generate two triangles per face,\r
-     * calculate weight of face as its area\r
-     * and split between the 4 vertices\r
-     */\r
-    for (let i in faces) {\r
-      let face = faces[i];\r
-\r
-      /** copy faces to class member */\r
-      this.faces.push(face);\r
-\r
-      /** generate triangles */\r
-      this.geometry.faces.push(new THREE.Face3(\r
-        face.a, face.b, face.c\r
-      ));\r
-      this.geometry.faces.push(new THREE.Face3(\r
-        face.c, face.b, face.d\r
-      ));\r
-\r
-      /**\r
-       * calculate area of face as combined area of\r
-       * its two composing triangles\r
-       */\r
-      let xLength = vectorLength(this.geometry.vertices[face.b], this.geometry.vertices[face.a]);\r
-      let yLength = vectorLength(this.geometry.vertices[face.c], this.geometry.vertices[face.a]);\r
-      let weight = xLength * yLength / 2;\r
-\r
-      xLength = vectorLength(this.geometry.vertices[face.b], this.geometry.vertices[face.d]);\r
-      yLength = vectorLength(this.geometry.vertices[face.c], this.geometry.vertices[face.d]);\r
-      weight += xLength * yLength / 2;\r
+    geometry.setIndex(indices);\r
+    geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));\r
+    //geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));\r
+    geometry.computeBoundingSphere();\r
+    geometry.computeVertexNormals();\r
 \r
-      /**\r
-       * split weight equally between four surrounding vertices\r
-       */\r
-      this.vertexWeights[face.a] += weight / 4;\r
-      this.vertexWeights[face.b] += weight / 4;\r
-      this.vertexWeights[face.c] += weight / 4;\r
-      this.vertexWeights[face.d] += weight / 4;\r
-    }\r
-\r
-    /**\r
-     * let THREE JS compute bounding sphere around generated mesh\r
-     * needed for View Frustum Culling internally\r
-     */\r
-    this.geometry.computeBoundingSphere();\r
+    return geometry;\r
   }\r
+  updateGeometry(geometry) {\r
 \r
-  /**\r
-   * generate a debug mesh for visualizing\r
-   * vertices and springs of the cloth\r
-   * and add it to scene for rendering\r
-   * @param {Scene} scene - Scene to add Debug Mesh to\r
-   */\r
-  createDebugMesh(scene) {\r
-    /**\r
-     * helper function to generate a single line\r
-     * between two Vertices with a given color\r
-     * @param {Vector3} from \r
-     * @param {Vector3} to \r
-     * @param {number} color \r
-     */\r
-    function addLine(from, to, color) {\r
-      let geometry = new THREE.Geometry();\r
-      geometry.vertices.push(from);\r
-      geometry.vertices.push(to);\r
-      let material = new THREE.LineBasicMaterial({ color: color, linewidth: 10 });\r
-      let line = new THREE.Line(geometry, material);\r
-      line.renderOrder = 1;\r
-      scene.add(line);\r
-    }\r
-    /**\r
-     * helper function to generate a small sphere\r
-     * at a given Vertex Position with color\r
-     * @param {Vector3} point \r
-     * @param {number} color \r
-     */\r
-    function addPoint(point, color) {\r
-      const geometry = new THREE.SphereGeometry(0.05, 32, 32);\r
-      const material = new THREE.MeshBasicMaterial({ color: color });\r
-      const sphere = new THREE.Mesh(geometry, material);\r
-      sphere.position.set(point.x, point.y, point.z);\r
-      scene.add(sphere);\r
-    }\r
-\r
-    let lineColor = 0x000000;\r
-    let pointColor = 0xff00000;\r
-\r
-    /**\r
-     * generate one line for each of the 6 springs\r
-     * and one point for each of the 4 vertices\r
-     * for all of the faces\r
-     */\r
-    for (let i in this.faces) {\r
-      let face = this.faces[i];\r
-      addLine(this.geometry.vertices[face.a], this.geometry.vertices[face.b], lineColor);\r
-      addLine(this.geometry.vertices[face.a], this.geometry.vertices[face.c], lineColor);\r
-      addLine(this.geometry.vertices[face.a], this.geometry.vertices[face.d], lineColor);\r
-      addLine(this.geometry.vertices[face.b], this.geometry.vertices[face.c], lineColor);\r
-      addLine(this.geometry.vertices[face.b], this.geometry.vertices[face.d], lineColor);\r
-      addLine(this.geometry.vertices[face.c], this.geometry.vertices[face.d], lineColor);\r
-\r
-      addPoint(this.geometry.vertices[face.a], pointColor);\r
-      addPoint(this.geometry.vertices[face.b], pointColor);\r
-      addPoint(this.geometry.vertices[face.c], pointColor);\r
-      addPoint(this.geometry.vertices[face.d], pointColor);\r
-    }\r
   }\r
-\r
-  previousPositions = [];\r
-  time = 0;\r
-  /**\r
-   * \r
-   * @param {number} dt \r
-   */\r
-  simulate(dt) {\r
-\r
-\r
-\r
-    for (let i in this.geometry.vertices) {\r
-      let currentPosition;\r
-      let acceleration = this.getAcceleration(i, dt);\r
\r
-      currentPosition = this.verlet(this.geometry.vertices[i], this.previousPositions[i], acceleration, dt/2000);\r
-     \r
-      this.previousPositions[i] = currentPosition;\r
-      this.geometry.vertices[i] = currentPosition;\r
-      \r
-    }\r
-    console.log(this.geometry.vertices[0]);\r
-    this.time += dt;\r
-\r
-    /**\r
-     * let THREE JS compute bounding sphere around generated mesh\r
-     * needed for View Frustum Culling internally\r
-     */\r
-\r
-    this.geometry.verticesNeedUpdate = true;\r
-    this.geometry.elementsNeedUpdate = true;\r
-    this.geometry.computeBoundingSphere();\r
-\r
-  }\r
-\r
-\r
-\r
-/**\r
- * Equation of motion for each vertex which represents the acceleration \r
- * @param {number} vertexIndex The index of the current vertex whose acceleration should be calculated\r
- *  @param {number} dt The time passed since last frame\r
- */\r
-getAcceleration(vertexIndex, dt) {\r
-\r
-  let vertex = this.geometry.vertices[vertexIndex];\r
-\r
-  // Mass of vertex\r
-  let M = this.vertexWeights[vertexIndex];\r
-  // constant gravity\r
-  let g = new THREE.Vector3(0, -1.8, 0);\r
-  // stiffness\r
-  let k = 5;\r
-\r
-  // Wind vector\r
-  let fWind = new THREE.Vector3(\r
-    Math.sin(vertex.x * vertex.y * this.time),\r
-    Math.cos(vertex.z* this.time),\r
-    Math.sin(Math.cos(5 * vertex.x * vertex.y * vertex.z))\r
-  );\r
-\r
-  /**\r
-   * constant determined by the properties of the surrounding fluids (air)\r
-   * achievement of cloth effects through try out\r
-   * */\r
-  let a = 1;\r
-\r
-  let velocity = new THREE.Vector3(\r
-    (vertex.x - this.previousPositions[vertexIndex].x) / dt,\r
-    (vertex.y - this.previousPositions[vertexIndex].y) / dt,\r
-    (vertex.z - this.previousPositions[vertexIndex].z) / dt\r
-  );\r
-\r
-\r
-  let fAirResistance = velocity.multiplyScalar(-a);\r
-\r
-  let springSum = new THREE.Vector3(0, 0, 0);\r
-\r
-  // Get the bounding springs and add them to the needed springs\r
-  for (let i in this.faces) {\r
-    if (this.faces[i].a == vertexIndex || this.faces[i].b == vertexIndex || this.faces[i].c == vertexIndex || this.faces[i].d == vertexIndex) {\r
-      for (let j in this.faces[i].springs) {\r
-        if (this.faces[i].springs[j].index1 == vertexIndex || this.faces[i].springs[j].index2 == vertexIndex) {\r
-\r
-          let spring = this.faces[i].springs[j];\r
-          let springDirection = spring.getDirection(this.geometry.vertices);\r
-\r
-\r
-          if (this.faces[i].springs[j].index1 == vertexIndex)\r
-            springDirection.multiplyScalar(-1);\r
-\r
-          springSum.add(springDirection.multiplyScalar(k * (spring.currentLength - spring.restLength)));\r
-\r
-        }\r
-      }\r
-    }\r
-  }\r
-\r
-  \r
-  let result = new THREE.Vector3(1, 1, 1);\r
-\r
-  \r
-  result.multiplyScalar(M).multiply(g).add(fWind).add(fAirResistance).sub(springSum);\r
-  \r
-\r
-  return result;\r
-\r
-\r
-}\r
-\r
-/**\r
- * The Verlet algorithm as an integrator \r
- * to get the next position of a vertex  \r
- * @param {Vector3} currentPosition \r
- * @param {Vector3} previousPosition \r
- * @param {Vector3} acceleration \r
- * @param {number} passedTime The delta time since last frame\r
- */\r
-verlet(currentPosition, previousPosition, acceleration, passedTime) {\r
-  // verlet algorithm\r
-  // next position = 2 * current Position - previous position + acceleration * (passed time)^2\r
-  // acceleration (dv/dt) = F(net)\r
-  // Dependency for one vertex: gravity, fluids/air, springs\r
-\r
-  let nextPosition = new THREE.Vector3(\r
-    2 * currentPosition.x - previousPosition.x + acceleration.x * (passedTime * passedTime),\r
-    2 * currentPosition.y - previousPosition.y + acceleration.y * (passedTime * passedTime),\r
-    2 * currentPosition.z - previousPosition.z + acceleration.z * (passedTime * passedTime),\r
-  );\r
-\r
-  return nextPosition;\r
-}\r
-\r
-\r
-}\r
-\r
+}
\ No newline at end of file