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

Last change on this file since 29693 was 29693, checked in by bmt11, 9 years ago

Added slider for wind control but the black box is a bug I have no figured out for some time now
Camera control is disabled whilst using the slider also :)

File size: 6.5 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;
64
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
110 var newPos = this.tmp.subVectors(this.position, this.previous);
111 newPos.multiplyScalar(DRAG).add(this.position);
112 newPos.add(this.a.multiplyScalar(timesq));
113
114 this.tmp = this.previous;
115 this.previous = this.position;
116 this.position = newPos;
117
118 this.a.set(0, 0, 0);
119
120}
121
122
123var diff = new THREE.Vector3();
124
125function satisifyConstrains(p1, p2, distance) {
126
127 diff.subVectors(p2.position, p1.position);
128 var currentDist = diff.length();
129 if (currentDist==0) return; // prevents division by 0
130 var correction = diff.multiplyScalar(1 - distance/currentDist);
131 var correctionHalf = correction.multiplyScalar(0.5);
132 p1.position.add(correctionHalf);
133 p2.position.sub(correctionHalf);
134
135}
136
137function Cloth(w, h) {
138 w = w || 10;
139 h = h || 10;
140 this.w = w;
141 this.h = h;
142
143 var particles = [];
144 var constrains = [];
145
146 var u, v;
147
148 // Create particles
149 for (v=0;v<=h;v++) {
150 for (u=0;u<=w;u++) {
151 particles.push(
152 new Particle(u/w, v/h, 0, MASS)
153 );
154 }
155 }
156
157 // Structural
158
159 for (v=0;v<h;v++) {
160 for (u=0;u<w;u++) {
161
162 constrains.push([
163 particles[index(u, v)],
164 particles[index(u, v+1)],
165 restDistance
166 ]);
167
168 constrains.push([
169 particles[index(u, v)],
170 particles[index(u+1, v)],
171 restDistance
172 ]);
173
174 }
175 }
176
177 for (u=w, v=0;v<h;v++) {
178 constrains.push([
179 particles[index(u, v)],
180 particles[index(u, v+1)],
181 restDistance
182
183 ]);
184 }
185
186 for (v=h, u=0;u<w;u++) {
187 constrains.push([
188 particles[index(u, v)],
189 particles[index(u+1, v)],
190 restDistance
191 ]);
192 }
193
194
195 // While many system uses shear and bend springs,
196 // the relax constrains model seem to be just fine
197 // using structural springs.
198 // Shear
199 var diagonalDist = Math.sqrt(restDistance * restDistance * 2);
200
201
202 for (v=0;v<h;v++) {
203 for (u=0;u<w;u++) {
204
205 constrains.push([
206 particles[index(u, v)],
207 particles[index(u+1, v+1)],
208 diagonalDist
209 ]);
210
211 constrains.push([
212 particles[index(u+1, v)],
213 particles[index(u, v+1)],
214 diagonalDist
215 ]);
216
217 }
218 }
219
220 this.particles = particles;
221 this.constrains = constrains;
222
223 function index(u, v) {
224 return u + v * (w + 1);
225 }
226
227 this.index = index;
228
229}
230
231function setPinning(bool){
232 pinning = bool;
233}
234
235function simulate(time) {
236 if (!lastTime) {
237 lastTime = time;
238 return;
239 }
240
241 // TIMESTEP = (time - lastTime);
242 // TIMESTEP = (TIMESTEP > 30) ? TIMESTEP / 1000 : 30 / 1000;
243 // TIMESTEP_SQ = TIMESTEP * TIMESTEP;
244 // lastTime = time;
245 // console.log(TIMESTEP);
246
247 var i, il, particles, particle, pt, constrains, constrain;
248
249 // Aerodynamics forces
250 if (wind) {
251 var face, faces = clothGeometry.faces, normal;
252
253 particles = cloth.particles;
254
255 for (i=0,il=faces.length;i<il;i++) {
256 face = faces[i];
257 normal = face.normal;
258
259 tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(windForce));
260 particles[face.a].addForce(tmpForce);
261 particles[face.b].addForce(tmpForce);
262 particles[face.c].addForce(tmpForce);
263 }
264 }
265
266 for (particles = cloth.particles, i=0, il = particles.length
267 ;i<il;i++) {
268 particle = particles[i];
269 particle.addForce(gravity);
270 //
271 var x = particle.position.x, y = particle.position.y, z = particle.position.z, t=Date.now() / 1000;
272 //windForce.set(Math.sin(x*y*t), Math.cos(z*t), Math.sin(Math.cos(5*x*y*z))).multiplyScalar(0);
273 particle.addForce(windForce);
274 particle.integrate(TIMESTEP_SQ);
275 }
276
277 // Start Constrains
278
279 constrains = cloth.constrains,
280 il = constrains.length;
281 for (i=0;i<il;i++) {
282 constrain = constrains[i];
283 satisifyConstrains(constrain[0], constrain[1], constrain[2]);
284 }
285
286 // Ball Constrains
287
288
289 ballPosition.z = -Math.sin(Date.now()/300) * 90 ; //+ 40;
290 ballPosition.x = Math.cos(Date.now()/200) * 70
291
292 // Pin Constrains
293 if(Boolean(pinning)){
294 for (i=0, il=pins.length;i<il;i++) {
295 var xy = pins[i];
296 var p = particles[xy];
297 p.position.copy(p.original);
298 p.previous.copy(p.original);
299 }
300 }
301
302}
Note: See TracBrowser for help on using the repository browser.