Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedNode.java @ 07037b8a

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.GLES30;
26

    
27
///////////////////////////////////////////////////////////////////////////////////////////////////
28
/**
29
 * Class which represents a Node in a Tree of (InputSurface,Mesh,Effects) triplets.
30
 * <p>
31
 * Having organized such sets into a Tree, we can then render any Node to any OutputSurface.
32
 * That recursively renders the set held in the Node and all its children.
33
 * <p>
34
 * The class takes special care to only render identical sub-trees once. Each Node holds a reference
35
 * to sub-class 'NodeData'. Two identical sub-trees attached at different points of the main tree
36
 * will point to the same NodeData; only the first of this is rendered (when mData.numRendered==0).
37
 */
38
public class DistortedNode implements DistortedAttacheable
39
  {
40
  private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
41
  private static long mNextNodeID =0;
42

    
43
  private MeshObject mMesh;
44
  private DistortedEffects mEffects;
45
  private DistortedInputSurface mSurface;
46
  private NodeData mData;
47

    
48
  private ArrayList<DistortedNode> mChildren;
49
  private int[] mNumChildren;  // ==mChildren.length(), but we only create mChildren if the first one gets added
50

    
51
  private class NodeData
52
    {
53
    long ID;
54
    int numPointingNodes;
55
    int numRender;
56
    ArrayList<Long> key;
57
    DistortedFramebuffer mFBO;
58

    
59
    NodeData(long id, ArrayList<Long> k)
60
      {
61
      ID              = id;
62
      key             = k;
63
      numPointingNodes= 1;
64
      numRender       =-1;
65
      mFBO            = null;
66
      }
67
    }
68
 
69
///////////////////////////////////////////////////////////////////////////////////////////////////
70

    
71
  static synchronized void onDestroy()
72
    {
73
    mNextNodeID = 0;
74
    mMapNodeID.clear();
75
    }
76

    
77
///////////////////////////////////////////////////////////////////////////////////////////////////
78

    
79
  private ArrayList<Long> generateIDList()
80
    {
81
    ArrayList<Long> ret = new ArrayList<>();
82
     
83
    ret.add( mSurface.getID() );
84

    
85
    if( mNumChildren[0]==0 )
86
      {
87
      ret.add(-mEffects.getID());
88
      }
89

    
90
    DistortedNode node;
91
   
92
    for(int i=0; i<mNumChildren[0]; i++)
93
      {
94
      node = mChildren.get(i);
95
      ret.add(node.mData.ID);
96
      }
97
   
98
    return ret;
99
    }
100

    
101
///////////////////////////////////////////////////////////////////////////////////////////////////
102
// Debug - print all the Node IDs
103

    
104
  @SuppressWarnings("unused")
105
  void debug(int depth)
106
    {
107
    String tmp="";
108
    int i;
109

    
110
    for(i=0; i<depth; i++) tmp +="   ";
111
    tmp += ("NodeID="+mData.ID+" nodes pointing: "+mData.numPointingNodes+" surfaceID="+
112
            mSurface.getID()+" FBO="+(mData.mFBO==null ? "null":mData.mFBO.getID()));
113

    
114
    android.util.Log.e("NODE", tmp);
115

    
116
    for(i=0; i<mNumChildren[0]; i++)
117
      mChildren.get(i).debug(depth+1);
118
    }
119

    
120
///////////////////////////////////////////////////////////////////////////////////////////////////
121
// Debug - print contents of the HashMap
122

    
123
  @SuppressWarnings("unused")
124
  static void debugMap()
125
    {
126
    NodeData tmp;
127

    
128
    for(ArrayList<Long> key: mMapNodeID.keySet())
129
      {
130
      tmp = mMapNodeID.get(key);
131
      android.util.Log.e("NODE", "NodeID: "+tmp.ID+" <-- "+key);
132
      }
133
    }
134

    
135
///////////////////////////////////////////////////////////////////////////////////////////////////
136

    
137
  void treeIsomorphism()
138
    {
139
    for(int i=0; i<mNumChildren[0]; i++)
140
      {
141
      mChildren.get(i).treeIsomorphism();
142
      }
143

    
144
    ArrayList<Long> newList = generateIDList();
145
    NodeData newData = mMapNodeID.get(newList);
146

    
147
    if( newData==null )
148
      {
149
      android.util.Log.d("NODE", "list "+newList+" not found!! node surfaceID="+mSurface.getID());
150

    
151
      newData = new NodeData(++mNextNodeID,newList);
152
      mMapNodeID.put(newList,newData);
153
      }
154
    else if( newData.ID != mData.ID )
155
      {
156
      android.util.Log.d("NODE", "list "+newList+" found!! node surfaceID="+mSurface.getID());
157

    
158
      newData.numPointingNodes++;
159
      }
160

    
161
    if( newData.ID != mData.ID )
162
      {
163
      boolean fboUsed = false;
164

    
165
      if( mNumChildren[0]>0 && newData.mFBO==null )
166
        {
167
        if( mData.mFBO!=null )
168
          {
169
          if( mData.numPointingNodes>1 )
170
            {
171
            android.util.Log.d("NODE", "creating1 new FBO of node surfaceID="+mSurface.getID());
172
            newData.mFBO = new DistortedFramebuffer(true,DistortedSurface.TYPE_TREE,mSurface.getWidth(),mSurface.getHeight());
173
            }
174
          else
175
            {
176
            android.util.Log.d("NODE", "copying over FBO of node surfaceID="+mSurface.getID());
177
            newData.mFBO = mData.mFBO;
178
            fboUsed = true;
179
            }
180
          }
181
        else
182
          {
183
          android.util.Log.d("NODE", "creating2 new FBO of node surfaceID="+mSurface.getID());
184
          newData.mFBO = new DistortedFramebuffer(true,DistortedSurface.TYPE_TREE,mSurface.getWidth(),mSurface.getHeight());
185
          }
186
        }
187
      if( mNumChildren[0]==0 && newData.mFBO!=null )
188
        {
189
        android.util.Log.d("NODE", "deleting FBO of newData node!!");
190
        newData.mFBO.markForDeletion();
191
        newData.mFBO = null;
192
        }
193

    
194
      if( --mData.numPointingNodes==0 )
195
        {
196
        android.util.Log.d("NODE", "deleting1 map key "+mData.key);
197

    
198
        mMapNodeID.remove(mData.key);
199

    
200
        if( !fboUsed && mData.mFBO!=null )
201
          {
202
          android.util.Log.d("NODE", "deleting FBO of node surfaceID="+mSurface.getID());
203

    
204
          mData.mFBO.markForDeletion();
205
          mData.mFBO = null;
206
          }
207
        }
208

    
209
      mData = newData;
210
      }
211
    }
212

    
213
///////////////////////////////////////////////////////////////////////////////////////////////////
214
// return the total number of render calls issued
215

    
216
  int drawRecursive(int renderNum, long currTime, DistortedOutputSurface surface)
217
    {
218
    int ret = 0;
219
    float halfX = mSurface.getWidth()/2.0f;
220
    float halfY = mSurface.getHeight()/2.0f;
221

    
222
    if( mNumChildren[0]>0 && mData.numRender!=renderNum )
223
      {
224
      mData.numRender = renderNum;
225
      mData.mFBO.setAsOutput();
226

    
227
      GLES30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
228
      GLES30.glClear( GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);
229

    
230
      if( mSurface.setAsInput() )
231
        {
232
        ret++;
233
        DistortedEffects.drawNoEffectsPriv(halfX, halfY, mMesh, mData.mFBO);
234
        }
235

    
236
      for(int i=0; i<mNumChildren[0]; i++)
237
        {
238
        ret += mChildren.get(i).drawRecursive(renderNum, currTime, mData.mFBO);
239
        }
240
      }
241

    
242
    DistortedInputSurface input = mNumChildren[0]==0 ? mSurface : mData.mFBO;
243

    
244
    if( input.setAsInput() )
245
      {
246
      ret++;
247
      mEffects.drawPriv(halfX, halfY, mMesh, surface, currTime);
248
      }
249

    
250
    return ret;
251
    }
252

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254
// PUBLIC API
255
///////////////////////////////////////////////////////////////////////////////////////////////////
256
/**
257
 * Constructs new Node.
258
 *     
259
 * @param surface InputSurface to put into the new Node.
260
 * @param effects DistortedEffects to put into the new Node.
261
 * @param mesh MeshObject to put into the new Node.
262
 */
263
  public DistortedNode(DistortedInputSurface surface, DistortedEffects effects, MeshObject mesh)
264
    {
265
    mSurface       = surface;
266
    mEffects       = effects;
267
    mMesh          = mesh;
268
    mChildren      = null;
269
    mNumChildren   = new int[1];
270
    mNumChildren[0]= 0;
271
   
272
    ArrayList<Long> list = new ArrayList<>();
273
    list.add(mSurface.getID());
274
    list.add(-mEffects.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,list);
285
      mMapNodeID.put(list, mData);
286
      }
287
    }
