~swag/armagetronad/0.2.9-sty+ct+ap-fork

« back to all changes in this revision

Viewing changes to src/thirdparty/particles/actions.cpp

  • Committer: luke-jr
  • Date: 2006-05-29 01:55:42 UTC
  • Revision ID: svn-v3-list-QlpoOTFBWSZTWZvbKhsAAAdRgAAQABK6798QIABURMgAAaeoNT1TxT1DQbKaeobXKiyAmlWT7Y5MkdJOtXDtB7w7DOGFBHiOBxaUIu7HQyyQSvxdyRThQkJvbKhs:7d95bf1e-0414-0410-9756-b78462a59f44:armagetronad%2Fbranches%2F0.2.8%2Farmagetronad:4612
Unify tags/branches of modules released together

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// actions.cpp
 
2
//
 
3
// Copyright 1997-1998 by David K. McAllister
 
4
//
 
5
// I used code Copyright 1997 by Jonathan P. Leech
 
6
// as an example in implenting this.
 
7
//
 
8
// This file implements the dynamics of particle actions.
 
9
 
 
10
#include "general.h"
 
11
#include <float.h>
 
12
#include <iostream>
 
13
 
 
14
#define SQRT2PI 2.506628274631000502415765284811045253006
 
15
#define ONEOVERSQRT2PI (1. / SQRT2PI)
 
16
 
 
17
// To offset [0 .. 1] vectors to [-.5 .. .5]
 
18
static pVector vHalf(0.5, 0.5, 0.5);
 
19
 
 
20
static inline pVector RandVec()
 
21
{
 
22
    return pVector(drand48(), drand48(), drand48());
 
23
}
 
24
 
 
25
// Return a random number with a normal distribution.
 
26
static inline float NRand(float sigma = 1.0f)
 
27
{
 
28
#define ONE_OVER_SIGMA_EXP (1.0f / 0.7975f)
 
29
 
 
30
    if(sigma == 0) return 0;
 
31
 
 
32
    float y;
 
33
    do
 
34
    {
 
35
        y = -logf(drand48());
 
36
    }
 
37
    while(drand48() > expf(-fsqr(y - 1.0f)*0.5f));
 
38
 
 
39
    if(rand() & 0x1)
 
40
        return y * sigma * ONE_OVER_SIGMA_EXP;
 
41
    else
 
42
        return -y * sigma * ONE_OVER_SIGMA_EXP;
 
43
}
 
44
 
 
45
void PAAvoid::Execute(ParticleGroup *group)
 
