Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedNode.java @ da7ce0d8

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

    
483
  public void debug(int depth)
484
    {
485
    String tmp="";  
486
    int i;
487
   
488
    for(i=0; i<depth; i++) tmp +="   ";
489
    tmp += (""+mData.ID);
490
   
491
    android.util.Log.e("node", tmp);
492
   
493
    for(i=0; i<mNumChildren[0]; i++)
494
      mChildren.get(i).debug(depth+1);
495
    }
496

    
497
///////////////////////////////////////////////////////////////////////////////////////////////////
498
  }
499

    
(4-4/28)