Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedFramebuffer.java @ 71c3fecc

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 WRITE-only Framebuffers from objects already constructed outside
33
 * of the library (the first constructor; primary use case: the screen) or an offscreen READ-WRITE
34
 * FBOs (used by the DistortedNode, 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 place 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;
52
  private float mFOV;
53

    
54
  private int[] texIds = new int[1];
55
  private int[] fboIds = new int[1];
56

    
57
  private boolean mMarked;
58

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

    
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63

    
64
  private void createProjection()
65
    {
66
    if( mFOV>0.0f )  // perspective projection
67
      {
68
      float ratio  = (float) mWidth / mHeight;
69
      float left   =-ratio;
70
      float right  = ratio;
71
      float bottom = -1.0f;
72
      float top    =  1.0f;
73
      float near= (float)(top / Math.tan(mFOV*Math.PI/360));
74
      mDistance = (int)(mHeight*near/(top-bottom));
75
      float far = 2*mDistance-near;
76
      mDepth = (int)((far-near)/2);
77

    
78
      if( far<=0 )
79
        {
80
        android.util.Log.e("FBO", "error: far<=0. width="+mWidth+" height="+mHeight+
81
                           " mFOV="+mFOV+" mDistance="+mDistance+" far="+far+" near="+near);
82
        }
83
      else
84
        Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
85
      }
86
    else             // parallel projection
87
      {
88
      float left   =-mWidth/2;
89
      float right  = mWidth/2;
90
      float bottom =-mHeight/2;
91
      float top    = mHeight/2;
92
      float near= (float)(top / Math.tan(Math.PI/360));
93
      mDistance = (int)(mHeight*near/(top-bottom));
94
      float far = 2*mDistance-near;
95
      mDepth = (int)((far-near)/2);
96

    
97
      Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
98
      }
99
    }
100

    
101
///////////////////////////////////////////////////////////////////////////////////////////////////
102
// must be called form a thread holding OpenGL Context
103

    
104
  private boolean createFBO()
105
    {
106
    GLES20.glGenTextures(1, texIds, 0);
107
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texIds[0]);
108
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
109
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
110
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
111
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
112
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mWidth, mHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
113

    
114
    GLES20.glGenFramebuffers(1, fboIds, 0);
115
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
116
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texIds[0], 0);
117

    
118
    int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
119

    
120
    if(status != GLES20.GL_FRAMEBUFFER_COMPLETE)
121
      {
122
      android.util.Log.e("DistortedFramebuffer", "failed to create framebuffer, error="+status);
123
      GLES20.glDeleteTextures(1, texIds, 0);
124
      GLES20.glDeleteFramebuffers(1, fboIds, 0);
125
      fboIds[0] = 0;
126
      texIds[0] = TEXTURE_FAILED_TO_CREATE;
127
      return false;
128
      }
129

    
130
    mList.add(this);
131
    android.util.Log.e("FBO", "created ("+mWidth+","+mHeight+") "+fboIds[0]);
132

    
133
    return true;
134
    }
135

    
136
///////////////////////////////////////////////////////////////////////////////////////////////////
137
// must be called form a thread holding OpenGL Context
138

    
139
  private void deleteFBO()
140
    {
141
    android.util.Log.e("FBO", "deleting ("+mWidth+","+mHeight+") "+fboIds[0]);
142

    
143
    GLES20.glDeleteTextures(1, texIds, 0);
144
    GLES20.glDeleteFramebuffers(1, fboIds, 0);
145

    
146
    fboIds[0] = 0;
147
    texIds[0] = TEXTURE_NOT_CREATED_YET;
148
    }
149

    
150
///////////////////////////////////////////////////////////////////////////////////////////////////
151
// must be called from a thread holding OpenGL Context
152

    
153
  private void delete()
154
    {
155
    if( texIds[0]>=0 ) deleteFBO();
156

    
157
    mProjectionMatrix = null;
158
    mMarked = false;
159
    }
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162

    
163
  void reset()
164
    {
165
    texIds[0] = TEXTURE_NOT_CREATED_YET;
166
    }
