Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedFramebuffer.java @ 05403bba

1 f6fb3c6d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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 8e34674e Leszek Koltunski
import java.util.Iterator;
26
import java.util.LinkedList;
27
28 f6fb3c6d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
29 dedacd82 Leszek Koltunski
/**
30
 * Class which represents a OpenGL Framebuffer object.
31 71c3fecc Leszek Koltunski
 * <p>
32 65362dd4 Leszek Koltunski
 * 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 e0b6c593 Leszek Koltunski
 * FBOs.
35 71c3fecc Leszek Koltunski
 * <p>
36 e0b6c593 Leszek Koltunski
 * Keep all objects created in a static LinkedList. The point: we need to be able to mark
37 8e34674e Leszek Koltunski
 * 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 65362dd4 Leszek Koltunski
 * framework where one is able to mark for deletion at any time and actual deletion takes place
40 8e34674e Leszek Koltunski
 * on the next render).
41 dedacd82 Leszek Koltunski
 */
42 ed13a5de Leszek Koltunski
public class DistortedFramebuffer
43 f6fb3c6d Leszek Koltunski
  {
44
  private static final int TEXTURE_FAILED_TO_CREATE = -1;
45
  private static final int TEXTURE_NOT_CREATED_YET  = -2;
46 b448e6b9 Leszek Koltunski
  private static final int TEXTURE_DONT_CREATE      = -3;
47 f6fb3c6d Leszek Koltunski
48 8e34674e Leszek Koltunski
  private static boolean mListMarked = false;
49
  private static LinkedList<DistortedFramebuffer> mList = new LinkedList<>();
50
51 da99dd30 Leszek Koltunski
  private int[] texIds = new int[1];
52
  private int[] fboIds = new int[1];
53 7304d89f Leszek Koltunski
54 8e34674e Leszek Koltunski
  private boolean mMarked;
55
56 da99dd30 Leszek Koltunski
  // Projection stuff
57
  private float mX, mY, mFOV;
58 e6cf7d50 Leszek Koltunski
  int mWidth,mHeight,mDepth;
59
  float mDistance;
60 f6fb3c6d Leszek Koltunski
  float[] mProjectionMatrix;
61 bd3da5b2 Leszek Koltunski
62 f6fb3c6d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
63 6537ba91 Leszek Koltunski
// Must be called from a thread holding OpenGL Context
64 f6fb3c6d Leszek Koltunski
65 7cf783cb Leszek Koltunski
  boolean createFBO()
66 f6fb3c6d Leszek Koltunski
    {
67 7cf783cb Leszek Koltunski
    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 f6fb3c6d Leszek Koltunski
77 7cf783cb Leszek Koltunski
      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 dedacd82 Leszek Koltunski
81 7cf783cb Leszek Koltunski
      int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
82 f6fb3c6d Leszek Koltunski
83 7cf783cb Leszek Koltunski
      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 f6fb3c6d Leszek Koltunski
93 7cf783cb Leszek Koltunski
      mList.add(this);
94
      //android.util.Log.e("FBO", "created ("+mWidth+","+mHeight+") "+fboIds[0]);
95
      }
96 16d8b8f3 Leszek Koltunski
97 f6fb3c6d Leszek Koltunski
    return true;
98
    }
99
100
///////////////////////////////////////////////////////////////////////////////////////////////////
101 6537ba91 Leszek Koltunski
// Must be called from a thread holding OpenGL Context
102 f6fb3c6d Leszek Koltunski
103
  private void deleteFBO()
104
    {
105 e6cf7d50 Leszek Koltunski
    if( texIds[0]>=0 )
106
      {
107 46e25345 Leszek Koltunski
      //android.util.Log.e("FBO", "deleting ("+mWidth+","+mHeight+") "+fboIds[0]);
108 f6fb3c6d Leszek Koltunski
109 e6cf7d50 Leszek Koltunski
      GLES20.glDeleteTextures(1, texIds, 0);
110
      GLES20.glDeleteFramebuffers(1, fboIds, 0);
111 bd3da5b2 Leszek Koltunski
112 e6cf7d50 Leszek Koltunski
      fboIds[0] = 0;
113
      texIds[0] = TEXTURE_NOT_CREATED_YET;
114
      }
115 bd3da5b2 Leszek Koltunski
116
    mMarked = false;
117
    }
118
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120
121
  void reset()
