Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedTree.java @ 8fa96e69

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 (Texture,Mesh,Effects) objects.
30
 *  
31
 * Having organized such sets into a Tree, we can then render any Node to any Framebuffer.
32
 * That recursively renders the set held in the Node and all its children.
33
 */
34
public class DistortedTree
35
  {
36
  private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
37
  private static long mNextNodeID =0;
38

    
39
  private MeshObject mMesh;
40
  private DistortedEffects mEffects;
41
  private DistortedTexture mTexture;
42
  private NodeData mData;
43

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

    
48
  private class NodeData
49
    {
50
    long ID;
51
    int numPointingNodes;
52
    int numRendered;
53
    DistortedFramebuffer mFBO;
54

    
55
    NodeData(long id)
56
      {
57
      ID              = id;
58
      numPointingNodes= 1;
59
      numRendered     = 0;
60
      mFBO            = null;
61
      }
62
    }
63
 
64
///////////////////////////////////////////////////////////////////////////////////////////////////
65

    
66
  static synchronized void onDestroy()
67
    {
68
    mNextNodeID = 0;
69
    mMapNodeID.clear();
70
    }
71

    
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73
// tree isomorphism
74
  
75
  private void RecomputeNodeID(ArrayList<Long> prev)
76
    {
77
    ArrayList<Long> curr = generateIDList();
78
     
79
    if( mParent==null )
80
      {
81
      adjustNodeData(prev,curr);
82
      }
83
    else
84
      {
85
      ArrayList<Long> parentPrev = mParent.generateIDList();
86
      adjustNodeData(prev,curr);
87
      mParent.RecomputeNodeID(parentPrev);
88
      }
89
    }
90

    
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92

    
93
  private ArrayList<Long> generateIDList()
94
    {
95
    ArrayList<Long> ret = new ArrayList<>();
96
     
97
    ret.add( mTexture.getID() );
98
    DistortedTree node;
99
   
100
    for(int i=0; i<mNumChildren[0]; i++)
101
      {
102
      node = mChildren.get(i);
103
      ret.add(node.mData.ID);
104
      }
105
   
106
    return ret;
107
    }
108

    
109
///////////////////////////////////////////////////////////////////////////////////////////////////
110

    
111
  private void adjustNodeData(ArrayList<Long> oldList, ArrayList<Long> newList)
112
    {
113
    boolean otherNodesPoint = (mData.numPointingNodes>1);
114

    
115
    if( otherNodesPoint ) mData.numPointingNodes--;
116
    else                  mMapNodeID.remove(oldList);
117

    
118
    NodeData newData = mMapNodeID.get(newList);
119
    
120
    if( newData==null )
121
      {
122
      if( otherNodesPoint )  mData = new NodeData(++mNextNodeID);
123
      else                   mData.ID = ++mNextNodeID;  // numPointingNodes must be 1 already
124

    
125
      if( newList.size()>1 )
126
        {
127
        if( mData.mFBO ==null )
128
          mData.mFBO = new DistortedFramebuffer(mTexture.getWidth(), mTexture.getHeight());
129
        }
130
      else
131
        {
132
        if( mData.mFBO !=null )
133
          {
134
          mData.mFBO.markForDeletion();
135
          mData.mFBO = null;
136
          }
137
        else
138
          {
139
          android.util.Log.e("DistortedTree", "adjustNodeData: impossible situation??");
140
          }
141
        }
142

    
143
      mMapNodeID.put(newList, mData);
144
      }
145
    else
146
      {
147
      newData.numPointingNodes++;
148
      mData = newData;
149
      }
150
    }
151

    
152
///////////////////////////////////////////////////////////////////////////////////////////////////  
153
// this will be called on startup and every time OpenGL context has been lost
154

    
155
  static void reset()
156
    {
157
    NodeData tmp;   
158
     
159
    for(ArrayList<Long> key: mMapNodeID.keySet())
160
      {
161
      tmp = mMapNodeID.get(key);
162
          
163
      if( tmp.mFBO != null ) tmp.numRendered = 0;
164
      }
165
    }
166

    
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168
// Debug - print all the Node IDs
169
/*
170
  void debug(int depth)
171
    {
172
    String tmp="";
173
    int i;
174

    
175
    for(i=0; i<depth; i++) tmp +="   ";
176
    tmp += (mData.ID+" (nodes: "+mData.numPointingNodes+")");
177

    
178
    android.util.Log.e("NODE", tmp);
179

    
180
    for(i=0; i<mNumChildren[0]; i++)
181
      mChildren.get(i).debug(depth+1);
182
    }
183

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185
// Debug - print contents of the HashMap
186

    
187
  static void debugMap()
188
    {
189
    NodeData tmp;
190

    
191
    for(ArrayList<Long> key: mMapNodeID.keySet())
192
      {
193
      tmp = mMapNodeID.get(key);
194

    
195
      android.util.Log.e("NODE", "key="+key+" NodeID: "+tmp.ID);
196
      }
197
    }
198
*/
199
///////////////////////////////////////////////////////////////////////////////////////////////////
200

    
201
  void drawRecursive(long currTime, DistortedFramebuffer df)
202
    {
203
    mTexture.createTexture();
204

    
205
    if( mNumChildren[0]<=0 )
206
      {
207
      mTexture.setAsInput();
208
      }
209
    else
210
      {
211
      mData.mFBO.createFBO();
212

    
213
      if( mData.numRendered==0 )
214
        {
215
        mData.mFBO.setAsOutput();
216

    
217
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
218
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
219

    
220
        if( mTexture.setAsInput() )
221
          DistortedEffects.drawNoEffectsPriv(mTexture.mHalfX, mTexture.mHalfY, mMesh, mData.mFBO);
222

    
223
        synchronized(this)
224
          {
225
          for(int i=0; i<mNumChildren[0]; i++)
226
            {
227
            mChildren.get(i).drawRecursive(currTime, mData.mFBO);
228
            }
229
          }
230
        }
231

    
232
      mData.numRendered++;
233
      mData.numRendered %= mData.numPointingNodes;
234
      mData.mFBO.setAsInput();
235
      }
236

    
237
    mEffects.drawPriv(mTexture.mHalfX, mTexture.mHalfY, mMesh, df, currTime);
238
    }
239

    
240
///////////////////////////////////////////////////////////////////////////////////////////////////
241
// PUBLIC API
242
///////////////////////////////////////////////////////////////////////////////////////////////////
243
/**
244
 * Constructs new Node of the Tree.
245
 *     
246
 * @param texture DistortedTexture to put into the new Node.
247
 * @param effects DistortedEffects to put into the new Node.
248
 * @param mesh MeshObject to put into the new Node.
249
 */
250
  public DistortedTree(DistortedTexture texture, DistortedEffects effects, MeshObject mesh)
251
    {
252
    mTexture= texture;
253
    mEffects= effects;
254
    mMesh   = mesh;
255
    mParent = null;
256
    mChildren = null;
257
    mNumChildren = new int[1];
258
    mNumChildren[0] = 0;
259
   
260
    ArrayList<Long> list = new ArrayList<>();
261
    list.add(mTexture.getID());
262

    
263
    mData = mMapNodeID.get(list);
264
   
265
    if( mData!=null )
266
      {
267
      mData.numPointingNodes++;
268
      }
269
    else
270
      {
271
      mData = new NodeData(++mNextNodeID);   
272
      mMapNodeID.put(list, mData);
273
      }
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////  
277
/**
278
 * Copy-constructs new Node of the Tree from another Node.
279
 *     
280
 * @param node The DistortedTree to copy data from.
281
 * @param flags bit field composed of a subset of the following:
282
 *        {@link Distorted#CLONE_BITMAP},  {@link Distorted#CLONE_MATRIX}, {@link Distorted#CLONE_VERTEX},
283
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
284
 *        For example flags = CLONE_BITMAP | CLONE_CHILDREN.
285
 */
286
  public DistortedTree(DistortedTree node, int flags)
287
    {
288
    mParent = null;
289
    mEffects= new DistortedEffects(node.mEffects,flags);
290
    mMesh = node.mMesh;
291

    
292
    if( (flags & Distorted.CLONE_BITMAP) != 0 )
293
      {
294
      mTexture = node.mTexture;
295
      }
296
    else
297
      {
298
      mTexture = new DistortedTexture(node.mTexture.getWidth(), node.mTexture.getHeight());
299
      }
300
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
301
      {
302
      mChildren = node.mChildren;
303
      mNumChildren = node.mNumChildren;
304
      }
305
    else
306
      {
307
      mChildren = null;
308
      mNumChildren = new int[1];
309
      mNumChildren[0] = 0;
310
      }
311
   
312
    ArrayList<Long> list = generateIDList();
313
   
314
    mData = mMapNodeID.get(list);
315
   
316
    if( mData!=null )
317
      {
318
      mData.numPointingNodes++;
319
      }
320
    else
321
      {
322
      mData = new NodeData(++mNextNodeID);   
323
      mMapNodeID.put(list, mData);
324
      }
325
    }
326
  
327
///////////////////////////////////////////////////////////////////////////////////////////////////
328
/**
329
 * Adds a new child to the last position in the list of our Node's children.
330
 * 
331
 * @param node The new Node to add.
332
 */
333
  public synchronized void attach(DistortedTree node)
334
    {
335
    ArrayList<Long> prev = generateIDList(); 
336
   
337
    if( mChildren==null ) mChildren = new ArrayList<>(2);
338
     
339
    node.mParent = this;
340
    mChildren.add(node);
341
    mNumChildren[0]++;
342
     
343
    RecomputeNodeID(prev);
344
    }
345
   
346
///////////////////////////////////////////////////////////////////////////////////////////////////
347
/**
348
 * Adds a new child to the last position in the list of our Node's children.
349
 * 
350
 * @param texture DistortedTexture to initialize our child Node with.
351
 * @param effects DistortedEffects to initialize our child Node with.
352
 * @param mesh MeshObject to initialize our child Node with.
353
 * @return the newly constructed child Node, or null if we couldn't allocate resources.
354
 */
355
  public synchronized DistortedTree attach(DistortedTexture texture, DistortedEffects effects, MeshObject mesh)
356
    {
357
    ArrayList<Long> prev = generateIDList(); 
358
      
359
    if( mChildren==null ) mChildren = new ArrayList<>(2);
360
    DistortedTree node = new DistortedTree(texture,effects,mesh);
361
    node.mParent = this;
362
    mChildren.add(node);
363
    mNumChildren[0]++;
364
   
365
    RecomputeNodeID(prev);
366

    
367
    return node;
368
    }
369
  
370
///////////////////////////////////////////////////////////////////////////////////////////////////
371
/**
372
 * Removes the first occurrence of a specified child from the list of children of our Node.
373
 * 
374
 * @param node The Node to remove.
375
 * @return <code>true</code> if the child was successfully removed.
376
 */
377
  public synchronized boolean detach(DistortedTree node)
378
    {
379
    if( mNumChildren[0]>0 )
380
      {
381
      ArrayList<Long> prev = generateIDList();  
382
         
383
      if( mChildren.remove(node) )
384
        {
385
        node.mParent = null;  
386
        mNumChildren[0]--;
387
     
388
        RecomputeNodeID(prev);
389
     
390
        return true;
391
        }
392
      }
393
   
394
    return false;
395
    }
396
  
397
///////////////////////////////////////////////////////////////////////////////////////////////////
398
/**
399
 * Removes the first occurrence of a specified child from the list of children of our Node.
400
 * 
401
 * @param effects DistortedEffects to remove.
402
 * @return <code>true</code> if the child was successfully removed.
403
 */
404
  public synchronized boolean detach(DistortedEffects effects)
405
    {
406
    long id = effects.getID();
407
    DistortedTree node;
408
   
409
    for(int i=0; i<mNumChildren[0]; i++)
410
      {
411
      node = mChildren.get(i);
412
     
413
      if( node.mEffects.getID()==id )
414
        {
415
        ArrayList<Long> prev = generateIDList();   
416
     
417
        node.mParent = null;  
418
        mChildren.remove(i);
419
        mNumChildren[0]--;
420
      
421
        RecomputeNodeID(prev);
422
      
423
        return true;
424
        }
425
      }
426
   
427
    return false;
428
    }
429
    
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431
/**
432
 * Removes all children Nodes.
433
 */
434
  public synchronized void detachAll()
435
    {
436
    for(int i=0; i<mNumChildren[0]; i++)
437
      {
438
      mChildren.get(i).mParent = null;
439
      }
440
   
441
    if( mNumChildren[0]>0 )
442
      {
443
      ArrayList<Long> prev = generateIDList();  
444
      
445
      mNumChildren[0] = 0;
446
      mChildren.clear();
447
      RecomputeNodeID(prev);
448
      }
449
    }
450

    
451
///////////////////////////////////////////////////////////////////////////////////////////////////
452
/**
453
 * Returns the DistortedEffects object that's in the Node.
454
 * 
455
 * @return The DistortedEffects contained in the Node.
456
 */
457
  public DistortedEffects getEffects()
458
    {
459
    return mEffects;
460
    }
461

    
462
///////////////////////////////////////////////////////////////////////////////////////////////////
463
/**
464
 * Returns the DistortedTexture object that's in the Node.
465
 *
466
 * @return The DistortedTexture contained in the Node.
467
 */
468
  public DistortedTexture getTexture()
469
    {
470
    return mTexture;
471
    }
472

    
473
///////////////////////////////////////////////////////////////////////////////////////////////////
474
/**
475
 * Returns the DistortedFramebuffer object that's in the Node.
476
 *
477
 * @return The DistortedFramebuffer contained in the Node.
478
 */
479
  public DistortedFramebuffer getFramebuffer()
480
    {
481
    return mData.mFBO;
482
    }
483

    
484
  }
(5-5/16)