Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedNode.java @ 9351ad55

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
    if( mData.numPointingNodes>1 ) mData.numPointingNodes--;
238
    else                           mMapNodeID.remove(oldList);  
239
   
240
    NodeData newData = mMapNodeID.get(newList);
241
    
242
    if( newData==null )
243
      {
244
      mData.ID = ++mNextNodeID;  
245
      mData.numPointingNodes = 1;
246
     
247
      if( newList.size()>1 && mData.mProjection==null )
248
        {     
249
        mData.mProjection = new DistortedProjection(true);
250
        mData.mProjection.onSurfaceChanged(mObject.getWidth(), mObject.getHeight());
251
        mData.mFramebufferID = 0;
252
        mData.mTextureID = TEXTURE_NOT_CREATED_YET;
253
        }
254
       
255
      mMapNodeID.put(newList, mData);
256
      }
257
    else
258
      {  
259
      newData.numPointingNodes++;
260
      mData = newData;
261
      }
262
    }
263

    
264
///////////////////////////////////////////////////////////////////////////////////////////////////  
265
// this will be called on startup and every time OpenGL context has been lost
266
// also call this from the constructor if the OpenGL context has been created already.
267
    
268
  static void reset()
269
    {
270
    NodeData tmp;   
271
     
272
    for(ArrayList<Long> key: mMapNodeID.keySet())
273
      {
274
      tmp = mMapNodeID.get(key);
275
          
276
      if( tmp.mProjection!=null )
277
        {
278
    	  tmp.mTextureID = TEXTURE_NOT_CREATED_YET;
279
        tmp.mRendered  = false;
280
        }
281
      }
282
    }
283
  
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285
// PUBLIC API
286
///////////////////////////////////////////////////////////////////////////////////////////////////
287
/**
288
 * Constructs new Node of the Tree.
289
 *     
290
 * @param obj DistortedObject to put into the new Node.
291
 */
292
  public DistortedNode(DistortedObject obj)
293
    {
294
    mObject = obj;
295
    mParent = null;
296
    mChildren = null;
297
    mNumChildren = new int[1];
298
    mNumChildren[0] = 0;
299
   
300
    ArrayList<Long> list = new ArrayList<>();
301
    list.add(obj.getID());
302
      
303
    mData = mMapNodeID.get(list);
304
   
305
    if( mData!=null )
306
      {
307
      mData.numPointingNodes++;
308
      }
309
    else
310
      {
311
      mData = new NodeData(++mNextNodeID);   
312
      mMapNodeID.put(list, mData);  
313
      }
314
    }
315

    
316
///////////////////////////////////////////////////////////////////////////////////////////////////  
317
/**
318
 * Copy-constructs new Node of the Tree from another Node.
319
 *     
320
 * @param node The DistortedNode to copy data from.
321
 * @param flags bit field composed of a subset of the following:
322
 *        {@link Distorted#CLONE_BITMAP},  {@link Distorted#CLONE_PRESHADER}, {@link Distorted#CLONE_VERTEX},
323
 *        {@link Distorted#CLONE_FRAGMENT} and {@link Distorted#CLONE_CHILDREN}.
324
 *        For example flags = CLONE_BITMAP | CLONE_CHILDREN.
325
 */
326
  public DistortedNode(DistortedNode node, int flags)
327
    {
328
    mParent = null;
329
    mObject = node.mObject.deepCopy(flags);
330

    
331
    if( (flags & Distorted.CLONE_CHILDREN) != 0 )
332
      {
333
      mChildren = node.mChildren;
334
      mNumChildren = node.mNumChildren;
335
      }
336
    else
337
      {
338
      mChildren = null;
339
      mNumChildren = new int[1];
340
      mNumChildren[0] = 0;
341
      }
342
   
343
    ArrayList<Long> list = generateIDList();
344
   
345
    mData = mMapNodeID.get(list);
346
   
347
    if( mData!=null )
348
      {
349
      mData.numPointingNodes++;
350
      }
351
    else
352
      {
353
      mData = new NodeData(++mNextNodeID);   
354
      mMapNodeID.put(list, mData);
355
      }
356
    }
357
  
358
///////////////////////////////////////////////////////////////////////////////////////////////////
359
/**
360
 * Adds a new child to the last position in the list of our Node's children.
361
 * 
362
 * @param node The new Node to add.
363
 */
364
  public synchronized void attach(DistortedNode node)
365
    {
366
    ArrayList<Long> prev = generateIDList(); 
367
   
368
    if( mChildren==null ) mChildren = new ArrayList<>(2);
369
     
370
    node.mParent = this;
371
    mChildren.add(node);
372
    mNumChildren[0]++;
373
     
374
    RecomputeNodeID(prev);
375
    }
