Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedFramebuffer.java @ 2e49718d

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

    
25
///////////////////////////////////////////////////////////////////////////////////////////////////
26
/**
27
 * Class which represents a OpenGL Framebuffer object.
28
 * <p>
29
 * User is able to create either Framebuffers from objects already constructed outside
30
 * of the library (the first constructor; primary use case: the screen) or an offscreen
31
 * FBOs.
32
 * <p>
33
 * Keep all objects created in a static LinkedList. The point: we need to be able to mark
34
 * Framebuffers for deletion, and delete all marked objects later at a convenient time (that's
35
 * because we can only delete from a thread that holds the OpenGL context so here we provide a
36
 * framework where one is able to mark for deletion at any time and actual deletion takes place
37
 * on the next render).
38
 */
39
public class DistortedFramebuffer extends DistortedRenderable
40
  {
41
  private int[] mDepthH = new int[1];
42
  private int[] mFBOH   = new int[1];
43

    
44
  private boolean mDepthEnabled;
45

    
46
  // Projection stuff
47
  private float mX, mY, mFOV;
48
  int mWidth,mHeight,mDepth;
49
  float mDistance;
50
  float[] mProjectionMatrix;
51

    
52
///////////////////////////////////////////////////////////////////////////////////////////////////
53
// Must be called from a thread holding OpenGL Context
54
// Watch out - this has the side-effect of binding a Texture and a Framebuffer!
55

    
56
  void create()
57
    {
58
    if( mColorH[0]==NOT_CREATED_YET )
59
      {
60
      GLES30.glGenTextures(1, mColorH, 0);
61
      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH[0]);
62
      GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_REPEAT);
63
      GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_REPEAT);
64
      GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
65
      GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
66
      GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, mSizeX, mSizeY, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
67

    
68
      GLES30.glGenFramebuffers(1, mFBOH, 0);
69
      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[0]);
70
      GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mColorH[0], 0);
71

    
72
      checkStatus("color");
73
      }
74
    if( mDepthEnabled && mDepthH[0]==NOT_CREATED_YET ) // we need to create a new DEPTH attachment
75
      {
76
      GLES30.glGenTextures(1, mDepthH, 0);
77
      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mDepthH[0]);
78
      GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_REPEAT);
79
      GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_REPEAT);
80
      GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
81
      GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_NEAREST);
82
      GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_DEPTH_COMPONENT, mSizeX, mSizeY, 0, GLES30.GL_DEPTH_COMPONENT, GLES30.GL_UNSIGNED_SHORT, null);
83

    
84
      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[0]);
85
      GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT, GLES30.GL_TEXTURE_2D, mDepthH[0], 0);
86

    
87
      checkStatus("depth");
88
      }
89
    if( !mDepthEnabled && mDepthH[0]!=NOT_CREATED_YET ) // we need to detach and destroy the DEPTH attachment.
90
      {
91
      GLES30.glDeleteTextures(1, mDepthH, 0);
92
      mDepthH[0]=NOT_CREATED_YET;
93
      }
94
    }
95

    
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97

    
98
  private boolean checkStatus(String message)
99
    {
100
    int status = GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER);
101

    
102
    if(status != GLES30.GL_FRAMEBUFFER_COMPLETE)
103
      {
104
      android.util.Log.e("DistortedFramebuffer", "FRAMEBUFFER INCOMPLETE, "+message+" error="+status);
105

    
106
      GLES30.glDeleteTextures(1, mColorH, 0);
107
      GLES30.glDeleteTextures(1, mDepthH, 0);
108
      GLES30.glDeleteFramebuffers(1, mFBOH, 0);
109
      mFBOH[0]   = 0;
110
      mColorH[0] = FAILED_TO_CREATE;
111
      mDepthH[0] = FAILED_TO_CREATE;
112

    
113
      return false;
114
      }
115

    
116
    return true;
117
    }
118

    
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120
// Must be called from a thread holding OpenGL Context
121

    
122
  void delete()
123
    {
124
    if( mColorH[0]>=0 )
125
      {
126
      if( mDepthH[0]>=0 )
127
        {
128
        GLES30.glDeleteTextures(1, mDepthH, 0);
129
        mDepthH[0]=NOT_CREATED_YET;
130
        }
131

    
132
      GLES30.glDeleteTextures(1, mColorH, 0);
133
      mColorH[0] = NOT_CREATED_YET;
134

    
135
      GLES30.glDeleteFramebuffers(1, mFBOH, 0);
136
      mFBOH[0] = 0;
137
      }
138
    }
139

    
140
///////////////////////////////////////////////////////////////////////////////////////////////////
141

    
142
  void setAsOutput()
143
    {
144
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[0]);
145

    
146
    if( mDepthH[0]!=NOT_CREATED_YET )