46
{
 
47
    float magdt = magnitude * dt;
 
48
 
 
49
    switch(position.type)
 
50
    {
 
51
    case PDPlane:
 
52
        {
 
53
            if(look_ahead < P_MAXFLOAT)
 
54
            {
 
55
                for(int i = 0; i < group->p_count; i++)
 
56
                {
 
57
                    Particle &m = group->list[i];
 
58
 
 
59
                    // p2 stores the plane normal (the a,b,c of the plane eqn).
 
60
                    // Old and new distances: dist(p,plane) = n * p + d
 
61
                    // radius1 stores -n*p, which is d.
 
62
                    float dist = m.pos * position.p2 + position.radius1;
 
63
 
 
64
                    if(dist < look_ahead)
 
65
                    {
 
66
                        float vm = m.vel.length();
 
67
                        pVector Vn = m.vel / vm;
 
68
                        // float dot = Vn * position.p2;
 
69
 
 
70
                        pVector tmp = (position.p2 * (magdt / (dist*dist+epsilon))) + Vn;
 
71
                        m.vel = tmp * (vm / tmp.length());
 
72
                    }
 
73
                }
 
74
            }
 
75
            else
 
76
            {
 
77
                for(int i = 0; i < group->p_count; i++)
 
78
                {
 
79
                    Particle &m = group->list[i];
 
80
 
 
81
                    // p2 stores the plane normal (the a,b,c of the plane eqn).
 
82
                    // Old and new distances: dist(p,plane) = n * p + d
 
83
                    // radius1 stores -n*p, which is d.
 
84
                    float dist = m.pos * position.p2 + position.radius1;
 
85
 
 
86
                    float vm = m.vel.length();
 
87
                    pVector Vn = m.vel / vm;
 
88
                    // float dot = Vn * position.p2;
 
89
 
 
90
                    pVector tmp = (position.p2 * (magdt / (dist*dist+epsilon))) + Vn;
 
91
                    m.vel = tmp * (vm / tmp.length());
 
92
                }
 
93
            }
 
94
        }
 
95
        break;
 
96
    case PDRectangle:
 
97
        {
 
98
            // Compute the inverse matrix of the plane basis.
 
99
            pVector &u = position.u;
 
100
            pVector &v = position.v;
 
101
 
 
102
            // The normalized bases are needed inside the loop.
 
103
            pVector un = u / position.radius1Sqr;
 
104
            pVector vn = v / position.radius2Sqr;
 
105
 
 
106
            // w = u cross v
 
107
            float wx = u.y*v.z-u.z*v.y;
 
108
            float wy = u.z*v.x-u.x*v.z;
 
109
            float wz = u.x*v.y-u.y*v.x;
 
110
 
 
111
            float det = 1/(wz*u.x*v.y-wz*u.y*v.x-u.z*wx*v.y-u.x*v.z*wy+v.z*wx*u.y+u.z*v.x*wy);
 
112
 
 
113
            pVector s1((v.y*wz-v.z*wy), (v.z*wx-v.x*wz), (v.x*wy-v.y*wx));
 
114
            s1 *= det;
 
115
            pVector s2((u.y*wz-u.z*wy), (u.z*wx-u.x*wz), (u.x*wy-u.y*wx));
 
116
            s2 *= -det;
 
117
 
 
118
            // See which particles bounce.
 
119
            for(int i = 0; i < group->p_count; i++)
 
120
            {
 
121
                Particle &m = group->list[i];
 
122
 
 
123
                // See if particle's current and next positions cross plane.
 
124
                // If not, couldn't bounce, so keep going.
 
125
                pVector pnext(m.pos + m.vel * dt * look_ahead);
 
126
 
 
127
                // p2 stores the plane normal (the a,b,c of the plane eqn).
 
128
                // Old and new distances: dist(p,plane) = n * p + d
 
129
                // radius1 stores -n*p, which is d.
 
130
                float distold = m.pos * position.p2 + position.radius1;
 
131
                float distnew = pnext * position.p2 + position.radius1;
 
132
 
 
133
                // Opposite signs if product < 0
 
134
                // There is no faster way to do this.
 
135
                if(distold * distnew >= 0)
 
136
                    continue;
 
137
 
 
138
                float nv = position.p2 * m.vel;
 
139
                float t = -distold / nv;
 
140
 
 
141
                // Actual intersection point p(t) = pos + vel t
 
142
                pVector phit(m.pos + m.vel * t);
 
143
 
 
144
                // Offset from origin in plane, p - origin
 
145
                pVector offset(phit - position.p1);
 
146
 
 
147
                // Dot product with basis vectors of old frame
 
148
                // in terms of new frame gives position in uv frame.
 
149
                float upos = offset * s1;
 
150
                float vpos = offset * s2;
 
151
 
 
152
                // Did it cross plane outside triangle?
 
153
                if(upos < 0 || vpos < 0 || upos > 1 || vpos > 1)
 
154
                    continue;
 
155
 
 
156
                // A hit! A most palpable hit!
 
157
                // Compute distance to the three edges.
 
158
                pVector uofs = (un * (un * offset)) - offset;
 
159
                float udistSqr = uofs.length2();
 
160
                pVector vofs = (vn * (vn * offset)) - offset;
 
161
                float vdistSqr = vofs.length2();
 
162
 
 
163
                pVector foffset((u + v) - offset);
 
164
                pVector fofs = (un * (un * foffset)) - foffset;
 
165
                float fdistSqr = fofs.length2();
 
166
                pVector gofs = (un * (un * foffset)) - foffset;
 
167
                float gdistSqr = gofs.length2();
 
168
 
 
169
                pVector S;
 
170
                if(udistSqr <= vdistSqr && udistSqr <= fdistSqr
 
171
                        && udistSqr <= gdistSqr) S = uofs;
 
172
                else if(vdistSqr <= fdistSqr && vdistSqr <= gdistSqr) S = vofs;
 
173
                else if(fdistSqr <= gdistSqr) S = fofs;
 
174
                else S = gofs;
 
175
 
 
176
                S.normalize();
 
177
 
 
178
                // We now have a vector to safety.
 
179
                float vm = m.vel.length();
 
180
                pVector Vn = m.vel / vm;
 
181
 
 
182
                // Blend S into V.
 
183
                pVector tmp = (S * (magdt / (t*t+epsilon))) + Vn;
 
184
                m.vel = tmp * (vm / tmp.length());
 
185
            }
 
186
        }
 
187
        break;
 
188
    case PDTriangle:
 
189
        {
 
190
            // Compute the inverse matrix of the plane basis.
 
191
            pVector &u = position.u;
 
192
            pVector &v = position.v;
 
193
 
 
194
            // The normalized bases are needed inside the loop.
 
195
            pVector un = u / position.radius1Sqr;
 
196
            pVector vn = v / position.radius2Sqr;
 
197
 
 
198
            // f is the third (non-basis) triangle edge.
 
199
            pVector f = v - u;
 
200
            pVector fn(f);
 
201
            fn.normalize();
 
202
 
 
203
            // w = u cross v
 
204
            float wx = u.y*v.z-u.z*v.y;
 
205
            float wy = u.z*v.x-u.x*v.z;
 
206
            float wz = u.x*v.y-u.y*v.x;
 
207
 
 
208
            float det = 1/(wz*u.x*v.y-wz*u.y*v.x-u.z*wx*v.y-u.x*v.z*wy+v.z*wx*u.y+u.z*v.x*wy);
 
209
 
 
210
            pVector s1((v.y*wz-v.z*wy), (v.z*wx-v.x*wz), (v.x*wy-v.y*wx));
 
211
            s1 *= det;
 
212
            pVector s2((u.y*wz-u.z*wy), (u.z*wx-u.x*wz), (u.x*wy-u.y*wx));
 
213
            s2 *= -det;
 
214
 
 
215
            // See which particles bounce.
 
216
            for(int i = 0; i < group->p_count; i++)
 
217
            {
 
218
                Particle &m = group->list[i];
 
219
 
 
220
                // See if particle's current and next positions cross plane.
 
221
                // If not, couldn't bounce, so keep going.
 
222
                pVector pnext(m.pos + m.vel * dt * look_ahead);
 
223
 
 
224
                // p2 stores the plane normal (the a,b,c of the plane eqn).
 
225
                // Old and new distances: dist(p,plane) = n * p + d
 
226
                // radius1 stores -n*p, which is d.
 
227
                float distold = m.pos * position.p2 + position.radius1;
 
228
                float distnew = pnext * position.p2 + position.radius1;
 
229
 
 
230
                // Opposite signs if product < 0
 
231
                // Is there a faster way to do this?
 
232
                if(distold * distnew >= 0)
 
233
                    continue;
 
234
 
 
235
                float nv = position.p2 * m.vel;
 
236
                float t = -distold / nv;
 
237
 
 
238
                // Actual intersection point p(t) = pos + vel t
 
239
                pVector phit(m.pos + m.vel * t);
 
240
 
 
241
                // Offset from origin in plane, p - origin
 
242
                pVector offset(phit - position.p1);
 
243
 
 
244
                // Dot product with basis vectors of old frame
 
245
                // in terms of new frame gives position in uv frame.
 
246
                float upos = offset * s1;
 
247
                float vpos = offset * s2;
 
248
 
 
249
                // Did it cross plane outside triangle?
 
250
                if(upos < 0 || vpos < 0 || (upos + vpos) > 1)
 
251
                    continue;
 
252
 
 
253
                // A hit! A most palpable hit!
 
254
                // Compute distance to the three edges.
 
255
                pVector uofs = (un * (un * offset)) - offset;
 
256
                float udistSqr = uofs.length2();
 
257
                pVector vofs = (vn * (vn * offset)) - offset;
 
258
                float vdistSqr = vofs.length2();
 
259
                pVector foffset(offset - u);
 
260
                pVector fofs = (fn * (fn * foffset)) - foffset;
 
261
                float fdistSqr = fofs.length2();
 
262
                pVector S;
 
263
                if(udistSqr <= vdistSqr && udistSqr <= fdistSqr) S = uofs;
 
264
                else if(vdistSqr <= fdistSqr) S = vofs;
 
265
                else S = fofs;
 
266
 
 
267
                S.normalize();
 
268
 
 
269
                // We now have a vector to safety.
 
270
                float vm = m.vel.length();
 
271
                pVector Vn = m.vel / vm;
 
272
 
 
273
                // Blend S into V.
 
274
                pVector tmp = (S * (magdt / (t*t+epsilon))) + Vn;
 
275
                m.vel = tmp * (vm / tmp.length());
 
276
            }
 
277
        }
 
278
        break;
 
279
    case PDDisc:
 
280
        {
 
281
            float r1Sqr = fsqr(position.radius1);
 
282
            float r2Sqr = fsqr(position.radius2);
 
283
 
 
284
            // See which particles bounce.
 
285
            for(int i = 0; i < group->p_count; i++)
 
286
            {
 
287
                Particle &m = group->list[i];
 
288
 
 
289
                // See if particle's current and next positions cross plane.
 
290
                // If not, couldn't bounce, so keep going.
 
291
                pVector pnext(m.pos + m.vel * dt * look_ahead);
 
292
 
 
293
                // p2 stores the plane normal (the a,b,c of the plane eqn).
 
294
                // Old and new distances: dist(p,plane) = n * p + d
 
295
                // radius1 stores -n*p, which is d. radius1Sqr stores d.
 
296
                float distold = m.pos * position.p2 + position.radius1Sqr;
 
297
                float distnew = pnext * position.p2 + position.radius1Sqr;
 
298
 
 
299
                // Opposite signs if product < 0
 
300
                // Is there a faster way to do this?
 
301
                if(distold * distnew >= 0)
 
302
                    continue;
 
303
 
 
304
                // Find position at the crossing point by parameterizing
 
305
                // p(t) = pos + vel * t
 
306
                // Solve dist(p(t),plane) = 0 e.g.
 
307
                // n * p(t) + D = 0 ->
 
308
                // n * p + t (n * v) + D = 0 ->
 
309
                // t = -(n * p + D) / (n * v)
 
310
                // Could factor n*v into distnew = distold + n*v and save a bit.
 
311
                // Safe since n*v != 0 assured by quick rejection test.
 
312
                // This calc is indep. of dt because we have established that it
 
313
                // will hit before dt. We just want to know when.
 
314
                float nv = position.p2 * m.vel;
 
315
                float t = -distold / nv;
 
316
 
 
317
                // Actual intersection point p(t) = pos + vel t
 
318
                pVector phit(m.pos + m.vel * t);
 
319
 
 
320
                // Offset from origin in plane, phit - origin
 
321
                pVector offset(phit - position.p1);
 
322
 
 
323
                float rad = offset.length2();
 
324
 
 
325
                if(rad > r1Sqr || rad < r2Sqr)
 
326
                    continue;
 
327
 
 
328
                // A hit! A most palpable hit!
 
329
                pVector S = offset;
 
330
                S.normalize();
 
331
 
 
332
                // We now have a vector to safety.
 
333
                float vm = m.vel.length();
 
334
                pVector Vn = m.vel / vm;
 
335
 
 
336
                // Blend S into V.
 
337
                pVector tmp = (S * (magdt / (t*t+epsilon))) + Vn;
 
338
                m.vel = tmp * (vm / tmp.length());
 
339
            }
 
340
        }
 
341
        break;
 
342
    case PDSphere:
 
343
        {
 
344
            float rSqr = position.radius1 * position.radius1;
 
345
 
 
346
            // See which particles are aimed toward the sphere.
 
347
            for(int i = 0; i < group->p_count; i++)
 
348
            {
 
349
                Particle &m = group->list[i];
 
350
 
 
351
                // First do a ray-sphere intersection test and
 
352
                // see if it's soon enough.
 
353
                // Can I do this faster without t?
 
354
                float vm = m.vel.length();
 
355
                pVector Vn = m.vel / vm;
 
356
 
 
357
                pVector L = position.p1 - m.pos;
 
358
                float v = L * Vn;
 
359
 
 
360
                float disc = rSqr - (L * L) + v * v;
 
361
                if(disc < 0)
 
362
                    continue; // I'm not heading toward it.
 
363
 
 
364
                // Compute length for second rejection test.
 
365
                float t = v - sqrtf(disc);
 
366
                if(t < 0 || t > (vm * look_ahead))
 
367
                    continue;
 
368
 
 
369
                // Get a vector to safety.
 
370
                pVector C = Vn ^ L;
 
371
                C.normalize();
 
372
                pVector S = Vn ^ C;
 
373
 
 
374
                // Blend S into V.
 
375
                pVector tmp = (S * (magdt / (t*t+epsilon))) + Vn;
 
376
                m.vel = tmp * (vm / tmp.length());
 
377
            }
 
378
        }
 
379
        break;
 
380
    default:
 
381
        break;
 
382
    }
 
383
}
 
384
 
 
385
void PABounce::Execute(ParticleGroup *group)
 
