Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedObjectTree.java @ 46e25345

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// Distorted is free software: you can redistribute it and/or modify                             //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Distorted is distributed in the hope that it will be useful,                                  //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.library;
21

    
22
import java.util.ArrayList;
23
import java.util.HashMap;
24

    
25
import android.opengl.GLES20;
26

    
27
///////////////////////////////////////////////////////////////////////////////////////////////////
28
/**
29
 * Class which represents a Node in a Tree of DistortedObjects.
30
 *  
31
 * Having organized a set of DistortedObjects into a Tree, we can then render any Node to any Framebuffer.
32
 * That recursively renders the Object held in the Node and all its children, along with whatever effects
33
 * each one of them has. 
34
 */
35
public class DistortedObjectTree
36
  {
37
  private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
38
  private static long mNextNodeID =0;
39

    
40
  private GridObject mGrid;
41
  private DistortedEffectQueues mQueues;
42
  private DistortedTexture mTexture;
43
  private NodeData mData;
44

    
45
  private DistortedObjectTree mParent;
46
  private ArrayList<DistortedObjectTree> mChildren;
47
  private int[] mNumChildren;  // ==mChildren.length(), but we only create mChildren if the first one gets added
48

    
49
  private class NodeData
50
    {
51
    long ID;
52
    int numPointingNodes;
53
    DistortedFramebuffer mDF;
54
    boolean mRendered;
55

    
56
    NodeData(long id)
57
      {
58
      ID              = id;
59
      numPointingNodes= 1;
60
      mDF             = null;
61
      mRendered       = false;
62
      }
63
    }
64
 
65
///////////////////////////////////////////////////////////////////////////////////////////////////
66

    
67
  static void release()
68
    {
69
    mNextNodeID = 0;
70
    mMapNodeID.clear();
71
    }
72

    
73
///////////////////////////////////////////////////////////////////////////////////////////////////
74

    
75
  private void markRecursive()
76
    {
77
    mData.mRendered = false;
78
   
79
    synchronized(this)
80
      {
81
      for(int i=0; i<mNumChildren[0]; i++) mChildren.get(i).markRecursive();
82
      }
83
    }
84
  
85
///////////////////////////////////////////////////////////////////////////////////////////////////
86
  
87
  private void drawRecursive(long currTime, DistortedFramebuffer df)
88
    {
89
    if( mNumChildren[0]<=0 )
90
      {
91
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture.mTextureDataH[0]);
92
      }
93
    else
94
      {
95
      if( !mData.mRendered )
96
        {
97
        mData.mRendered = true;
98
        mData.mDF.setAsOutput();
99

    
100
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
101
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
102
      
103
        if( mTexture.mBitmapSet[0] )
104
          {
105
          GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture.mTextureDataH[0]);
106
          mQueues.drawNoEffectsPriv(mTexture, mGrid, mData.mDF);
107
          }
108
      
109
        synchronized(this)
110
          {
111
          for(int i=0; i<mNumChildren[0]; i++)
112
            {
113
            mChildren.get(i).drawRecursive(currTime, mData.mDF);
114
            }
115
          }
116
        }
117

    
118
      df.setAsOutput();
119
      mData.mDF.setAsInput();
120
      }
121
    
122
    mQueues.drawPriv(currTime, mTexture, mGrid, df);
123
    }
124
  
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126
// tree isomorphism
127
  
128
  private void RecomputeNodeID(ArrayList<Long> prev)
129
    {
130
    ArrayList<Long> curr = generateIDList();
131
     
132
    if( mParent==null )
133
      {
134
      adjustNodeData(prev,curr);
135
      }
136
    else
137
      {
138
      ArrayList<Long> parentPrev = mParent.generateIDList();
139
      adjustNodeData(prev,curr);
140
      mParent.RecomputeNodeID(parentPrev);
141
      }
142
    }
143

    
144
///////////////////////////////////////////////////////////////////////////////////////////////////
145

    
146
  private ArrayList<Long> generateIDList()