288

    
289
///////////////////////////////////////////////////////////////////////////////////////////////////  
290
/**
291
 * Copy-constructs new Node 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_SURFACE},  {@link Distorted#CLONE_MATRIX}, {@link Distorted#CLONE_VERTEX},
296
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
297
 *        For example flags = CLONE_SURFACE | CLONE_CHILDREN.
298
 */
299
  public DistortedNode(DistortedNode node, int flags)
300
    {
301
    mEffects= new DistortedEffects(node.mEffects,flags);
302
    mMesh = node.mMesh;
303

    
304
    if( (flags & Distorted.CLONE_SURFACE) != 0 )
305
      {
306
      mSurface = node.mSurface;
307
      }
308
    else
309
      {
310
      int w = node.mSurface.getWidth();
311
      int h = node.mSurface.getHeight();
312

    
313
      if( node.mSurface instanceof DistortedTexture )
314
        {
315
        mSurface = new DistortedTexture(w,h, DistortedSurface.TYPE_TREE);
316
        }
317
      else if( node.mSurface instanceof DistortedFramebuffer )
318
        {
319
        boolean hasDepth = ((DistortedFramebuffer) node.mSurface).hasDepth();
320
        mSurface = new DistortedFramebuffer(hasDepth,DistortedSurface.TYPE_TREE,w,h);
321
        }
322
      }
323
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
324
      {
325
      if( node.mChildren==null )     // do NOT copy over the NULL!
326
        {
327
        node.mChildren = new ArrayList<>(2);
328
        }
329

    
330
      mChildren = node.mChildren;
331
      mNumChildren = node.mNumChildren;
332
      }
333
    else
334
      {
335
      mChildren = null;
336
      mNumChildren = new int[1];
337
      mNumChildren[0] = 0;
338
      }
339
   
340
    ArrayList<Long> list = generateIDList();
341
   
342
    mData = mMapNodeID.get(list);
343
   
344
    if( mData!=null )
345
      {
346
      mData.numPointingNodes++;
347
      }
348
    else
349
      {
350
      mData = new NodeData(++mNextNodeID,list);
351
      mMapNodeID.put(list, mData);
352
      }
353
    }
