Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedFramebuffer.java @ 484a52a7

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 android.opengl.GLES20;
23
import android.opengl.Matrix;
24

    
25
import java.util.Iterator;
26
import java.util.LinkedList;
27

    
28
///////////////////////////////////////////////////////////////////////////////////////////////////
29
/**
30
 * Class which represents a OpenGL Framebuffer object.
31
 * <p>
32
 * User is able to create either Framebuffers from objects already constructed outside
33
 * of the library (the first constructor; primary use case: the screen) or an offscreen
34
 * FBOs (used by the DistortedTree, but also can be used by external users of the library)
35
 * <p>
36
 * Also, keep all objects created in a static LinkedList. The point: we need to be able to mark
37
 * Framebuffers for deletion, and delete all marked objects later at a convenient time (that's
38
 * because we can only delete from a thread that holds the OpenGL context so here we provide a
39
 * framework where one is able to mark for deletion at any time and actual deletion takes place
40
 * on the next render).
41
 */
42
public class DistortedFramebuffer
43
  {
44
  private static final int TEXTURE_FAILED_TO_CREATE = -1;
45
  private static final int TEXTURE_NOT_CREATED_YET  = -2;
46
  private static final int TEXTURE_DONT_CREATE      = -3;
47

    
48
  private static boolean mListMarked = false;
49
  private static LinkedList<DistortedFramebuffer> mList = new LinkedList<>();
50

    
51
  private float mX, mY, mFOV;
52

    
53
  int[] texIds = new int[1];
54
  int[] fboIds = new int[1];
55

    
56
  private boolean mMarked;
57

    
58
  int mWidth,mHeight,mDepth;
59
  float mDistance;
60
  float[] mProjectionMatrix;
61

    
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63
// Must be called from a thread holding OpenGL Context
64

    
65
  boolean createFBO()
66
    {
67
    if( texIds[0]==TEXTURE_NOT_CREATED_YET )
68
      {
69
      GLES20.glGenTextures(1, texIds, 0);
70
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texIds[0]);
71
      GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
72
      GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
73
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
74
      GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
75
      GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mWidth, mHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
76

    
77
      GLES20.glGenFramebuffers(1, fboIds, 0);
78
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
79
      GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texIds[0], 0);
80

    
81
      int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
82

    
83
      if(status != GLES20.GL_FRAMEBUFFER_COMPLETE)
84
        {
85
        android.util.Log.e("DistortedFramebuffer", "failed to create framebuffer, error="+status);
86
        GLES20.glDeleteTextures(1, texIds, 0);
87
        GLES20.glDeleteFramebuffers(1, fboIds, 0);
88
        fboIds[0] = 0;
89
        texIds[0] = TEXTURE_FAILED_TO_CREATE;
90
        return false;
91
        }
92

    
93
      mList.add(this);
94
      //android.util.Log.e("FBO", "created ("+mWidth+","+mHeight+") "+fboIds[0]);
95
      }
96

    
97
    return true;
98
    }
99

    
100
///////////////////////////////////////////////////////////////////////////////////////////////////
101
// Must be called from a thread holding OpenGL Context
102

    
103
  private void deleteFBO()
104
    {
105
    if( texIds[0]>=0 )
106
      {
107
      //android.util.Log.e("FBO", "deleting ("+mWidth+","+mHeight+") "+fboIds[0]);
108

    
109
      GLES20.glDeleteTextures(1, texIds, 0);
110
      GLES20.glDeleteFramebuffers(1, fboIds, 0);
111

    
112
      fboIds[0] = 0;
113
      texIds[0] = TEXTURE_NOT_CREATED_YET;
114
      }
115

    
116
    mMarked = false;
117
    }
118

    
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120

    
121
  void reset()
122
    {
123
    if( texIds[0]!=TEXTURE_DONT_CREATE)
124
      texIds[0] = TEXTURE_NOT_CREATED_YET;
125
    }
126

    
127
///////////////////////////////////////////////////////////////////////////////////////////////////
128

    
129
  private void createProjection()
