Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedFramebuffer.java @ 09d4f4b1

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 b455eb48 Leszek Koltunski
 * FBOs (used by the DistortedObjectTree, but also can be used by external users of the library)
35 71c3fecc Leszek Koltunski
 * <p>
36 8e34674e Leszek Koltunski
 * 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 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 1942537e Leszek Koltunski
  private float mX, mY, mFOV;
52 f6fb3c6d Leszek Koltunski
53 7304d89f Leszek Koltunski
  private int[] texIds = new int[1];
54
  private int[] fboIds = new int[1];
55
56 8e34674e Leszek Koltunski
  private boolean mMarked;
57
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
  private boolean createFBO()
66
    {
67 64a642c1 Leszek Koltunski
    GLES20.glGenTextures(1, texIds, 0);
68 7304d89f Leszek Koltunski
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texIds[0]);
69 f6fb3c6d Leszek Koltunski
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
70
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
71
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
72
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
73
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mWidth, mHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
74
75 64a642c1 Leszek Koltunski
    GLES20.glGenFramebuffers(1, fboIds, 0);
76
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
77 7304d89f Leszek Koltunski
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texIds[0], 0);
78 dedacd82 Leszek Koltunski
79 f6fb3c6d Leszek Koltunski
    int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
80
81
    if(status != GLES20.GL_FRAMEBUFFER_COMPLETE)
82
      {
83 64a642c1 Leszek Koltunski
      android.util.Log.e("DistortedFramebuffer", "failed to create framebuffer, error="+status);
84
      GLES20.glDeleteTextures(1, texIds, 0);
85
      GLES20.glDeleteFramebuffers(1, fboIds, 0);
86 7304d89f Leszek Koltunski
      fboIds[0] = 0;
87
      texIds[0] = TEXTURE_FAILED_TO_CREATE;
88 f6fb3c6d Leszek Koltunski
      return false;
89
      }
90
91 8e34674e Leszek Koltunski
    mList.add(this);
92 46e25345 Leszek Koltunski
    //android.util.Log.e("FBO", "created ("+mWidth+","+mHeight+") "+fboIds[0]);
93 16d8b8f3 Leszek Koltunski
94 f6fb3c6d Leszek Koltunski
    return true;
95
    }
96
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98 6537ba91 Leszek Koltunski
// Must be called from a thread holding OpenGL Context
99 f6fb3c6d Leszek Koltunski
100
  private void deleteFBO()
101
    {
102 e6cf7d50 Leszek Koltunski
    if( texIds[0]>=0 )
103
      {
104 46e25345 Leszek Koltunski
      //android.util.Log.e("FBO", "deleting ("+mWidth+","+mHeight+") "+fboIds[0]);
105 f6fb3c6d Leszek Koltunski
106 e6cf7d50 Leszek Koltunski
      GLES20.glDeleteTextures(1, texIds, 0);
107
      GLES20.glDeleteFramebuffers(1, fboIds, 0);
108 bd3da5b2 Leszek Koltunski
109 e6cf7d50 Leszek Koltunski
      fboIds[0] = 0;
110
      texIds[0] = TEXTURE_NOT_CREATED_YET;
111
      }
112 bd3da5b2 Leszek Koltunski
113
    mMarked = false;
114
    }
115
116 09d4f4b1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
117
// Must be called from a thread holding OpenGL Context
118
119
  void setAsOutput()
120
    {
121
    if( texIds[0]==TEXTURE_NOT_CREATED_YET ) createFBO();
122
123
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
124
    }
125
126
///////////////////////////////////////////////////////////////////////////////////////////////////
127
// Must be called from a thread holding OpenGL Context
128
129
  void setAsInput()
130
    {
131
    if( texIds[0]==TEXTURE_NOT_CREATED_YET ) createFBO();
132
133
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texIds[0]);
134
    }
135
136 bd3da5b2 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
137
138
  void reset()
139
    {
140 1942537e Leszek Koltunski
    if( texIds[0]!=TEXTURE_DONT_CREATE)
141
      texIds[0] = TEXTURE_NOT_CREATED_YET;
142 bd3da5b2 Leszek Koltunski
    }