354

    
355
///////////////////////////////////////////////////////////////////////////////////////////////////
356
/**
357
 * Adds a new child to the last position in the list of our Node's children.
358
 * <p>
359
 * We cannot do this mid-render - actual attachment will be done just before the next render, by the
360
 * DistortedAttachDeamon (by calling attachNow())
361
 *
362
 * @param node The new Node to add.
363
 */
364
  public void attach(DistortedNode node)
365
    {
366
    DistortedAttachDaemon.attach(this,node);
367
    }
368

    
369
///////////////////////////////////////////////////////////////////////////////////////////////////
370
/**
371
 * Adds a new child to the last position in the list of our Node's children.
372
 * <p>
373
 * We cannot do this mid-render - actual attachment will be done just before the next render, by the
374
 * DistortedAttachDeamon (by calling attachNow())
375
 *
376
 * @param surface InputSurface to initialize our child Node with.
377
 * @param effects DistortedEffects to initialize our child Node with.
378
 * @param mesh MeshObject to initialize our child Node with.
379
 * @return the newly constructed child Node, or null if we couldn't allocate resources.
380
 */
381
  public DistortedNode attach(DistortedInputSurface surface, DistortedEffects effects, MeshObject mesh)
382
    {
383
    DistortedNode node = new DistortedNode(surface,effects,mesh);
384
    DistortedAttachDaemon.attach(this,node);
385
    return node;
386
    }
387

    
388
///////////////////////////////////////////////////////////////////////////////////////////////////
389
/**
390
 * This is not really part of the public API. Has to be public only because it is a part of the
391
 * DistortedAttacheable interface, which should really be a class that we extend here instead but
392
 * Java has no multiple inheritance.
393
 *
394
 * @param node The new Node to add.
395
 */
396
  public void attachNow(DistortedNode node)
397
    {
398
    if( mChildren==null ) mChildren = new ArrayList<>(2);
399

    
400
    mChildren.add(node);
401
    mNumChildren[0]++;
402
    }
403

    
404
///////////////////////////////////////////////////////////////////////////////////////////////////
405
/**
406
 * Removes the first occurrence of a specified child from the list of children of our Node.
407
 * <p>
408
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
409
 * DistortedAttachDeamon (by calling detachNow())
410
 *
411
 * @param node The Node to remove.
412
 */
