Project

General

Profile

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

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

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 DistortedNode 
36
  {
37
  private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
38
  private static long mNextNodeID =0;
39

    
40
  private DistortedObjectGrid mGrid;
41
  private DistortedObject mObject;
42
  private NodeData mData;
43

    
44
  private DistortedNode mParent;
45
  private ArrayList<DistortedNode> 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
    DistortedFramebuffer mDF;
53
    boolean mRendered;
54

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

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

    
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73

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

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

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

    
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144

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

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162

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

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

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

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

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

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

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

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

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

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

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

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

    
252
      android.util.Log.e("NODE", "key="+key+" NodeID: "+tmp.ID);
253
      }
254
    }
255
*/
256
///////////////////////////////////////////////////////////////////////////////////////////////////
257
// PUBLIC API
258
///////////////////////////////////////////////////////////////////////////////////////////////////
259
/**
260
 * Constructs new Node of the Tree.
261
 *     
262
 * @param obj DistortedObject to put into the new Node.
263
 */
264
  public DistortedNode(DistortedObject obj, DistortedObjectGrid grid)
265
    {
266
    mObject = obj;
267
    mGrid   = grid;
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
    mObject = new DistortedObject(node.mObject,flags);
303
    mGrid   = node.mGrid;
304

    
305
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
306
      {
307
      mChildren = node.mChildren;
308
      mNumChildren = node.mNumChildren;
309
      }
310
    else
311
      {
312
      mChildren = null;
313
      mNumChildren = new int[1];
314
      mNumChildren[0] = 0;
315
      }
316
   
317
    ArrayList<Long> list = generateIDList();
318
   
319
    mData = mMapNodeID.get(list);
320
   
321
    if( mData!=null )
322
      {
323
      mData.numPointingNodes++;
324
      }
325
    else
326
      {
327
      mData = new NodeData(++mNextNodeID);   
328
      mMapNodeID.put(list, mData);
329
      }
330
    }
331
  
332
///////////////////////////////////////////////////////////////////////////////////////////////////
333
/**
334
 * Adds a new child to the last position in the list of our Node's children.
335
 * 
336
 * @param node The new Node to add.
337
 */
338
  public synchronized void attach(DistortedNode node)
339
    {
340
    ArrayList<Long> prev = generateIDList(); 
341
   
342
    if( mChildren==null ) mChildren = new ArrayList<>(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 obj DistortedObject 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(DistortedObject obj, DistortedObjectGrid grid)
359
    {
360
    ArrayList<Long> prev = generateIDList(); 
361
      
362
    if( mChildren==null ) mChildren = new ArrayList<>(2);
363
    DistortedNode node = new DistortedNode(obj,grid);
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 obj DistortedObject to remove.
405
 * @return <code>true</code> if the child was successfully removed.
406
 */
407
  public synchronized boolean detach(DistortedObject obj)
408
    {
409
    long id = obj.getID();
410
    DistortedNode node;
411
   
412
    for(int i=0; i<mNumChildren[0]; i++)
413
      {
414
      node = mChildren.get(i);
415
     
416
      if( node.mObject.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.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
463

    
464
    markRecursive();
465
    drawRecursive(currTime,Distorted.mFramebuffer);
466
    }
467

    
468
///////////////////////////////////////////////////////////////////////////////////////////////////
469
/**
470
 * Draws the Node, and all its children, to the Framebuffer passed.
471
 *
472
 * @param currTime Current time, in milliseconds.
473
 * @param df       Framebuffer to render this to.
474
 */
475
  public void draw(long currTime, DistortedFramebuffer df)
476
    {
477
    df.setAsOutput();
478
    markRecursive();
479
    drawRecursive(currTime,df);
480
    }
481

    
482
///////////////////////////////////////////////////////////////////////////////////////////////////
483
/**
484
 * Returns the DistortedObject object that's in the Node.
485
 * 
486
 * @return The DistortedObject contained in the Node.
487
 */
488
  public DistortedObject getObject()
489
    {
490
    return mObject;
491
    }
492

    
493
///////////////////////////////////////////////////////////////////////////////////////////////////
494
  }
495

    
(5-5/14)