386
{
 
387
    switch(position.type)
 
388
    {
 
389
    case PDTriangle:
 
390
        {
 
391
            // Compute the inverse matrix of the plane basis.
 
392
            pVector &u = position.u;
 
393
            pVector &v = position.v;
 
394
 
 
395
            // w = u cross v
 
396
            float wx = u.y*v.z-u.z*v.y;
 
397
            float wy = u.z*v.x-u.x*v.z;
 
398
            float wz = u.x*v.y-u.y*v.x;
 
399
 
 
400
            float det = 1/(wz*u.x*v.y-wz*u.y*v.x-u.z*wx*v.y-u.x*v.z*wy+v.z*wx*u.y+u.z*v.x*wy);
 
401
 
 
402
            pVector s1((v.y*wz-v.z*wy), (v.z*wx-v.x*wz), (v.x*wy-v.y*wx));
 
403
            s1 *= det;
 
404
            pVector s2((u.y*wz-u.z*wy), (u.z*wx-u.x*wz), (u.x*wy-u.y*wx));
 
405
            s2 *= -det;
 
406
 
 
407
            // See which particles bounce.
 
408
            for(int i = 0; i < group->p_count; i++)
 
409
            {
 
410
                Particle &m = group->list[i];
 
411
 
 
412
                // See if particle's current and next positions cross plane.
 
413
                // If not, couldn't bounce, so keep going.
 
414
                pVector pnext(m.pos + m.vel * dt);
 
415
 
 
416
                // p2 stores the plane normal (the a,b,c of the plane eqn).
 
417
                // Old and new distances: dist(p,plane) = n * p + d
 
418
                // radius1 stores -n*p, which is d.
 
419
                float distold = m.pos * position.p2 + position.radius1;
 
420
                float distnew = pnext * position.p2 + position.radius1;
 
421
 
 
422
                // Opposite signs if product < 0
 
423
                // Is there a faster way to do this?
 
424
                if(distold * distnew >= 0)
 
425
                    continue;
 
426
 
 
427
                // Find position at the crossing point by parameterizing
 
428
                // p(t) = pos + vel * t
 
429
                // Solve dist(p(t),plane) = 0 e.g.
 
430
                // n * p(t) + D = 0 ->
 
431
                // n * p + t (n * v) + D = 0 ->
 
432
                // t = -(n * p + D) / (n * v)
 
433
                // Could factor n*v into distnew = distold + n*v and save a bit.
 
434
                // Safe since n*v != 0 assured by quick rejection test.
 
435
                // This calc is indep. of dt because we have established that it
 
436
                // will hit before dt. We just want to know when.
 
437
                float nv = position.p2 * m.vel;
 
438
                float t = -distold / nv;
 
439
 
 
440
                // Actual intersection point p(t) = pos + vel t
 
441
                pVector phit(m.pos + m.vel * t);
 
442
 
 
443
                // Offset from origin in plane, p - origin
 
444
                pVector offset(phit - position.p1);
 
445
 
 
446
                // Dot product with basis vectors of old frame
 
447
                // in terms of new frame gives position in uv frame.
 
448
                float upos = offset * s1;
 
449
                float vpos = offset * s2;
 
450
 
 
451
                // Did it cross plane outside triangle?
 
452
                if(upos < 0 || vpos < 0 || (upos + vpos) > 1)
 
453
                    continue;
 
454
 
 
455
                // A hit! A most palpable hit!
 
456
 
 
457
                // Compute tangential and normal components of velocity
 
458
                pVector vn(position.p2 * nv); // Normal Vn = (V.N)N
 
459
                pVector vt(m.vel - vn); // Tangent Vt = V - Vn
 
460
 
 
461
                // Compute new velocity heading out:
 
462
                // Don't apply friction if tangential velocity < cutoff
 
463
                if(vt.length2() <= cutoffSqr)
 
464
                    m.vel = vt - vn * resilience;
 
465
                else
 
466
                    m.vel = vt * oneMinusFriction - vn * resilience;
 
467
            }
 
468
        }
 
469
        break;
 
470
    case PDDisc:
 
471
        {
 
472
            float r1Sqr = fsqr(position.radius1);
 
473
            float r2Sqr = fsqr(position.radius2);
 
474
 
 
475
            // See which particles bounce.
 
476
            for(int i = 0; i < group->p_count; i++)
 
477
            {
 
478
                Particle &m = group->list[i];
 
479
 
 
480
                // See if particle's current and next positions cross plane.
 
481
                // If not, couldn't bounce, so keep going.
 
482
                pVector pnext(m.pos + m.vel * dt);
 
483
 
 
484
                // p2 stores the plane normal (the a,b,c of the plane eqn).
 
485
                // Old and new distances: dist(p,plane) = n * p + d
 
486
                // radius1 stores -n*p, which is d. radius1Sqr stores d.
 
487
                float distold = m.pos * position.p2 + position.radius1Sqr;
 
488
                float distnew = pnext * position.p2 + position.radius1Sqr;
 
489
 
 
490
                // Opposite signs if product < 0
 
491
                // Is there a faster way to do this?
 
492
                if(distold * distnew >= 0)
 
493
                    continue;
 
494
 
 
495
                // Find position at the crossing point by parameterizing
 
496
                // p(t) = pos + vel * t
 
497
                // Solve dist(p(t),plane) = 0 e.g.
 
498
                // n * p(t) + D = 0 ->
 
499
                // n * p + t (n * v) + D = 0 ->
 
500
                // t = -(n * p + D) / (n * v)
 
501
                // Could factor n*v into distnew = distold + n*v and save a bit.
 
502
                // Safe since n*v != 0 assured by quick rejection test.
 
503
                // This calc is indep. of dt because we have established that it
 
504
                // will hit before dt. We just want to know when.
 
505
                float nv = position.p2 * m.vel;
 
506
                float t = -distold / nv;
 
507
 
 
508
                // Actual intersection point p(t) = pos + vel t
 
509
                pVector phit(m.pos + m.vel * t);
 
510
 
 
511
                // Offset from origin in plane, phit - origin
 
512
                pVector offset(phit - position.p1);
 
513
 
 
514
                float rad = offset.length2();
 
515
 
 
516
                if(rad > r1Sqr || rad < r2Sqr)
 
517
                    continue;
 
518
 
 
519
                // A hit! A most palpable hit!
 
520
 
 
521
                // Compute tangential and normal components of velocity
 
522
                pVector vn(position.p2 * nv); // Normal Vn = (V.N)N
 
523
                pVector vt(m.vel - vn); // Tangent Vt = V - Vn
 
524
 
 
525
                // Compute new velocity heading out:
 
526
                // Don't apply friction if tangential velocity < cutoff
 
527
                if(vt.length2() <= cutoffSqr)
 
528
                    m.vel = vt - vn * resilience;
 
529
                else
 
530
                    m.vel = vt * oneMinusFriction - vn * resilience;
 
531
            }
 
532
        }
 
533
        break;
 
534
    case PDPlane:
 
535
        {
 
536
            // See which particles bounce.
 
537
            for(int i = 0; i < group->p_count; i++)
 
538
            {
 
539
                Particle &m = group->list[i];
 
540
 
 
541
                // See if particle's current and next positions cross plane.
 
542
                // If not, couldn't bounce, so keep going.
 
543
                pVector pnext(m.pos + m.vel * dt);
 
544
 
 
545
                // p2 stores the plane normal (the a,b,c of the plane eqn).
 
546
                // Old and new distances: dist(p,plane) = n * p + d
 
547
                // radius1 stores -n*p, which is d.
 
548
                float distold = m.pos * position.p2 + position.radius1;
 
549
                float distnew = pnext * position.p2 + position.radius1;
 
550
 
 
551
                // Opposite signs if product < 0
 
552
                if(distold * distnew >= 0)
 
553
                    continue;
 
554
 
 
555
                // Compute tangential and normal components of velocity
 
556
                float nmag = m.vel * position.p2;
 
557
                pVector vn(position.p2 * nmag); // Normal Vn = (V.N)N
 
558
                pVector vt(m.vel - vn); // Tangent Vt = V - Vn
 
559
 
 
560
                // Compute new velocity heading out:
 
561
                // Don't apply friction if tangential velocity < cutoff
 
562
                if(vt.length2() <= cutoffSqr)
 
563
                    m.vel = vt - vn * resilience;
 
564
                else
 
565
                    m.vel = vt * oneMinusFriction - vn * resilience;
 
566
            }
 
567
        }
 
568
        break;
 
569
    case PDRectangle:
 
570
        {
 
571
            // Compute the inverse matrix of the plane basis.
 
572
            pVector &u = position.u;
 
573
            pVector &v = position.v;
 
574
 
 
575
            // w = u cross v
 
576
            float wx = u.y*v.z-u.z*v.y;
 
577
            float wy = u.z*v.x-u.x*v.z;
 
578
            float wz = u.x*v.y-u.y*v.x;
 
579
 
 
580
            float det = 1/(wz*u.x*v.y-wz*u.y*v.x-u.z*wx*v.y-u.x*v.z*wy+v.z*wx*u.y+u.z*v.x*wy);
 
581
 
 
582
            pVector s1((v.y*wz-v.z*wy), (v.z*wx-v.x*wz), (v.x*wy-v.y*wx));
 
583
            s1 *= det;
 
584
            pVector s2((u.y*wz-u.z*wy), (u.z*wx-u.x*wz), (u.x*wy-u.y*wx));
 
585
            s2 *= -det;
 
586
 
 
587
            // See which particles bounce.
 
588
            for(int i = 0; i < group->p_count; i++)
 
589
            {
 
590
                Particle &m = group->list[i];
 
591
 
 
592
                // See if particle's current and next positions cross plane.
 
593
                // If not, couldn't bounce, so keep going.
 
594
                pVector pnext(m.pos + m.vel * dt);
 
595
 
 
596
                // p2 stores the plane normal (the a,b,c of the plane eqn).
 
597
                // Old and new distances: dist(p,plane) = n * p + d
 
598
                // radius1 stores -n*p, which is d.
 
599
                float distold = m.pos * position.p2 + position.radius1;
 
600
                float distnew = pnext * position.p2 + position.radius1;
 
601
 
 
602
                // Opposite signs if product < 0
 
603
                if(distold * distnew >= 0)
 
604
                    continue;
 
605
 
 
606
                // Find position at the crossing point by parameterizing
 
607
                // p(t) = pos + vel * t
 
608
                // Solve dist(p(t),plane) = 0 e.g.
 
609
                // n * p(t) + D = 0 ->
 
610
                // n * p + t (n * v) + D = 0 ->
 
611
                // t = -(n * p + D) / (n * v)
 
612
                float t = -distold / (position.p2 * m.vel);
 
613
 
 
614
                // Actual intersection point p(t) = pos + vel t
 
615
                pVector phit(m.pos + m.vel * t);
 
616
 
 
617
                // Offset from origin in plane, p - origin
 
618
                pVector offset(phit - position.p1);
 
619
 
 
620
                // Dot product with basis vectors of old frame
 
621
                // in terms of new frame gives position in uv frame.
 
622
                float upos = offset * s1;
 
623
                float vpos = offset * s2;
 
624
 
 
625
                // Crossed plane outside bounce region if !(0<=[uv]pos<=1)
 
626
                if(upos < 0 || upos > 1 || vpos < 0 || vpos > 1)
 
627
                    continue;
 
628
 
 
629
                // A hit! A most palpable hit!
 
630
 
 
631
                // Compute tangential and normal components of velocity
 
632
                float nmag = m.vel * position.p2;
 
633
                pVector vn(position.p2 * nmag); // Normal Vn = (V.N)N
 
634
                pVector vt(m.vel - vn); // Tangent Vt = V - Vn
 
635
 
 
636
                // Compute new velocity heading out:
 
637
                // Don't apply friction if tangential velocity < cutoff
 
638
                if(vt.length2() <= cutoffSqr)
 
639
                    m.vel = vt - vn * resilience;
 
640
                else
 
641
                    m.vel = vt * oneMinusFriction - vn * resilience;
 
642
            }
 
643
        }
 
644
        break;
 
645
    case PDSphere:
 
646
        {
 
647
            // Sphere that particles bounce off
 
648
            // The particles are always forced out of the sphere.
 
649
            for(int i = 0; i < group->p_count; i++)
 
650
            {
 
651
                Particle &m = group->list[i];
 
652
 
 
653
                // See if particle's next position is inside domain.
 
654
                // If so, bounce it.
 
655
                pVector pnext(m.pos + m.vel * dt);
 
656
 
 
657
                if(position.Within(pnext))
 
658
                {
 
659
                    // See if we were inside on previous timestep.
 
660
                    bool pinside = position.Within(m.pos);
 
661
 
 
662
                    // Normal to surface. This works for a sphere. Isn't
 
663
                    // computed quite right, should extrapolate particle
 
664
                    // position to surface.
 
665
                    pVector n(m.pos - position.p1);
 
666
                    n.normalize();
 
667
 
 
668
                    // Compute tangential and normal components of velocity
 
669
                    float nmag = m.vel * n;
 
670
 
 
671
                    pVector vn(n * nmag); // Normal Vn = (V.N)N
 
672
                    pVector vt = m.vel - vn; // Tangent Vt = V - Vn
 
673
 
 
674
                    if(pinside)
 
675
                    {
 
676
                        // Previous position was inside. If normal component of
 
677
                        // velocity points in, reverse it. This effectively
 
678
                        // repels particles which would otherwise be trapped
 
679
                        // in the sphere.
 
680
                        if(nmag < 0)
 
681
                            m.vel = vt - vn;
 
682
                    }
 
683
                    else
 
684
                    {
 
685
                        // Previous position was outside -> particle will cross
 
686
                        // surface boundary. Reverse normal component of velocity,
 
687
                        // and apply friction (if Vt >= cutoff) and resilience.
 
688
 
 
689
                        // Compute new velocity heading out:
 
690
                        // Don't apply friction if tangential velocity < cutoff
 
691
                        if(vt.length2() <= cutoffSqr)
 
692
                            m.vel = vt - vn * resilience;
 
693
                        else
 
694
                            m.vel = vt * oneMinusFriction - vn * resilience;
 
695
                    }
 
696
                }
 
697
            }
 
698
        }
 
699
    default:
 
700
        break;
 
701
    }
 
702
}
 