376
   
377
///////////////////////////////////////////////////////////////////////////////////////////////////
378
/**
379
 * Adds a new child to the last position in the list of our Node's children.
380
 * 
381
 * @param obj DistortedObject to initialize our child Node with.
382
 * @return the newly constructed child Node, or null if we couldn't allocate resources.
383
 */
384
  public synchronized DistortedNode attach(DistortedObject obj)
385
    {
386
    ArrayList<Long> prev = generateIDList(); 
387
      
388
    if( mChildren==null ) mChildren = new ArrayList<>(2);
389
    DistortedNode node = new DistortedNode(obj);
390
    node.mParent = this;
391
    mChildren.add(node);
392
    mNumChildren[0]++;
393
   
394
    RecomputeNodeID(prev);
395
   
396
    return node;
397
    }
398
  
399
///////////////////////////////////////////////////////////////////////////////////////////////////
400
/**
401
 * Removes the first occurrence of a specified child from the list of children of our Node.
402
 * 
403
 * @param node The Node to remove.
404
 * @return <code>true</code> if the child was successfully removed.
405
 */
406
  public synchronized boolean detach(DistortedNode node)
407
    {
408
    if( mNumChildren[0]>0 )
409
      {
410
      ArrayList<Long> prev = generateIDList();  
411
         
412
      if( mChildren.remove(node) )
413
        {
414
        node.mParent = null;  
415
        mNumChildren[0]--;
416
     
417
        RecomputeNodeID(prev);
418
     
419
        return true;
420
        }
421
      }
422
   
423
    return false;
424
    }
425
  
426
///////////////////////////////////////////////////////////////////////////////////////////////////
427
/**
428
 * Removes the first occurrence of a specified child from the list of children of our Node.
429
 * 
430
 * @param obj DistortedObject to remove.
431
 * @return <code>true</code> if the child was successfully removed.
432
 */
433
  public synchronized boolean detach(DistortedObject obj)
434
    {
435
    long id = obj.getID();
436
    DistortedNode node;
437
   
438
    for(int i=0; i<mNumChildren[0]; i++)
439
      {
440
      node = mChildren.get(i);
441
     
442
      if( node.mObject.getID()==id )
443
        {
444
        ArrayList<Long> prev = generateIDList();   
445
     
446
        node.mParent = null;  
447
        mChildren.remove(i);
448
        mNumChildren[0]--;
449
      
450
        RecomputeNodeID(prev);
451
      
452
        return true;
453
        }
454
      }
455
   
456
    return false;
457
    }
458
    
459
///////////////////////////////////////////////////////////////////////////////////////////////////
460
/**
461
 * Removes all children Nodes.
462
 */
463
  public synchronized void detachAll()
464
    {
465
    for(int i=0; i<mNumChildren[0]; i++)
466
      {
467
      mChildren.get(i).mParent = null;
468
      }
469
   
470
    if( mNumChildren[0]>0 )
471
      {
472
      ArrayList<Long> prev = generateIDList();  
473
      
474
      mNumChildren[0] = 0;
475
      mChildren.clear();
476
      RecomputeNodeID(prev);
477
      }
478
    }
479
  
480
///////////////////////////////////////////////////////////////////////////////////////////////////
481
/**
482
 * Draws the Node, and all its children, to the default framebuffer 0 (i.e. the screen).
483
 *   
484
 * @param currTime Current time, in milliseconds.
485
 */
486
  public void draw(long currTime)
487
    {  
488
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
489
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
490
    GLES20.glUniform1i(Distorted.mTextureUniformH, 0);  
491
    
492
    markRecursive();
493
    drawRecursive(currTime,Distorted.mProjection,0);
494
    }
495
 
496
///////////////////////////////////////////////////////////////////////////////////////////////////
497
/**
498
 * Returns the DistortedObject object that's in the Node.
499
 * 
500
 * @return The DistortedObject contained in the Node.
501
 */
502
  public DistortedObject getObject()
503
    {
504
    return mObject;
505
    }
506
  
507
///////////////////////////////////////////////////////////////////////////////////////////////////
508
// Debug - print all the Node IDs
509

    
510
  public void debug(int depth)
511
    {
512
    String tmp="";  
513
    int i;
514
   
515
    for(i=0; i<depth; i++) tmp +="   ";
516
    tmp += (""+mData.ID);
517
   
518
    android.util.Log.e("node", tmp);
519
   
520
    for(i=0; i<mNumChildren[0]; i++)
521
      mChildren.get(i).debug(depth+1);
522
    }
523

    
524
///////////////////////////////////////////////////////////////////////////////////////////////////
525
  }
526

    
(4-4/30)