Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedNode.java @ 19bed2df

1
package org.distorted.library;
2

    
3
import java.util.ArrayList;
4
import java.util.HashMap;
5

    
6
import android.opengl.GLES20;
7

    
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9
/**
10
 * Class which represents a Node in a Tree of DistortedBitmaps.
11
 *  
12
 * Having organized a set of DistortedBitmaps into a Tree, we can then render any Node to any Framebuffer.
13
 * That recursively renders the Bitmap held in the Node and all its children, along with whatever effects
14
 * each one of them has. 
15
 */
16
public class DistortedNode 
17
  {
18
  private static final int TEXTURE_FAILED_TO_CREATE = -1;   
19
  private static final int TEXTURE_NOT_CREATED_YET  = -2;   
20
   
21
  private DistortedObject mObject;
22
  private NodeData mData;
23
  
24
  private DistortedNode mParent;
25
  private ArrayList<DistortedNode> mChildren;
26
  private int[] mNumChildren;  // ==mChildren.length(), but we only create mChildren if the first one gets added
27
  
28
  private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
29
  private static long mNextNodeID =0;
30

    
31
  //////////////////////////////////////////////////////////////////
32
  
33
  private class NodeData
34
    {
35
    long ID;  
36
    int numPointingNodes;
37
    int mFramebufferID;
38
    int mTextureID;
39
    DistortedProjection mProjection;
40
    boolean mRendered;
41
    
42
  //////////////////////////////////////////////////////////////////
43
 
44
   public NodeData(long id)
45
     {
46
     ID = id;
47
     numPointingNodes= 1;
48
     mFramebufferID = 0;
49
     mTextureID = TEXTURE_NOT_CREATED_YET;
50
     mProjection = null;
51
     mRendered = false;
52
     }
53
   
54
  //////////////////////////////////////////////////////////////////
55

    
56
    boolean createFBO()
57
      {  
58
      int[] textureIds = new int[1];
59
      GLES20.glGenTextures(1, textureIds, 0);
60
      mTextureID = textureIds[0];
61
      int[] mFBORenderToTexture = new int[1];
62
      
63
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
64
      GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
65
      GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
66
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
67
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
68
      GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mProjection.width, mProjection.height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
69

    
70
      GLES20.glGenFramebuffers(1, mFBORenderToTexture, 0);
71
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFBORenderToTexture[0]);
72
      GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mTextureID, 0);
73
      int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
74
    
75
      if(status != GLES20.GL_FRAMEBUFFER_COMPLETE)
76
        {
77
        android.util.Log.e("Node", "failed to create framebuffer, error="+status);  
78
        
79
        GLES20.glDeleteTextures(1, textureIds, 0);
80
        GLES20.glDeleteFramebuffers(1, mFBORenderToTexture, 0);
81
        mFramebufferID = 0;
82
        mTextureID = TEXTURE_FAILED_TO_CREATE;
83
        return false;
84
        }
85
      
86
      mFramebufferID = mFBORenderToTexture[0];
87
      
88
      return true;
89
      }
90
   
91
  //////////////////////////////////////////////////////////////////
92

    
93
    void deleteFBO()
94
      {
95
      int[] textureIds = new int[1];
96
      int[] mFBORenderToTexture = new int[1];
97
     
98
      textureIds[0] = mTextureID;
99
      mFBORenderToTexture[0] = mFramebufferID;
100
      
101
      GLES20.glDeleteTextures(1, textureIds, 0);
102
      GLES20.glDeleteFramebuffers(1, mFBORenderToTexture, 0);
103
      
104
      mFramebufferID = 0;
105
      mTextureID = TEXTURE_NOT_CREATED_YET;
106
      }
107
   }
108
 
109
///////////////////////////////////////////////////////////////////////////////////////////////////
110

    
111
  private void markRecursive()
112
    {
113
    mData.mRendered = false;
114
   
115
    synchronized(this)
116
      {
117
      for(int i=0; i<mNumChildren[0]; i++) mChildren.get(i).markRecursive();
118
      }
119
    }
120
  