703
 
 
704
// Set the secondary position of each particle to be its position.
 
705
void PACallActionList::Execute(ParticleGroup *group)
 
706
{
 
707
    pCallActionList(action_list_num);
 
708
}
 
709
 
 
710
// Set the secondary position of each particle to be its position.
 
711
void PACopyVertexB::Execute(ParticleGroup *group)
 
712
{
 
713
    int i;
 
714
 
 
715
    if(copy_pos)
 
716
    {
 
717
        for(i = 0; i < group->p_count; i++)
 
718
        {
 
719
            Particle &m = group->list[i];
 
720
            m.posB = m.pos;
 
721
        }
 
722
    }
 
723
 
 
724
    if(copy_vel)
 
725
    {
 
726
        for(i = 0; i < group->p_count; i++)
 
727
        {
 
728
            Particle &m = group->list[i];
 
729
            m.velB = m.vel;
 
730
        }
 
731
    }
 
732
}
 
733
 
 
734
// Dampen velocities
 
735
void PADamping::Execute(ParticleGroup *group)
 
736
{
 
737
    // This is important if dt is != 1.
 
738
    pVector one(1,1,1);
 
739
    pVector scale(one - ((one - damping) * dt));
 
740
 
 
741
    for(int i = 0; i < group->p_count; i++)
 
742
    {
 
743
        Particle &m = group->list[i];
 
744
        float vSqr = m.vel.length2();
 
745
 
 
746
        if(vSqr >= vlowSqr && vSqr <= vhighSqr)
 
747
        {
 
748
            m.vel.x *= scale.x;
 
749
            m.vel.y *= scale.y;
 
750
            m.vel.z *= scale.z;
 
751
        }
 
752
    }
 
753
}
 
754
 
 
755
// Exert force on each particle away from explosion center
 
756
void PAExplosion::Execute(ParticleGroup *group)
 
757
{
 
758
    float radius = velocity * age;
 
759
    float magdt = magnitude * dt;
 
760
    float oneOverSigma = 1.0f / stdev;
 
761
    float inexp = -0.5f*fsqr(oneOverSigma);
 
762
    float outexp = ONEOVERSQRT2PI * oneOverSigma;
 
763
 
 
764
    for(int i = 0; i < group->p_count; i++)
 
765
    {
 
766
        Particle &m = group->list[i];
 
767
 
 
768
        // Figure direction to particle.
 
769
        pVector dir(m.pos - center);
 
770
        float distSqr = dir.length2();
 
771
        float dist = sqrtf(distSqr);
 
772
        float DistFromWaveSqr = fsqr(radius - dist);
 
773
 
 
774
        float Gd = exp(DistFromWaveSqr * inexp) * outexp;
 
775
 
 
776
        m.vel += dir * (Gd * magdt / (dist * (distSqr + epsilon)));
 
777
    }
 
778
 
 
779
    age += dt;
 
780
}
 
781
 
 
782
// Follow the next particle in the list
 
783
void PAFollow::Execute(ParticleGroup *group)
 
784
{
 
785
    float magdt = magnitude * dt;
 
786
    float max_radiusSqr = max_radius * max_radius;
 
787
 
 
788
    if(max_radiusSqr < P_MAXFLOAT)
 
789
    {
 
790
        for(int i = 0; i < group->p_count - 1; i++)
 
791
        {
 
792
            Particle &m = group->list[i];
 
793
 
 
794
            // Accelerate toward the particle after me in the list.
 
795
            pVector tohim(group->list[i+1].pos - m.pos); // tohim = p1 - p0
 
796
            float tohimlenSqr = tohim.length2();
 
797
 
 
798
            if(tohimlenSqr < max_radiusSqr)
 
799
            {
 
800
                // Compute force exerted between the two bodies
 
801
                m.vel += tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon)));
 
802
            }
 
803
        }
 
804
    }
 
805
    else
 
806
    {
 
807
        for(int i = 0; i < group->p_count - 1; i++)
 
808
        {
 
809
            Particle &m = group->list[i];
 
810
 
 
811
            // Accelerate toward the particle after me in the list.
 
812
            pVector tohim(group->list[i+1].pos - m.pos); // tohim = p1 - p0
 
813
            float tohimlenSqr = tohim.length2();
 
814
 
 
815
            // Compute force exerted between the two bodies
 
816
            m.vel += tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon)));
 
817
        }
 
818
    }
 
819
}
 
820
 
 
821
// Inter-particle gravitation
 
822
void PAGravitate::Execute(ParticleGroup *group)
 
823
{
 
824
    float magdt = magnitude * dt;
 
825
    float max_radiusSqr = max_radius * max_radius;
 
826
 
 
827
    if(max_radiusSqr < P_MAXFLOAT)
 
828
    {
 
829
        for(int i = 0; i < group->p_count; i++)
 
830
        {
 
831
            Particle &m = group->list[i];
 
832
 
 
833
            // Add interactions with other particles
 
834
            for(int j = i + 1; j < group->p_count; j++)
 
835
            {
 
836
                Particle &mj = group->list[j];
 
837
 
 
838
                pVector tohim(mj.pos - m.pos); // tohim = p1 - p0
 
839
                float tohimlenSqr = tohim.length2();
 
840
 
 
841
                if(tohimlenSqr < max_radiusSqr)
 
842
                {
 
843
                    // Compute force exerted between the two bodies
 
844
                    pVector acc(tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon))));
 
845
 
 
846
                    m.vel += acc;
 
847
                    mj.vel -= acc;
 
848
                }
 
849
            }
 
850
        }
 
851
    }
 
852
    else
 
853
    {
 
854
        for(int i = 0; i < group->p_count; i++)
 
855
        {
 
856
            Particle &m = group->list[i];
 
857
 
 
858
            // Add interactions with other particles
 
859
            for(int j = i + 1; j < group->p_count; j++)
 
860
            {
 
861
                Particle &mj = group->list[j];
 
862
 
 
863
                pVector tohim(mj.pos - m.pos); // tohim = p1 - p0
 
864
                float tohimlenSqr = tohim.length2();
 
865
 
 
866
                // Compute force exerted between the two bodies
 
867
                pVector acc(tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon))));
 
868
 
 
869
                m.vel += acc;
 
870
                mj.vel -= acc;
 
871
            }
 
872
        }
 
873
    }
 
874
}
 
875
 
 
876
// Acceleration in a constant direction
 
877
void PAGravity::Execute(ParticleGroup *group)
 
878
{
 
879
    pVector ddir(direction * dt);
 
880
 
 
881
    for(int i = 0; i < group->p_count; i++)
 
882
    {
 
883
        // Step velocity with acceleration
 
884
        group->list[i].vel += ddir;
 
885
    }
 
886
}
 
887
 
 
888
// Accelerate particles along a line
 
889
void PAJet::Execute(ParticleGroup *group)
 