147
    {
148
    ArrayList<Long> ret = new ArrayList<>();
149
     
150
    ret.add( mNumChildren[0]>0 ? mTexture.getBitmapID() : mTexture.getID() );
151
    DistortedObjectTree node;
152
   
153
    for(int i=0; i<mNumChildren[0]; i++)
154
      {
155
      node = mChildren.get(i);
156
      ret.add(node.mData.ID);
157
      }
158
   
159
    return ret;
160
    }
161

    
162
///////////////////////////////////////////////////////////////////////////////////////////////////
163

    
164
  private void adjustNodeData(ArrayList<Long> oldList, ArrayList<Long> newList)
165
    {
166
    boolean otherNodesPoint = (mData.numPointingNodes>1);
167

    
168
    if( otherNodesPoint ) mData.numPointingNodes--;
169
    else                  mMapNodeID.remove(oldList);
170
   
171
    NodeData newData = mMapNodeID.get(newList);
172
    
173
    if( newData==null )
174
      {
175
      if( otherNodesPoint )  mData = new NodeData(++mNextNodeID);
176
      else                   mData.ID = ++mNextNodeID;  // numPointingNodes must be 1 already
177

    
178
      if( newList.size()>1 )
179
        {
180
        if( mData.mDF==null )
181
          mData.mDF = new DistortedFramebuffer(mTexture.getWidth(), mTexture.getHeight());
182
        }
183
      else
184
        {
185
        if( mData.mDF!=null )
186
          {
187
          mData.mDF.markForDeletion();
188
          mData.mDF = null;
189
          }
190
        else
191
          {
192
          android.util.Log.e("DistortedObjectTree", "adjustNodeData: impossible situation??");
193
          }
194
        }
195

    
196
      mMapNodeID.put(newList, mData);
197
      }
198
    else
199
      {
200
      newData.numPointingNodes++;
201
      mData = newData;
202
      }
203
    }
204

    
205
///////////////////////////////////////////////////////////////////////////////////////////////////  
206
// this will be called on startup and every time OpenGL context has been lost
207
// also call this from the constructor if the OpenGL context has been created already.
208
    
209
  static void reset()
210
    {
211
    NodeData tmp;   
212
     
213
    for(ArrayList<Long> key: mMapNodeID.keySet())
214
      {
215
      tmp = mMapNodeID.get(key);
216
          
217
      if( tmp.mDF != null )
218
        {
219
    	  tmp.mDF.reset();
220
        tmp.mRendered  = false;
221
        }
222
      }
223
    }
224

    
225
///////////////////////////////////////////////////////////////////////////////////////////////////
226
// Debug - print all the Node IDs
227
/*
228
  void debug(int depth)
229
    {
230
    String tmp="";
231
    int i;
232

    
233
    for(i=0; i<depth; i++) tmp +="   ";
234
    tmp += (""+mData.ID);
235

    
236
    android.util.Log.e("node", tmp);
237

    
238
    for(i=0; i<mNumChildren[0]; i++)
239
      mChildren.get(i).debug(depth+1);
240
    }
241

    
242
///////////////////////////////////////////////////////////////////////////////////////////////////
243
// Debug - print contents of the HashMap
244

    
245
  static void debugMap()
246
    {
247
    NodeData tmp;
248

    
249
    for(ArrayList<Long> key: mMapNodeID.keySet())
250
      {
251
      tmp = mMapNodeID.get(key);
252

    
253
      android.util.Log.e("NODE", "key="+key+" NodeID: "+tmp.ID);
254
      }
255
    }
256
*/
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258
// PUBLIC API
259
///////////////////////////////////////////////////////////////////////////////////////////////////
260
/**
261
 * Constructs new Node of the Tree.
262
 *     
263
 * @param texture DistortedTexture to put into the new Node.
264
 * @param queues  DistortedEffectQueues to put into the new Node.
265
 * @param grid GridObject to put into the new Node.
266
 */
267
  public DistortedObjectTree(DistortedTexture texture, DistortedEffectQueues queues, GridObject grid)