121
///////////////////////////////////////////////////////////////////////////////////////////////////
122
  
123
  private void drawRecursive(long currTime, DistortedProjection dp, int mFBO)
124
    {
125
    if( mNumChildren[0]<=0 )
126
      {
127
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mObject.mTextureDataH[0]);
128
      
129
      if( mData.mProjection!=null )
130
        {
131
        mData.deleteFBO();
132
        mData.mProjection = null;
133
        }
134
      }
135
    else
136
      {
137
      if( mData.mRendered==false )
138
        {
139
        mData.mRendered = true;
140
       
141
        if( mData.mTextureID==TEXTURE_NOT_CREATED_YET ) mData.createFBO();
142
       
143
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mData.mFramebufferID);
144
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
145
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
146
      
147
        if( mObject.mBitmapSet[0] )
148
          {
149
          GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mObject.mTextureDataH[0]);
150
          mObject.drawNoEffectsPriv(mData.mProjection);
151
          }
152
      
153
        synchronized(this)
154
          {
155
          for(int i=0; i<mNumChildren[0]; i++)
156
            {
157
            mChildren.get(i).drawRecursive(currTime, mData.mProjection, mData.mFramebufferID);
158
            }
159
          }
160
        }
161
      
162
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFBO);
163
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mData.mTextureID);   // this is safe because we must have called createFBO() above before.     
164
      }
165
    
166
    mObject.drawPriv(currTime, dp);
167
    }
168
  
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170
// tree isomorphism
171
  
172
  private void RecomputeNodeID(ArrayList<Long> prev)
173
    {
174
    ArrayList<Long> curr = generateIDList();
175
     
176
    if( mParent==null )
177
      {
178
      adjustNodeData(prev,curr);
179
      }
180
    else
181
      {
182
      ArrayList<Long> parentPrev = mParent.generateIDList();
183
      adjustNodeData(prev,curr);
184
      mParent.RecomputeNodeID(parentPrev);
185
      }
186
    }
187

    
188
///////////////////////////////////////////////////////////////////////////////////////////////////
189

    
190
  private ArrayList<Long> generateIDList()
191
    {
192
    ArrayList<Long> ret = new ArrayList<>();
193
     
194
    ret.add( mNumChildren[0]>0 ? mObject.getBitmapID() : mObject.getID() );
195
    DistortedNode node;
196
   
197
    for(int i=0; i<mNumChildren[0]; i++)
198
      {
199
      node = mChildren.get(i);
200
      ret.add(node.mData.ID);
201
      }
202
   
203
    return ret;
204
    }
205

    
206
///////////////////////////////////////////////////////////////////////////////////////////////////
207

    
208
  private void adjustNodeData(ArrayList<Long> oldList, ArrayList<Long> newList)
209
    {
210
    if( mData.numPointingNodes>1 ) mData.numPointingNodes--;
211
    else                           mMapNodeID.remove(oldList);  
212
   
213
    NodeData newData = mMapNodeID.get(newList);
214
    
215
    if( newData==null )
216
      {
217
      mData.ID = ++mNextNodeID;  
218
      mData.numPointingNodes = 1;
219
     
220
      if( newList.size()>1 && mData.mProjection==null )
221
        {     
222
        mData.mProjection = new DistortedProjection(true);
223
        mData.mProjection.onSurfaceChanged(mObject.getWidth(), mObject.getHeight());
224
        mData.mFramebufferID = 0;
225
        mData.mTextureID = TEXTURE_NOT_CREATED_YET;
226
        }
227
       
228
      mMapNodeID.put(newList, mData);
229
      }
230
    else
231
      {  
232
      newData.numPointingNodes++;
233
      mData = newData;
234
      }
235
    }
236

    
237
///////////////////////////////////////////////////////////////////////////////////////////////////  
238
// this will be called on startup and every time OpenGL context has been lost
239
// also call this from the constructor if the OpenGL context has been created already.
240
    
241
  static void reset()