890
{
 
891
    float magdt = magnitude * dt;
 
892
    float max_radiusSqr = max_radius * max_radius;
 
893
 
 
894
    if(max_radiusSqr < P_MAXFLOAT)
 
895
    {
 
896
        for(int i = 0; i < group->p_count; i++)
 
897
        {
 
898
            Particle &m = group->list[i];
 
899
 
 
900
            // Figure direction to particle.
 
901
            pVector dir(m.pos - center);
 
902
 
 
903
            // Distance to jet (force drops as 1/r^2)
 
904
            // Soften by epsilon to avoid tight encounters to infinity
 
905
            float rSqr = dir.length2();
 
906
 
 
907
            if(rSqr < max_radiusSqr)
 
908
            {
 
909
                pVector accel;
 
910
                acc.Generate(accel);
 
911
 
 
912
                // Step velocity with acceleration
 
913
                m.vel += accel * (magdt / (rSqr + epsilon));
 
914
            }
 
915
        }
 
916
    }
 
917
    else
 
918
    {
 
919
        for(int i = 0; i < group->p_count; i++)
 
920
        {
 
921
            Particle &m = group->list[i];
 
922
 
 
923
            // Figure direction to particle.
 
924
            pVector dir(m.pos - center);
 
925
 
 
926
            // Distance to jet (force drops as 1/r^2)
 
927
            // Soften by epsilon to avoid tight encounters to infinity
 
928
            float rSqr = dir.length2();
 
929
 
 
930
            pVector accel;
 
931
            acc.Generate(accel);
 
932
 
 
933
            // Step velocity with acceleration
 
934
            m.vel += accel * (magdt / (rSqr + epsilon));
 
935
        }
 
936
    }
 
937
}
 
938
 
 
939
// Get rid of older particles
 
940
void PAKillOld::Execute(ParticleGroup *group)
 
941
{
 
942
    // Must traverse list in reverse order so Remove will work
 
943
    for(int i = group->p_count-1; i >= 0; i--)
 
944
    {
 
945
        Particle &m = group->list[i];
 
946
 
 
947
        if(!((m.age < age_limit) ^ kill_less_than))
 
948
            group->Remove(i);
 
949
    }
 
950
}
 
951
 
 
952
// Match velocity to near neighbors
 
953
void PAMatchVelocity::Execute(ParticleGroup *group)
 
954
{
 
955
    float magdt = magnitude * dt;
 
956
    float max_radiusSqr = max_radius * max_radius;
 
957
 
 
958
    if(max_radiusSqr < P_MAXFLOAT)
 
959
    {
 
960
        for(int i = 0; i < group->p_count; i++)
 
961
        {
 
962
            Particle &m = group->list[i];
 
963
 
 
964
            // Add interactions with other particles
 
965
            for(int j = i + 1; j < group->p_count; j++)
 
966
            {
 
967
                Particle &mj = group->list[j];
 
968
 
 
969
                pVector tohim(mj.pos - m.pos); // tohim = p1 - p0
 
970
                float tohimlenSqr = tohim.length2();
 
971
 
 
972
                if(tohimlenSqr < max_radiusSqr)
 
973
                {
 
974
                    // Compute force exerted between the two bodies
 
975
                    pVector acc(mj.vel * (magdt / (tohimlenSqr + epsilon)));
 
976
 
 
977
                    m.vel += acc;
 
978
                    mj.vel -= acc;
 
979
                }
 
980
            }
 
981
        }
 
982
    }
 
983
    else
 
984
    {
 
985
        for(int i = 0; i < group->p_count; i++)
 
986
        {
 
987
            Particle &m = group->list[i];
 
988
 
 
989
            // Add interactions with other particles
 
990
            for(int j = i + 1; j < group->p_count; j++)
 
991
            {
 
992
                Particle &mj = group->list[j];
 
993
 
 
994
                pVector tohim(mj.pos - m.pos); // tohim = p1 - p0
 
995
                float tohimlenSqr = tohim.length2();
 
996
 
 
997
                // Compute force exerted between the two bodies
 
998
                pVector acc(mj.vel * (magdt / (tohimlenSqr + epsilon)));
 
999
 
 
1000
                m.vel += acc;
 
1001
                mj.vel -= acc;
 
1002
            }
 
1003
        }
 
1004
    }
 
1005
}
 
1006
 
 
1007
void PAMove::Execute(ParticleGroup *group)
 
1008
{
 
1009
    // Step particle positions forward by dt, and age the particles.
 
1010
    for(int i = 0; i < group->p_count; i++)
 
1011
    {
 
1012
        Particle &m = group->list[i];
 
1013
 
 
1014
        m.age += dt;
 
1015
        m.pos += m.vel * dt;
 
1016
    }
 
1017
}
 
1018
 
 
1019
// Accelerate particles towards a line
 
1020
void PAOrbitLine::Execute(ParticleGroup *group)
 
1021
{
 
1022
    float magdt = magnitude * dt;
 
1023
    float max_radiusSqr = max_radius * max_radius;
 
1024
 
 
1025
    if(max_radiusSqr < P_MAXFLOAT)
 
1026
    {
 
1027
        for(int i = 0; i < group->p_count; i++)
 
1028
        {
 
1029
            Particle &m = group->list[i];
 
1030
 
 
1031
            // Figure direction to particle from base of line.
 
1032
            pVector f(m.pos - p);
 
1033
 
 
1034
            pVector w(axis * (f * axis));
 
1035
 
 
1036
            // Direction from particle to nearest point on line.
 
1037
            pVector into = w - f;
 
1038
 
 
1039
            // Distance to line (force drops as 1/r^2, normalize by 1/r)
 
1040
            // Soften by epsilon to avoid tight encounters to infinity
 
1041
            float rSqr = into.length2();
 
1042
 
 
1043
            if(rSqr < max_radiusSqr)
 
1044
                // Step velocity with acceleration
 
1045
                m.vel += into * (magdt / (sqrtf(rSqr) + (rSqr + epsilon)));
 
1046
        }
 
1047
    }
 
1048
    else
 
1049
    {
 
1050
        // Removed because it causes pipeline stalls.
 
1051
        for(int i = 0; i < group->p_count; i++)
 
1052
        {
 
1053
            Particle &m = group->list[i];
 
1054
 
 
1055
            // Figure direction to particle from base of line.
 
1056
            pVector f(m.pos - p);
 
1057
 
 
1058
            pVector w(axis * (f * axis));
 
1059
 
 
1060
            // Direction from particle to nearest point on line.
 
1061
            pVector into = w - f;
 
1062
 
 
1063
            // Distance to line (force drops as 1/r^2, normalize by 1/r)
 
1064
            // Soften by epsilon to avoid tight encounters to infinity
 
1065
            float rSqr = into.length2();
 
1066
 
 
1067
            // Step velocity with acceleration
 
1068
            m.vel += into * (magdt / (sqrtf(rSqr) + (rSqr + epsilon)));
 
1069
        }
 
1070
    }
 
1071
}
 
1072
 
 
1073
// Accelerate particles towards a point
 
1074
void PAOrbitPoint::Execute(ParticleGroup *group)
 
1075
{
 
1076
    float magdt = magnitude * dt;
 
1077
    float max_radiusSqr = max_radius * max_radius;
 
1078
 
 
1079
    if(max_radiusSqr < P_MAXFLOAT)
 
1080
    {
 
1081
        for(int i = 0; i < group->p_count; i++)
 
1082
        {
 
1083
            Particle &m = group->list[i];
 
1084
 
 
1085
            // Figure direction to particle.
 
1086
            pVector dir(center - m.pos);
 
1087
 
 
1088
            // Distance to gravity well (force drops as 1/r^2, normalize by 1/r)
 
1089
            // Soften by epsilon to avoid tight encounters to infinity
 
1090
            float rSqr = dir.length2();
 
1091
 
 
1092
            // Step velocity with acceleration
 
1093
            if(rSqr < max_radiusSqr)
 
1094
                m.vel += dir * (magdt / (sqrtf(rSqr) + (rSqr + epsilon)));
 
1095
        }
 
1096
    }
 
1097
    else
 
1098
    {
 
1099
        // Avoids pipeline stalls.
 
1100
        for(int i = 0; i < group->p_count; i++)
 
1101
        {
 
1102
            Particle &m = group->list[i];
 
1103
 
 
1104
            // Figure direction to particle.
 
1105
            pVector dir(center - m.pos);
 
1106
 
 
1107
            // Distance to gravity well (force drops as 1/r^2, normalize by 1/r)
 
1108
            // Soften by epsilon to avoid tight encounters to infinity
 
1109
            float rSqr = dir.length2();
 
1110
 
 
1111
            // Step velocity with acceleration
 
1112
            m.vel += dir * (magdt / (sqrtf(rSqr) + (rSqr + epsilon)));
 
1113
        }
 
1114
    }
 
1115
}
 
1116
 
 
1117
// Accelerate in random direction each time step
 
1118
void PARandomAccel::Execute(ParticleGroup *group)
 
1119
{
 
1120
    for(int i = 0; i < group->p_count; i++)
 
1121
    {
 
1122
        Particle &m = group->list[i];
 
1123
 
 
1124
        pVector acceleration;
 
1125
        gen_acc.Generate(acceleration);
 
1126
 
 
1127
        // dt will affect this by making a higher probability of
 
1128
        // being near the original velocity after unit time. Smaller
 
1129
        // dt approach a normal distribution instead of a square wave.
 
1130
        m.vel += acceleration * dt;
 
1131
    }
 
1132
}
 
1133
 
 
1134
// Immediately displace position randomly
 
1135
void PARandomDisplace::Execute(ParticleGroup *group)
 
1136
{
 
1137
    for(int i = 0; i < group->p_count; i++)
 
1138
    {
 
1139
        Particle &m = group->list[i];
 
1140
 
 
1141
        pVector displacement;
 
1142
        gen_disp.Generate(displacement);
 
1143
 
 
1144
        // dt will affect this by making a higher probability of
 
1145
        // being near the original position after unit time. Smaller
 
1146
        // dt approach a normal distribution instead of a square wave.
 
1147
        m.pos += displacement * dt;
 
1148
    }
 
1149
}
 
1150
 
 
1151
// Immediately assign a random velocity
 
