source: other-projects/nz-flag-design/trunk/render-3d/Flag_files/Flag.js@ 29475

Last change on this file since 29475 was 29475, checked in by davidb, 9 years ago

Initial set of files

File size: 7.9 KB
Line 
1/*
2 * Aug 9 2012
3 * Its Singapore's National Day, so
4 * Making a quick tweaks to simulate the Singapore flag in the wind
5 *
6 */
7/*
8 * Aug 3 2012
9 *
10 * Since I started working for a new startup not too long ago,
11 * I commute between home and work for over 2 hours a day.
12 * Although this means less time on three.js,
13 * I try getting a little coding on the train.
14 *
15 * This set of experiments started from a simple hook's law doodle,
16 * to spring simulation, string simulation, and I realized
17 * I once again stepped onto physics and particle simulation,
18 * this time, more specifically soft body physics.
19 *
20 * Based on the "Advanced Character Physics" article,
21 * this experiment attempts to use a "massless"
22 * cloth simulation model. It's somewhat similiar
23 * but simplier to most cloth simulations I found.
24 *
25 * This was coded out fairly quickly, so expect more to come
26 * meanwhile feel free to experiment yourself and share
27 *
28 * Cheers,
29 * Graphics Noob (aka @Blurspline, zz85)
30 */
31
32// Suggested Readings
33
34// Advanced Character Physics by Thomas Jakobsen Character - http://web.archive.org/web/20070610223835/http:/www.teknikus.dk/tj/gdc2001.htm
35// http://freespace.virgin.net/hugo.elias/models/m_cloth.htm
36// http://en.wikipedia.org/wiki/Cloth_modeling
37// http://cg.alexandra.dk/tag/spring-mass-system/
38// Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf
39
40var DAMPING = 0.02;
41var DRAG = 1 - DAMPING;
42var MASS = .2;
43var restDistance = 20;
44
45
46var xSegs = 15; // ratio is 2:3
47var ySegs = 10; //
48
49var clothFunction = plane(restDistance * xSegs, restDistance * ySegs);
50
51var cloth = new Cloth(xSegs, ySegs);
52
53var GRAVITY = 981 * 1.4; //
54var gravity = new THREE.Vector3( 0, -GRAVITY, 0 ).multiplyScalar(MASS);
55
56
57var TIMESTEP = 18 / 1000;
58var TIMESTEP_SQ = TIMESTEP * TIMESTEP;
59
60var pins = [];
61var pinning = true;
62
63var wind = true;
64var windStrength = 2;
65var windForce = new THREE.Vector3(0,0,0);
66
67var ballPosition = new THREE.Vector3(0, -45, 0);
68var ballSize = 60; //40
69
70var tmpForce = new THREE.Vector3();
71
72var lastTime;
73
74
75
76
77function plane(width, height) {
78
79 return function(u, v) {
80 var x = u * width; //(u-0.5)
81 var y = v * height;
82 var z = 0;
83
84 return new THREE.Vector3(x, y, z);
85 };
86}
87
88function Particle(x, y, z, mass) {
89 this.position = clothFunction(x, y); // position
90 this.previous = clothFunction(x, y); // previous
91 this.original = clothFunction(x, y);
92 this.a = new THREE.Vector3(0, 0, 0); // acceleration
93 this.mass = mass;
94 this.invMass = 1 / mass;
95 this.tmp = new THREE.Vector3();
96 this.tmp2 = new THREE.Vector3();
97}
98
99// Force -> Acceleration
100Particle.prototype.addForce = function(force) {
101 this.a.add(
102 this.tmp2.copy(force).multiplyScalar(this.invMass)
103 );
104};
105
106
107// Performs verlet integration
108Particle.prototype.integrate = function(timesq) {
109 var newPos = this.tmp.subVectors(this.position, this.previous);
110 newPos.multiplyScalar(DRAG).add(this.position);
111 newPos.add(this.a.multiplyScalar(timesq));
112
113 this.tmp = this.previous;
114 this.previous = this.position;
115 this.position = newPos;
116
117 this.a.set(0, 0, 0);
118}
119
120
121var diff = new THREE.Vector3();
122
123function satisifyConstrains(p1, p2, distance) {
124 diff.subVectors(p2.position, p1.position);
125 var currentDist = diff.length();
126 if (currentDist==0) return; // prevents division by 0
127 var correction = diff.multiplyScalar(1 - distance/currentDist);
128 var correctionHalf = correction.multiplyScalar(0.5);
129 p1.position.add(correctionHalf);
130 p2.position.sub(correctionHalf);
131
132 // float difference = (restingDistance - d) / d
133 // im1 = 1 / p1.mass // inverse mass quantities
134 // im2 = 1 / p2.mass
135 // p1.position += delta * (im1 / (im1 + im2)) * stiffness * difference
136
137}
138
139function Cloth(w, h) {
140 w = w || 10;
141 h = h || 10;
142 this.w = w;
143 this.h = h;
144
145 var particles = [];
146 var constrains = [];
147
148 var u, v;
149
150 // Create particles
151 for (v=0;v<=h;v++) {
152 for (u=0;u<=w;u++) {
153 particles.push(
154 new Particle(u/w, v/h, 0, MASS)
155 );
156 }
157 }
158
159 // Structural
160
161 for (v=0;v<h;v++) {
162 for (u=0;u<w;u++) {
163
164 constrains.push([
165 particles[index(u, v)],
166 particles[index(u, v+1)],
167 restDistance
168 ]);
169
170 constrains.push([
171 particles[index(u, v)],
172 particles[index(u+1, v)],
173 restDistance
174 ]);
175
176 }
177 }
178
179 for (u=w, v=0;v<h;v++) {
180 constrains.push([
181 particles[index(u, v)],
182 particles[index(u, v+1)],
183 restDistance
184
185 ]);
186 }
187
188 for (v=h, u=0;u<w;u++) {
189 constrains.push([
190 particles[index(u, v)],
191 particles[index(u+1, v)],
192 restDistance
193 ]);
194 }
195
196
197 // While many system uses shear and bend springs,
198 // the relax constrains model seem to be just fine
199 // using structural springs.
200 // Shear
201 var diagonalDist = Math.sqrt(restDistance * restDistance * 2);
202
203
204 for (v=0;v<h;v++) {
205 for (u=0;u<w;u++) {
206
207 constrains.push([
208 particles[index(u, v)],
209 particles[index(u+1, v+1)],
210 diagonalDist
211 ]);
212
213 constrains.push([
214 particles[index(u+1, v)],
215 particles[index(u, v+1)],
216 diagonalDist
217 ]);
218
219 }
220 }
221
222
223 // // Bend
224
225 // var wlen = restDistance * 2;
226 // var hlen = restDistance * 2;
227 // diagonalDist = Math.sqrt(wlen * wlen + hlen * hlen);
228
229 // for (v=0;v<h-1;v++) {
230 // for (u=0;u<w-1;u++) {
231 // constrains.push([
232 // particles[index(u, v)],
233 // particles[index(u+2, v)],
234 // wlen
235 // ]);
236
237 // constrains.push([
238 // particles[index(u, v)],
239 // particles[index(u, v+2)],
240 // hlen
241 // ]);
242
243 // constrains.push([
244 // particles[index(u, v)],
245 // particles[index(u+2, v+2)],
246 // diagonalDist
247 // ]);
248
249 // constrains.push([
250 // particles[index(u, v+2)],
251 // particles[index(u+2, v+2)],
252 // wlen
253 // ]);
254
255 // constrains.push([
256 // particles[index(u+2, v+2)],
257 // particles[index(u+2, v+2)],
258 // hlen
259 // ]);
260
261 // constrains.push([
262 // particles[index(u+2, v)],
263 // particles[index(u, v+2)],
264 // diagonalDist
265 // ]);
266
267 // }
268 // }
269
270
271 this.particles = particles;
272 this.constrains = constrains;
273
274 function index(u, v) {
275 return u + v * (w + 1);
276 }
277
278 this.index = index;
279
280}
281
282function setPinning(bool){
283 pinning = bool;
284}
285
286function simulate(time) {
287 if (!lastTime) {
288 lastTime = time;
289 return;
290 }
291
292 // TIMESTEP = (time - lastTime);
293 // TIMESTEP = (TIMESTEP > 30) ? TIMESTEP / 1000 : 30 / 1000;
294 // TIMESTEP_SQ = TIMESTEP * TIMESTEP;
295 // lastTime = time;
296 // console.log(TIMESTEP);
297
298 var i, il, particles, particle, pt, constrains, constrain;
299
300 // Aerodynamics forces
301 if (wind) {
302 var face, faces = clothGeometry.faces, normal;
303
304 particles = cloth.particles;
305
306 for (i=0,il=faces.length;i<il;i++) {
307 face = faces[i];
308 normal = face.normal;
309
310 tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(windForce));
311 particles[face.a].addForce(tmpForce);
312 particles[face.b].addForce(tmpForce);
313 particles[face.c].addForce(tmpForce);
314 }
315 }
316
317 for (particles = cloth.particles, i=0, il = particles.length
318 ;i<il;i++) {
319 particle = particles[i];
320 particle.addForce(gravity);
321 //
322 // var x = particle.position.x, y = particle.position.y, z = particle.position.z, t=Date.now() / 1000;
323 // windForce.set(Math.sin(x*y*t), Math.cos(z*t), Math.sin(Math.cos(5*x*y*z))).multiplyScalar(100);
324 // particle.addForce(windForce);
325 particle.integrate(TIMESTEP_SQ);
326 }
327
328 // Start Constrains
329
330 constrains = cloth.constrains,
331 il = constrains.length;
332 for (i=0;i<il;i++) {
333 constrain = constrains[i];
334 satisifyConstrains(constrain[0], constrain[1], constrain[2]);
335 }
336
337 // Ball Constrains
338
339
340 ballPosition.z = -Math.sin(Date.now()/300) * 90 ; //+ 40;
341 ballPosition.x = Math.cos(Date.now()/200) * 70
342
343 if (sphere.visible)
344 for (particles = cloth.particles, i=0, il = particles.length
345 ;i<il;i++) {
346 particle = particles[i];
347 pos = particle.position;
348 diff.subVectors(pos, ballPosition);
349 if (diff.length() < ballSize) {
350 // collided
351 diff.normalize().multiplyScalar(ballSize);
352 pos.copy(ballPosition).add(diff);
353 }
354 }
355
356 // Pin Constrains
357 if(Boolean(pinning)){
358 for (i=0, il=pins.length;i<il;i++) {
359 var xy = pins[i];
360 var p = particles[xy];
361 p.position.copy(p.original);
362 p.previous.copy(p.original);
363 }
364 }
365
366}
Note: See TracBrowser for help on using the repository browser.