242
    {
243
    NodeData tmp;   
244
     
245
    for(ArrayList<Long> key: mMapNodeID.keySet())
246
      {
247
      tmp = mMapNodeID.get(key);
248
          
249
      if( tmp.mProjection!=null )
250
        {
251
    	  tmp.mTextureID = TEXTURE_NOT_CREATED_YET;
252
        tmp.mRendered  = false;
253
        }
254
      }
255
    }
256
  
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258
// PUBLIC API
259
///////////////////////////////////////////////////////////////////////////////////////////////////
260
/**
261
 * Constructs new Node of the Tree.
262
 *     
263
 * @param obj DistortedObject to put into the new Node.
264
 */
265
  public DistortedNode(DistortedObject obj)
266
    {
267
    mObject = obj;
268
    mParent = null;
269
    mChildren = null;
270
    mNumChildren = new int[1];
271
    mNumChildren[0] = 0;
272
   
273
    ArrayList<Long> list = new ArrayList<>();
274
    list.add(obj.getID());
275
      
276
    mData = mMapNodeID.get(list);
277
   
278
    if( mData!=null )
279
      {
280
      mData.numPointingNodes++;
281
      }
282
    else
283
      {
284
      mData = new NodeData(++mNextNodeID);   
285
      mMapNodeID.put(list, mData);  
286
      }
287
    }
288

    
289
///////////////////////////////////////////////////////////////////////////////////////////////////  
290
/**
291
 * Copy-constructs new Node of the Tree from another Node.
292
 *     
293
 * @param node The DistortedNode to copy data from.
294
 * @param flags bit field composed of a subset of the following:
295
 *        {@link Distorted#CLONE_BITMAP},  {@link Distorted#CLONE_MATRIX}, {@link Distorted#CLONE_VERTEX},
296
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
297
 *        For example flags = CLONE_BITMAP | CLONE_CHILDREN.
298
 */
299
  public DistortedNode(DistortedNode node, int flags)
300
    {
301
    mParent = null;
302

    
303
    if( node.mObject instanceof DistortedBitmap)
304
      mObject = new DistortedBitmap( (DistortedBitmap)node.mObject, flags);
305
    else if( node.mObject instanceof DistortedCubes)
306
      mObject = new DistortedCubes( (DistortedCubes)node.mObject, flags);
307
    else
308
      throw new RuntimeException("unsupported type of object!");
309

    
310
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
311
      {
312
      mChildren = node.mChildren;
313
      mNumChildren = node.mNumChildren;
314
      }
315
    else
316
      {
317
      mChildren = null;
318
      mNumChildren = new int[1];
319
      mNumChildren[0] = 0;
320
      }
321
   
322
    ArrayList<Long> list = generateIDList();
323
   
324
    mData = mMapNodeID.get(list);
325
   
326
    if( mData!=null )
327
      {
328
      mData.numPointingNodes++;
329
      }
330
    else
331
      {
332
      mData = new NodeData(++mNextNodeID);   
333
      mMapNodeID.put(list, mData);
334
      }
335
    }
336
  
337
///////////////////////////////////////////////////////////////////////////////////////////////////
338
/**
339
 * Adds a new child to the last position in the list of our Node's children.
340
 * 
341
 * @param node The new Node to add.
342
 */
343
  public synchronized void attach(DistortedNode node)
344
    {
345
    ArrayList<Long> prev = generateIDList(); 
346
   
347
    if( mChildren==null ) mChildren = new ArrayList<>(2);
348
     
349
    node.mParent = this;
350
    mChildren.add(node);
351
    mNumChildren[0]++;
352
     
353
    RecomputeNodeID(prev);
354
    }
355
   
356
///////////////////////////////////////////////////////////////////////////////////////////////////
357
/**
358
 * Adds a new child to the last position in the list of our Node's children.
359
 * 
360
 * @param bmp DistortedBitmap to initialize our child Node with.
361
 * @return the newly constructed child Node, or null if we couldn't allocate resources.
362
 */
363
  public synchronized DistortedNode attach(DistortedBitmap bmp)
