Project

General

Profile

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

library / src / main / java / org / distorted / library / type / Dynamic4D.java @ 07037b8a

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 4-dimensional implementation of the Dynamic class to interpolate between a list
27
* of Static4Ds.
28
*/
29

    
30
public class Dynamic4D extends Dynamic implements Data4D
31
  {
32
  private Vector<Static4D> vv;
33
  private Static4D prev, curr, next;
34

    
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36
// no array bounds checking!
37
  
38
  private void vec(int c)
39
    {
40
    int p = c>0 ? c-1: numPoints-1;
41
    int n = c<numPoints-1 ? c+1: 0;
42
    
43
    prev = vv.elementAt(p);
44
    curr = vv.elementAt(c);
45
    next = vv.elementAt(n);
46

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

    
179
///////////////////////////////////////////////////////////////////////////////////////////////////
180
// PUBLIC API
181
///////////////////////////////////////////////////////////////////////////////////////////////////
182
/**
183
 * Default constructor.
184
 */
185
  public Dynamic4D()
186
    {
187
    super(0,0.5f,4);
188
    vv = new Vector<>();
189
    }
190

    
191
///////////////////////////////////////////////////////////////////////////////////////////////////
192

    
193
/**
194
 * Default constructor.
195
 *
196
 * @param duration number of milliseconds it takes to do a full loop/path from first vector to the
197
 *                 last and back to the first
198
 * @param count    number of loops/paths we will do; mCount = 1.5 means we go from the first vector
199
 *                 to the last, back to first, and to the last again.
200
 */
201
  public Dynamic4D(int duration, float count)
202
    {
203
    super(duration,count,4);
204
    vv = new Vector<>();
205
    }
206

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

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

    
272
      numPoints++;
273
      cacheDirty = true;
274
      }
275
    }
276

    
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278
/**
279
 * Adds a new Static4D to the location'th place in our List of Points to interpolate through.
280
 *   
281
 * @param location Index in our List to add the new Point at.
282
 * @param v The Static4D to add.
283
 */  
284
  public synchronized void add(int location, Static4D v)
285
    {
286
    if( v!=null )
287
      {
288
      vv.add(location, v);
289
      
290
      if( vn!=null ) vn.add(new VectorNoise());
291
      
292
      switch(numPoints)
293
        {
294
        case 0: break;
295
        case 1: computeOrthonormalBase2(vv.elementAt(0),v);
296
                break;
297
        case 2: vc.add(new VectorCache());
298
                vc.add(new VectorCache());
299
                vc.add(new VectorCache());
300
                break;
301
        default:vc.add(location,new VectorCache());
302
        }
303

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

    
338
      numPoints--;
339
      found = true;
340
      n = vv.indexOf(v);
341
      }
342
   
343
    if( found ) 
344
      {
345
      cacheDirty=true;
346
      }
347
   
348
    return found;
349
    }
350

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

    
377
      numPoints--;
378
      cacheDirty = true; 
379
      return true;
380
      }
381

    
382
    return false;
383
    }
384
  
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386
/**
387
 * Removes all Points.
388
 */
389
  public synchronized void removeAll()
390
    {
391
    numPoints = 0;
392
    vv.removeAllElements();
393
    vc.removeAllElements();
394
    cacheDirty = false;
395
   
396
    if( vn!=null ) vn.removeAllElements();
397
    }
398

    
399
///////////////////////////////////////////////////////////////////////////////////////////////////
400
/**
401
 * Sets the 'smoothness' of interpolation.
402
 * <p>
403
 * When Noise=0 (the default), we interpolate between our Points through the most smooth path possible.
404
 * Increasing noise makes the Dynamic increasingly deviate from this path, pseudo-randomly speeding
405
 * up and slowing down, etc.
406
 *
407
 * @param noise The noise level. Permitted range: 0 <= noise <= 1.
408
 */
409

    
410
  public synchronized void setNoise(Static4D noise)
411
    {
412
    if( vn==null )
413
      {
414
      vn = new Vector<>();
415
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
416

    
417
      if( mDimension>=2 )
418
        {
419
        mFactor = new float[mDimension-1];
420
        }
421

    
422
      mNoise = new float[mDimension];
423
      }
424

    
425
    if( noise.x<0.0f ) noise.x = 0.0f;
426
    if( noise.x>1.0f ) noise.x = 1.0f;
427
    if( noise.y<0.0f ) noise.y = 0.0f;
428
    if( noise.y>1.0f ) noise.y = 1.0f;
429
    if( noise.z<0.0f ) noise.z = 0.0f;
430
    if( noise.z>1.0f ) noise.z = 1.0f;
431
    if( noise.w<0.0f ) noise.w = 0.0f;
432
    if( noise.w>1.0f ) noise.w = 1.0f;
433

    
434
    mNoise[0] = noise.x;
435
    mNoise[1] = noise.y;
436
    mNoise[2] = noise.z;
437
    mNoise[3] = noise.w;
438
    }
