Project

General

Profile

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

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

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 final int TEXTURE_FAILED_TO_CREATE = -1;   
38
  private static final int TEXTURE_NOT_CREATED_YET  = -2;   
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 static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
48
  private static long mNextNodeID =0;
49

    
50
  //////////////////////////////////////////////////////////////////
51
  
52
  private class NodeData
53
    {
54
    long ID;  
55
    int numPointingNodes;
56
    int mFramebufferID;
57
    int mTextureID;
58
    DistortedProjection mProjection;
59
    boolean mRendered;
60
    
61
  //////////////////////////////////////////////////////////////////
62
 
63
   public NodeData(long id)
64
     {
65
     ID = id;
66
     numPointingNodes= 1;
67
     mFramebufferID = 0;
68
     mTextureID = TEXTURE_NOT_CREATED_YET;
69
     mProjection = null;
70
     mRendered = false;
71
     }
72
   
73
  //////////////////////////////////////////////////////////////////
74

    
75
    boolean createFBO()
76
      {  
77
      int[] textureIds = new int[1];
78
      GLES20.glGenTextures(1, textureIds, 0);
79
      mTextureID = textureIds[0];
80
      int[] mFBORenderToTexture = new int[1];
81
      
82
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
83
      GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
84
      GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
85
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
86
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
87
      GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mProjection.width, mProjection.height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
88

    
89
      GLES20.glGenFramebuffers(1, mFBORenderToTexture, 0);
90
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFBORenderToTexture[0]);
91
      GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mTextureID, 0);
92
      int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
93
    
94
      if(status != GLES20.GL_FRAMEBUFFER_COMPLETE)
95
        {
96
        android.util.Log.e("Node", "failed to create framebuffer, error="+status);  
97
        
98
        GLES20.glDeleteTextures(1, textureIds, 0);
99
        GLES20.glDeleteFramebuffers(1, mFBORenderToTexture, 0);
100
        mFramebufferID = 0;
101
        mTextureID = TEXTURE_FAILED_TO_CREATE;
102
        return false;
103
        }
104
      
105
      mFramebufferID = mFBORenderToTexture[0];
106
      
107
      return true;
108
      }
109
   
110
  //////////////////////////////////////////////////////////////////
111

    
112
    void deleteFBO()
113
      {
114
      int[] textureIds = new int[1];
115
      int[] mFBORenderToTexture = new int[1];
116
     
117
      textureIds[0] = mTextureID;
118
      mFBORenderToTexture[0] = mFramebufferID;
119
      
120
      GLES20.glDeleteTextures(1, textureIds, 0);
121
      GLES20.glDeleteFramebuffers(1, mFBORenderToTexture, 0);
122
      
123
      mFramebufferID = 0;
124
      mTextureID = TEXTURE_NOT_CREATED_YET;
125
      }
126
   }
127
 
128
///////////////////////////////////////////////////////////////////////////////////////////////////
129

    
130
  static void release()
131
    {
132
    mNextNodeID = 0;
133
    mMapNodeID.clear();
134
    }
135

    
136
///////////////////////////////////////////////////////////////////////////////////////////////////
137

    
138
  private void markRecursive()
139
    {
140
    mData.mRendered = false;
141
   
142
    synchronized(this)
143
      {
144
      for(int i=0; i<mNumChildren[0]; i++) mChildren.get(i).markRecursive();
145
      }
146
    }
147
  
148
///////////////////////////////////////////////////////////////////////////////////////////////////
149
  
150
  private void drawRecursive(long currTime, DistortedProjection dp, int mFBO)
151
    {
152
    if( mNumChildren[0]<=0 )
153
      {
154
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mObject.mTextureDataH[0]);
155
      
156
      if( mData.mProjection!=null )
157
        {
158
        mData.deleteFBO();
159
        mData.mProjection = null;
160
        }
161
      }
162
    else
