Project

General

Profile

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

library / src / main / java / org / distorted / library / main / DistortedNode.java @ 7a5e538a

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.main;
21

    
22
import android.opengl.GLES31;
23

    
24
import org.distorted.library.mesh.MeshBase;
25

    
26
import java.util.ArrayList;
27
import java.util.Collections;
28
import java.util.HashMap;
29

    
30
///////////////////////////////////////////////////////////////////////////////////////////////////
31
/**
32
 * Class which represents a Node in a Tree of (InputSurface,Mesh,Effects) triplets.
33
 * <p>
34
 * Having organized such sets into a Tree, we can then render any Node to any OutputSurface.
35
 * That recursively renders the set held in the Node and all its children.
36
 * <p>
37
 * The class takes special care to only render identical sub-trees once. Each Node holds a reference
38
 * to sub-class 'NodeData'. Two identical sub-trees attached at different points of the main tree
39
 * will point to the same NodeData; only the first of this is rendered (mData.numRender!).
40
 */
41
public class DistortedNode implements DistortedMaster.Slave
42
  {
43
  private static final int ATTACH = 0;
44
  private static final int DETACH = 1;
45
  private static final int DETALL = 2;
46
  private static final int SORT   = 3;
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 Job
52
    {
53
    int type;
54
    DistortedNode node;
55

    
56
    Job(int t, DistortedNode n)
57
      {
58
      type = t;
59
      node = n;
60
      }
61
    }
62

    
63
  private ArrayList<Job> mJobs = new ArrayList<>();
64

    
65
  private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
66
  private static long mNextNodeID =0;
67

    
68
  private boolean mRenderWayOIT;
69
  private DistortedNode mParent;
70
  private DistortedOutputSurface mSurfaceParent;
71
  private MeshBase mMesh;
72
  private DistortedEffects mEffects;
73
  private DistortedSurface mSurface;
74
  private DistortedRenderState mState;
75
  private NodeData mData;
76
  private int mFboW, mFboH, mFboDepthStencil;
77

    
78
  private class NodeData
79
    {
80
    long ID;
81
    int numPointingNodes;
82
    long currTime;
83
    ArrayList<Long> key;
84
    DistortedFramebuffer mFBO;
85

    
86
    NodeData(long id, ArrayList<Long> k)
87
      {
88
      ID              = id;
89
      key             = k;
90
      numPointingNodes= 1;
91
      currTime        =-1;
92
      mFBO            = null;
93
      }
94
    }
95

    
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97

    
98
  static synchronized void onDestroy()
99
    {
100
    mNextNodeID = 0;
101
    mMapNodeID.clear();
102
    }
103

    
104
///////////////////////////////////////////////////////////////////////////////////////////////////
105

    
106
  private ArrayList<Long> generateIDList()
107
    {
108
    ArrayList<Long> ret = new ArrayList<>();
109

    
110
    if( mNumChildren[0]==0 )
111
      {
112
      // add a negative number so this leaf never gets confused with a internal node
113
      // with a single child that happens to have ID identical to some leaf's Effects ID.
114
      ret.add(-mEffects.getID());
115
      }
116
    else
117
      {
118
      DistortedNode node;
119
   
120
      for(int i=0; i<mNumChildren[0]; i++)
121
        {
122
        node = mChildren.get(i);
123
        ret.add(node.mData.ID);
124
        }
125

    
126
      // A bit questionable decision here - we are sorting the children IDs, which means
127
      // that order in which we draw the children is going to be undefined (well, this is not
128
      // strictly speaking true - when rendering, if no postprocessing and isomorphism are
129
      // involved, we *DO* render the children in order they were added; if however there
130
      // are two internal nodes with the same list of identical children, just added in a
131
      // different order each time, then we consider them isomorphic, i.e. identical and only
132
      // render the first one. If then two children of such 'pseudo-isomorphic' nodes are at
133
      // exactly the same Z-height this might result in some unexpected sights).
134
      //
135
      // Reason: with the children being sorted by postprocessing buckets, the order is
136
      // undefined anyway (although only when postprocessing is applied).
137
      //
138
      // See the consequences in the 'Olympic' app - remove a few leaves and add them back in
139
      // different order. You will see the number of renders go back to the original 15.
140
      Collections.sort(ret);
141
      }
142

    
143
    ret.add( 0, mSurface.getID() );
144

    
145
    return ret;
146
    }
147

    
148
///////////////////////////////////////////////////////////////////////////////////////////////////
149
// Debug - print all the Node IDs
150

    
151
  @SuppressWarnings("unused")
152
  void debug(int depth)
153
    {
154
    String tmp="";
155
    int i;
156

    
157
    for(i=0; i<depth; i++) tmp +="   ";
158
    tmp += ("NodeID="+mData.ID+" nodes pointing: "+mData.numPointingNodes+" surfaceID="+
159
            mSurface.getID()+" FBO="+(mData.mFBO==null ? "null":mData.mFBO.getID()))+
160
            " parent sID="+(mParent==null ? "null": (mParent.mSurface.getID()));
161

    
162
    android.util.Log.e("NODE", tmp);
163

    
164
    for(i=0; i<mNumChildren[0]; i++)
165
      mChildren.get(i).debug(depth+1);
166
    }
167

    
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169
// Debug - print contents of the HashMap
170

    
171
  @SuppressWarnings("unused")
172
  static void debugMap()
173
    {
174
    NodeData tmp;
175

    
176
    for(ArrayList<Long> key: mMapNodeID.keySet())
177
      {
178
      tmp = mMapNodeID.get(key);
179
      android.util.Log.e("NODE", "NodeID: "+tmp.ID+" <-- "+key);
180
      }
181
    }
182

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184
// tree isomorphism algorithm
185

    
186
  private void adjustIsomorphism()
187
    {
188
    ArrayList<Long> newList = generateIDList();
189
    NodeData newData = mMapNodeID.get(newList);
190

    
191
    if( newData!=null )
192
      {
193
      newData.numPointingNodes++;
194
      }
195
    else
196
      {
197
      newData = new NodeData(++mNextNodeID,newList);
198
      mMapNodeID.put(newList,newData);
199
      }
200

    
201
    boolean deleteOldFBO = false;
202
    boolean createNewFBO = false;
203

    
204
    if( --mData.numPointingNodes==0 )
205
      {
206
      mMapNodeID.remove(mData.key);
207
      if( mData.mFBO!=null ) deleteOldFBO=true;
208
      }
209
    if( mNumChildren[0]>0 && newData.mFBO==null )
210
      {
211
      createNewFBO = true;
212
      }
213
    if( mNumChildren[0]==0 && newData.mFBO!=null )
214
      {
215
      newData.mFBO.markForDeletion();
216
      android.util.Log.e("NODE", "ERROR!! this NodeData cannot possibly contain a non-null FBO!! "+newData.mFBO.getID() );
217
      newData.mFBO = null;
218
      }
219

    
220
    if( deleteOldFBO && createNewFBO )
221
      {
222
      newData.mFBO = mData.mFBO;  // just copy over
223
      //android.util.Log.d("NODE", "copying over FBOs "+mData.mFBO.getID() );
224
      }
225
    else if( deleteOldFBO )
226
      {
227
      mData.mFBO.markForDeletion();
228
      //android.util.Log.d("NODE", "deleting old FBO "+mData.mFBO.getID() );
229
      mData.mFBO = null;
230
      }
231
    else if( createNewFBO )
232
      {
233
      int width  = mFboW <= 0 ? mSurface.getWidth()  : mFboW;
234
      int height = mFboH <= 0 ? mSurface.getHeight() : mFboH;
235
      newData.mFBO = new DistortedFramebuffer(1,mFboDepthStencil, DistortedSurface.TYPE_TREE, width, height);
236
      //android.util.Log.d("NODE", "creating new FBO "+newData.mFBO.getID() );
237
      }
238

    
239
    mData = newData;
240

    
241
    if( mParent!=null ) mParent.adjustIsomorphism();
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245
// return the total number of render calls issued
246

    
247
  int drawNoBlend(long currTime, DistortedOutputSurface surface)
248
    {
249
    DistortedSurface input = mNumChildren[0]==0 ? mSurface : mData.mFBO;
250

    
251
    if( input.setAsInput() )
252
      {
253
      mState.apply();
254
      GLES31.glDisable(GLES31.GL_BLEND);
255
      mEffects.drawPriv(mSurface.getWidth()/2.0f, mSurface.getHeight()/2.0f, mMesh, surface, currTime);
256
      GLES31.glEnable(GLES31.GL_BLEND);
257
      return 1;
258
      }
259

    
260
    return 0;
261
    }
262

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264
// Use the Order Independent Transparency method to draw a non-postprocessed child.
265

    
266
  int drawOIT(long currTime, DistortedOutputSurface surface)
267
    {
268
    DistortedSurface input = mNumChildren[0]==0 ? mSurface : mData.mFBO;
269

    
270
    if( input.setAsInput() )
271
      {
272
      mState.apply();
273
      mEffects.drawPrivOIT(mSurface.getWidth()/2.0f, mSurface.getHeight()/2.0f, mMesh, surface, currTime);
274
      return 1;
275
      }
276

    
277
    return 0;
278
    }
279

    
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281
// return the total number of render calls issued
282

    
283
  int draw(long currTime, DistortedOutputSurface surface)
284
    {
285
    DistortedSurface input = mNumChildren[0]==0 ? mSurface : mData.mFBO;
286

    
287
    if( input.setAsInput() )
288
      {
289
      mState.apply();
290
      mEffects.drawPriv(mSurface.getWidth()/2.0f, mSurface.getHeight()/2.0f, mMesh, surface, currTime);
291
      return 1;
292
      }
293

    
294
    return 0;
295
    }
296

    
297
///////////////////////////////////////////////////////////////////////////////////////////////////
298
// return the total number of render calls issued
299

    
300
  int renderRecursive(long currTime)
301
    {
302
    int numRenders = 0;
303

    
304
    if( mNumChildren[0]>0 && mData.currTime!=currTime )
305
      {
306
      mData.currTime = currTime;
307

    
308
      for (int i=0; i<mNumChildren[0]; i++)
309
        {
310
        numRenders += mChildren.get(i).renderRecursive(currTime);
311
        }
312

    
313
      if( mData.mFBO==null )
314
        {
315
        int width  = mFboW <= 0 ? mSurface.getWidth()  : mFboW;
316
        int height = mFboH <= 0 ? mSurface.getHeight() : mFboH;
317
        mData.mFBO = new DistortedFramebuffer(1,mFboDepthStencil, DistortedSurface.TYPE_TREE, width, height);
318
        }
319

    
320
      mData.mFBO.setAsOutput(currTime);
321

    
322
      if( mSurface.setAsInput() )
323
        {
324
        numRenders++;
325
        DistortedEffects.blitPriv(mData.mFBO);
326
        }
327

    
328
      numRenders += mData.mFBO.renderChildren(currTime,mNumChildren[0],mChildren,0, mRenderWayOIT);
329
      }
330

    
331
    return numRenders;
332
    }
333

    
334
///////////////////////////////////////////////////////////////////////////////////////////////////
335

    
336
  void setSurfaceParent(DistortedOutputSurface dep)
337
    {
338
    mSurfaceParent = dep;
339
    mParent = null;
340
    }
341

    
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343

    
344
  void sort()
345
    {
346
    if( mParent!=null )
347
      {
348
      mParent.mChildren.remove(this);
349
      DistortedMaster.addSortingByBuckets(mParent.mChildren,this);
350
      }
351
    else if( mSurfaceParent!=null )
352
      {
353
      ArrayList<DistortedNode> children = mSurfaceParent.getChildren();
354
      children.remove(this);
355
      DistortedMaster.addSortingByBuckets(children,this);
356
      }
357
    }
358

    
359
///////////////////////////////////////////////////////////////////////////////////////////////////
360

    
361
  EffectQueuePostprocess getPostprocessQueue()
362
    {
363
    return mEffects.getPostprocess();
364
    }
365

    
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367
// PUBLIC API
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369
/**
370
 * Constructs new Node.
371
 *     
372
 * @param surface InputSurface to put into the new Node.
373
 * @param effects DistortedEffects to put into the new Node.
374
 * @param mesh MeshBase to put into the new Node.
375
 */
376
  public DistortedNode(DistortedSurface surface, DistortedEffects effects, MeshBase mesh)
377
    {
378
    mSurface       = surface;
379
    mEffects       = effects;
380
    mMesh          = mesh;
381
    mState         = new DistortedRenderState();
382
    mChildren      = null;
383
    mNumChildren   = new int[1];
384
    mNumChildren[0]= 0;
385
    mParent        = null;
386
    mSurfaceParent = null;
387
    mRenderWayOIT  = false;
388

    
389
    mFboW            = 0;  // i.e. take this from
390
    mFboH            = 0;  // mSurface's dimensions
391
    mFboDepthStencil = DistortedFramebuffer.DEPTH_NO_STENCIL;
392

    
393
    ArrayList<Long> list = new ArrayList<>();
394
    list.add(mSurface.getID());
395
    list.add(-mEffects.getID());
396

    
397
    mData = mMapNodeID.get(list);
398
   
399
    if( mData!=null )
400
      {
401
      mData.numPointingNodes++;
402
      }
403
    else
404
      {
405
      mData = new NodeData(++mNextNodeID,list);
406
      mMapNodeID.put(list, mData);
407
      }
408

    
409
    mEffects.newNode(this);
410
    }
411

    
412
///////////////////////////////////////////////////////////////////////////////////////////////////  
413
/**
414
 * Copy-constructs new Node from another Node.
415
 *     
416
 * @param node The DistortedNode to copy data from.
417
 * @param flags bit field composed of a subset of the following:
418
 *        {@link Distorted#CLONE_SURFACE},  {@link Distorted#CLONE_MATRIX}, {@link Distorted#CLONE_VERTEX},
419
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
420
 *        For example flags = CLONE_SURFACE | CLONE_CHILDREN.
421
 */
422
  public DistortedNode(DistortedNode node, int flags)
423
    {
424
    mEffects      = new DistortedEffects(node.mEffects,flags);
425
    mMesh         = node.mMesh;
426
    mState        = new DistortedRenderState();
427
    mParent       = null;
428
    mSurfaceParent= null;
429
    mRenderWayOIT = false;
430

    
431
    mFboW            = node.mFboW;
432
    mFboH            = node.mFboH;
433
    mFboDepthStencil = node.mFboDepthStencil;
434

    
435
    if( (flags & Distorted.CLONE_SURFACE) != 0 )
436
      {
437
      mSurface = node.mSurface;
438
      }
439
    else
440
      {
441
      int w = node.mSurface.getWidth();
442
      int h = node.mSurface.getHeight();
443

    
444
      if( node.mSurface instanceof DistortedTexture )
445
        {
446
        mSurface = new DistortedTexture(w,h, DistortedSurface.TYPE_TREE);
447
        }
448
      else if( node.mSurface instanceof DistortedFramebuffer )
449
        {
450
        int depthStencil = DistortedFramebuffer.NO_DEPTH_NO_STENCIL;
451

    
452
        if( ((DistortedFramebuffer) node.mSurface).hasDepth() )
453
          {
454
          boolean hasStencil = ((DistortedFramebuffer) node.mSurface).hasStencil();
455
          depthStencil = (hasStencil ? DistortedFramebuffer.BOTH_DEPTH_STENCIL:DistortedFramebuffer.DEPTH_NO_STENCIL);
456
          }
457

    
458
        mSurface = new DistortedFramebuffer(1,depthStencil,DistortedSurface.TYPE_TREE,w,h);
459
        }
460
      }
461
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
462
      {
463
      if( node.mChildren==null )     // do NOT copy over the NULL!
464
        {
465
        node.mChildren = new ArrayList<>(2);
466
        }
467

    
468
      mChildren = node.mChildren;
469
      mNumChildren = node.mNumChildren;
470
      }
471
    else
472
      {
473
      mChildren = null;
474
      mNumChildren = new int[1];
475
      mNumChildren[0] = 0;
476
      }
477
   
478
    ArrayList<Long> list = generateIDList();
479
   
480
    mData = mMapNodeID.get(list);
481
   
482
    if( mData!=null )
483
      {
484
      mData.numPointingNodes++;
485
      }
486
    else
487
      {
488
      mData = new NodeData(++mNextNodeID,list);
489
      mMapNodeID.put(list, mData);
490
      }
491

    
492
    mEffects.newNode(this);
493
    }
494

    
495
///////////////////////////////////////////////////////////////////////////////////////////////////
496
  /**
497
   * When rendering this Node, should we use the Order Independent Transparency render more?
498
   * <p>
499
   * There are two modes of rendering: the fast 'normal' way, which however renders transparent
500
   * fragments in different ways depending on which fragments get rendered first, or the slower
501
   * 'oit' way, which renders transparent fragments correctly regardless of their order.
502
   *
503
   * @param oit True if we want to render more slowly, but in a way which accounts for transparency.
504
   */
505
  public void setOrderIndependentTransparency(boolean oit)
506
    {
507
    mRenderWayOIT = oit;
508
    }
509

    
510
///////////////////////////////////////////////////////////////////////////////////////////////////
511
  /**
512
   * When rendering this Node, should we use the Order Independent Transparency render more?
513
   * <p>
514
   * There are two modes of rendering: the fast 'normal' way, which however renders transparent
515
   * fragments in different ways depending on which fragments get rendered first, or the slower
516
   * 'oit' way, which renders transparent fragments correctly regardless of their order.
517
   *
518
   * @param oit True if we want to render more slowly, but in a way which accounts for transparency.
519
   * @param initialSize Initial number of transparent fragments we expect, in screenfulls.
520
   *                    I.e '1.0' means 'the scene we are going to render contains about 1 screen
521
   *                    worth of transparent fragments'. Valid values: 0.0 &lt; initialSize &lt; 10.0
522
   *                    Even if you get this wrong, the library will detect that there are more
523
   *                    transparent fragments than it has space for and readjust its internal buffers,
524
   *                    but only after a few frames during which one will probably see missing objects.
525
   */
526
  public void setOrderIndependentTransparency(boolean oit, float initialSize)
527
    {
528
    mRenderWayOIT = oit;
529

    
530
    if( initialSize>0.0f && initialSize<10.0f )
531
      DistortedEffects.setSSBOSize(initialSize);
532
    }
533

    
534
///////////////////////////////////////////////////////////////////////////////////////////////////
535
/**
536
 * Adds a new child to the last position in the list of our Node's children.
537
 * <p>
538
 * We cannot do this mid-render - actual attachment will be done just before the next render, by the
539
 * DistortedMaster (by calling doWork())
540
 *
541
 * @param node The new Node to add.
542
 */
543
  public void attach(DistortedNode node)
544
    {
545
    mJobs.add(new Job(ATTACH,node));
546
    DistortedMaster.newSlave(this);
547
    }
548

    
549
///////////////////////////////////////////////////////////////////////////////////////////////////
550
/**
551
 * Adds a new child to the last position in the list of our Node's children.
552
 * <p>
553
 * We cannot do this mid-render - actual attachment will be done just before the next render, by the
554
 * DistortedMaster (by calling doWork())
555
 *
556
 * @param surface InputSurface to initialize our child Node with.
557
 * @param effects DistortedEffects to initialize our child Node with.
558
 * @param mesh MeshBase to initialize our child Node with.
559
 * @return the newly constructed child Node, or null if we couldn't allocate resources.
560
 */
561
  public DistortedNode attach(DistortedSurface surface, DistortedEffects effects, MeshBase mesh)
562
    {
563
    DistortedNode node = new DistortedNode(surface,effects,mesh);
564
    mJobs.add(new Job(ATTACH,node));
565
    DistortedMaster.newSlave(this);
566
    return node;
567
    }
568

    
569
///////////////////////////////////////////////////////////////////////////////////////////////////
570
/**
571
 * Removes the first occurrence of a specified child from the list of children of our Node.
572
 * <p>
573
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
574
 * DistortedMaster (by calling doWork())
575
 *
576
 * @param node The Node to remove.
577
 */
578
  public void detach(DistortedNode node)
579
    {
580
    mJobs.add(new Job(DETACH,node));
581
    DistortedMaster.newSlave(this);
582
    }
583

    
584
///////////////////////////////////////////////////////////////////////////////////////////////////
585
/**
586
 * Removes the first occurrence of a specified child from the list of children of our Node.
587
 * <p>
588
 * A bit questionable method as there can be many different Nodes attached as children, some
589
 * of them having the same Effects but - for instance - different Mesh. Use with care.
590
 * <p>
591
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
592
 * DistortedMaster (by calling doWork())
593
 *
594
 * @param effects DistortedEffects to remove.
595
 */
596
  public void detach(DistortedEffects effects)
597
    {
598
    long id = effects.getID();
599
    DistortedNode node;
600
    boolean detached = false;
601

    
602
    for(int i=0; i<mNumChildren[0]; i++)
603
      {
604
      node = mChildren.get(i);
605

    
606
      if( node.getEffects().getID()==id )
607
        {
608
        detached = true;
609
        mJobs.add(new Job(DETACH,node));
610
        DistortedMaster.newSlave(this);
611
        break;
612
        }
613
      }
614

    
615
    if( !detached )
616
      {
617
      // if we failed to detach any, it still might be the case that
618
      // there's an ATTACH job that we need to cancel.
619
      int num = mJobs.size();
620
      Job job;
621

    
622
      for(int i=0; i<num; i++)
623
        {
624
        job = mJobs.get(i);
625

    
626
        if( job.type==ATTACH && job.node.getEffects()==effects )
627
          {
628
          mJobs.remove(i);
629
          break;
630
          }
631
        }
632
      }
633
    }
634

    
635
///////////////////////////////////////////////////////////////////////////////////////////////////
636
/**
637
 * Removes all children Nodes.
638
 * <p>
639
 * We cannot do this mid-render - actual detachment will be done just before the next render, by the
640
 * DistortedMaster (by calling doWork())
641
 */
642
  public void detachAll()
643
    {
644
    mJobs.add(new Job(DETALL,null));
645
    DistortedMaster.newSlave(this);
646
    }
647

    
648
///////////////////////////////////////////////////////////////////////////////////////////////////
649
/**
650
 * This is not really part of the public API. Has to be public only because it is a part of the
651
 * DistortedSlave interface, which should really be a class that we extend here instead but
652
 * Java has no multiple inheritance.
653
 *
654
 * @y.exclude
655
 */
656
  public void doWork()
657
    {
658
    int num = mJobs.size();
659
    Job job;
660

    
661
    int numChanges=0;
662

    
663
    for(int i=0; i<num; i++)
664
      {
665
      job = mJobs.remove(0);
666

    
667
      switch(job.type)
668
        {
669
        case ATTACH: numChanges++;
670
                     if( mChildren==null ) mChildren = new ArrayList<>(2);
671
                     job.node.mParent = this;
672
                     job.node.mSurfaceParent = null;
673
                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
674
                     mNumChildren[0]++;
675
                     break;
676
        case DETACH: numChanges++;
677
                     if( mNumChildren[0]>0 && mChildren.remove(job.node) )
678
                       {
679
                       job.node.mParent = null;
680
                       job.node.mSurfaceParent = null;
681
                       mNumChildren[0]--;
682
                       }
683
                     break;
684
        case DETALL: numChanges++;
685
                     if( mNumChildren[0]>0 )
686
                       {
687
                       DistortedNode tmp;
688

    
689
                       for(int j=mNumChildren[0]-1; j>=0; j--)
690
                         {
691
                         tmp = mChildren.remove(j);
692
                         tmp.mParent = null;
693
                         tmp.mSurfaceParent = null;
694
                         }
695

    
696
                       mNumChildren[0] = 0;
697
                       }
698
                     break;
699
        case SORT  : mChildren.remove(job.node);
700
                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
701
                     break;
702
        }
703
      }
704

    
705
    if( numChanges>0 ) adjustIsomorphism();
706
    }
707

    
708
///////////////////////////////////////////////////////////////////////////////////////////////////
709
/**
710
 * Returns the DistortedEffects object that's in the Node.
711
 * 
712
 * @return The DistortedEffects contained in the Node.
713
 */
714
  public DistortedEffects getEffects()
715
    {
716
    return mEffects;
717
    }
718

    
719
///////////////////////////////////////////////////////////////////////////////////////////////////
720
/**
721
 * Returns the DistortedSurface object that's in the Node.
722
 *
723
 * @return The DistortedSurface contained in the Node.
724
 */
725
  public DistortedSurface getSurface()
726
    {
727
    return mSurface;
728
    }
729

    
730
///////////////////////////////////////////////////////////////////////////////////////////////////
731
  /**
732
   * Returns the DistortedSurface object that's in the Node.
733
   *
734
   * @return The DistortedSurface contained in the Node (if a leaf), or the FBO (if an internal Node)
735
   */
736
  public DistortedSurface getInternalSurface()
737
    {
738
    return mNumChildren[0]==0 ? mSurface : mData.mFBO;
739
    }
740

    
741
///////////////////////////////////////////////////////////////////////////////////////////////////
742
/**
743
 * Returns the Mesh object that's in the Node.
744
 *
745
 * @return Mesh contained in the Node.
746
 */
747
  public MeshBase getMesh()
748
    {
749
    return mMesh;
750
    }
751

    
752
///////////////////////////////////////////////////////////////////////////////////////////////////
753
/**
754
 * Resizes the DistortedFramebuffer object that we render this Node to.
755
 */
756
  public void resize(int width, int height)
757
    {
758
    mFboW = width;
759
    mFboH = height;
760

    
761
    if ( mData.mFBO !=null )
762
      {
763
      // TODO: potentially allocate a new NodeData if we have to
764
      mData.mFBO.resize(width,height);
765
      }
766
    }
767

    
768
///////////////////////////////////////////////////////////////////////////////////////////////////
769
/**
770
 * Enables/disables DEPTH and STENCIL buffers in the Framebuffer object that we render this Node to.
771
 */
772
  public void enableDepthStencil(int depthStencil)
773
    {
774
    mFboDepthStencil = depthStencil;
775

    
776
    if ( mData.mFBO !=null )
777
      {
778
      // TODO: potentially allocate a new NodeData if we have to
779
      mData.mFBO.enableDepthStencil(depthStencil);
780
      }
781
    }
782

    
783
///////////////////////////////////////////////////////////////////////////////////////////////////
784
// APIs that control how to set the OpenGL state just before rendering this Node.
785
///////////////////////////////////////////////////////////////////////////////////////////////////
786
/**
787
 * When rendering this Node, use ColorMask (r,g,b,a).
788
 *
789
 * @param r Write to the RED color channel when rendering this Node?
790
 * @param g Write to the GREEN color channel when rendering this Node?
791
 * @param b Write to the BLUE color channel when rendering this Node?
792
 * @param a Write to the ALPHA channel when rendering this Node?
793
 */
794
  @SuppressWarnings("unused")
795
  public void glColorMask(boolean r, boolean g, boolean b, boolean a)
796
    {
797
    mState.glColorMask(r,g,b,a);
798
    }
799

    
800
///////////////////////////////////////////////////////////////////////////////////////////////////
801
/**
802
 * When rendering this Node, switch on writing to Depth buffer?
803
 *
804
 * @param mask Write to the Depth buffer when rendering this Node?
805
 */
806
  @SuppressWarnings("unused")
807
  public void glDepthMask(boolean mask)
808
    {
809
    mState.glDepthMask(mask);
810
    }
811

    
812
///////////////////////////////////////////////////////////////////////////////////////////////////
813
/**
814
 * When rendering this Node, which bits of the Stencil buffer to write to?
815
 *
816
 * @param mask Marks the bits of the Stencil buffer we will write to when rendering this Node.
817
 */
818
  @SuppressWarnings("unused")
819
  public void glStencilMask(int mask)
820
    {
821
    mState.glStencilMask(mask);
822
    }
823

    
824
///////////////////////////////////////////////////////////////////////////////////////////////////
825
/**
826
 * When rendering this Node, which Tests to enable?
827
 *
828
 * @param test Valid values: GL_DEPTH_TEST, GL_STENCIL_TEST, GL_BLEND
829
 */
830
  @SuppressWarnings("unused")
831
  public void glEnable(int test)
832
    {
833
    mState.glEnable(test);
834
    }
835

    
836
///////////////////////////////////////////////////////////////////////////////////////////////////
837
/**
838
 * When rendering this Node, which Tests to enable?
839
 *
840
 * @param test Valid values: GL_DEPTH_TEST, GL_STENCIL_TEST, GL_BLEND
841
 */
842
  @SuppressWarnings("unused")
843
  public void glDisable(int test)
844
    {
845
    mState.glDisable(test);
846
    }
847

    
848
///////////////////////////////////////////////////////////////////////////////////////////////////
849
/**
850
 * When rendering this Node, use the following StencilFunc.
851
 *
852
 * @param func Valid values: GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL
853
 * @param ref  Reference valut to compare our stencil with.
854
 * @param mask Mask used when comparing.
855
 */
856
  @SuppressWarnings("unused")
857
  public void glStencilFunc(int func, int ref, int mask)
858
    {
859
    mState.glStencilFunc(func,ref,mask);
860
    }
861

    
862
///////////////////////////////////////////////////////////////////////////////////////////////////
863
/**
864
 * When rendering this Node, use the following StencilOp.
865
 * <p>
866
 * Valid values of all 3 parameters: GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
867
 *
868
 * @param sfail  What to do when Stencil Test fails.
869
 * @param dpfail What to do when Depth Test fails.
870
 * @param dppass What to do when Depth Test passes.
871
 */
872
  @SuppressWarnings("unused")
873
  public void glStencilOp(int sfail, int dpfail, int dppass)
874
    {
875
    mState.glStencilOp(sfail,dpfail,dppass);
876
    }
877

    
878
///////////////////////////////////////////////////////////////////////////////////////////////////
879
/**
880
 * When rendering this Node, use the following DepthFunc.
881
 *
882
 * @param func Valid values: GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL
883
 */
884
  @SuppressWarnings("unused")
885
  public void glDepthFunc(int func)
886
    {
887
    mState.glDepthFunc(func);
888
    }
889

    
890
///////////////////////////////////////////////////////////////////////////////////////////////////
891
/**
892
 * When rendering this Node, use the following Blending mode.
893
 * <p>
894
 * Valid values: GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
895
 *               GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR,
896
 *               GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_SRC_ALPHA_SATURATE
897
 *
898
 * @param src Source Blend function
899
 * @param dst Destination Blend function
900
 */
901
  @SuppressWarnings("unused")
902
  public void glBlendFunc(int src, int dst)
903
    {
904
    mState.glBlendFunc(src,dst);
905
    }
906

    
907
///////////////////////////////////////////////////////////////////////////////////////////////////
908
/**
909
 * Before rendering this Node, clear the following buffers.
910
 * <p>
911
 * Valid values: 0, or bitwise OR of one or more values from the set GL_COLOR_BUFFER_BIT,
912
 *               GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT.
913
 * Default: 0
914
 *
915
 * @param mask bitwise OR of BUFFER_BITs to clear.
916
 */
917
  @SuppressWarnings("unused")
918
  public void glClear(int mask)
919
    {
920
    mState.glClear(mask);
921
    }
922
  }
(6-6/17)