Project

General

Profile

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

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

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(3));
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(3));
250
                vc.add(new VectorCache(3));
251
                vc.add(new VectorCache(3));
252
                break;
253
        default:vc.add(new VectorCache(3));
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(3));
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(3));
282
                vc.add(new VectorCache(3));
283
                vc.add(new VectorCache(3));
284
                break;
285
        default:vc.add(location,new VectorCache(3));
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(mDimension));
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
 * Writes the results of interpolation between the Points at time 'time' to the passed float buffer.
424
 * <p>
425
 * Since this is a 3-dimensional Dynamic, the resulting interpolated Static3D gets written
426
 * to three locations in the buffer: buffer[offset], buffer[offset+1] and buffer[offset+2]. 
427
 * 
428
 * @param buffer Float buffer we will write the resulting Static3D to.
429
 * @param offset Offset in the buffer where to write the result.
430
 * @param time Time of interpolation. Time=0.0 would return the first Point, Time=0.5 - the last,
431
 *             time=1.0 - the first again, and time 0.1 would be 1/5 of the way between the first and the last Points.
432
 */    
433
  synchronized void interpolate(float[] buffer, int offset, float time)
434
    {  
435
    switch(numPoints)
436
      {
437
      case 0: buffer[offset  ] = 0.0f;
438
              buffer[offset+1] = 0.0f;
439
              buffer[offset+2] = 0.0f;
440
              break;
441
      case 1: curr = vv.elementAt(0);
442
              buffer[offset  ] = curr.x;
443
              buffer[offset+1] = curr.y;
444
              buffer[offset+2] = curr.z;
445
              break;
446
      case 2: curr = vv.elementAt(0);
447
              next = vv.elementAt(1);
448
             
449
              if( mMode==MODE_LOOP || mMode==MODE_PATH ) time = (time>0.5f ? 2-2*time : 2*time);
450
             
451
              if( vn!=null )
452
                {
453
                time = noise(time,0);
454
            
455
                buffer[offset  ] = (next.x-curr.x)*time + curr.x + (baseV[1][0]*mFactor[0] + baseV[2][0]*mFactor[1]);
456
                buffer[offset+1] = (next.y-curr.y)*time + curr.y + (baseV[1][1]*mFactor[0] + baseV[2][1]*mFactor[1]);
457
                buffer[offset+2] = (next.z-curr.z)*time + curr.z + (baseV[1][2]*mFactor[0] + baseV[2][2]*mFactor[1]);
458
                }
459
              else
460
                {
461
                buffer[offset  ] = (next.x-curr.x)*time + curr.x;
462
                buffer[offset+1] = (next.y-curr.y)*time + curr.y;
463
                buffer[offset+2] = (next.z-curr.z)*time + curr.z;
464
                }
465
             
466
              break;
467
      default:float t = time;
468
        
469
              switch(mMode)
470
                {
471
                case MODE_LOOP: time = time*numPoints;
472
                                break;
473
                case MODE_PATH: time = (time<=0.5f) ? 2*time*(numPoints-1) : 2*(1-time)*(numPoints-1);
474
                                break;
475
                case MODE_JUMP: time = time*(numPoints-1);
476
                                break;
477
                }
478
           
479
              int vecCurr = (int)time;
480
              time = time-vecCurr;
481
      
482
              if( vecCurr>=0 && vecCurr<numPoints )
483
                {
484
                if( cacheDirty ) recomputeCache();    // recompute cache if we have added or remove vectors since last computation
485
                else if( mVecCurr!= vecCurr )         // ...or if we have just passed a vector and the vector we are currently flying to has changed
486
                  {
487
                  int vecNext;   
488
                  mVecCurr = vecCurr;
489
                       
490
                  switch(mMode)
491
                    {
492
                    case MODE_LOOP: vecNext = vecCurr==numPoints-1 ? 0:vecCurr+1; 
493
                                    break;
494
                    case MODE_PATH: if( t<0.5f ) vecNext = vecCurr==numPoints-1 ? numPoints-2: vecCurr+1;  
495
                                    else         vecNext = vecCurr==0 ? 1 : vecCurr-1;  
496
                                    break;
497
                    case MODE_JUMP: vecNext = vecCurr==numPoints-1 ? 1:vecCurr+1;
498
                                    break;
499
                    default       : vecNext = 0;                
500
                    }
501
              
502
                  next = vv.elementAt(vecNext);
503
                  tmp2 = vc.elementAt(vecNext);
504
              
505
                  if( tmp2.cached[0]!=next.x || tmp2.cached[1]!=next.y || tmp2.cached[2]!=next.z ) recomputeCache();
506
                  }
507
            
508
                tmp1 = vc.elementAt(vecCurr);
509
               
510
                if( vn!=null )
511
                  {
512
                  time = noise(time,vecCurr);
513
              
514
                  computeOrthonormalBaseMore(time,tmp1);
515
                 
516
                  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]);
517
                  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]);
518
                  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]);
519
                  }
520
                else
521
                  {
522
                  buffer[offset  ]= ((tmp1.a[0]*time+tmp1.b[0])*time+tmp1.c[0])*time+tmp1.d[0];
523
                  buffer[offset+1]= ((tmp1.a[1]*time+tmp1.b[1])*time+tmp1.c[1])*time+tmp1.d[1];
524
                  buffer[offset+2]= ((tmp1.a[2]*time+tmp1.b[2])*time+tmp1.c[2])*time+tmp1.d[2];
525
                  }
526
               
527
                break;
528
                }
529
       }
530
     }  
531

    
532
  }
533
///////////////////////////////////////////////////////////////////////////////////////////////////
534
//
(9-9/17)