130
    {
131
    if( mWidth>0 && mHeight>1 )
132
      {
133
      if( mFOV>0.0f )  // perspective projection
134
        {
135
        float left   = (-mX-mWidth /2.0f)/mHeight;
136
        float right  = (-mX+mWidth /2.0f)/mHeight;
137
        float bottom = (-mY-mHeight/2.0f)/mHeight;
138
        float top    = (-mY+mHeight/2.0f)/mHeight;
139
        float near   = (top-bottom) / (2.0f*(float)Math.tan(mFOV*Math.PI/360));
140
        mDistance    = mHeight*near/(top-bottom);
141
        float far    = 2*mDistance-near;
142
        mDepth       = (int)((far-near)/2);
143

    
144
        Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
145
        }
146
      else             // parallel projection
147
        {
148
        float left   = -mX-mWidth /2.0f;
149
        float right  = -mX+mWidth /2.0f;
150
        float bottom = -mY-mHeight/2.0f;
151
        float top    = -mY+mHeight/2.0f;
152
        float near   = (mWidth+mHeight)/2;
153
        mDistance    = 2*near;
154
        float far    = 3*near;
155
        mDepth       = (int)near;
156

    
157
        Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
158
        }
159
      }
160
    }
161

    
162
///////////////////////////////////////////////////////////////////////////////////////////////////
163

    
164
  static synchronized void onDestroy()
165
    {
166
    // There are issues with this. Namely, if one
167
    // 1. creates a DObjectTree (somewhere else than onSurfaceCreated of constructor so it does not get re-created on re-launch)
168
    // 2. exits the app (here mList would be cleared)
169
    // 3. re-launches the app
170
    // 4. deletes some nodes
171
    // then the underlying Framebuffers will never be deleted!
172

    
173
    mListMarked = false;
174
    mList.clear();
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178
// must be called form a thread holding OpenGL Context
179

    
180
  static synchronized void deleteAllMarked()
181
    {
182
    if( mListMarked )
183
      {
184
      DistortedFramebuffer tmp;
185
      Iterator<DistortedFramebuffer> iterator = mList.iterator();
186

    
187
      while(iterator.hasNext())
188
        {
189
        tmp = iterator.next();
190

    
191
        if( tmp.mMarked )
192
          {
193
          tmp.deleteFBO();
194
          iterator.remove();
195
          }
196
        }
197

    
198
      mListMarked = false;
199
      }
200
    }
201

    
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203
// PUBLIC API
204
///////////////////////////////////////////////////////////////////////////////////////////////////
205
/**
206
 * Create a new offscreen Framebuffer.
207
 *
208
 * @param width Width of the COLOR attachment.
209
 * @param height Height of the COLOR attachment.
210
 */
211
  @SuppressWarnings("unused")
212
  public DistortedFramebuffer(int width, int height)
213
    {
214
    mProjectionMatrix = new float[16];
215

    
216
    mHeight  = height;
217
    mWidth   = width;
218
    fboIds[0]= -1;
219
    texIds[0]= TEXTURE_NOT_CREATED_YET;
220
    mFOV     = 60.0f;
221
    mX       = 0.0f;
222
    mY       = 0.0f;
223
    mMarked  = false;
224

    
225
    createProjection();
226
    }
227

    
228
///////////////////////////////////////////////////////////////////////////////////////////////////
229
/**
230
 * Create a new Framebuffer from an already created OpenGL Framebuffer.
231
 * <p>
232
 * Has to be followed by a 'resize()' to set the size.
233
 *
234
 * @param fbo the ID of a OpenGL Framebuffer object. Typically 0 (the screen)
235
 */
236
  public DistortedFramebuffer(int fbo)
237
    {
238
    mProjectionMatrix = new float[16];
239

    
240
    fboIds[0]= fbo;
241
    texIds[0]= TEXTURE_DONT_CREATE;
242
    mFOV     = 60.0f;
243
    mX       = 0.0f;
244
    mY       = 0.0f;
245
    mMarked  = false;
246
    }
247

    
248
///////////////////////////////////////////////////////////////////////////////////////////////////
249
/**
250
 * Draw the (texture,grid,effects) object to the Framebuffer.
251
 * <p>
252
 * Must be called from a thread holding OpenGL Context
253
 *
254
 * @param tex input Texture to use.
255
 * @param grid Class descendant from GridObject
256
 * @param effects The DistortedEffects to use when rendering
257
 * @param time Current time, in milliseconds.
258
 */
259
  public void renderTo(DistortedTexture tex, GridObject grid, DistortedEffects effects, long time)
260
    {
261
    tex.createTexture();
262
    createFBO();
263
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
264
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.mTextureDataH[0]);
265
    effects.drawPriv(tex.mHalfX, tex.mHalfY, grid, this, time);
266
    DistortedFramebuffer.deleteAllMarked();
267
    DistortedTexture.deleteAllMarked();
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271
/**
272
 * Draw the (framebuffer,grid,effects) object to the Framebuffer.
273
 * <p>
274
 * Must be called from a thread holding OpenGL Context
275
 *
276
 * @param fbo The Framebuffer whose COLOR attachment 0 will be used as input texture.
277
 *            Please note that rendering from an FBO to itself is unsupported by OpenGL!
278
 * @param grid Class descendant from GridObject
279
 * @param effects The DistortedEffects to use when rendering
280
 * @param time Current time, in milliseconds.
281
 */
282
  public void renderTo(DistortedFramebuffer fbo, GridObject grid, DistortedEffects effects, long time)
283
    {
284
    fbo.createFBO();
285

    
286
    if( fbo.texIds[0]>=0 )    // we cannot (yet?) render to FBO created with the second constructor.
287
      {
288
      createFBO();
289
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
290
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fbo.texIds[0]);
291
      effects.drawPriv(fbo.mWidth/2, fbo.mHeight/2, grid, this, time);
292
      DistortedFramebuffer.deleteAllMarked();
293
      DistortedTexture.deleteAllMarked();
294
      }
295
    }
