Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedNode.java @ 9361b337

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

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

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

    
501
///////////////////////////////////////////////////////////////////////////////////////////////////
502
  }
503

    
(4-4/28)