122
    {
123 1942537e Leszek Koltunski
    if( texIds[0]!=TEXTURE_DONT_CREATE)
124
      texIds[0] = TEXTURE_NOT_CREATED_YET;
125 bd3da5b2 Leszek Koltunski
    }
126
127 da99dd30 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
128
129
  void setAsOutput()
130
    {
131
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
132
    }
133
134
///////////////////////////////////////////////////////////////////////////////////////////////////
135
136
  void setAsInput()
137
    {
138
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texIds[0]);
139
    }
140
141 09d4f4b1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
142
143
  private void createProjection()
144
    {
145
    if( mWidth>0 && mHeight>1 )
146
      {
147
      if( mFOV>0.0f )  // perspective projection
148
        {
149
        float left   = (-mX-mWidth /2.0f)/mHeight;
150
        float right  = (-mX+mWidth /2.0f)/mHeight;
151
        float bottom = (-mY-mHeight/2.0f)/mHeight;
152
        float top    = (-mY+mHeight/2.0f)/mHeight;
153
        float near   = (top-bottom) / (2.0f*(float)Math.tan(mFOV*Math.PI/360));
154
        mDistance    = mHeight*near/(top-bottom);
155
        float far    = 2*mDistance-near;
156
        mDepth       = (int)((far-near)/2);
157
158
        Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
159
        }
160
      else             // parallel projection
161
        {
162
        float left   = -mX-mWidth /2.0f;
163
        float right  = -mX+mWidth /2.0f;
164
        float bottom = -mY-mHeight/2.0f;
165
        float top    = -mY+mHeight/2.0f;
166
        float near   = (mWidth+mHeight)/2;
167
        mDistance    = 2*near;
168
        float far    = 3*near;
169
        mDepth       = (int)near;
170
171
        Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
172
        }
173
      }
174
    }
175
176 8e34674e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
177
178 7b8086eb Leszek Koltunski
  static synchronized void onDestroy()
179 8e34674e Leszek Koltunski
    {
180 7cf783cb Leszek Koltunski
    // There are issues with this. Namely, if one
181
    // 1. creates a DObjectTree (somewhere else than onSurfaceCreated of constructor so it does not get re-created on re-launch)
182 80ae684e Leszek Koltunski
    // 2. exits the app (here mList would be cleared)
183 7cf783cb Leszek Koltunski
    // 3. re-launches the app
184
    // 4. deletes some nodes
185
    // then the underlying Framebuffers will never be deleted!
186
187 8e34674e Leszek Koltunski
    mListMarked = false;
188
    mList.clear();
189
    }
190
191
///////////////////////////////////////////////////////////////////////////////////////////////////
192
// must be called form a thread holding OpenGL Context
193
194
  static synchronized void deleteAllMarked()
195
    {
196
    if( mListMarked )
197
      {
198
      DistortedFramebuffer tmp;
199
      Iterator<DistortedFramebuffer> iterator = mList.iterator();
200
201
      while(iterator.hasNext())
202
        {
203
        tmp = iterator.next();
204
205
        if( tmp.mMarked )
206
          {
207 e6cf7d50 Leszek Koltunski
          tmp.deleteFBO();
208 8e34674e Leszek Koltunski
          iterator.remove();
209
          }
210
        }
211
212
      mListMarked = false;
213
      }
214
    }
215 57578636 Leszek Koltunski
216 f6fb3c6d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
217
// PUBLIC API
218 dedacd82 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
219
/**
220 57578636 Leszek Koltunski
 * Create a new offscreen Framebuffer.
221 dedacd82 Leszek Koltunski
 *
222
 * @param width Width of the COLOR attachment.
223
 * @param height Height of the COLOR attachment.
224
 */
225 1942537e Leszek Koltunski
  @SuppressWarnings("unused")
226 ed13a5de Leszek Koltunski
  public DistortedFramebuffer(int width, int height)
227 f6fb3c6d Leszek Koltunski
    {
228
    mProjectionMatrix = new float[16];
229
230 57578636 Leszek Koltunski
    mHeight  = height;
231
    mWidth   = width;
232
    fboIds[0]= -1;
233
    texIds[0]= TEXTURE_NOT_CREATED_YET;
234
    mFOV     = 60.0f;
235
    mX       = 0.0f;
236
    mY       = 0.0f;
237
    mMarked  = false;
238 bd3da5b2 Leszek Koltunski
239 f6fb3c6d Leszek Koltunski
    createProjection();
240
    }