296

    
297
///////////////////////////////////////////////////////////////////////////////////////////////////
298
/**
299
 * Draws the Tree, and all its children, to the Framebuffer.
300
 * <p>
301
 * Must be called from a thread holding OpenGL Context
302
 *
303
 * @param dt DistortedTree to render.
304
 * @param time Current time, in milliseconds. This will be passed to all the Effects stored in the Tree.
305
 */
306
  public void renderTo(DistortedTree dt, long time)
307
    {
308
    createFBO();
309
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
310
    dt.drawRecursive(time,this);
311
    DistortedFramebuffer.deleteAllMarked();
312
    DistortedTexture.deleteAllMarked();
313
    }
314

    
315
///////////////////////////////////////////////////////////////////////////////////////////////////
316
/**
317
 * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
318
 */
319
  public void markForDeletion()
320
    {
321
    //android.util.Log.e("FBO", "marking for deletion ("+mWidth+","+mHeight+") "+fboIds[0]);
322

    
323
    mListMarked = true;
324
    mMarked     = true;
325
    }
326

    
327
///////////////////////////////////////////////////////////////////////////////////////////////////
328
/**
329
 * Create new Projection matrix.
330
 *
331
 * @param fov Vertical 'field of view' of the Projection frustrum (in degrees).
332
 * @param x X-coordinate of the point at which our camera looks at. 0 is the center.
333
 * @param y Y-coordinate of the point at which our camera looks at. 0 is the center.
334
 */
335
  public void setProjection(float fov, float x, float y)
336
    {
337
    mFOV = fov;
338
    mX   = x;
339
    mY   = y;
340

    
341
    createProjection();
342
    }
343

    
344
///////////////////////////////////////////////////////////////////////////////////////////////////
345
/**
346
 * Resize the underlying Framebuffer.
347
 *
348
 * As the Framebuffer is not created until the first render, typical usage of this API is actually
349
 * to set the size of an not-yet-created Framebuffer of an object that has been created with the
350
 * second constructor.
351
 * <p>
352
 * Fully creating an object, rendering to it, then resizing mid-render is also possible. Actual
353
 * resize takes place on the next render.
354
 *
355
 * @param width The new width.
356
 * @param height The new height.
357
 */
358
  public void resize(int width, int height)
359
    {
360
    if( mWidth!=width || mHeight!=height )
361
      {
362
      mWidth = width;
363
      mHeight= height;
364

    
365
      createProjection();
366

    
367
      if( texIds[0]>0 ) markForDeletion();
368
      }
369
    }
370

    
371
///////////////////////////////////////////////////////////////////////////////////////////////////
372
/**
373
 * Return the ID of the Texture (COLOR attachment 0) that's backing this FBO.
374
 * <p>
375
 * Catch: this will only work if the library has had time to actually create the texture. Remember
376
 * that the texture gets created only on first render, thus creating a Texture object and immediately
377
 * calling this method will return an invalid (negative) result.
378
 *
379
 * @returns If there was not a single render between creation of the Object and calling this method on
380
 *          it, return a negative value. Otherwise, return ID of COLOR attachment 0.
381
 */
382
  public int getTextureID()
383
    {
384
    return texIds[0];
385
    }
386
  }
(3-3/15)