Project

General

Profile

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

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

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

    
163
///////////////////////////////////////////////////////////////////////////////////////////////////
164
// PUBLIC API
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166
/**
167
 * Default constructor.
168
 */
169
  public Dynamic3D()
170
    {
171
    super(0,0.5f,3);
172
    vv = new Vector<>();
173
    }
174

    
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176

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

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

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

    
256
      numPoints++;
257
      cacheDirty = true;
258
      }
259
    }
260

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

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

    
322
      numPoints--;
323
      found = true;
324
      n = vv.indexOf(v);
325
      }
326
   
327
    if( found ) 
328
      {
329
      cacheDirty=true;
330
      }
331
   
332
    return found;
333
    }
334

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

    
361
      numPoints--;
362
      cacheDirty = true; 
363
      return true;
364
      }
365

    
366
   return false;
367
   }
368
  
369
///////////////////////////////////////////////////////////////////////////////////////////////////
370
/**
371
 * Removes all Points.
372
 */
373
  public synchronized void removeAll()
374
    {
375
    numPoints = 0;
376
    vv.removeAllElements();
377
    vc.removeAllElements();
378
    cacheDirty = false;
379
   
380
    if( vn!=null ) vn.removeAllElements();
381
    }
382

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

    
394
  public synchronized void setNoise(Static3D noise)
395
    {
396
    if( vn==null )
397
      {
398
      vn = new Vector<>();
399
      for(int i=0; i<numPoints; i++) vn.add(new VectorNoise());
400

    
401
      if( mDimension>=2 )
402
        {
403
        mFactor = new float[mDimension-1];
404
        }
405

    
406
      mNoise = new float[mDimension];
407
      }
408

    
409
    if( noise.x<0.0f ) noise.x = 0.0f;
410
    if( noise.x>1.0f ) noise.x = 1.0f;
411
    if( noise.y<0.0f ) noise.y = 0.0f;
412
    if( noise.y>1.0f ) noise.y = 1.0f;
413
    if( noise.z<0.0f ) noise.z = 0.0f;
414
    if( noise.z>1.0f ) noise.z = 1.0f;
415

    
416
    mNoise[0] = noise.x;
417
    mNoise[1] = noise.y;
418
    mNoise[2] = noise.z;
419
    }
420

    
421
///////////////////////////////////////////////////////////////////////////////////////////////////
422

    
423
  synchronized void interpolate(float[] buffer, int offset, float time)
424
    {  
425
    switch(numPoints)
426
      {
427
      case 0: buffer[offset  ] = 0.0f;
428
              buffer[offset+1] = 0.0f;
429
              buffer[offset+2] = 0.0f;
430
              break;
431
      case 1: curr = vv.elementAt(0);
432
              buffer[offset  ] = curr.x;
433
              buffer[offset+1] = curr.y;
434
              buffer[offset+2] = curr.z;
435
              break;
436
      case 2: curr = vv.elementAt(0);
437
              next = vv.elementAt(1);
438

    
439
              int segment2= (int)(2*time);
440

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

    
443
              if( vn!=null )
444
                {
445
                if( segment2 != mSegment )
446
                  {
447
                  if(mMode!=MODE_JUMP || mSegment==1) vn.elementAt(0).computeNoise();
448
                  mSegment = segment2;
449
                  }
450

    
451
                time = noise(time,0);
452
            
453
                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
454
                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
455
                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
456
                }
457
              else
458
                {
459
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
460
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
461
                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
462
                }
463
             
464
              break;
465
      default:float t = time;
466
              int vecCurr, segment;
467

    
468
              switch(mMode)
469
                {
470
                case MODE_LOOP: time = time*numPoints;
471
                                segment = (int)time;
472
                                vecCurr = segment;
473
                                break;
474
                case MODE_PATH: segment = (int)(2*t*(numPoints-1));
475

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

    
495
              if( vecCurr>=0 && vecCurr<numPoints )
496
                {
497
                if( cacheDirty ) recomputeCache();  // recompute cache if we have added or remove vectors since last computation
498
                else if( mSegment!= segment )       // ...or if we have just passed a vector and the vector we are currently flying to has changed
499
                  {
500
                  int vecNext;
501

    
502
                  switch(mMode)
503
                    {
504
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1;
505
                                    break;
506
                    case MODE_PATH: if( t<=0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;
507
                                    else          vecNext = vecCurr==0 ? 1 : vecCurr-1;
508
                                    break;
509
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
510
                                    break;
511
                    default       : vecNext = 0;
512
                    }
513

    
514
                  next = vv.elementAt(vecNext);
515
                  tmp2 = vc.elementAt(vecNext);
516

    
517
                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y || tmp2.cached[2]!=next.z ) recomputeCache();
518
                  }
519

    
520
                if( mSegment!= segment && vn!=null ) vn.elementAt(vecCurr).computeNoise();
521

    
522
                mSegment = segment;
523

    
524
                time = time-vecCurr;
525
            
526
                tmp1 = vc.elementAt(vecCurr);
527

    
528
                if( vn!=null )
529
                  {
530
                  time = noise(time,vecCurr);
531
              
532
                  computeOrthonormalBaseMore(time,tmp1);
533
                 
534
                  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]);
535
                  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]);
536
                  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]);
537
                  }
538
                else
539
                  {
540
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
541
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
542
                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2];
543
                  }
544
               
545
                break;
546
                }
547
       }
548
     }  
549

    
550
  }
551
///////////////////////////////////////////////////////////////////////////////////////////////////
552
//
(9-9/17)