Project

General

Profile

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

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

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
  static void release()
112
    {
113
    mNextNodeID = 0;
114
    mMapNodeID.clear();
115
    }
116

    
117
///////////////////////////////////////////////////////////////////////////////////////////////////
118

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

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  private ArrayList<Long> generateIDList()
199
    {
200
    ArrayList<Long> ret = new ArrayList<>();
201
     
202
    ret.add( mNumChildren[0]>0 ? mObject.getBitmapID() : mObject.getID() );
203
    DistortedNode node;
204
   
205
    for(int i=0; i<mNumChildren[0]; i++)
206
      {
207
      node = mChildren.get(i);
208
      ret.add(node.mData.ID);
209
      }
210
   
211
    return ret;
212
    }
213

    
214
///////////////////////////////////////////////////////////////////////////////////////////////////
215

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

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

    
297
///////////////////////////////////////////////////////////////////////////////////////////////////  
298
/**
299
 * Copy-constructs new Node of the Tree from another Node.
300
 *     
301
 * @param node The DistortedNode to copy data from.
302
 * @param flags bit field composed of a subset of the following:
303
 *        {@link Distorted#CLONE_BITMAP},  {@link Distorted#CLONE_MATRIX}, {@link Distorted#CLONE_VERTEX},
304
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
305
 *        For example flags = CLONE_BITMAP | CLONE_CHILDREN.
306
 */
307
  public DistortedNode(DistortedNode node, int flags)
308
    {
309
    mParent = null;
310
    mObject = node.mObject.deepCopy(flags);
311

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

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

    
505
///////////////////////////////////////////////////////////////////////////////////////////////////
506
  }
507

    
(4-4/28)