Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedNode.java @ 8e34674e

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 DistortedObject mObject;
41
  private NodeData mData;
42

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

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

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

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

    
71
///////////////////////////////////////////////////////////////////////////////////////////////////
72

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

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

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

    
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143

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

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

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

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

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

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

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

    
223
///////////////////////////////////////////////////////////////////////////////////////////////////
224
// Debug - print all the Node IDs
225

    
226
  void debug(int depth)
227
    {
228
    String tmp="";
229
    int i;
230

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

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

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

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

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

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

    
251
      android.util.Log.e("NODE", "key="+key+" NodeID: "+tmp.ID);
252
      }
253
    }
254

    
255
///////////////////////////////////////////////////////////////////////////////////////////////////
256
// PUBLIC API
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258
/**
259
 * Constructs new Node of the Tree.
260
 *     
261
 * @param obj DistortedObject to put into the new Node.
262
 */
263
  public DistortedNode(DistortedObject obj)
264
    {
265
    mObject = obj;
266
    mParent = null;
267
    mChildren = null;
268
    mNumChildren = new int[1];
269
    mNumChildren[0] = 0;
270
   
271
    ArrayList<Long> list = new ArrayList<>();
272
    list.add(obj.getID());
273
      
274
    mData = mMapNodeID.get(list);
275
   
276
    if( mData!=null )
277
      {
278
      mData.numPointingNodes++;
279
      }
280
    else
281
      {
282
      mData = new NodeData(++mNextNodeID);   
283
      mMapNodeID.put(list, mData);  
284
      }
285
    }
286

    
287
///////////////////////////////////////////////////////////////////////////////////////////////////  
288
/**
289
 * Copy-constructs new Node of the Tree from another Node.
290
 *     
291
 * @param node The DistortedNode to copy data from.
292
 * @param flags bit field composed of a subset of the following:
293
 *        {@link Distorted#CLONE_BITMAP},  {@link Distorted#CLONE_MATRIX}, {@link Distorted#CLONE_VERTEX},
294
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
295
 *        For example flags = CLONE_BITMAP | CLONE_CHILDREN.
296
 */
297
  public DistortedNode(DistortedNode node, int flags)
298
    {
299
    mParent = null;
300
    mObject = node.mObject.deepCopy(flags);
301

    
302
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
303
      {
304
      mChildren = node.mChildren;
305
      mNumChildren = node.mNumChildren;
306
      }
307
    else
308
      {
309
      mChildren = null;
310
      mNumChildren = new int[1];
311
      mNumChildren[0] = 0;
312
      }
313
   
314
    ArrayList<Long> list = generateIDList();
315
   
316
    mData = mMapNodeID.get(list);
317
   
318
    if( mData!=null )
319
      {
320
      mData.numPointingNodes++;
321
      }
322
    else
323
      {
324
      mData = new NodeData(++mNextNodeID);   
325
      mMapNodeID.put(list, mData);
326
      }
327
    }
328
  
329
///////////////////////////////////////////////////////////////////////////////////////////////////
330
/**
331
 * Adds a new child to the last position in the list of our Node's children.
332
 * 
333
 * @param node The new Node to add.
334
 */
335
  public synchronized void attach(DistortedNode node)
336
    {
337
    ArrayList<Long> prev = generateIDList(); 
338
   
339
    if( mChildren==null ) mChildren = new ArrayList<>(2);
340
     
341
    node.mParent = this;
342
    mChildren.add(node);
343
    mNumChildren[0]++;
344
     
345
    RecomputeNodeID(prev);
346
    }
347
   
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349
/**
350
 * Adds a new child to the last position in the list of our Node's children.
351
 * 
352
 * @param obj DistortedObject 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 DistortedNode attach(DistortedObject obj)
356
    {
357
    ArrayList<Long> prev = generateIDList(); 
358
      
359
    if( mChildren==null ) mChildren = new ArrayList<>(2);
360
    DistortedNode node = new DistortedNode(obj);
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(DistortedNode 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 obj DistortedObject to remove.
402
 * @return <code>true</code> if the child was successfully removed.
403
 */
404
  public synchronized boolean detach(DistortedObject obj)
405
    {
406
    long id = obj.getID();
407
    DistortedNode node;
408
   
409
    for(int i=0; i<mNumChildren[0]; i++)
410
      {
411
      node = mChildren.get(i);
412
     
413
      if( node.mObject.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
 * Draws the Node, and all its children, to the default framebuffer 0 (i.e. the screen).
454
 *   
455
 * @param currTime Current time, in milliseconds.
456
 */
457
  public void draw(long currTime)
458
    {  
459
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
460
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
461
    GLES20.glUniform1i(Distorted.mTextureUniformH, 0);  
462

    
463
    markRecursive();
464
    drawRecursive(currTime,Distorted.mFramebuffer);
465
    }
466
 
467
///////////////////////////////////////////////////////////////////////////////////////////////////
468
/**
469
 * Returns the DistortedObject object that's in the Node.
470
 * 
471
 * @return The DistortedObject contained in the Node.
472
 */
473
  public DistortedObject getObject()
474
    {
475
    return mObject;
476
    }
477

    
478
///////////////////////////////////////////////////////////////////////////////////////////////////
479
  }
480

    
(8-8/17)