Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedNode.java @ 6a06a912

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

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

    
498
///////////////////////////////////////////////////////////////////////////////////////////////////
499
  }
500

    
(4-4/28)