147
      {
148
      GLES30.glEnable(GLES30.GL_DEPTH_TEST);
149
      GLES30.glDepthMask(true);
150
      }
151
    else
152
      {
153
      GLES30.glDisable(GLES30.GL_DEPTH_TEST);
154
      GLES30.glDepthMask(false);
155
      }
156
    }
157

    
158
///////////////////////////////////////////////////////////////////////////////////////////////////
159

    
160
  private void createProjection()
161
    {
162
    if( mWidth>0 && mHeight>1 )
163
      {
164
      if( mFOV>0.0f )  // perspective projection
165
        {
166
        float left   = (-mX-mWidth /2.0f)/mHeight;
167
        float right  = (-mX+mWidth /2.0f)/mHeight;
168
        float bottom = (-mY-mHeight/2.0f)/mHeight;
169
        float top    = (-mY+mHeight/2.0f)/mHeight;
170
        float near   = (top-bottom) / (2.0f*(float)Math.tan(mFOV*Math.PI/360));
171
        mDistance    = mHeight*near/(top-bottom);
172
        float far    = 2*mDistance-near;
173
        mDepth       = (int)((far-near)/2);
174

    
175
        Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
176
        }
177
      else             // parallel projection
178
        {
179
        float left   = -mX-mWidth /2.0f;
180
        float right  = -mX+mWidth /2.0f;
181
        float bottom = -mY-mHeight/2.0f;
182
        float top    = -mY+mHeight/2.0f;
183
        float near   = (mWidth+mHeight)/2;
184
        mDistance    = 2*near;
185
        float far    = 3*near;
186
        mDepth       = (int)near;
187

    
188
        Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
189
        }
190
      }
191
    }
192

    
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194
// if new size fits into the size of the underlying Texture, just change the projection without
195
// reallocating the Texture. Otherwise, we need to reallocate.
196
//
197
// Must be called form a thread holding the OpenGL context.
198

    
199
  void resizeFast(int width, int height)
200
    {
201
    if( mWidth!=width || mHeight!=height )
202
      {
203
      mWidth = width;
204
      mHeight= height;
205

    
206
      createProjection();
207

    
208
      if( mWidth> mSizeX || mHeight> mSizeY)
209
        {
210
        mSizeX = mWidth;
211
        mSizeY = mHeight;
212
        delete();
213
        }
214
      }
215

    
216
    create();
217
    }
218

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220
// PUBLIC API
221
///////////////////////////////////////////////////////////////////////////////////////////////////
222
/**
223
 * Create a new offscreen Framebuffer.
224
 *
225
 * @param width Width of the COLOR attachment.
226
 * @param height Height of the COLOR attachment.
227
 * @param depthEnabled Add DEPTH attachment?
228
 */
229
  @SuppressWarnings("unused")
230
  public DistortedFramebuffer(int width, int height, boolean depthEnabled)
231
    {
232
    super(width,height,NOT_CREATED_YET);
233

    
234
    mProjectionMatrix = new float[16];
235

    
236
    mHeight      = height;
237
    mWidth       = width;
238
    mFOV         = 60.0f;
239
    mX           = 0.0f;
240
    mY           = 0.0f;
241
    mDepthEnabled= depthEnabled;
242
    mFBOH[0]     = NOT_CREATED_YET;
243
    mDepthH[0]   = NOT_CREATED_YET;
244

    
245
    createProjection();
246
    }
247

    
248
///////////////////////////////////////////////////////////////////////////////////////////////////
249

    
250
/**
251
 * Create a new offscreen Framebuffer.
252
 *
253
 * @param width Width of the COLOR attachment.
254
 * @param height Height of the COLOR attachment.
255
 */
256
  @SuppressWarnings("unused")
257
  public DistortedFramebuffer(int width, int height)
258
    {
259
    super(width,height,NOT_CREATED_YET);
260

    
261
    mProjectionMatrix = new float[16];
262

    
263
    mHeight      = height;
264
    mWidth       = width;
265
    mFOV         = 60.0f;
266
    mX           = 0.0f;
267
    mY           = 0.0f;
268
    mDepthEnabled= false;
269
    mFBOH[0]     = NOT_CREATED_YET;
270
    mDepthH[0]   = NOT_CREATED_YET;
271

    
272
    createProjection();
273
    }
274

    
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276
/**
277
 * Create a new Framebuffer from an already created OpenGL Framebuffer.
278
 * <p>
279
 * Has to be followed by a 'resize()' to set the size.
280
 *
281
 * @param fbo the ID of a OpenGL Framebuffer object. Typically 0 (the screen)
282
 */
283
  public DistortedFramebuffer(int fbo)