163
      {
164
      if( mData.mRendered==false )
165
        {
166
        mData.mRendered = true;
167
       
168
        if( mData.mTextureID==TEXTURE_NOT_CREATED_YET ) mData.createFBO();
169
       
170
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mData.mFramebufferID);
171
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
172
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
173
      
174
        if( mObject.mBitmapSet[0] )
175
          {
176
          GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mObject.mTextureDataH[0]);
177
          mObject.drawNoEffectsPriv(mData.mProjection);
178
          }
179
      
180
        synchronized(this)
181
          {
182
          for(int i=0; i<mNumChildren[0]; i++)
183
            {
184
            mChildren.get(i).drawRecursive(currTime, mData.mProjection, mData.mFramebufferID);
185
            }
186
          }
187
        }
188
      
189
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFBO);
190
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mData.mTextureID);   // this is safe because we must have called createFBO() above before.     
191
      }
192
    
193
    mObject.drawPriv(currTime, dp);
194
    }
195
  
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197
// tree isomorphism
198
  
199
  private void RecomputeNodeID(ArrayList<Long> prev)
200
    {
201
    ArrayList<Long> curr = generateIDList();
202
     
203
    if( mParent==null )
204
      {
205
      adjustNodeData(prev,curr);
206
      }
207
    else
208
      {
209
      ArrayList<Long> parentPrev = mParent.generateIDList();
210
      adjustNodeData(prev,curr);
211
      mParent.RecomputeNodeID(parentPrev);
212
      }
213
    }
214

    
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216

    
217
  private ArrayList<Long> generateIDList()
218
    {
219
    ArrayList<Long> ret = new ArrayList<>();
220
     
221
    ret.add( mNumChildren[0]>0 ? mObject.getBitmapID() : mObject.getID() );
222
    DistortedNode node;
223
   
224
    for(int i=0; i<mNumChildren[0]; i++)
225
      {
226
      node = mChildren.get(i);
227
      ret.add(node.mData.ID);
228
      }
229
   
230
    return ret;
231
    }
232

    
233
///////////////////////////////////////////////////////////////////////////////////////////////////
234

    
235
  private void adjustNodeData(ArrayList<Long> oldList, ArrayList<Long> newList)
236
    {
237
    boolean otherNodesPoint = (mData.numPointingNodes>1);
238

    
239
    if( otherNodesPoint ) mData.numPointingNodes--;
240
    else                  mMapNodeID.remove(oldList);
241
   
242
    NodeData newData = mMapNodeID.get(newList);
243
    
244
    if( newData==null )
245
      {
246
      if( otherNodesPoint )  mData = new NodeData(++mNextNodeID);
247
      else                   mData.ID = ++mNextNodeID;  // numPointingNodes must be 1 already
248

    
249
      if( newList.size()>1 && mData.mProjection==null )
250
        {     
251
        mData.mProjection = new DistortedProjection(true);
252
        mData.mProjection.onSurfaceChanged(mObject.getWidth(), mObject.getHeight());
253
        mData.mFramebufferID = 0;
254
        mData.mTextureID = TEXTURE_NOT_CREATED_YET;
255
        }
256
       
257
      mMapNodeID.put(newList, mData);
258
      }
259
    else
260
      {
261
      newData.numPointingNodes++;
262
      mData = newData;
263
      }
264
    }
265

    
266
///////////////////////////////////////////////////////////////////////////////////////////////////  
267
// this will be called on startup and every time OpenGL context has been lost
268
// also call this from the constructor if the OpenGL context has been created already.
269
    
270
  static void reset()
271
    {
272
    NodeData tmp;   
273
     
274
    for(ArrayList<Long> key: mMapNodeID.keySet())
275
      {
276
      tmp = mMapNodeID.get(key);
277
          
278
      if( tmp.mProjection!=null )
279
        {
280
    	  tmp.mTextureID = TEXTURE_NOT_CREATED_YET;
281
        tmp.mRendered  = false;
282
        }
283
      }
284
    }
285

    
286
///////////////////////////////////////////////////////////////////////////////////////////////////
287
// Debug - print all the Node IDs
288

    
289
  void debug(int depth)