268
    {
269
    mTexture= texture;
270
    mQueues = queues;
271
    mGrid   = grid;
272
    mParent = null;
273
    mChildren = null;
274
    mNumChildren = new int[1];
275
    mNumChildren[0] = 0;
276
   
277
    ArrayList<Long> list = new ArrayList<>();
278
    list.add(queues.getID());   // TODO: this should depend on both texture and queues! Maybe even on grid as well
279
      
280
    mData = mMapNodeID.get(list);
281
   
282
    if( mData!=null )
283
      {
284
      mData.numPointingNodes++;
285
      }
286
    else
287
      {
288
      mData = new NodeData(++mNextNodeID);   
289
      mMapNodeID.put(list, mData);  
290
      }
291
    }
292

    
293
///////////////////////////////////////////////////////////////////////////////////////////////////  
294
/**
295
 * Copy-constructs new Node of the Tree from another Node.
296
 *     
297
 * @param node The DistortedObjectTree to copy data from.
298
 * @param flags bit field composed of a subset of the following:
299
 *        {@link Distorted#CLONE_BITMAP},  {@link Distorted#CLONE_MATRIX}, {@link Distorted#CLONE_VERTEX},
300
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
301
 *        For example flags = CLONE_BITMAP | CLONE_CHILDREN.
302
 */
303
  public DistortedObjectTree(DistortedObjectTree node, int flags)
304
    {
305
    mParent = null;
306
    mTexture= new DistortedTexture(node.mTexture,flags);
307
    mQueues = new DistortedEffectQueues(node.mQueues, flags);
308
    mGrid   = node.mGrid;
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(DistortedObjectTree 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 texture DistortedTexture to initialize our child Node with.
361
 * @param queues DistortedEffectQueues to initialize our child Node with.
362
 * @param grid GridObject 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 DistortedObjectTree attach(DistortedTexture texture, DistortedEffectQueues queues, GridObject grid)
366
    {
367
    ArrayList<Long> prev = generateIDList(); 
368
      
369
    if( mChildren==null ) mChildren = new ArrayList<>(2);
370
    DistortedObjectTree node = new DistortedObjectTree(texture,queues,grid);
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(DistortedObjectTree 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 queues DistortedEffectQueues to remove.
412
 * @return <code>true</code> if the child was successfully removed.
413
 */
414
  public synchronized boolean detach(DistortedEffectQueues queues)
415
    {
416
    long id = queues.getID();
417
    DistortedObjectTree node;
418
   
419
    for(int i=0; i<mNumChildren[0]; i++)
420
      {
421
      node = mChildren.get(i);
422
     
423
      if( node.mQueues.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.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
470
    markRecursive();
471
    drawRecursive(currTime,Distorted.mFramebuffer);
472
    }
473

    
474
///////////////////////////////////////////////////////////////////////////////////////////////////
475
/**
476
 * Draws the Node, and all its children, to the Framebuffer passed.
477
 *
478
 * @param currTime Current time, in milliseconds.
479
 * @param df       Framebuffer to render this to.
480
 */
481
  public void draw(long currTime, DistortedFramebuffer df)
482
    {
483
    df.setAsOutput();
484
    markRecursive();
485
    drawRecursive(currTime,df);
486
    }
487

    
488
///////////////////////////////////////////////////////////////////////////////////////////////////
489
/**
490
 * Returns the DistortedEffectQueues object that's in the Node.
491
 * 
492
 * @return The DistortedEffectQueues contained in the Node.
493
 */
494
  public DistortedEffectQueues getQueues()
495
    {
496
    return mQueues;
497
    }
498

    
499
///////////////////////////////////////////////////////////////////////////////////////////////////
500
/**
501
 * Returns the DistortedTexture object that's in the Node.
502
 *
503
 * @return The DistortedTexture contained in the Node.
504
 */
505
  public DistortedTexture getTexture()
506
    {
507
    return mTexture;
508
    }
509

    
510
///////////////////////////////////////////////////////////////////////////////////////////////////
511
  }
512

    
(4-4/15)