241
242 b448e6b9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
243 dedacd82 Leszek Koltunski
/**
244 57578636 Leszek Koltunski
 * Create a new Framebuffer from an already created OpenGL Framebuffer.
245
 * <p>
246
 * Has to be followed by a 'resize()' to set the size.
247 dedacd82 Leszek Koltunski
 *
248
 * @param fbo the ID of a OpenGL Framebuffer object. Typically 0 (the screen)
249
 */
250 ed13a5de Leszek Koltunski
  public DistortedFramebuffer(int fbo)
251 b448e6b9 Leszek Koltunski
    {
252
    mProjectionMatrix = new float[16];
253
254 57578636 Leszek Koltunski
    fboIds[0]= fbo;
255
    texIds[0]= TEXTURE_DONT_CREATE;
256
    mFOV     = 60.0f;
257
    mX       = 0.0f;
258
    mY       = 0.0f;
259
    mMarked  = false;
260 b448e6b9 Leszek Koltunski
    }
261
262 a0f644b7 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
263
/**
264 05403bba Leszek Koltunski
 * Draw the (texture,mesh,effects) object to the Framebuffer.
265 a0f644b7 Leszek Koltunski
 * <p>
266 d254d550 Leszek Koltunski
 * Must be called from a thread holding OpenGL Context.
267 a0f644b7 Leszek Koltunski
 *
268
 * @param tex input Texture to use.
269 05403bba Leszek Koltunski
 * @param mesh Class descendant from MeshObject
270 a0f644b7 Leszek Koltunski
 * @param effects The DistortedEffects to use when rendering
271
 * @param time Current time, in milliseconds.
272
 */
273 05403bba Leszek Koltunski
  public void renderTo(DistortedTexture tex, MeshObject mesh, DistortedEffects effects, long time)
274 a0f644b7 Leszek Koltunski
    {
275
    tex.createTexture();
276 e7a20702 Leszek Koltunski
    tex.setAsInput();
277 a0f644b7 Leszek Koltunski
    createFBO();
278
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
279 05403bba Leszek Koltunski
    effects.drawPriv(tex.mHalfX, tex.mHalfY, mesh, this, time);
280 a0f644b7 Leszek Koltunski
    DistortedFramebuffer.deleteAllMarked();
281
    DistortedTexture.deleteAllMarked();
282
    }
283
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285 52358355 Leszek Koltunski
/**
286 05403bba Leszek Koltunski
 * Draw the (framebuffer,mesh,effects) object to the Framebuffer.
287 52358355 Leszek Koltunski
 * <p>
288 d254d550 Leszek Koltunski
 * Must be called from a thread holding OpenGL Context.
289 52358355 Leszek Koltunski
 *
290 432442f9 Leszek Koltunski
 * @param fbo The Framebuffer (previously created with the first constructor, drawing FROM the screen
291 a0700272 Leszek Koltunski
 *            is unsupported!) whose COLOR attachment 0 will be used as input texture.
292 484a52a7 Leszek Koltunski
 *            Please note that rendering from an FBO to itself is unsupported by OpenGL!
293 05403bba Leszek Koltunski
 * @param mesh Class descendant from MeshObject
294 52358355 Leszek Koltunski
 * @param effects The DistortedEffects to use when rendering
295
 * @param time Current time, in milliseconds.
296
 */
297 05403bba Leszek Koltunski
  public void renderTo(DistortedFramebuffer fbo, MeshObject mesh, DistortedEffects effects, long time)
298 52358355 Leszek Koltunski
    {
299
    fbo.createFBO();
300
301 a0700272 Leszek Koltunski
    if( fbo.texIds[0]>0 )    // fbo created with the first constructor
302 52358355 Leszek Koltunski
      {
303
      createFBO();
304
      GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
305
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fbo.texIds[0]);
306 05403bba Leszek Koltunski
      effects.drawPriv(fbo.mWidth/2, fbo.mHeight/2, mesh, this, time);
307 52358355 Leszek Koltunski
      DistortedFramebuffer.deleteAllMarked();
308
      DistortedTexture.deleteAllMarked();
309
      }
310
    }