364
    {
365
    ArrayList<Long> prev = generateIDList(); 
366
      
367
    if( mChildren==null ) mChildren = new ArrayList<>(2);
368
    DistortedNode node = new DistortedNode(bmp);
369
    node.mParent = this;
370
    mChildren.add(node);
371
    mNumChildren[0]++;
372
   
373
    RecomputeNodeID(prev);
374
   
375
    return node;
376
    }
377
  
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379
/**
380
 * Removes the first occurrence of a specified child from the list of children of our Node.
381
 * 
382
 * @param node The Node to remove.
383
 * @return <code>true</code> if the child was successfully removed.
384
 */
385
  public synchronized boolean detach(DistortedNode node)
386
    {
387
    if( mNumChildren[0]>0 )
388
      {
389
      ArrayList<Long> prev = generateIDList();  
390
         
391
      if( mChildren.remove(node) )
392
        {
393
        node.mParent = null;  
394
        mNumChildren[0]--;
395
     
396
        RecomputeNodeID(prev);
397
     
398
        return true;
399
        }
400
      }
401
   
402
    return false;
403
    }
404
  
405
///////////////////////////////////////////////////////////////////////////////////////////////////
406
/**
407
 * Removes the first occurrence of a specified child from the list of children of our Node.
408
 * 
409
 * @param obj DistortedObject to remove.
410
 * @return <code>true</code> if the child was successfully removed.
411
 */
412
  public synchronized boolean detach(DistortedObject obj)
413
    {
414
    long id = obj.getID();
415
    DistortedNode node;
416
   
417
    for(int i=0; i<mNumChildren[0]; i++)
418
      {
419
      node = mChildren.get(i);
420
     
421
      if( node.mObject.getID()==id )
422
        {
423
        ArrayList<Long> prev = generateIDList();   
424
     
425
        node.mParent = null;  
426
        mChildren.remove(i);
427
        mNumChildren[0]--;
428
      
429
        RecomputeNodeID(prev);
430
      
431
        return true;
432
        }
433
      }
434
   
435
    return false;
436
    }
437
    
438
///////////////////////////////////////////////////////////////////////////////////////////////////
439
/**
440
 * Removes all children Nodes.
441
 */
442
  public synchronized void detachAll()
443
    {
444
    for(int i=0; i<mNumChildren[0]; i++)
445
      {
446
      mChildren.get(i).mParent = null;
447
      }
448
   
449
    if( mNumChildren[0]>0 )
450
      {
451
      ArrayList<Long> prev = generateIDList();  
452
      
453
      mNumChildren[0] = 0;
454
      mChildren.clear();
455
      RecomputeNodeID(prev);
456
      }
457
    }
458
  
459
///////////////////////////////////////////////////////////////////////////////////////////////////
460
/**
461
 * Draws the Node, and all its children, to the default framebuffer 0 (i.e. the screen).
462
 *   
463
 * @param currTime Current time, in milliseconds.
464
 */
465
  public void draw(long currTime)
466
    {  
467
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
468
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
469
    GLES20.glUniform1i(Distorted.mTextureUniformH, 0);  
470
    
471
    markRecursive();
472
    drawRecursive(currTime,Distorted.mProjection,0);
473
    }
474
 
475
///////////////////////////////////////////////////////////////////////////////////////////////////
476
/**
477
 * Returns the DistortedObject object that's in the Node.
478
 * 
479
 * @return The DistortedObject contained in the Node.
480
 */
481
  public DistortedObject getObject()
482
    {
483
    return mObject;
484
    }
485
  
486
///////////////////////////////////////////////////////////////////////////////////////////////////
487
// Debug - print all the Node IDs
488

    
489
  public void debug(int depth)
490
    {
491
    String tmp="";  
492
    int i;
493
   
494
    for(i=0; i<depth; i++) tmp +="   ";
495
    tmp += (""+mData.ID);
496
   
497
    android.util.Log.e("node", tmp);
498
   
499
    for(i=0; i<mNumChildren[0]; i++)
500
      mChildren.get(i).debug(depth+1);
501
    }
502

    
503
///////////////////////////////////////////////////////////////////////////////////////////////////
504
  }
505

    
(4-4/28)