1152
void PARandomVelocity::Execute(ParticleGroup *group)
 
1153
{
 
1154
    for(int i = 0; i < group->p_count; i++)
 
1155
    {
 
1156
        Particle &m = group->list[i];
 
1157
 
 
1158
        pVector velocity;
 
1159
        gen_vel.Generate(velocity);
 
1160
 
 
1161
        // Shouldn't multiply by dt because velocities are
 
1162
        // invariant of dt. How should dt affect this?
 
1163
        m.vel = velocity;
 
1164
    }
 
1165
}
 
1166
 
 
1167
#if 0
 
1168
// Produce coefficients of a velocity function v(t)=at^2 + bt + c
 
1169
// satisfying initial x(0)=x0,v(0)=v0 and desired x(t)=xf,v(t)=vf,
 
1170
// where x = x(0) + integrate(v(T),0,t)
 
1171
static inline void _pconstrain(float x0, float v0, float xf, float vf,
 
1172
                               float t, float *a, float *b, float *c)
 
1173
{
 
1174
    *c = v0;
 
1175
    *b = 2 * (-t*vf - 2*t*v0 + 3*xf - 3*x0) / (t * t);
 
1176
    *a = 3 * (t*vf + t*v0 - 2*xf + 2*x0) / (t * t * t);
 
1177
}
 
1178
#endif
 
1179
 
 
1180
// Over time, restore particles to initial positions
 
1181
// Put all particles on the surface of a statue, explode the statue,
 
1182
// and then suck the particles back to the original position. Cool!
 
1183
void PARestore::Execute(ParticleGroup *group)
 
1184
{
 
1185
    if(time_left <= 0)
 
1186
    {
 
1187
        for(int i = 0; i < group->p_count; i++)
 
1188
        {
 
1189
            Particle &m = group->list[i];
 
1190
 
 
1191
            // Already constrained, keep it there.
 
1192
            m.pos = m.posB;
 
1193
            m.vel = pVector(0,0,0);
 
1194
        }
 
1195
    }
 
1196
    else
 
1197
    {
 
1198
        float t = time_left;
 
1199
        float dtSqr = dt * dt;
 
1200
        float tSqrInv2dt = dt * 2.0f / (t * t);
 
1201
        float tCubInv3dtSqr = dtSqr * 3.0f / (t * t * t);
 
1202
 
 
1203
        for(int i = 0; i < group->p_count; i++)
 
1204
        {
 
1205
#if 1
 
1206
            Particle &m = group->list[i];
 
1207
 
 
1208
            // Solve for a desired-behavior velocity function in each axis
 
1209
            // _pconstrain(m.pos.x, m.vel.x, m.posB.x, 0., timeLeft, &a, &b, &c);
 
1210
 
 
1211
            // Figure new velocity at next timestep
 
1212
            // m.vel.x = a * dtSqr + b * dt + c;
 
1213
 
 
1214
            float b = (-2*t*m.vel.x + 3*m.posB.x - 3*m.pos.x) * tSqrInv2dt;
 
1215
            float a = (t*m.vel.x - m.posB.x - m.posB.x + m.pos.x + m.pos.x) * tCubInv3dtSqr;
 
1216
 
 
1217
            // Figure new velocity at next timestep
 
1218
            m.vel.x += a + b;
 
1219
 
 
1220
            b = (-2*t*m.vel.y + 3*m.posB.y - 3*m.pos.y) * tSqrInv2dt;
 
1221
            a = (t*m.vel.y - m.posB.y - m.posB.y + m.pos.y + m.pos.y) * tCubInv3dtSqr;
 
1222
 
 
1223
            // Figure new velocity at next timestep
 
1224
            m.vel.y += a + b;
 
1225
 
 
1226
            b = (-2*t*m.vel.z + 3*m.posB.z - 3*m.pos.z) * tSqrInv2dt;
 
1227
            a = (t*m.vel.z - m.posB.z - m.posB.z + m.pos.z + m.pos.z) * tCubInv3dtSqr;
 
1228
 
 
1229
            // Figure new velocity at next timestep
 
1230
            m.vel.z += a + b;
 
1231
#else
 
1232
            Particle &m = group->list[i];
 
1233
 
 
1234
            // XXX Optimize this.
 
1235
            // Solve for a desired-behavior velocity function in each axis
 
1236
            float a, b, c; // Coefficients of velocity function needed
 
1237
 
 
1238
            _pconstrain(m.pos.x, m.vel.x, m.posB.x, 0.,
 
1239
                        timeLeft, &a, &b, &c);
 
1240
 
 
1241
            // Figure new velocity at next timestep
 
1242
            m.vel.x = a * dtSqr + b * dt + c;
 
1243
 
 
1244
            _pconstrain(m.pos.y, m.vel.y, m.posB.y, 0.,
 
1245
                        timeLeft, &a, &b, &c);
 
1246
 
 
1247
            // Figure new velocity at next timestep
 
1248
            m.vel.y = a * dtSqr + b * dt + c;
 
1249
 
 
1250
            _pconstrain(m.pos.z, m.vel.z, m.posB.z, 0.,
 
1251
                        timeLeft, &a, &b, &c);
 
1252
 
 
1253
            // Figure new velocity at next timestep
 
1254
            m.vel.z = a * dtSqr + b * dt + c;
 
1255
 
 
1256
#endif
 
1257
        }
 
1258
    }
 
1259
 
 
1260
    time_left -= dt;
 
1261
}
 
1262
 
 
1263
// Kill particles with positions on wrong side of the specified domain
 
1264
void PASink::Execute(ParticleGroup *group)
 
1265
{
 
1266
    // Must traverse list in reverse order so Remove will work
 
1267
    for(int i = group->p_count-1; i >= 0; i--)
 
1268
    {
 
1269
        Particle &m = group->list[i];
 
1270
 
 
1271
        // Remove if inside/outside flag matches object's flag
 
1272
        if(!(position.Within(m.pos) ^ kill_inside))
 
1273
            group->Remove(i);
 
1274
    }
 
1275
}
 
1276
 
 
1277
// Kill particles with velocities on wrong side of the specified domain
 
1278
void PASinkVelocity::Execute(ParticleGroup *group)
 
1279
{
 
1280
    // Must traverse list in reverse order so Remove will work
 
1281
    for(int i = group->p_count-1; i >= 0; i--)
 
1282
    {
 
1283
        Particle &m = group->list[i];
 
1284
 
 
1285
        // Remove if inside/outside flag matches object's flag
 
1286
        if(!(velocity.Within(m.vel) ^ kill_inside))
 
1287
            group->Remove(i);
 
1288
    }
 
1289
}
 
1290
 
 
1291
// Randomly add particles to the system
 
1292
void PASource::Execute(ParticleGroup *group)
 
1293
{
 
1294
    int rate = int(floor(particle_rate * dt));
 
1295
 
 
1296
    // Dither the fraction particle in time.
 
1297
    if(drand48() < particle_rate * dt - float(rate))
 
1298
        rate++;
 
1299
 
 
1300
    // Don't emit more than it can hold.
 
1301
    if(group->p_count + rate > group->max_particles)
 
1302
        rate = group->max_particles - group->p_count;
 
1303
 
 
1304
    pVector pos, posB, vel, col, siz;
 
1305
 
 
1306
    if(vertexB_tracks)
 
1307
    {
 
1308
        for(int i = 0; i < rate; i++)
 
1309
        {
 
1310
            position.Generate(pos);
 
1311
            size.Generate(siz);
 
1312
            velocity.Generate(vel);
 
1313
            color.Generate(col);
 
1314
            float ag = age + NRand(age_sigma);
 
1315
 
 
1316
            group->Add(pos, pos, siz, vel, col, alpha, ag);
 
1317
        }
 
1318
    }
 
1319
    else
 
1320
    {
 
1321
        for(int i = 0; i < rate; i++)
 
1322
        {
 
1323
            position.Generate(pos);
 
1324
            positionB.Generate(posB);
 
1325
            size.Generate(siz);
 
1326
            velocity.Generate(vel);
 
1327
            color.Generate(col);
 
1328
            float ag = age + NRand(age_sigma);
 
1329
 
 
1330
            group->Add(pos, posB, siz, vel, col, alpha, ag);
 
1331
        }
 
1332
    }
 
1333
}
 
1334
 
 
1335
void PASpeedLimit::Execute(ParticleGroup *group)
 
1336
{
 
1337
    float min_sqr = min_speed*min_speed;
 
1338
    float max_sqr = max_speed*max_speed;
 
1339
 
 
1340
    for(int i = 0; i < group->p_count; i++)
 
1341
    {
 
1342
        Particle &m = group->list[i];
 
1343
        float sSqr = m.vel.length2();
 
1344
        if(sSqr<min_sqr && sSqr)
 
1345
        {
 
1346
            float s = sqrtf(sSqr);
 
1347
            m.vel *= (min_speed/s);
 
1348
        }
 
1349
        else if(sSqr>max_sqr)
 
1350
        {
 
1351
            float s = sqrtf(sSqr);
 
1352
            m.vel *= (max_speed/s);
 
1353
        }
 
1354
    }
 
1355
}
 
1356
 
 
1357
// Change color of all particles toward the specified color
 
1358
void PATargetColor::Execute(ParticleGroup *group)
 
1359
{
 
1360
    float scaleFac = scale * dt;
 
1361
 
 
1362
    for(int i = 0; i < group->p_count; i++)
 
1363
    {
 
1364
        Particle &m = group->list[i];
 
1365
        m.color += (color - m.color) * scaleFac;
 
1366
        m.alpha += (alpha - m.alpha) * scaleFac;
 
1367
    }
 
1368
}
 
1369
 
 
1370
// Change sizes of all particles toward the specified size
 