143
144 09d4f4b1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
145
146
  private void createProjection()
147
    {
148
    if( mWidth>0 && mHeight>1 )
149
      {
150
      if( mFOV>0.0f )  // perspective projection
151
        {
152
        float left   = (-mX-mWidth /2.0f)/mHeight;
153
        float right  = (-mX+mWidth /2.0f)/mHeight;
154
        float bottom = (-mY-mHeight/2.0f)/mHeight;
155
        float top    = (-mY+mHeight/2.0f)/mHeight;
156
        float near   = (top-bottom) / (2.0f*(float)Math.tan(mFOV*Math.PI/360));
157
        mDistance    = mHeight*near/(top-bottom);
158
        float far    = 2*mDistance-near;
159
        mDepth       = (int)((far-near)/2);
160
161
        Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
162
        }
163
      else             // parallel projection
164
        {
165
        float left   = -mX-mWidth /2.0f;
166
        float right  = -mX+mWidth /2.0f;
167
        float bottom = -mY-mHeight/2.0f;
168
        float top    = -mY+mHeight/2.0f;
169
        float near   = (mWidth+mHeight)/2;
170
        mDistance    = 2*near;
171
        float far    = 3*near;
172
        mDepth       = (int)near;
173
174
        Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
175
        }
176
      }
177
    }
178
179 8e34674e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
180
181
  static synchronized void release()
182
    {
183
    mListMarked = false;
184
    mList.clear();
185
    }
186
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188
// must be called form a thread holding OpenGL Context
189
190
  static synchronized void deleteAllMarked()
191
    {
192
    if( mListMarked )
193
      {
194
      DistortedFramebuffer tmp;
195
      Iterator<DistortedFramebuffer> iterator = mList.iterator();
196
197
      while(iterator.hasNext())
198
        {
199
        tmp = iterator.next();
200
201
        if( tmp.mMarked )
202
          {
203 e6cf7d50 Leszek Koltunski
          tmp.deleteFBO();
204 8e34674e Leszek Koltunski
          iterator.remove();
205
          }
206
        }
207
208
      mListMarked = false;
209
      }
210
    }
211 57578636 Leszek Koltunski
212 f6fb3c6d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
213
// PUBLIC API
214 dedacd82 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
215
/**
216 57578636 Leszek Koltunski
 * Create a new offscreen Framebuffer.
217 dedacd82 Leszek Koltunski
 *
218
 * @param width Width of the COLOR attachment.
219
 * @param height Height of the COLOR attachment.
220
 */
221 1942537e Leszek Koltunski
  @SuppressWarnings("unused")
222 ed13a5de Leszek Koltunski
  public DistortedFramebuffer(int width, int height)
223 f6fb3c6d Leszek Koltunski
    {
224
    mProjectionMatrix = new float[16];
225
226 57578636 Leszek Koltunski
    mHeight  = height;
227
    mWidth   = width;
228
    fboIds[0]= -1;
229
    texIds[0]= TEXTURE_NOT_CREATED_YET;
230
    mFOV     = 60.0f;
231
    mX       = 0.0f;
232
    mY       = 0.0f;
233
    mMarked  = false;
234 bd3da5b2 Leszek Koltunski
235 f6fb3c6d Leszek Koltunski
    createProjection();
236
    }
237
238 b448e6b9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
239 dedacd82 Leszek Koltunski
/**
240 57578636 Leszek Koltunski
 * Create a new Framebuffer from an already created OpenGL Framebuffer.
241
 * <p>
242
 * Has to be followed by a 'resize()' to set the size.
243 dedacd82 Leszek Koltunski
 *
244
 * @param fbo the ID of a OpenGL Framebuffer object. Typically 0 (the screen)
245
 */
246 ed13a5de Leszek Koltunski
  public DistortedFramebuffer(int fbo)
