Project

General

Profile

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

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

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="+mSurface.getID()+")");
112

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

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

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

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

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

    
134
///////////////////////////////////////////////////////////////////////////////////////////////////
135

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

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

    
146
    if( newData==null )
147
      {
148
      newData = new NodeData(++mNextNodeID,newList);
149
      mMapNodeID.put(newList,newData);
150
      }
151
    else if( newData.ID != mData.ID )
152
      {
153
      newData.numPointingNodes++;
154
      }
155

    
156
    if( newData.ID != mData.ID )
157
      {
158
      boolean fboUsed = false;
159

    
160
      if( mNumChildren[0]>0 && newData.mFBO==null )
161
        {
162
        if( mData.mFBO!=null )
163
          {
164
          android.util.Log.d("NODE", "copying over FBO of node surfaceID="+mSurface.getID());
165

    
166
          newData.mFBO = mData.mFBO;
167
          fboUsed = true;
168
          }
169
        else
170
          {
171
          android.util.Log.d("NODE", "creating new FBO of node surfaceID="+mSurface.getID());
172
          newData.mFBO = new DistortedFramebuffer(true,DistortedSurface.TYPE_TREE,mSurface.getWidth(),mSurface.getHeight());
173
          }
174
        }
175
      if( mNumChildren[0]==0 && newData.mFBO!=null )
176
        {
177
        android.util.Log.d("NODE", "deleting FBO of newData node!!");
178

    
179

    
180
        newData.mFBO.markForDeletion();
181
        newData.mFBO = null;
182
        }
183

    
184
      if( --mData.numPointingNodes==0 )
185
        {
186
        mMapNodeID.remove(mData.key);
187

    
188
        if( !fboUsed && mData.mFBO!=null )
189
          {
190
          android.util.Log.d("NODE", "deleting FBO of node surfaceID="+mSurface.getID());
191

    
192
          mData.mFBO.markForDeletion();
193
          mData.mFBO = null;
194
          }
195
        }
196

    
197
      mData = newData;
198
      }
199
    }
200

    
201
///////////////////////////////////////////////////////////////////////////////////////////////////
202
// return the total number of render calls issued
203

    
204
  int drawRecursive(int renderNum, long currTime, DistortedOutputSurface surface)
205
    {
206
    int ret = 0;
207
    float halfX = mSurface.getWidth()/2.0f;
208
    float halfY = mSurface.getHeight()/2.0f;
209

    
210
    if( mNumChildren[0]>0 && mData.numRender!=renderNum )
211
      {
212
      mData.numRender = renderNum;
213
      mData.mFBO.setAsOutput();
214

    
215
      GLES30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
216
      GLES30.glClear( GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);
217

    
218
      if( mSurface.setAsInput() )
219
        {
220
        ret++;
221
        DistortedEffects.drawNoEffectsPriv(halfX, halfY, mMesh, mData.mFBO);
222
        }
223

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

    
230
    DistortedInputSurface input = mNumChildren[0]==0 ? mSurface : mData.mFBO;
231

    
232
    if( input.setAsInput() )
233
      {
234
      ret++;
235
      mEffects.drawPriv(halfX, halfY, mMesh, surface, currTime);
236
      }
237

    
238
    return ret;
239
    }
240

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

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

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

    
292
    if( (flags & Distorted.CLONE_SURFACE) != 0 )
293
      {
294
      mSurface = node.mSurface;
295
      }
296
    else
297
      {
298
      int w = node.mSurface.getWidth();
299
      int h = node.mSurface.getHeight();
300

    
301
      if( node.mSurface instanceof DistortedTexture )
302
        {
303
        mSurface = new DistortedTexture(w,h, DistortedSurface.TYPE_TREE);
304
        }
305
      else if( node.mSurface instanceof DistortedFramebuffer )
306
        {
307
        boolean hasDepth = ((DistortedFramebuffer) node.mSurface).hasDepth();
308
        mSurface = new DistortedFramebuffer(hasDepth,DistortedSurface.TYPE_TREE,w,h);
309
        }
310
      }
311
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
312
      {
313
      if( node.mChildren==null )     // do NOT copy over the NULL!
314
        {
315
        node.mChildren = new ArrayList<>(2);
316
        }
317

    
318
      mChildren = node.mChildren;
319
      mNumChildren = node.mNumChildren;
320
      }
321
    else
322
      {
323
      mChildren = null;
324
      mNumChildren = new int[1];
325
      mNumChildren[0] = 0;
326
      }
327
   
328
    ArrayList<Long> list = generateIDList();
329
   
330
    mData = mMapNodeID.get(list);
331
   
332
    if( mData!=null )
333
      {
334
      mData.numPointingNodes++;
335
      }
336
    else
337
      {
338
      mData = new NodeData(++mNextNodeID,list);
339
      mMapNodeID.put(list, mData);
340
      }
341
    }
342

    
343
///////////////////////////////////////////////////////////////////////////////////////////////////
344
/**
345
 * Adds a new child to the last position in the list of our Node's children.
346
 * <p>
347
 * We cannot do this mid-render - actual attachment will be done just before the next render, by the
348
 * DistortedAttachDeamon (by calling attachNow())
349
 *
350
 * @param node The new Node to add.
351
 */
352
  public void attach(DistortedNode node)
353
    {
354
    DistortedAttachDaemon.attach(this,node);
355
    }