290
    {
291
    String tmp="";
292
    int i;
293

    
294
    for(i=0; i<depth; i++) tmp +="   ";
295
    tmp += (""+mData.ID);
296

    
297
    android.util.Log.e("node", tmp);
298

    
299
    for(i=0; i<mNumChildren[0]; i++)
300
      mChildren.get(i).debug(depth+1);
301
    }
302

    
303
///////////////////////////////////////////////////////////////////////////////////////////////////
304
// Debug - print contents of the HashMap
305

    
306
  static void debugMap()
307
    {
308
    NodeData tmp;
309

    
310
    for(ArrayList<Long> key: mMapNodeID.keySet())
311
      {
312
      tmp = mMapNodeID.get(key);
313

    
314
      android.util.Log.e("NODE", "key="+key+" NodeID: "+tmp.ID);
315
      }
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319
// PUBLIC API
320
///////////////////////////////////////////////////////////////////////////////////////////////////
321
/**
322
 * Constructs new Node of the Tree.
323
 *     
324
 * @param obj DistortedObject to put into the new Node.
325
 */
326
  public DistortedNode(DistortedObject obj)
327
    {
328
    mObject = obj;
329
    mParent = null;
330
    mChildren = null;
331
    mNumChildren = new int[1];
332
    mNumChildren[0] = 0;
333
   
334
    ArrayList<Long> list = new ArrayList<>();
335
    list.add(obj.getID());
336
      
337
    mData = mMapNodeID.get(list);
338
   
339
    if( mData!=null )
340
      {
341
      mData.numPointingNodes++;
342
      }
343
    else
344
      {
345
      mData = new NodeData(++mNextNodeID);   
346
      mMapNodeID.put(list, mData);  
347
      }
348
    }
349

    
350
///////////////////////////////////////////////////////////////////////////////////////////////////  
351
/**
352
 * Copy-constructs new Node of the Tree from another Node.
353
 *     
354
 * @param node The DistortedNode to copy data from.
355
 * @param flags bit field composed of a subset of the following:
356
 *        {@link Distorted#CLONE_BITMAP},  {@link Distorted#CLONE_PRESHADER}, {@link Distorted#CLONE_VERTEX},
357
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
358
 *        For example flags = CLONE_BITMAP | CLONE_CHILDREN.
359
 */
360
  public DistortedNode(DistortedNode node, int flags)
361
    {
362
    mParent = null;
363
    mObject = node.mObject.deepCopy(flags);
364

    
365
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
366
      {
367
      mChildren = node.mChildren;
368
      mNumChildren = node.mNumChildren;
369
      }
370
    else
371
      {
372
      mChildren = null;
373
      mNumChildren = new int[1];
374
      mNumChildren[0] = 0;
375
      }
376
   
377
    ArrayList<Long> list = generateIDList();
378
   
379
    mData = mMapNodeID.get(list);
380
   
381
    if( mData!=null )
382
      {
383
      mData.numPointingNodes++;
384
      }
385
    else
386
      {
387
      mData = new NodeData(++mNextNodeID);   
388
      mMapNodeID.put(list, mData);
389
      }
390
    }
391
  
392
///////////////////////////////////////////////////////////////////////////////////////////////////
393
/**
394
 * Adds a new child to the last position in the list of our Node's children.
395
 * 
396
 * @param node The new Node to add.
397
 */
398
  public synchronized void attach(DistortedNode node)
399
    {
400
    ArrayList<Long> prev = generateIDList(); 
401
   
402
    if( mChildren==null ) mChildren = new ArrayList<>(2);
403
     
404
    node.mParent = this;
405
    mChildren.add(node);
406
    mNumChildren[0]++;
407
     
408
    RecomputeNodeID(prev);
409
    }
410
   
411
///////////////////////////////////////////////////////////////////////////////////////////////////
412
/**
413
 * Adds a new child to the last position in the list of our Node's children.
414
 * 
415
 * @param obj DistortedObject to initialize our child Node with.
416
 * @return the newly constructed child Node, or null if we couldn't allocate resources.
417
 */
418
  public synchronized DistortedNode attach(DistortedObject obj)
419
    {
420
    ArrayList<Long> prev = generateIDList(); 
421
      
422
    if( mChildren==null ) mChildren = new ArrayList<>(2);
423
    DistortedNode node = new DistortedNode(obj);
424
    node.mParent = this;
425
    mChildren.add(node);
426
    mNumChildren[0]++;
427
   
428
    RecomputeNodeID(prev);
429

    
430
    return node;
431
    }
432
  
433
///////////////////////////////////////////////////////////////////////////////////////////////////
434
/**
435
 * Removes the first occurrence of a specified child from the list of children of our Node.
436
 * 
437
 * @param node The Node to remove.
438
 * @return <code>true</code> if the child was successfully removed.
439
 */
440
  public synchronized boolean detach(DistortedNode node)
441
    {
442
    if( mNumChildren[0]>0 )
443
      {
444
      ArrayList<Long> prev = generateIDList();  
445
         
446
      if( mChildren.remove(node) )
447
        {
448
        node.mParent = null;  
449
        mNumChildren[0]--;
450
     
451
        RecomputeNodeID(prev);
452
     
453
        return true;
454
        }
455
      }
456
   
457
    return false;
458
    }
459
  
460
///////////////////////////////////////////////////////////////////////////////////////////////////
461
/**
462
 * Removes the first occurrence of a specified child from the list of children of our Node.
463
 * 
464
 * @param obj DistortedObject to remove.
465
 * @return <code>true</code> if the child was successfully removed.
466
 */
467
  public synchronized boolean detach(DistortedObject obj)
468
    {
469
    long id = obj.getID();
470
    DistortedNode node;
471
   
472
    for(int i=0; i<mNumChildren[0]; i++)
473
      {
474
      node = mChildren.get(i);
475
     
476
      if( node.mObject.getID()==id )
477
        {
478
        ArrayList<Long> prev = generateIDList();   
479
     
480
        node.mParent = null;  
481
        mChildren.remove(i);
482
        mNumChildren[0]--;
483
      
484
        RecomputeNodeID(prev);
485
      
486
        return true;
487
        }
488
      }
489
   
490
    return false;
491
    }
492
    
493
///////////////////////////////////////////////////////////////////////////////////////////////////
494
/**
495
 * Removes all children Nodes.
496
 */
497
  public synchronized void detachAll()
498
    {
499
    for(int i=0; i<mNumChildren[0]; i++)
500
      {
501
      mChildren.get(i).mParent = null;
502
      }
503
   
504
    if( mNumChildren[0]>0 )
505
      {
506
      ArrayList<Long> prev = generateIDList();  
507
      
508
      mNumChildren[0] = 0;
509
      mChildren.clear();
510
      RecomputeNodeID(prev);
511
      }
512
    }
513
  
514
///////////////////////////////////////////////////////////////////////////////////////////////////
515
/**
516
 * Draws the Node, and all its children, to the default framebuffer 0 (i.e. the screen).
517
 *   
518
 * @param currTime Current time, in milliseconds.
519
 */
520
  public void draw(long currTime)
521
    {  
522
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
523
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
524
    GLES20.glUniform1i(Distorted.mTextureUniformH, 0);  
525
    
526
    markRecursive();
527
    drawRecursive(currTime,Distorted.mProjection,0);
528
    }
529
 
530
///////////////////////////////////////////////////////////////////////////////////////////////////
531
/**
532
 * Returns the DistortedObject object that's in the Node.
533
 * 
534
 * @return The DistortedObject contained in the Node.
535
 */
536
  public DistortedObject getObject()
537
    {
538
    return mObject;
539
    }
540

    
541
///////////////////////////////////////////////////////////////////////////////////////////////////
542
  }
543

    
(6-6/17)