247 b448e6b9 Leszek Koltunski
    {
248
    mProjectionMatrix = new float[16];
249
250 57578636 Leszek Koltunski
    fboIds[0]= fbo;
251
    texIds[0]= TEXTURE_DONT_CREATE;
252
    mFOV     = 60.0f;
253
    mX       = 0.0f;
254
    mY       = 0.0f;
255
    mMarked  = false;
256 b448e6b9 Leszek Koltunski
    }
257
258 f6fb3c6d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
259 dedacd82 Leszek Koltunski
/**
260
 * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
261
 */
262 bd3da5b2 Leszek Koltunski
  public void markForDeletion()
263 f6fb3c6d Leszek Koltunski
    {
264 46e25345 Leszek Koltunski
    //android.util.Log.e("FBO", "marking for deletion ("+mWidth+","+mHeight+") "+fboIds[0]);
265 16d8b8f3 Leszek Koltunski
266 8e34674e Leszek Koltunski
    mListMarked = true;
267
    mMarked     = true;
268 f6fb3c6d Leszek Koltunski
    }
269
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271 dedacd82 Leszek Koltunski
/**
272
 * Create new Projection matrix.
273
 *
274
 * @param fov Vertical 'field of view' of the Projection frustrum (in degrees).
275 46a52b78 Leszek Koltunski
 * @param x X-coordinate of the point at which our camera looks at. 0 is the center.
276
 * @param y Y-coordinate of the point at which our camera looks at. 0 is the center.
277 dedacd82 Leszek Koltunski
 */
278
  public void setProjection(float fov, float x, float y)
279 f6fb3c6d Leszek Koltunski
    {
280 dedacd82 Leszek Koltunski
    mFOV = fov;
281 f6fb3c6d Leszek Koltunski
    mX   = x;
282
    mY   = y;
283
284
    createProjection();
285
    }
286
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288 dedacd82 Leszek Koltunski
/**
289
 * Resize the underlying Framebuffer.
290
 *
291
 * As the Framebuffer is not created until the first render, typical usage of this API is actually
292
 * to set the size of an not-yet-created Framebuffer of an object that has been created with the
293
 * second constructor.
294 71c3fecc Leszek Koltunski
 * <p>
295 65362dd4 Leszek Koltunski
 * Fully creating an object, rendering to it, then resizing mid-render is also possible. Actual
296
 * resize takes place on the next render.
297 dedacd82 Leszek Koltunski
 *
298
 * @param width The new width.
299
 * @param height The new height.
300
 */
301 f6fb3c6d Leszek Koltunski
  public void resize(int width, int height)
302
    {
303 bd3da5b2 Leszek Koltunski
    if( mWidth!=width || mHeight!=height )
304
      {
305
      mWidth = width;
306
      mHeight= height;
307 f6fb3c6d Leszek Koltunski
308 bd3da5b2 Leszek Koltunski
      createProjection();
309 f6fb3c6d Leszek Koltunski
310 7304d89f Leszek Koltunski
      if( texIds[0]>0 ) markForDeletion();
311 bd3da5b2 Leszek Koltunski
      }
312 f6fb3c6d Leszek Koltunski
    }
313 d1e740c5 Leszek Koltunski
314
///////////////////////////////////////////////////////////////////////////////////////////////////
315
/**
316 09d4f4b1 Leszek Koltunski
 * Return the ID of the Texture (COLOR attachment 0) that's backing this FBO.
317
 * <p>
318
 * Catch: this will only work if the library has had time to actually create the texture. Remember
319
 * that the texture gets created only on first render, thus creating a Texture object and immediately
320
 * calling this method will return an invalid (negative) result.
321
 *
322
 * @returns If there was not a single render between creation of the Object and calling this method on
323
 *          it, return a negative value. Otherwise, return ID of COLOR attachment 0.
324 d1e740c5 Leszek Koltunski
 */
325 09d4f4b1 Leszek Koltunski
  public int getTextureID()
326 d1e740c5 Leszek Koltunski
    {
327 09d4f4b1 Leszek Koltunski
    return texIds[0];
328 d1e740c5 Leszek Koltunski
    }
329 f6fb3c6d Leszek Koltunski
  }