167

    
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169

    
170
  static synchronized void release()
171
    {
172
    mListMarked = false;
173
    mList.clear();
174
    }
175

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

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

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

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

    
197
      mListMarked = false;
198
      }
199
    }
200
///////////////////////////////////////////////////////////////////////////////////////////////////
201
// PUBLIC API
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203
/**
204
 * Create a new offscreen FBO.
205
 *
206
 * @param width Width of the COLOR attachment.
207
 * @param height Height of the COLOR attachment.
208
 */
209
  public DistortedFramebuffer(int width, int height)
210
    {
211
    mProjectionMatrix = new float[16];
212

    
213
    mHeight        = height;
214
    mWidth         = width;
215
    fboIds[0]      = -1;
216
    texIds[0]      = TEXTURE_NOT_CREATED_YET;
217
    mFOV           = 60.0f;
218
    mX             = 0.0f;
219
    mY             = 0.0f;
220

    
221
    mMarked = false;
222

    
223
    createProjection();
224
    }
225

    
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227
/**
228
 * Create a new DistortedFramebuffer from an already created OpenGL Framebuffer.
229
 *
230
 * @param fbo the ID of a OpenGL Framebuffer object. Typically 0 (the screen)
231
 */
232
  public DistortedFramebuffer(int fbo)
233
    {
234
    mProjectionMatrix = new float[16];
235

    
236
    fboIds[0]      = fbo;
237
    texIds[0]      = TEXTURE_DONT_CREATE;
238
    mFOV           = 60.0f;
239
    mX             = 0.0f;
240
    mY             = 0.0f;
241

    
242
    mMarked = false;
243
    }
244

    
245
///////////////////////////////////////////////////////////////////////////////////////////////////
246
/**
247
 * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
248
 */
249
  public void markForDeletion()
250
    {
251
    android.util.Log.e("FBO", "marking for deletion ("+mWidth+","+mHeight+") "+fboIds[0]);
252

    
253
    mListMarked = true;
254
    mMarked     = true;
255
    }
256

    
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258
/**
259
 * Create new Projection matrix.
260
 *
261
 * @param fov Vertical 'field of view' of the Projection frustrum (in degrees).
262
 * @param x X-coordinate of the point at which our camera looks at. -mWidth/2 &lt; x &lt; mWidth/2
263
 * @param y Y-coordinate of the point at which our camera looks at. -mHeight/2 &lt; y &lt; mHeight/2
264
 */
265
  public void setProjection(float fov, float x, float y)
266
    {
267
    mFOV = fov;
268
    mX   = x;
269
    mY   = y;
270

    
271
    createProjection();
272
    }
273

    
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275
/**
276
 * Resize the underlying Framebuffer.
277
 *
278
 * As the Framebuffer is not created until the first render, typical usage of this API is actually
279
 * to set the size of an not-yet-created Framebuffer of an object that has been created with the
280
 * second constructor.
281
 * <p>
282
 * Fully creating an object, rendering to it, then resizing mid-render is also possible.
283
 *
284
 * @param width The new width.
285
 * @param height The new height.
286
 */
287
  public void resize(int width, int height)
288
    {
289
    if( mWidth!=width || mHeight!=height )
290
      {
291
      mWidth = width;
292
      mHeight= height;
293

    
294
      createProjection();
295

    
296
      if( texIds[0]>0 ) markForDeletion();
297
      }
298
    }
299

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301
/**
302
 * Set this as the Framebuffer to write to.
303
 */
304

    
305
  public void setAsOutput()
306
    {
307
    if( texIds[0]==TEXTURE_NOT_CREATED_YET ) createFBO();
308

    
309
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboIds[0]);
310
    }
311

    
312
///////////////////////////////////////////////////////////////////////////////////////////////////
313
/**
314
 * Set this as the Framebuffer to read from.
315
 */
316

    
317
  public void setAsInput()
318
    {
319
    if( texIds[0]==TEXTURE_NOT_CREATED_YET ) createFBO();
320

    
321
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texIds[0]);
322
    }
323

    
324
  }
(6-6/17)