284
    {
285
    super(0,0,DONT_CREATE);
286

    
287
    mProjectionMatrix = new float[16];
288

    
289
    mFOV         = 60.0f;
290
    mX           = 0.0f;
291
    mY           = 0.0f;
292
    mDepthEnabled= true;
293
    mFBOH[0]     = fbo;
294
    mDepthH[0]   = DONT_CREATE;
295
    }
296

    
297
///////////////////////////////////////////////////////////////////////////////////////////////////
298
/**
299
 * Create a new DEPTH buffer and attach it or (param=false) detach an existing DEPTh attachment and destroy it.
300
 *
301
 * @param enable <bold>true</bold> if we want to attach a new DEPTH buffer to the FBO.<br>
302
 *               <bold>false</bold> if we want to detach the DEPTH attachment.
303
 */
304
  public void enableDepthAttachment(boolean enable)
305
    {
306
    mDepthEnabled = enable;
307
    }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310

    
311
/**
312
 * Draw the (texture,mesh,effects) object to the Framebuffer.
313
 * <p>
314
 * Must be called from a thread holding OpenGL Context.
315
 *
316
 * @param ren input Renderable to use.
317
 * @param mesh Class descendant from MeshObject
318
 * @param effects The DistortedEffects to use when rendering
319
 * @param time Current time, in milliseconds.
320
 */
321
  public void renderTo(DistortedRenderable ren, MeshObject mesh, DistortedEffects effects, long time)
322
    {
323
    ren.create();  // Watch out  - this needs to be before
324
    create();      // the 'setAsInput' because this has side-effects!
325

    
326
    if( ren.setAsInput() )
327
      {
328
      DistortedRenderable.deleteAllMarked();
329
      effects.drawPriv(ren.getWidth()/2.0f, ren.getHeight()/2.0f, mesh, this, time);
330
      }
331
    }
332

    
333
///////////////////////////////////////////////////////////////////////////////////////////////////
334
/**
335
 * Draws the Tree, and all its children, to the Framebuffer.
336
 * <p>
337
 * Must be called from a thread holding OpenGL Context.
338
 *
339
 * @param dt DistortedTree to render.
340
 * @param time Current time, in milliseconds. This will be passed to all the Effects stored in the Tree.
341
 */
342
  public void renderTo(DistortedTree dt, long time)
343
    {
344
    DistortedRenderable.deleteAllMarked();
345
    create();
346
    dt.drawRecursive(time,this);
347
    }
348

    
349
///////////////////////////////////////////////////////////////////////////////////////////////////
350
/**
351
 * Create new Projection matrix.
352
 *
353
 * @param fov Vertical 'field of view' of the Projection frustrum (in degrees).
354
 * @param x X-coordinate of the point at which our camera looks at. 0 is the center.
355
 * @param y Y-coordinate of the point at which our camera looks at. 0 is the center.
356
 */
357
  public void setProjection(float fov, float x, float y)
358
    {
359
    mFOV = fov;
360
    mX   = x;
361
    mY   = y;
362

    
363
    createProjection();
364
    }
365

    
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367
/**
368
 * Resize the underlying Framebuffer.
369
 *
370
 * As the Framebuffer is not created until the first render, typical usage of this API is actually
371
 * to set the size of an not-yet-created Framebuffer of an object that has been created with the
372
 * second constructor.
373
 * <p>
374
 * Fully creating an object, rendering to it, then resizing mid-render is also possible. Actual
375
 * resize takes place on the next render.
376
 *
377
 * @param width The new width.
378
 * @param height The new height.
379
 */
380
  public void resize(int width, int height)
381
    {
382
    if( mWidth!=width || mHeight!=height )
383
      {
384
      mWidth    = width;
385
      mHeight   = height;
386
      mSizeX = width;
387
      mSizeY = height;
388

    
389
      createProjection();
390

    
391
      if( mColorH[0]>0 ) markForDeletion();
392
      }
393
    }
394

    
395
///////////////////////////////////////////////////////////////////////////////////////////////////
396
/**
397
 * Return the ID of the Texture (COLOR attachment 0) that's backing this FBO.
398
 * <p>
399
 * Catch: this will only work if the library has had time to actually create the texture. Remember
400
 * that the texture gets created only on first render, thus creating a Texture object and immediately
401
 * calling this method will return an invalid (negative) result.
402
 *
403
 * @return If there was not a single render between creation of the Object and calling this method on
404
 *         it, return a negative value. Otherwise, return ID of COLOR attachment 0.
405
 */
406
  public int getTextureID()
407
    {
408
    return mColorH[0];
409
    }
410

    
411
///////////////////////////////////////////////////////////////////////////////////////////////////
412
/**
413
 * Return true if the FBO contains a DEPTH attachment.
414
 *
415
 * @return <bold>true</bold> if the FBO contains a DEPTH attachment.
416
 */
417
  public boolean hasDepth()
418
    {
419
    return mDepthEnabled;
420
    }
421
  }
(3-3/17)