1371
void PATargetSize::Execute(ParticleGroup *group)
 
1372
{
 
1373
    float scaleFac_x = scale.x * dt;
 
1374
    float scaleFac_y = scale.y * dt;
 
1375
    float scaleFac_z = scale.z * dt;
 
1376
 
 
1377
    for(int i = 0; i < group->p_count; i++)
 
1378
    {
 
1379
        Particle &m = group->list[i];
 
1380
        pVector dif(size - m.size);
 
1381
        dif.x *= scaleFac_x;
 
1382
        dif.y *= scaleFac_y;
 
1383
        dif.z *= scaleFac_z;
 
1384
        m.size += dif;
 
1385
    }
 
1386
}
 
1387
 
 
1388
// Change velocity of all particles toward the specified velocity
 
1389
void PATargetVelocity::Execute(ParticleGroup *group)
 
1390
{
 
1391
    float scaleFac = scale * dt;
 
1392
 
 
1393
    for(int i = 0; i < group->p_count; i++)
 
1394
    {
 
1395
        Particle &m = group->list[i];
 
1396
        m.vel += (velocity - m.vel) * scaleFac;
 
1397
    }
 
1398
}
 
1399
 
 
1400
// Immediately displace position using vortex
 
1401
// Vortex tip at center, around axis, with magnitude
 
1402
// and tightness exponent
 
1403
void PAVortex::Execute(ParticleGroup *group)
 
1404
{
 
1405
    float magdt = magnitude * dt;
 
1406
    float max_radiusSqr = max_radius * max_radius;
 
1407
 
 
1408
    if(max_radiusSqr < P_MAXFLOAT)
 
1409
    {
 
1410
        for(int i = 0; i < group->p_count; i++)
 
1411
        {
 
1412
            Particle &m = group->list[i];
 
1413
 
 
1414
            // Vector from tip of vortex
 
1415
            pVector offset(m.pos - center);
 
1416
 
 
1417
            // Compute distance from particle to tip of vortex.
 
1418
            float rSqr = offset.length2();
 
1419
 
 
1420
            // Don't do anything to particle if too close or too far.
 
1421
            if(rSqr > max_radiusSqr)
 
1422
                continue;
 
1423
 
 
1424
            float r = sqrtf(rSqr);
 
1425
 
 
1426
            // Compute normalized offset vector.
 
1427
            pVector offnorm(offset / r);
 
1428
 
 
1429
            // Construct orthogonal vector frame in which to rotate
 
1430
            // transformed point around origin
 
1431
            float axisProj = offnorm * axis; // offnorm . axis
 
1432
 
 
1433
            // Components of offset perpendicular and parallel to axis
 
1434
            pVector w(axis * axisProj); // parallel component
 
1435
            pVector u(offnorm - w); // perpendicular component
 
1436
 
 
1437
            // Perpendicular component completing frame:
 
1438
            pVector v(axis ^ u);
 
1439
 
 
1440
            // Figure amount of rotation
 
1441
            // Resultant is (cos theta) u + (sin theta) v
 
1442
            float theta = magdt / (rSqr + epsilon);
 
1443
            float s = sinf(theta);
 
1444
            float c = cosf(theta);
 
1445
 
 
1446
            offset = (u * c + v * s + w) * r;
 
1447
 
 
1448
            // Translate back to object space
 
1449
            m.pos = offset + center;
 
1450
        }
 
1451
    }
 
1452
    else
 
1453
    {
 
1454
        for(int i = 0; i < group->p_count; i++)
 
1455
        {
 
1456
            Particle &m = group->list[i];
 
1457
 
 
1458
            // Vector from tip of vortex
 
1459
            pVector offset(m.pos - center);
 
1460
 
 
1461
            // Compute distance from particle to tip of vortex.
 
1462
            float rSqr = offset.length2();
 
1463
 
 
1464
            float r = sqrtf(rSqr);
 
1465
 
 
1466
            // Compute normalized offset vector.
 
1467
            pVector offnorm(offset / r);
 
1468
 
 
1469
            // Construct orthogonal vector frame in which to rotate
 
1470
            // transformed point around origin
 
1471
            float axisProj = offnorm * axis; // offnorm . axis
 
1472
 
 
1473
            // Components of offset perpendicular and parallel to axis
 
1474
            pVector w(axis * axisProj); // parallel component
 
1475
            pVector u(offnorm - w); // perpendicular component
 
1476
 
 
1477
            // Perpendicular component completing frame:
 
1478
            pVector v(axis ^ u);
 
1479
 
 
1480
            // Figure amount of rotation
 
1481
            // Resultant is (cos theta) u + (sin theta) v
 
1482
            float theta = magdt / (rSqr + epsilon);
 
1483
            float s = sinf(theta);
 
1484
            float c = cosf(theta);
 
1485
 
 
1486
            offset = (u * c + v * s + w) * r;
 
1487
 
 
1488
            // Translate back to object space
 
1489
            m.pos = offset + center;
 
1490
        }
 
1491
    }
 
1492
}
 
1493
 
 
1494
////////////////////////////////////////////////////////////////////////////////
 
1495
// Stuff for the pDomain.
 
1496
 
 
1497
pDomain::pDomain(PDomainEnum dtype, float a0, float a1,
 
1498
                 float a2, float a3, float a4, float a5,
 
1499
                 float a6, float a7, float a8)
 
1500
{
 
1501
    type = dtype;
 
1502
    switch(type)
 
1503
    {
 
1504
    case PDPoint:
 
1505
        p1 = pVector(a0, a1, a2);
 
1506
        break;
 
1507
    case PDLine:
 
1508
        {
 
1509
            p1 = pVector(a0, a1, a2);
 
1510
            pVector tmp(a3, a4, a5);
 
1511
            // p2 is vector from p1 to other endpoint.
 
1512
            p2 = tmp - p1;
 
1513
        }
 
1514
        break;
 
1515
    case PDBox:
 
1516
        // p1 is the min corner. p2 is the max corner.
 
1517
        if(a0 < a3)
 
1518
        {
 
1519
            p1.x = a0; p2.x = a3;
 
1520
        }
 
1521
        else
 
1522
        {
 
1523
            p1.x = a3; p2.x = a0;
 
1524
        }
 
1525
        if(a1 < a4)
 
1526
        {
 
1527
            p1.y = a1; p2.y = a4;
 
1528
        }
 
1529
        else
 
1530
        {
 
1531
            p1.y = a4; p2.y = a1;
 
1532
        }
 
1533
        if(a2 < a5)
 
1534
        {
 
1535
            p1.z = a2; p2.z = a5;
 
1536
        }
 
1537
        else
 
1538
        {
 
1539
            p1.z = a5; p2.z = a2;
 
1540
        }
 
1541
        break;
 
1542
    case PDTriangle:
 
1543
        {
 
1544
            p1 = pVector(a0, a1, a2);
 
1545
            pVector tp2 = pVector(a3, a4, a5);
 
1546
            pVector tp3 = pVector(a6, a7, a8);
 
1547
 
 
1548
            u = tp2 - p1;
 
1549
            v = tp3 - p1;
 
1550
 
 
1551
            // The rest of this is needed for bouncing.
 
1552
            radius1Sqr = u.length();
 
1553
            pVector tu = u / radius1Sqr;
 
1554
            radius2Sqr = v.length();
 
1555
            pVector tv = v / radius2Sqr;
 
1556
 
 
1557
            p2 = tu ^ tv; // This is the non-unit normal.
 
1558
            p2.normalize(); // Must normalize it.
 
1559
 
 
1560
            // radius1 stores the d of the plane eqn.
 
1561
            radius1 = -(p1 * p2);
 
1562
        }
 
1563
        break;
 
1564
    case PDRectangle:
 
1565
        {
 
1566
            p1 = pVector(a0, a1, a2);
 
1567
            u = pVector(a3, a4, a5);
 
1568
            v = pVector(a6, a7, a8);
 
1569
 
 
1570
            // The rest of this is needed for bouncing.
 
1571
            radius1Sqr = u.length();
 
1572
            pVector tu = u / radius1Sqr;
 
1573
            radius2Sqr = v.length();
 
1574
            pVector tv = v / radius2Sqr;
 
1575
 
 
1576
            p2 = tu ^ tv; // This is the non-unit normal.
 
1577
            p2.normalize(); // Must normalize it.
 
1578
 
 
1579
            // radius1 stores the d of the plane eqn.
 
1580
            radius1 = -(p1 * p2);
 
1581
        }
 
1582
        break;
 
1583
    case PDPlane:
 
1584
        {
 
1585
            p1 = pVector(a0, a1, a2);
 
1586
            p2 = pVector(a3, a4, a5);
 
1587
            p2.normalize(); // Must normalize it.
 
1588
 
 
1589
            // radius1 stores the d of the plane eqn.
 
1590
            radius1 = -(p1 * p2);
 
1591
        }
 
1592
        break;
 
1593
    case PDSphere:
 
1594
        p1 = pVector(a0, a1, a2);
 
1595
        if(a3 > a4)
 
1596
        {
 
1597
            radius1 = a3; radius2 = a4;
 
1598
        }
 
1599
        else
 
1600
        {
 
1601
            radius1 = a4; radius2 = a3;
 
1602
        }
 
1603
        radius1Sqr = radius1 * radius1;
 
1604
        radius2Sqr = radius2 * radius2;
 
1605
        break;
 
1606
    case PDCone:
 
1607
    case PDCylinder:
 
1608
        {
 
1609
            // p2 is a vector from p1 to the other end of cylinder.
 
1610
            // p1 is apex of cone.
 
1611
 
 
1612
            p1 = pVector(a0, a1, a2);
 
1613
            pVector tmp(a3, a4, a5);
 
1614
            p2 = tmp - p1;
 
1615
 
 
1616
            if(a6 > a7)
 
1617
            {
 
1618
                radius1 = a6; radius2 = a7;
 
1619
            }
 
1620
            else
 
1621
            {
 
1622
                radius1 = a7; radius2 = a6;
 
1623
            }
 
1624
            radius1Sqr = fsqr(radius1);
 
1625
 
 
1626
            // Given an arbitrary nonzero vector n, make two orthonormal
 
1627
            // vectors u and v forming a frame [u,v,n.normalize()].
 
1628
            pVector n = p2;
 
1629
            float p2l2 = n.length2(); // Optimize this.
 
1630
            n.normalize();
 
1631
 
 
1632
            // radius2Sqr stores 1 / (p2.p2)
 
1633
            // XXX Used to have an actual if.
 
1634
            radius2Sqr = p2l2 ? 1.0f / p2l2 : 0.0f;
 
1635
 
 
1636
            // Find a vector orthogonal to n.
 
1637
            pVector basis(1.0f, 0.0f, 0.0f);
 
1638
            if(fabs(basis * n) > 0.999)
 
1639
                basis = pVector(0.0f, 1.0f, 0.0f);
 
1640
 
 
1641
            // Project away N component, normalize and cross to get
 
1642
            // second orthonormal vector.
 
1643
            u = basis - n * (basis * n);
 
1644
            u.normalize();
 
1645
            v = n ^ u;
 
1646
        }
 
1647
        break;
 
1648
    case PDBlob:
 
1649
        {
 
1650
            p1 = pVector(a0, a1, a2);
 
1651
            radius1 = a3;
 
1652
            float tmp = 1./radius1;
 
1653
            radius2Sqr = -0.5f*fsqr(tmp);
 
1654
            radius2 = ONEOVERSQRT2PI * tmp;
 
1655
        }
 
1656
        break;
 
1657
    case PDDisc:
 
1658
        {
 
1659
            p1 = pVector(a0, a1, a2); // Center point
 
1660
            p2 = pVector(a3, a4, a5); // Normal (not used in Within and Generate)
 
1661
            p2.normalize();
 
1662
 
 
1663
            if(a6 > a7)
 
1664
            {
 
1665
                radius1 = a6; radius2 = a7;
 
1666
            }
 
1667
            else
 
1668
            {
 
1669
                radius1 = a7; radius2 = a6;
 
1670
            }
 
1671
 
 
1672
            // Find a vector orthogonal to n.
 
1673
            pVector basis(1.0f, 0.0f, 0.0f);
 
1674
            if(fabs(basis * p2) > 0.999)
 
1675
                basis = pVector(0.0f, 1.0f, 0.0f);
 
1676
 
 
1677
            // Project away N component, normalize and cross to get
 
1678
            // second orthonormal vector.
 
1679
            u = basis - p2 * (basis * p2);
 
1680
            u.normalize();
 
1681
            v = p2 ^ u;
 
1682
            radius1Sqr = -(p1 * p2); // D of the plane eqn.
 
1683
        }
 
1684
        break;
 
1685
    }
 
1686
}
 