439

    
440
///////////////////////////////////////////////////////////////////////////////////////////////////
441

    
442
  synchronized void interpolate(float[] buffer, int offset, float time)
443
    {  
444
    switch(numPoints)
445
      {
446
      case 0: buffer[offset  ] = 0.0f;
447
              buffer[offset+1] = 0.0f;
448
              buffer[offset+2] = 0.0f;
449
              buffer[offset+3] = 0.0f;
450
              break;
451
      case 1: curr = vv.elementAt(0);
452
              buffer[offset  ] = curr.x;
453
              buffer[offset+1] = curr.y;
454
              buffer[offset+2] = curr.z;
455
              buffer[offset+3] = curr.w;
456
              break;
457
      case 2: curr = vv.elementAt(0);
458
              next = vv.elementAt(1);
459

    
460
              int segment2= (int)(2*time);
461

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

    
464
              if( vn!=null )
465
                {
466
                if( segment2 != mSegment )
467
                  {
468
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
469
                  mSegment = segment2;
470
                  }
471

    
472
                time = noise(time,0);
473

    
474
                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1] + baseV[3][0]*mFactor[2]);
475
                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1] + baseV[3][1]*mFactor[2]);
476
                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1] + baseV[3][2]*mFactor[2]);
477
                buffer[offset+3] = (next.w-curr.w)*time + curr.w + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2]);
478
                }
479
              else
480
                {
481
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
482
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
483
                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
484
                buffer[offset+3] = (next.w-curr.w)*time + curr.w;
485
                }
486
                
487
              break;
488
      default:float t = time;
489
              int vecCurr, segment;
490

    
491
              switch(mMode)
492
                {
493
                case MODE_LOOP: time = time*numPoints;
494
                                segment = (int)time;
495
                                vecCurr = segment;
496
                                break;
497
                case MODE_PATH: segment = (int)(2*t*(numPoints-1));
498

    
499
                                if( t<=0.5f )  // this has to be <= (otherwise when effect ends at t=0.5, then time=1.0
500
                                  {            // and end position is slightly not equal to the end point => might not get autodeleted!
501
                                  time = 2*t*(numPoints-1);
502
                                  vecCurr = segment;
503
                                  }
504
                                else
505
                                  {
506
                                  time = 2*(1-t)*(numPoints-1);
507
                                  vecCurr = 2*numPoints-3-segment;
508
                                  }
509
                                break;
510
                case MODE_JUMP: time = time*(numPoints-1);
511
                                segment = (int)time;
512
                                vecCurr = segment;
513
                                break;
514
                default       : vecCurr = 0;
515
                                segment = 0;
516
                }
517

    
518
              if( vecCurr>=0 && vecCurr<numPoints )
519
                {
520
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
521
                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
522
                  {
523
                  int vecNext;
524

    
525
                  switch(mMode)
526
                    {
527
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1;
528
                                    break;
529
                    case MODE_PATH: if( t<=0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;
530
                                    else          vecNext = vecCurr==0 ? 1 : vecCurr-1;
531
                                    break;
532
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
533
                                    break;
534
                    default       : vecNext = 0;
535
                    }
536

    
537
                  next = vv.elementAt(vecNext);
538
                  tmp2 = vc.elementAt(vecNext);
539

    
540
                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y || tmp2.cached[2]!=next.z || tmp2.cached[3]!=next.w ) recomputeCache();
541
                  }
542

    
543
                if( mSegment!= segment && vn!=null ) vn.elementAt(vecCurr).computeNoise();
544

    
545
                mSegment = segment;
546

    
547
                time = time-vecCurr;
548
            
549
                tmp1 = vc.elementAt(vecCurr);
550
               
551
                if( vn!=null )
552
                  {
553
                  time = noise(time,vecCurr);
554
              
555
                  computeOrthonormalBaseMore(time,tmp1);
556

    
557
                  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] + baseV[3][0]*mFactor[2]);
558
                  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] + baseV[3][1]*mFactor[2]);
559
                  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] + baseV[3][2]*mFactor[2]);
560
                  buffer[offset+3]= ((tmp1.a[3]*time+tmp1.b[3])*time+tmp1.c[3])*time+tmp1.d[3] + (baseV[1][3]*mFactor[0] + baseV[2][3]*mFactor[1] + baseV[3][3]*mFactor[2]);
561
                  }
562
                else
563
                  {
564
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
565
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
566
                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2];
567
                  buffer[offset+3]= ((tmp1.a[3]*time+tmp1.b[3])*time+tmp1.c[3])*time+tmp1.d[3];
568
                  }
569
 
570
                break;
571
                }
572
      }
573
    }  
574

    
575
  }
576
///////////////////////////////////////////////////////////////////////////////////////////////////
577
//
(10-10/17)