Project

General

Profile

Download (18.3 KB) Statistics
| Branch: | Revision:

library / src / main / java / org / distorted / library / type / Dynamic3D.java @ bdb341bc

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// Distorted is free software: you can redistribute it and/or modify                             //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Distorted is distributed in the hope that it will be useful,                                  //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.library.type;
21

    
22
import java.util.Vector;
23

    
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25
/** 
26
* A 3-dimensional implementation of the Dynamic class to interpolate between a list
27
* of Static3Ds.
28
*/
29

    
30
public class Dynamic3D extends Dynamic implements Data3D
31
  {
32
  private Vector<Static3D> vv;
33
  private Static3D prev, curr, next;
34
/*
35
private float a0,a1,a2;
36
private float f0, f1;
37
private float b00,b01,b02;
38
private float b10,b11,b12;
39
private float b20,b21,b22;
40
private float oldTime;
41
*/
42
///////////////////////////////////////////////////////////////////////////////////////////////////
43
// no array bounds checking!
44
  
45
  private void vec(int c)
46
    {
47
    int p = c>0 ? c-1: numPoints-1;
48
    int n = c<numPoints-1 ? c+1: 0;
49
    
50
    prev = vv.elementAt(p);
51
    curr = vv.elementAt(c);
52
    next = vv.elementAt(n);
53

    
54
    tmp1 = vc.elementAt(c);
55
    
56
    float px = curr.x - prev.x;
57
    float py = curr.y - prev.y;
58
    float pz = curr.z - prev.z;
59
    float nx = next.x - curr.x;
60
    float ny = next.y - curr.y;
61
    float nz = next.z - curr.z;
62
     
63
    float d = nx*nx+ny*ny+nz*nz;
64
    
65
    if( d>0 )
66
      {
67
      float q = (float)Math.sqrt((px*px+py*py+pz*pz)/d);
68
      
69
      if( q>1 )
70
        {
71
        tmp1.tangent[0] = nx+px/q;
72
        tmp1.tangent[1] = ny+py/q;
73
        tmp1.tangent[2] = nz+pz/q;
74
        }
75
      else
76
        {
77
        tmp1.tangent[0] = px+nx*q;
78
        tmp1.tangent[1] = py+ny*q;
79
        tmp1.tangent[2] = pz+nz*q;
80
        }
81
      }
82
    else
83
      {
84
      tmp1.tangent[0] = 0.0f;
85
      tmp1.tangent[1] = 0.0f;
86
      tmp1.tangent[2] = 0.0f;
87
      }
88
    }
89
    
90
///////////////////////////////////////////////////////////////////////////////////////////////////
91
  
92
  private void recomputeCache()
93
    {  
94
    if( numPoints==1 )
95
      {
96
      tmp1= vc.elementAt(0);
97
      curr= vv.elementAt(0);
98
        
99
      tmp1.a[0] = tmp1.a[1] = tmp1.a[2] = 0.0f;
100
      tmp1.b[0] = tmp1.b[1] = tmp1.b[2] = 0.0f;
101
      tmp1.c[0] = curr.x;
102
      tmp1.c[1] = curr.y;
103
      tmp1.c[2] = curr.z;
104
      tmp1.d[0] = tmp1.d[1] = tmp1.d[2] = 0.0f;
105
      }
106
    else if( numPoints==2 )
107
      {
108
      tmp1= vc.elementAt(0);
109
      tmp2= vc.elementAt(1);
110
      curr= vv.elementAt(0);
111
      next= vv.elementAt(1);
112
          
113
      tmp1.a[0] = tmp1.a[1] = tmp1.a[2] = 0.0f;
114
      tmp1.b[0] = tmp1.b[1] = tmp1.b[2] = 0.0f;
115
      tmp1.c[0] = next.x - curr.x;
116
      tmp1.c[1] = next.y - curr.y;
117
      tmp1.c[2] = next.z - curr.z;
118
      tmp1.d[0] = curr.x;
119
      tmp1.d[1] = curr.y;
120
      tmp1.d[2] = curr.z;
121
      
122
      tmp2.a[0] = tmp2.a[1] = tmp2.a[2] = 0.0f;
123
      tmp2.b[0] = tmp2.b[1] = tmp2.b[2] = 0.0f;
124
      tmp2.c[0] = curr.x - next.x;
125
      tmp2.c[1] = curr.y - next.y;
126
      tmp2.c[2] = curr.z - next.z;
127
      tmp2.d[0] = next.x;
128
      tmp2.d[1] = next.y;
129
      tmp2.d[2] = next.z;
130
      }
131
    else
132
      {
133
      int i, n;  
134
         
135
      for(i=0; i<numPoints; i++) vec(i);
136
   
137
      for(i=0; i<numPoints; i++)
138
        {
139
        n = i<numPoints-1 ? i+1:0;  
140
      
141
        tmp1= vc.elementAt(i);
142
        tmp2= vc.elementAt(n);
143
        curr= vv.elementAt(i);
144
        next= vv.elementAt(n);
145
      
146
        tmp1.cached[0] = curr.x;
147
        tmp1.cached[1] = curr.y;
148
        tmp1.cached[2] = curr.z;
149
        
150
        tmp1.a[0] =  2*curr.x +   tmp1.tangent[0] - 2*next.x + tmp2.tangent[0];
151
        tmp1.b[0] = -3*curr.x - 2*tmp1.tangent[0] + 3*next.x - tmp2.tangent[0];
152
        tmp1.c[0] = tmp1.tangent[0];
153
        tmp1.d[0] = curr.x;
154
      
155
        tmp1.a[1] =  2*curr.y +   tmp1.tangent[1] - 2*next.y + tmp2.tangent[1];
156
        tmp1.b[1] = -3*curr.y - 2*tmp1.tangent[1] + 3*next.y - tmp2.tangent[1];
157
        tmp1.c[1] = tmp1.tangent[1];
158
        tmp1.d[1] = curr.y;
159
      
160
        tmp1.a[2] =  2*curr.z +   tmp1.tangent[2] - 2*next.z + tmp2.tangent[2];
161
        tmp1.b[2] = -3*curr.z - 2*tmp1.tangent[2] + 3*next.z - tmp2.tangent[2];
162
        tmp1.c[2] = tmp1.tangent[2];
163
        tmp1.d[2] = curr.z;
164
        }
165
      }
166
   
167
    cacheDirty = false;
168
    }
169

    
170
///////////////////////////////////////////////////////////////////////////////////////////////////
171
// PUBLIC API
172
///////////////////////////////////////////////////////////////////////////////////////////////////
173
/**
174
 * Default constructor.
175
 */
176
  public Dynamic3D()
177
    {
178
    super(0,0.5f,3);
179
    vv = new Vector<>();
180
    }
181

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
/**
185
 * Default constructor.
186
 *
187
 * @param duration number of milliseconds it takes to do a full loop/path from first vector to the
188
 *                 last and back to the first
189
 * @param count    number of loops/paths we will do; mCount = 1.5 means we go from the first vector
190
 *                 to the last, back to first, and to the last again.
191
 */
192
  public Dynamic3D(int duration, float count)
193
    {
194
    super(duration,count,3);
195
    vv = new Vector<>();
196
    }
197

    
198
///////////////////////////////////////////////////////////////////////////////////////////////////
199
/**
200
 * Returns the location'th Static3D.
201
 *   
202
 * @param location the index of the Point we are interested in.
203
 * @return The Static3D, if 0<=location&lt;getNumPoints(), or null otherwise.
204
 */  
205
  public synchronized Static3D getPoint(int location)
206
    {
207
    return (location>=0 && location<numPoints) ? vv.elementAt(location) : null;  
208
    }
209
  
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211
/**
212
 * Resets the location'th Point.
213
 * 
214
 * @param location the index of the Point we are setting.
215
 * @param x New value of its first float.
216
 */
217
  public synchronized void setPoint(int location, float x, float y, float z)
218
    {
219
    if( location>=0 && location<numPoints )
220
      {
221
      curr = vv.elementAt(location);
222
   
223
      if( curr!=null )
224
        {
225
        curr.set(x,y,z);
226
        cacheDirty=true;
227
        }
228
      }
229
    }
230

    
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232
/**
233
 * Adds a new Static3D to the end of our list of Points to interpolate through.
234
 * <p>   
235
 * Only a reference to the Point gets added to the List; this means that one can add a Point 
236
 * here, and later on {@link Static3D#set(float,float,float)} it to some new value and the
237
 * change will be seamlessly reflected in the interpolated path.  
238
 * <p>
239
 * A Point can be added multiple times.
240
 *   
241
 * @param v The Point to add.
242
 */    
243
  public synchronized void add(Static3D v)
244
    {
245
    if( v!=null )
246
      {
247
      vv.add(v);
248
        
249
      if( vn!=null ) vn.add(new VectorNoise());
250
       
251
      switch(numPoints)
252
        {
253
        case 0: break;
254
        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
255
                break;
256
        case 2: vc.add(new VectorCache());
257
                vc.add(new VectorCache());
258
                vc.add(new VectorCache());
259
                break;
260
        default:vc.add(new VectorCache());
261
        }
262

    
263
      numPoints++;
264
      cacheDirty = true;
265
      }
266
    }
267

    
268
///////////////////////////////////////////////////////////////////////////////////////////////////
269
/**
270
 * Adds a new Static3D to the location'th place in our List of Points to interpolate through.
271
 *   
272
 * @param location Index in our List to add the new Point at.
273
 * @param v The Point to add.
274
 */  
275
  public synchronized void add(int location, Static3D v)
276
    {
277
    if( v!=null )
278
      {
279
      vv.add(location, v);
280
      
281
      if( vn!=null ) vn.add(new VectorNoise());
282
      
283
      switch(numPoints)
284
        {
285
        case 0: break;
286
        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
287
                break;
288
        case 2: vc.add(new VectorCache());
289
                vc.add(new VectorCache());
290
                vc.add(new VectorCache());
291
                break;
292
        default:vc.add(location,new VectorCache());
293
        }
294

    
295
      numPoints++;
296
      cacheDirty = true;
297
      }
298
    }
299
  
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301
/**
302
 * Removes all occurrences of Point v from the List of Points to interpolate through.  
303
 * 
304
 * @param v The Point to remove.
305
 * @return <code>true</code> if we have removed at least one Point.
306
 */
307
  public synchronized boolean remove(Static3D v)
308
    {
309
    int n = vv.indexOf(v);
310
    boolean found = false;
311
   
312
    while( n>=0 ) 
313
      {
314
      vv.remove(n);
315
     
316
      if( vn!=null ) vn.remove(0);
317
     
318
      switch(numPoints)
319
        {
320
        case 0:
321
        case 1: 
322
        case 2: break;
323
        case 3: vc.removeAllElements();
324
                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
325
                break;
326
        default:vc.remove(n);
327
        }
328

    
329
      numPoints--;
330
      found = true;
331
      n = vv.indexOf(v);
332
      }
333
   
334
    if( found ) 
335
      {
336
      cacheDirty=true;
337
      }
338
   
339
    return found;
340
    }
341

    
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343
/**
344
 * Removes a location'th Point from the List of Points we interpolate through.
345
 * 
346
 * @param location index of the Point we want to remove. 
347
 * @return <code>true</code> if location is valid, i.e. if 0<=location&lt;getNumPoints().
348
 */
349
  public synchronized boolean remove(int location)
350
    {
351
    if( location>=0 && location<numPoints ) 
352
      {
353
      vv.removeElementAt(location);
354
       
355
      if( vn!=null ) vn.remove(0);
356
      
357
      switch(numPoints)
358
        {
359
        case 0:
360
        case 1: 
361
        case 2: break;
362
        case 3: vc.removeAllElements();
363
                computeOrthonormalBase2(vv.elementAt(0),vv.elementAt(1));
364
                break;
365
        default:vc.removeElementAt(location);
366
        }
367

    
368
      numPoints--;
369
      cacheDirty = true; 
370
      return true;
371
      }
372

    
373
   return false;
374
   }
375
  
376
///////////////////////////////////////////////////////////////////////////////////////////////////
377
/**
378
 * Removes all Points.
379
 */
380
  public synchronized void removeAll()
381
    {
382
    numPoints = 0;
383
    vv.removeAllElements();
384
    vc.removeAllElements();
385
    cacheDirty = false;
386
   
387
    if( vn!=null ) vn.removeAllElements();
388
    }
389

    
390
///////////////////////////////////////////////////////////////////////////////////////////////////
391
/**
392
 * Sets the 'smoothness' of interpolation.
393
 * <p>
394
 * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible.
395
 * Increasing noise makes the Dynamic increasingly deviate from this path, pseudo-randomly speeding
396
 * up and slowing down, etc.
397
 *
398
 * @param noise The noise level. Permitted range: 0 <= noise <= 1.
399
 */
400

    
401
  public synchronized void setNoise(Static3D noise)
402
    {
403
    if( vn==null )
404
      {
405
      vn = new Vector<>();
406
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
407

    
408
      if( mDimension>=2 )
409
        {
410
        mFactor = new float[mDimension-1];
411
        }
412

    
413
      mNoise = new float[mDimension];
414
      }
415

    
416
    if( noise.x<0.0f ) noise.x = 0.0f;
417
    if( noise.x>1.0f ) noise.x = 1.0f;
418
    if( noise.y<0.0f ) noise.y = 0.0f;
419
    if( noise.y>1.0f ) noise.y = 1.0f;
420
    if( noise.z<0.0f ) noise.z = 0.0f;
421
    if( noise.z>1.0f ) noise.z = 1.0f;
422

    
423
    mNoise[0] = noise.x;
424
    mNoise[1] = noise.y;
425
    mNoise[2] = noise.z;
426
    }
427

    
428
///////////////////////////////////////////////////////////////////////////////////////////////////
429

    
430
  synchronized void interpolate(float[] buffer, int offset, float time)
431
    {  
432
    switch(numPoints)
433
      {
434
      case 0: buffer[offset  ] = 0.0f;
435
              buffer[offset+1] = 0.0f;
436
              buffer[offset+2] = 0.0f;
437
              break;
438
      case 1: curr = vv.elementAt(0);
439
              buffer[offset  ] = curr.x;
440
              buffer[offset+1] = curr.y;
441
              buffer[offset+2] = curr.z;
442
              break;
443
      case 2: curr = vv.elementAt(0);
444
              next = vv.elementAt(1);
445

    
446
              int segment2= (int)(2*time);
447

    
448
              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
449

    
450
              if( vn!=null )
451
                {
452
                if( segment2 != mSegment )
453
                  {
454
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
455
                  mSegment = segment2;
456
                  }
457

    
458
                time = noise(time,0);
459
            
460
                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
461
                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
462
                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
463
                }
464
              else
465
                {
466
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
467
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
468
                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
469
                }
470
             
471
              break;
472
      default:float t = time;
473
              int vecCurr, segment;
474

    
475
              switch(mMode)
476
                {
477
                case MODE_LOOP: time = time*numPoints;
478
                                segment = (int)time;
479
                                vecCurr = segment;
480
                                break;
481
                case MODE_PATH: segment = (int)(2*t*(numPoints-1));
482

    
483
                                if( t<=0.5f )  // this has to be <= (otherwise when effect ends at t=0.5, then time=1.0
484
                                  {            // and end position is slightly not equal to the end point => might not get autodeleted!
485
                                  time = 2*t*(numPoints-1);
486
                                  vecCurr = segment;
487
                                  }
488
                                else
489
                                  {
490
                                  time = 2*(1-t)*(numPoints-1);
491
                                  vecCurr = 2*numPoints-3-segment;
492
                                  }
493
                                break;
494
                case MODE_JUMP: time = time*(numPoints-1);
495
                                segment = (int)time;
496
                                vecCurr = segment;
497
                                break;
498
                default       : vecCurr = 0;
499
                                segment = 0;
500
                }
501

    
502
              if( vecCurr>=0 && vecCurr<numPoints )
503
                {
504
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
505
                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
506
                  {
507
                  int vecNext;
508

    
509
                  switch(mMode)
510
                    {
511
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1;
512
                                    break;
513
                    case MODE_PATH: if( t<=0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;
514
                                    else          vecNext = vecCurr==0 ? 1 : vecCurr-1;
515
                                    break;
516
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
517
                                    break;
518
                    default       : vecNext = 0;
519
                    }
520

    
521
                  next = vv.elementAt(vecNext);
522
                  tmp2 = vc.elementAt(vecNext);
523

    
524
                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y || tmp2.cached[2]!=next.z ) recomputeCache();
525
                  }
526

    
527
                if( mSegment!= segment && vn!=null ) vn.elementAt(vecCurr).computeNoise();
528

    
529
                mSegment = segment;
530

    
531
                time = time-vecCurr;
532
            
533
                tmp1 = vc.elementAt(vecCurr);
534

    
535
                if( vn!=null )
536
                  {
537
                  time = noise(time,vecCurr);
538
              
539
                  computeOrthonormalBaseMore(time,tmp1);
540
                 
541
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0] + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
542
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1] + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
543
                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2] + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
544
                  }
545
                else
546
                  {
547
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
548
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
549
                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2];
550
                  }
551
               
552
                break;
553
                }
554
       }
555
     }  
556

    
557
  }
558
///////////////////////////////////////////////////////////////////////////////////////////////////
559
//
(9-9/17)