356

    
357
///////////////////////////////////////////////////////////////////////////////////////////////////
358
/**
359
 * Adds a new child to the last position in the list of our Node's children.
360
 * <p>
361
 * We cannot do this mid-render - actual attachment will be done just before the next render, by the
362
 * DistortedAttachDeamon (by calling attachNow())
363
 *
364
 * @param surface InputSurface to initialize our child Node with.
365
 * @param effects DistortedEffects to initialize our child Node with.
366
 * @param mesh MeshObject to initialize our child Node with.
367
 * @return the newly constructed child Node, or null if we couldn't allocate resources.
368
 */
369
  public DistortedNode attach(DistortedInputSurface surface, DistortedEffects effects, MeshObject mesh)
370
    {
371
    DistortedNode node = new DistortedNode(surface,effects,mesh);
372
    DistortedAttachDaemon.attach(this,node);
373
    return node;
374
    }
375

    
376
///////////////////////////////////////////////////////////////////////////////////////////////////
377
/**
378
 * This is not really part of the public API. Has to be public only because it is a part of the
379
 * DistortedAttacheable interface, which should really be a class that we extend here instead but
380
 * Java has no multiple inheritance.
381
 *
382
 * @param node The new Node to add.
383
 */
384
  public void attachNow(DistortedNode node)
385
    {
386
    if( mChildren==null ) mChildren = new ArrayList<>(2);
387

    
388
    mChildren.add(node);
389
    mNumChildren[0]++;
390
    }
391

    
392
///////////////////////////////////////////////////////////////////////////////////////////////////
393
/**
394
 * Removes the first occurrence of a specified child from the list of children of our Node.
395
 * <p>
396
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
397
 * DistortedAttachDeamon (by calling detachNow())
398
 *
399
 * @param node The Node to remove.
400
 */
401
  public void detach(DistortedNode node)
402
    {
403
    DistortedAttachDaemon.detach(this,node);
404
    }
405

    
406
///////////////////////////////////////////////////////////////////////////////////////////////////
407
/**
408
 * Removes the first occurrence of a specified child from the list of children of our Node.
409
 * <p>
410
 * A bit questionable method as there can be many different Nodes attached as children, some
411
 * of them having the same Effects but - for instance - different Mesh. Use with care.
412
 * <p>
413
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
414
 * DistortedAttachDeamon (by calling detachNow())
415
 *
416
 * @param effects DistortedEffects to remove.
417
 */
418
  public void detach(DistortedEffects effects)
419
    {
420
    long id = effects.getID();
421
    DistortedNode node;
422

    
423
    for(int i=0; i<mNumChildren[0]; i++)
424
      {
425
      node = mChildren.get(i);
426

    
427
      if( node.mEffects.getID()==id )
428
        {
429
        DistortedAttachDaemon.detach(this,node);
430
        break;
431
        }
432
      }
433
    }
434

    
435
///////////////////////////////////////////////////////////////////////////////////////////////////
436
/**
437
 * This is not really part of the public API. Has to be public only because it is a part of the
438
 * DistortedAttacheable interface, which should really be a class that we extend here instead but
439
 * Java has no multiple inheritance.
440
 *
441
 * @param node The Node to remove.
442
 */
443
  public void detachNow(DistortedNode node)
444
    {
445
    if( mNumChildren[0]>0 && mChildren.remove(node) )
446
      {
447
      mNumChildren[0]--;
448

    
449
      if( mNumChildren[0]==0 && mData.mFBO!=null )
450
        {
451
        mData.mFBO.markForDeletion();
452
        mData.mFBO = null;
453
        }
454
      }
455
    }
456

    
457
///////////////////////////////////////////////////////////////////////////////////////////////////
458
/**
459
 * Removes all children Nodes.
460
 * <p>
461
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
462
 * DistortedAttachDeamon (by calling detachAllNow())
463
 */
464
  public void detachAll()
465
    {
466
    DistortedAttachDaemon.detachAll(this);
467
    }
468

    
469
///////////////////////////////////////////////////////////////////////////////////////////////////
470
/**
471
 * This is not really part of the public API. Has to be public only because it is a part of the
472
 * DistortedAttacheable interface, which should really be a class that we extend here instead but
473
 * Java has no multiple inheritance.
474
 */
475
  public void detachAllNow()
476
    {
477
    if( mNumChildren[0]>0 )
478
      {
479
      mNumChildren[0] = 0;
480
      mChildren.clear();
481

    
482
      if( mData.mFBO!=null )
483
        {
484
        mData.mFBO.markForDeletion();
485
        mData.mFBO = null;
486
        }
487
      }
488
    }
489

    
490
///////////////////////////////////////////////////////////////////////////////////////////////////
491
/**
492
 * Returns the DistortedEffects object that's in the Node.
493
 * 
494
 * @return The DistortedEffects contained in the Node.
495
 */
496
  public DistortedEffects getEffects()
497
    {
498
    return mEffects;
499
    }
500

    
501
///////////////////////////////////////////////////////////////////////////////////////////////////
502
/**
503
 * Returns the DistortedInputSurface object that's in the Node.
504
 *
505
 * @return The DistortedInputSurface contained in the Node.
506
 */
507
  public DistortedInputSurface getSurface()
508
    {
509
    return mSurface;
510
    }
511

    
512
///////////////////////////////////////////////////////////////////////////////////////////////////
513
/**
514
 * Returns the DistortedFramebuffer object that's in the Node.
515
 *
516
 * @return The DistortedFramebuffer contained in the Node.
517
 */
518
  public DistortedFramebuffer getFramebuffer()
519
    {
520
    return mData.mFBO;
521
    }
522

    
523
  }
(7-7/22)