1 const DAMPING = 0.03;
\r
2 const DRAG = 1 - DAMPING;
\r
4 const GRAVITY = new THREE.Vector3(0, -9.81 * MASS, 0);
\r
12 constructor(p1, p2, restDist) {
\r
15 this.restDist = restDist;
\r
19 const diff = this.p2.position.clone().sub(this.p1.position);
\r
20 const currentDist = diff.length();
\r
21 if (currentDist == 0) return;
\r
22 if (currentDist <= this.restDist) return;
\r
23 //const correction = diff.multiplyScalar(1 - (this.restDist / currentDist));
\r
24 const correction = diff.multiplyScalar((currentDist - this.restDist) / currentDist);
\r
25 correction.multiplyScalar(K);
\r
26 correction.clampLength(0, 1);
\r
27 const correctionHalf = correction.multiplyScalar(0.5);
\r
28 if (this.p1.movable && this.p2.movable) {
\r
29 this.p1.position.add(correctionHalf);
\r
30 this.p2.position.sub(correctionHalf);
\r
31 } else if (! this.p1.movable && this.p2.movable) {
\r
32 this.p2.position.sub(correction);
\r
33 } else if (this.p1.movable && ! this.p2.movable) {
\r
34 this.p1.position.add(correction);
\r
42 constructor(x, y, z, mass) {
\r
43 this.position = new THREE.Vector3(x, y, z);
\r
44 this.previous = new THREE.Vector3(x, y, z);
\r
45 this.acceleration = new THREE.Vector3(0, 0, 0);
\r
49 this.acceleration.add(
\r
50 force.clone().multiplyScalar(1/this.mass)
\r
55 // next position = 2 * current Position - previous position + acceleration * (passed time)^2
\r
56 // acceleration (dv/dt) = F(net)
\r
57 const nextPosition = this.position.clone().sub(this.previous);
\r
58 nextPosition.multiplyScalar(DRAG);
\r
59 nextPosition.add(this.position);
\r
60 nextPosition.add(this.acceleration.multiplyScalar(dt*dt));
\r
63 this.previous = this.position;
\r
64 this.position = nextPosition;
\r
67 this.acceleration.set(0, 0, 0);
\r
72 constructor(width, height, numPointsWidth, numPointsHeight) {
\r
74 this.height = height;
\r
75 this.numPointsWidth = numPointsWidth;
\r
76 this.numPointsHeight = numPointsHeight;
\r
77 this.windFactor = new THREE.Vector3(5, 2, 2);
\r
80 * distance between two vertices horizontally/vertically
\r
81 * divide by the number of points minus one
\r
82 * because there are (n - 1) lines between n vertices
\r
84 let stepWidth = width / (numPointsWidth - 1);
\r
85 let stepHeight = height / (numPointsHeight - 1);
\r
88 * iterate over the number of vertices in x/y axis
\r
89 * and add a new Particle to "particles"
\r
91 this.particles = [];
\r
92 for (let y = 0; y < numPointsHeight; y++) {
\r
93 for (let x = 0; x < numPointsWidth; x++) {
\r
94 this.particles.push(
\r
96 (x - ((numPointsWidth-1)/2)) * stepWidth,
\r
97 height - (y + ((numPointsHeight-1)/2)) * stepHeight,
\r
104 //this.particles[this.getVertexIndex(0, 0)].movable = false;
\r
106 for (let i = 0; i <= n; i++)
\r
107 this.particles[this.getVertexIndex(0, Math.floor((numPointsHeight-1)*(i/n)))].movable = false;
\r
108 //this.particles[this.getVertexIndex(0, numPointsHeight-1)].movable = false;
\r
109 //this.particles[this.getVertexIndex(numPointsWidth-1, 0)].movable = false;
\r
111 const REST_DIST_X = width / (numPointsWidth-1);
\r
112 const REST_DIST_Y = height / (numPointsHeight-1);
\r
115 * generate constraints (springs)
\r
117 this.constraints = [];
\r
118 for (let y = 0; y < numPointsHeight; y++) {
\r
119 for (let x = 0; x < numPointsWidth; x++) {
\r
120 if (x < numPointsWidth-1) {
\r
121 this.constraints.push(new Constraint(
\r
122 this.particles[this.getVertexIndex(x, y)],
\r
123 this.particles[this.getVertexIndex(x+1, y)],
\r
127 if (y < numPointsHeight-1) {
\r
128 this.constraints.push(new Constraint(
\r
129 this.particles[this.getVertexIndex(x, y)],
\r
130 this.particles[this.getVertexIndex(x, y+1)],
\r
137 generateGeometry() {
\r
138 const geometry = new THREE.BufferGeometry();
\r
140 const vertices = [];
\r
141 const normals = [];
\r
142 const indices = [];
\r
144 for (let particle of this.particles) {
\r
146 particle.position.x,
\r
147 particle.position.y,
\r
148 particle.position.z);
\r
151 const numPointsWidth = this.numPointsWidth;
\r
152 const numPointsHeight = this.numPointsHeight;
\r
155 * generate faces based on 4 vertices
\r
156 * and 6 springs each
\r
158 for (let y = 0; y < numPointsHeight - 1; y++) {
\r
159 for (let x = 0; x < numPointsWidth - 1; x++) {
\r
161 this.getVertexIndex(x, y),
\r
162 this.getVertexIndex(x+1, y),
\r
163 this.getVertexIndex(x+1, y+1)
\r
166 this.getVertexIndex(x, y),
\r
167 this.getVertexIndex(x+1, y+1),
\r
168 this.getVertexIndex(x, y+1)
\r
173 geometry.setIndex(indices);
\r
174 geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
\r
175 //geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
\r
176 geometry.computeBoundingSphere();
\r
177 geometry.computeVertexNormals();
\r
181 updateGeometry(geometry) {
\r
182 const positions = geometry.attributes.position.array;
\r
183 for (let i in this.particles) {
\r
184 let p = this.particles[i];
\r
185 positions[i*3+0] = p.position.x;
\r
186 positions[i*3+1] = p.position.y;
\r
187 positions[i*3+2] = p.position.z;
\r
189 geometry.attributes.position.needsUpdate = true;
\r
190 geometry.computeBoundingSphere();
\r
191 geometry.computeVertexNormals();
\r
194 let now = performance.now();
\r
195 for (let particle of this.particles) {
\r
196 let vertex = particle.position;
\r
197 let fWind = new THREE.Vector3(
\r
198 this.windFactor.x * (Math.sin(vertex.x * vertex.y * now)+1),
\r
199 this.windFactor.y * Math.cos(vertex.z * now),
\r
200 this.windFactor.z * Math.sin(Math.cos(5 * vertex.x * vertex.y * vertex.z))
\r
202 // normalize then multiply?
\r
204 particle.addForce(fWind);
\r
205 // calculate wind with normal?
\r
207 particle.addForce(GRAVITY);
\r
209 particle.verlet(dt);
\r
213 for (let constraint of this.constraints) {
\r
214 constraint.satisfy();
\r
216 //console.log(tmpCorrection);
\r
219 * helper function to calculate index of vertex
\r
220 * in "vertices" array based on its x and y positions
\r
222 * @param {number} x - x index of vertex
\r
223 * @param {number} y - y index of vertex
\r
225 getVertexIndex(x, y) {
\r
226 return y * this.numPointsWidth + x;
\r