413
  public void detach(DistortedNode node)
414
    {
415
    DistortedAttachDaemon.detach(this,node);
416
    }
417

    
418
///////////////////////////////////////////////////////////////////////////////////////////////////
419
/**
420
 * Removes the first occurrence of a specified child from the list of children of our Node.
421
 * <p>
422
 * A bit questionable method as there can be many different Nodes attached as children, some
423
 * of them having the same Effects but - for instance - different Mesh. Use with care.
424
 * <p>
425
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
426
 * DistortedAttachDeamon (by calling detachNow())
427
 *
428
 * @param effects DistortedEffects to remove.
429
 */
430
  public void detach(DistortedEffects effects)
431
    {
432
    long id = effects.getID();
433
    DistortedNode node;
434

    
435
    for(int i=0; i<mNumChildren[0]; i++)
436
      {
437
      node = mChildren.get(i);
438

    
439
      if( node.mEffects.getID()==id )
440
        {
441
        DistortedAttachDaemon.detach(this,node);
442
        break;
443
        }
444
      }
445
    }
446

    
447
///////////////////////////////////////////////////////////////////////////////////////////////////
448
/**
449
 * This is not really part of the public API. Has to be public only because it is a part of the
450
 * DistortedAttacheable interface, which should really be a class that we extend here instead but
451
 * Java has no multiple inheritance.
452
 *
453
 * @param node The Node to remove.
454
 */
455
  public void detachNow(DistortedNode node)
456
    {
457
    if( mNumChildren[0]>0 && mChildren.remove(node) )
458
      {
459
      mNumChildren[0]--;
460

    
461
      if( mNumChildren[0]==0 && mData.mFBO!=null )
462
        {
463
        if( --mData.numPointingNodes==0 )
464
          {
465
          mData.mFBO.markForDeletion();
466
          android.util.Log.d("NODE", "deleting2 map key "+mData.key);
467

    
468
          mMapNodeID.remove(mData.key);
469
          }
470
        mData.mFBO = null;
471
        }
472
      }
473
    }
474

    
475
///////////////////////////////////////////////////////////////////////////////////////////////////
476
/**
477
 * Removes all children Nodes.
478
 * <p>
479
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
480
 * DistortedAttachDeamon (by calling detachAllNow())
481
 */
482
  public void detachAll()
483
    {
484
    DistortedAttachDaemon.detachAll(this);
485
    }
486

    
487
///////////////////////////////////////////////////////////////////////////////////////////////////
488
/**
489
 * This is not really part of the public API. Has to be public only because it is a part of the
490
 * DistortedAttacheable interface, which should really be a class that we extend here instead but
491
 * Java has no multiple inheritance.
492
 */
493
  public void detachAllNow()
494
    {
495
    if( mNumChildren[0]>0 )
496
      {
497
      mNumChildren[0] = 0;
498
      mChildren.clear();
499

    
500
      if( mData.mFBO!=null )
501
        {
502
        if( --mData.numPointingNodes==0 )
503
          {
504
          mData.mFBO.markForDeletion();
505
          android.util.Log.d("NODE", "deleting3 map key "+mData.key);
506

    
507
          mMapNodeID.remove(mData.key);
508
          }
509
        mData.mFBO = null;
510
        }
511
      }
512
    }
513

    
514
///////////////////////////////////////////////////////////////////////////////////////////////////
515
/**
516
 * Returns the DistortedEffects object that's in the Node.
517
 * 
518
 * @return The DistortedEffects contained in the Node.
519
 */
520
  public DistortedEffects getEffects()
521
    {
522
    return mEffects;
523
    }
524

    
525
///////////////////////////////////////////////////////////////////////////////////////////////////
526
/**
527
 * Returns the DistortedInputSurface object that's in the Node.
528
 *
529
 * @return The DistortedInputSurface contained in the Node.
530
 */
531
  public DistortedInputSurface getSurface()
532
    {
533
    return mSurface;
534
    }
535

    
536
///////////////////////////////////////////////////////////////////////////////////////////////////
537
/**
538
 * Returns the DistortedFramebuffer object that's in the Node.
539
 *
540
 * @return The DistortedFramebuffer contained in the Node.
541
 */
542
  public DistortedFramebuffer getFramebuffer()
543
    {
544
    return mData.mFBO;
545
    }
546

    
547
  }
(7-7/22)