311
312
///////////////////////////////////////////////////////////////////////////////////////////////////
313 a0f644b7 Leszek Koltunski
/**
314
 * Draws the Tree, and all its children, to the Framebuffer.
315
 * <p>
316 d254d550 Leszek Koltunski
 * Must be called from a thread holding OpenGL Context.
317 a0f644b7 Leszek Koltunski
 *
318
 * @param dt DistortedTree to render.
319
 * @param time Current time, in milliseconds. This will be passed to all the Effects stored in the Tree.
320
 */
321
  public void renderTo(DistortedTree dt, long time)
322
    {
323
    createFBO();
324
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
325
    dt.drawRecursive(time,this);
326
    DistortedFramebuffer.deleteAllMarked();
327
    DistortedTexture.deleteAllMarked();
328
    }
329
330 f6fb3c6d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
331 dedacd82 Leszek Koltunski
/**
332
 * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
333
 */
334 bd3da5b2 Leszek Koltunski
  public void markForDeletion()
335 f6fb3c6d Leszek Koltunski
    {
336 46e25345 Leszek Koltunski
    //android.util.Log.e("FBO", "marking for deletion ("+mWidth+","+mHeight+") "+fboIds[0]);
337 16d8b8f3 Leszek Koltunski
338 8e34674e Leszek Koltunski
    mListMarked = true;
339
    mMarked     = true;
340 f6fb3c6d Leszek Koltunski
    }
341
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343 dedacd82 Leszek Koltunski
/**
344
 * Create new Projection matrix.
345
 *
346
 * @param fov Vertical 'field of view' of the Projection frustrum (in degrees).
347 46a52b78 Leszek Koltunski
 * @param x X-coordinate of the point at which our camera looks at. 0 is the center.
348
 * @param y Y-coordinate of the point at which our camera looks at. 0 is the center.
349 dedacd82 Leszek Koltunski
 */
350
  public void setProjection(float fov, float x, float y)
351 f6fb3c6d Leszek Koltunski
    {
352 dedacd82 Leszek Koltunski
    mFOV = fov;
353 f6fb3c6d Leszek Koltunski
    mX   = x;
354
    mY   = y;
355
356
    createProjection();
357
    }
358
359
///////////////////////////////////////////////////////////////////////////////////////////////////
360 dedacd82 Leszek Koltunski
/**
361
 * Resize the underlying Framebuffer.
362
 *
363
 * As the Framebuffer is not created until the first render, typical usage of this API is actually
364
 * to set the size of an not-yet-created Framebuffer of an object that has been created with the
365
 * second constructor.
366 71c3fecc Leszek Koltunski
 * <p>
367 65362dd4 Leszek Koltunski
 * Fully creating an object, rendering to it, then resizing mid-render is also possible. Actual
368
 * resize takes place on the next render.
369 dedacd82 Leszek Koltunski
 *
370
 * @param width The new width.
371
 * @param height The new height.
372
 */
373 f6fb3c6d Leszek Koltunski
  public void resize(int width, int height)
374
    {
375 bd3da5b2 Leszek Koltunski
    if( mWidth!=width || mHeight!=height )
376
      {
377
      mWidth = width;
378
      mHeight= height;
379 f6fb3c6d Leszek Koltunski
380 bd3da5b2 Leszek Koltunski
      createProjection();
381 f6fb3c6d Leszek Koltunski
382 7304d89f Leszek Koltunski
      if( texIds[0]>0 ) markForDeletion();
383 bd3da5b2 Leszek Koltunski
      }
384 f6fb3c6d Leszek Koltunski
    }
385 d1e740c5 Leszek Koltunski
386
///////////////////////////////////////////////////////////////////////////////////////////////////
387
/**
388 09d4f4b1 Leszek Koltunski
 * Return the ID of the Texture (COLOR attachment 0) that's backing this FBO.
389
 * <p>
390
 * Catch: this will only work if the library has had time to actually create the texture. Remember
391
 * that the texture gets created only on first render, thus creating a Texture object and immediately
392
 * calling this method will return an invalid (negative) result.
393
 *
394 ab12f06b Leszek Koltunski
 * @return If there was not a single render between creation of the Object and calling this method on
395
 *         it, return a negative value. Otherwise, return ID of COLOR attachment 0.
396 d1e740c5 Leszek Koltunski
 */
397 09d4f4b1 Leszek Koltunski
  public int getTextureID()
398 d1e740c5 Leszek Koltunski
    {
399 09d4f4b1 Leszek Koltunski
    return texIds[0];
400 d1e740c5 Leszek Koltunski
    }
401 f6fb3c6d Leszek Koltunski
  }