1687
 
 
1688
// Determines if pos is inside the domain
 
1689
bool pDomain::Within(const pVector &pos) const
 
1690
{
 
1691
    switch (type)
 
1692
    {
 
1693
    case PDBox:
 
1694
        return !((pos.x < p1.x) || (pos.x > p2.x) ||
 
1695
                 (pos.y < p1.y) || (pos.y > p2.y) ||
 
1696
                 (pos.z < p1.z) || (pos.z > p2.z));
 
1697
    case PDPlane:
 
1698
        // Distance from plane = n * p + d
 
1699
        // Inside is the positive half-space.
 
1700
        return pos * p2 >= -radius1;
 
1701
    case PDSphere:
 
1702
        {
 
1703
            pVector rvec(pos - p1);
 
1704
            float rSqr = rvec.length2();
 
1705
            return rSqr <= radius1Sqr && rSqr >= radius2Sqr;
 
1706
        }
 
1707
    case PDCylinder:
 
1708
    case PDCone:
 
1709
        {
 
1710
            // This is painful and slow. Might be better to do quick
 
1711
            // accept/reject tests.
 
1712
            // Let p2 = vector from base to tip of the cylinder
 
1713
            // x = vector from base to test point
 
1714
            // x . p2
 
1715
            // dist = ------ = projected distance of x along the axis
 
1716
            // p2. p2 ranging from 0 (base) to 1 (tip)
 
1717
            //
 
1718
            // rad = x - dist * p2 = projected vector of x along the base
 
1719
            // p1 is the apex of the cone.
 
1720
 
 
1721
            pVector x(pos - p1);
 
1722
 
 
1723
            // Check axial distance
 
1724
            // radius2Sqr stores 1 / (p2.p2)
 
1725
            float dist = (p2 * x) * radius2Sqr;
 
1726
            if(dist < 0.0f || dist > 1.0f)
 
1727
                return false;
 
1728
 
 
1729
            // Check radial distance; scale radius along axis for cones
 
1730
            pVector xrad = x - p2 * dist; // Radial component of x
 
1731
            float rSqr = xrad.length2();
 
1732
 
 
1733
            if(type == PDCone)
 
1734
                return (rSqr <= fsqr(dist * radius1) &&
 
1735
                        rSqr >= fsqr(dist * radius2));
 
1736
            else
 
1737
                return (rSqr <= radius1Sqr && rSqr >= fsqr(radius2));
 
1738
        }
 
1739
    case PDBlob:
 
1740
        {
 
1741
            pVector x(pos - p1);
 
1742
            // return exp(-0.5 * xSq * Sqr(oneOverSigma)) * ONEOVERSQRT2PI * oneOverSigma;
 
1743
            float Gx = expf(x.length2() * radius2Sqr) * radius2;
 
1744
            return (drand48() < Gx);
 
1745
        }
 
1746
    case PDPoint:
 
1747
    case PDLine:
 
1748
    case PDRectangle:
 
1749
    case PDTriangle:
 
1750
    case PDDisc:
 
1751
    default:
 
1752
        return false; // XXX Is there something better?
 
1753
    }
 
1754
}
 
1755
 
 
1756
// Generate a random point uniformly distrbuted within the domain
 
1757
void pDomain::Generate(pVector &pos) const
 
1758
{
 
1759
    switch (type)
 
1760
    {
 
1761
    case PDPoint:
 
1762
        pos = p1;
 
1763
        break;
 
1764
    case PDLine:
 
1765
        pos = p1 + p2 * drand48();
 
1766
        break;
 
1767
    case PDBox:
 
1768
        // Scale and translate [0,1] random to fit box
 
1769
        pos.x = p1.x + (p2.x - p1.x) * drand48();
 
1770
        pos.y = p1.y + (p2.y - p1.y) * drand48();
 
1771
        pos.z = p1.z + (p2.z - p1.z) * drand48();
 
1772
        break;
 
1773
    case PDTriangle:
 
1774
        {
 
1775
            float r1 = drand48();
 
1776
            float r2 = drand48();
 
1777
            if(r1 + r2 < 1.0f)
 
1778
                pos = p1 + u * r1 + v * r2;
 
1779
            else
 
1780
                pos = p1 + u * (1.0f-r1) + v * (1.0f-r2);
 
1781
        }
 
1782
        break;
 
1783
    case PDRectangle:
 
1784
        pos = p1 + u * drand48() + v * drand48();
 
1785
        break;
 
1786
    case PDPlane: // How do I sensibly make a point on an infinite plane?
 
1787
        pos = p1;
 
1788
        break;
 
1789
    case PDSphere:
 
1790
        // Place on [-1..1] sphere
 
1791
        pos = RandVec() - vHalf;
 
1792
        pos.normalize();
 
1793
 
 
1794
        // Scale unit sphere pos by [0..r] and translate
 
1795
        // (should distribute as r^2 law)
 
1796
        if(radius1 == radius2)
 
1797
            pos = p1 + pos * radius1;
 
1798
        else
 
1799
            pos = p1 + pos * (radius2 + drand48() * (radius1 - radius2));
 
1800
        break;
 
1801
    case PDCylinder:
 
1802
    case PDCone:
 
1803
        {
 
1804
            // For a cone, p2 is the apex of the cone.
 
1805
            float dist = drand48(); // Distance between base and tip
 
1806
            float theta = drand48() * 2.0f * float(M_PI); // Angle around axis
 
1807
            // Distance from axis
 
1808
            float r = radius2 + drand48() * (radius1 - radius2);
 
1809
 
 
1810
            float x = r * cosf(theta); // Weighting of each frame vector
 
1811
            float y = r * sinf(theta);
 
1812
 
 
1813
            // Scale radius along axis for cones
 
1814
            if(type == PDCone)
 
1815
            {
 
1816
                x *= dist;
 
1817
                y *= dist;
 
1818
            }
 
1819
 
 
1820
            pos = p1 + p2 * dist + u * x + v * y;
 
1821
        }
 
1822
        break;
 
1823
    case PDBlob:
 
1824
        pos.x = p1.x + NRand(radius1);
 
1825
        pos.y = p1.y + NRand(radius1);
 
1826
        pos.z = p1.z + NRand(radius1);
 
1827
 
 
1828
        break;
 
1829
    case PDDisc:
 
1830
        {
 
1831
            float theta = drand48() * 2.0f * float(M_PI); // Angle around normal
 
1832
            // Distance from center
 
1833
            float r = radius2 + drand48() * (radius1 - radius2);
 
1834
 
 
1835
            float x = r * cosf(theta); // Weighting of each frame vector
 
1836
            float y = r * sinf(theta);
 
1837
 
 
1838
            pos = p1 + u * x + v * y;
 
1839
        }
 
1840
        break;
 
1841
    default:
 
1842
        pos = pVector(0,0,0);
 
1843
    }
 
1844
}