Revision c5369f1b
Added by Leszek Koltunski about 7 years ago
src/main/java/org/distorted/library/DistortedFramebuffer.java | ||
---|---|---|
20 | 20 |
package org.distorted.library; |
21 | 21 |
|
22 | 22 |
import android.opengl.GLES30; |
23 |
import android.opengl.Matrix; |
|
24 | 23 |
|
25 | 24 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
26 | 25 |
/** |
27 | 26 |
* Class which represents a OpenGL Framebuffer object. |
28 | 27 |
* <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). |
|
28 |
* User is able to create offscreen FBOs and both a) render to them b) use their COLOR0 attachment as |
|
29 |
* an input texture. |
|
38 | 30 |
*/ |
39 |
public class DistortedFramebuffer extends DistortedRenderable |
|
31 |
public class DistortedFramebuffer extends DistortedRenderable implements DistortedInputSurface, DistortedOutputSurface
|
|
40 | 32 |
{ |
41 | 33 |
private int[] mDepthH = new int[1]; |
42 | 34 |
private int[] mFBOH = new int[1]; |
43 |
|
|
44 | 35 |
private boolean mDepthEnabled; |
45 |
|
|
46 |
// Projection stuff |
|
47 |
private float mX, mY, mFOV; |
|
48 |
int mWidth,mHeight,mDepth; |
|
49 |
float mDistance; |
|
50 |
float[] mProjectionMatrix; |
|
36 |
private DistortedProjection mProjection; |
|
51 | 37 |
|
52 | 38 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
53 | 39 |
// Must be called from a thread holding OpenGL Context |
54 | 40 |
// Watch out - this has the side-effect of binding a Texture and a Framebuffer! |
55 | 41 |
|
56 |
void create() |
|
42 |
public void create()
|
|
57 | 43 |
{ |
58 | 44 |
if( mColorH[0]==NOT_CREATED_YET ) |
59 | 45 |
{ |
... | ... | |
146 | 132 |
if( mDepthEnabled ) mDepthH[0] = NOT_CREATED_YET; |
147 | 133 |
} |
148 | 134 |
|
149 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
150 |
|
|
151 |
void setAsOutput() |
|
152 |
{ |
|
153 |
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[0]); |
|
154 |
|
|
155 |
if( mDepthH[0]!=NOT_CREATED_YET ) |
|
156 |
{ |
|
157 |
GLES30.glEnable(GLES30.GL_DEPTH_TEST); |
|
158 |
GLES30.glDepthMask(true); |
|
159 |
} |
|
160 |
else |
|
161 |
{ |
|
162 |
GLES30.glDisable(GLES30.GL_DEPTH_TEST); |
|
163 |
GLES30.glDepthMask(false); |
|
164 |
} |
|
165 |
} |
|
166 |
|
|
167 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
168 |
|
|
169 |
private void createProjection() |
|
170 |
{ |
|
171 |
if( mWidth>0 && mHeight>1 ) |
|
172 |
{ |
|
173 |
if( mFOV>0.0f ) // perspective projection |
|
174 |
{ |
|
175 |
float left = (-mX-mWidth /2.0f)/mHeight; |
|
176 |
float right = (-mX+mWidth /2.0f)/mHeight; |
|
177 |
float bottom = (-mY-mHeight/2.0f)/mHeight; |
|
178 |
float top = (-mY+mHeight/2.0f)/mHeight; |
|
179 |
float near = (top-bottom) / (2.0f*(float)Math.tan(mFOV*Math.PI/360)); |
|
180 |
mDistance = mHeight*near/(top-bottom); |
|
181 |
float far = 2*mDistance-near; |
|
182 |
mDepth = (int)((far-near)/2); |
|
183 |
|
|
184 |
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far); |
|
185 |
} |
|
186 |
else // parallel projection |
|
187 |
{ |
|
188 |
float left = -mX-mWidth /2.0f; |
|
189 |
float right = -mX+mWidth /2.0f; |
|
190 |
float bottom = -mY-mHeight/2.0f; |
|
191 |
float top = -mY+mHeight/2.0f; |
|
192 |
float near = (mWidth+mHeight)/2; |
|
193 |
mDistance = 2*near; |
|
194 |
float far = 3*near; |
|
195 |
mDepth = (int)near; |
|
196 |
|
|
197 |
Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far); |
|
198 |
} |
|
199 |
} |
|
200 |
} |
|
201 |
|
|
202 | 135 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
203 | 136 |
// if new size fits into the size of the underlying Texture, just change the projection without |
204 | 137 |
// reallocating the Texture. Otherwise, we need to reallocate. |
... | ... | |
207 | 140 |
|
208 | 141 |
void resizeFast(int width, int height) |
209 | 142 |
{ |
210 |
if( mWidth!=width || mHeight!=height )
|
|
143 |
if( mProjection.resize(width,height) )
|
|
211 | 144 |
{ |
212 |
mWidth = width; |
|
213 |
mHeight= height; |
|
214 |
|
|
215 |
createProjection(); |
|
216 |
|
|
217 |
if( mWidth> mSizeX || mHeight> mSizeY) |
|
145 |
if( width> mSizeX || height> mSizeY) |
|
218 | 146 |
{ |
219 |
mSizeX = mWidth;
|
|
220 |
mSizeY = mHeight;
|
|
147 |
mSizeX = width;
|
|
148 |
mSizeY = height;
|
|
221 | 149 |
delete(); |
222 | 150 |
} |
223 | 151 |
} |
... | ... | |
240 | 168 |
{ |
241 | 169 |
super(width,height,NOT_CREATED_YET); |
242 | 170 |
|
243 |
mProjectionMatrix = new float[16]; |
|
244 |
|
|
245 |
mHeight = height; |
|
246 |
mWidth = width; |
|
247 |
mFOV = 60.0f; |
|
248 |
mX = 0.0f; |
|
249 |
mY = 0.0f; |
|
171 |
mProjection = new DistortedProjection(width,height); |
|
250 | 172 |
mDepthEnabled= depthEnabled; |
251 | 173 |
mFBOH[0] = NOT_CREATED_YET; |
252 | 174 |
mDepthH[0] = NOT_CREATED_YET; |
253 |
|
|
254 |
createProjection(); |
|
255 | 175 |
} |
256 | 176 |
|
257 | 177 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
... | ... | |
267 | 187 |
{ |
268 | 188 |
super(width,height,NOT_CREATED_YET); |
269 | 189 |
|
270 |
mProjectionMatrix = new float[16]; |
|
271 |
|
|
272 |
mHeight = height; |
|
273 |
mWidth = width; |
|
274 |
mFOV = 60.0f; |
|
275 |
mX = 0.0f; |
|
276 |
mY = 0.0f; |
|
190 |
mProjection = new DistortedProjection(width,height); |
|
277 | 191 |
mDepthEnabled= false; |
278 | 192 |
mFBOH[0] = NOT_CREATED_YET; |
279 | 193 |
mDepthH[0] = NOT_CREATED_YET; |
280 |
|
|
281 |
createProjection(); |
|
282 | 194 |
} |
283 | 195 |
|
284 | 196 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
285 | 197 |
/** |
286 |
* Create a new Framebuffer from an already created OpenGL Framebuffer. |
|
287 |
* <p> |
|
288 |
* Has to be followed by a 'resize()' to set the size. |
|
289 |
* |
|
290 |
* @param fbo the ID of a OpenGL Framebuffer object. Typically 0 (the screen) |
|
198 |
* Bind the underlying rectangle of pixels as a OpenGL Texture. |
|
291 | 199 |
*/ |
292 |
public DistortedFramebuffer(int fbo)
|
|
200 |
public boolean setAsInput()
|
|
293 | 201 |
{ |
294 |
super(0,0,DONT_CREATE); |
|
202 |
if( mColorH[0]>0 ) |
|
203 |
{ |
|
204 |
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH[0]); |
|
205 |
return true; |
|
206 |
} |
|
207 |
|
|
208 |
return false; |
|
209 |
} |
|
295 | 210 |
|
296 |
mProjectionMatrix = new float[16]; |
|
211 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
212 |
/** |
|
213 |
* Bind this Surface as a Framebuffer we can render to. |
|
214 |
*/ |
|
215 |
public void setAsOutput() |
|
216 |
{ |
|
217 |
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[0]); |
|
297 | 218 |
|
298 |
mFOV = 60.0f; |
|
299 |
mX = 0.0f; |
|
300 |
mY = 0.0f; |
|
301 |
mDepthEnabled= true; |
|
302 |
mFBOH[0] = fbo; |
|
303 |
mDepthH[0] = DONT_CREATE; |
|
219 |
if( mDepthH[0]!=NOT_CREATED_YET ) |
|
220 |
{ |
|
221 |
GLES30.glEnable(GLES30.GL_DEPTH_TEST); |
|
222 |
GLES30.glDepthMask(true); |
|
223 |
} |
|
224 |
else |
|
225 |
{ |
|
226 |
GLES30.glDisable(GLES30.GL_DEPTH_TEST); |
|
227 |
GLES30.glDepthMask(false); |
|
228 |
} |
|
304 | 229 |
} |
305 | 230 |
|
306 | 231 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
... | ... | |
322 | 247 |
* <p> |
323 | 248 |
* Must be called from a thread holding OpenGL Context. |
324 | 249 |
* |
325 |
* @param ren input Renderable to use.
|
|
250 |
* @param surface InputSurface to skin our Mesh with.
|
|
326 | 251 |
* @param mesh Class descendant from MeshObject |
327 | 252 |
* @param effects The DistortedEffects to use when rendering |
328 | 253 |
* @param time Current time, in milliseconds. |
329 | 254 |
*/ |
330 |
public void renderTo(DistortedRenderable ren, MeshObject mesh, DistortedEffects effects, long time)
|
|
255 |
public void renderTo(DistortedInputSurface surface, MeshObject mesh, DistortedEffects effects, long time)
|
|
331 | 256 |
{ |
332 |
ren.create(); // Watch out - this needs to be before
|
|
333 |
create(); // the 'setAsInput' because this has side-effects! |
|
257 |
surface.create(); // Watch out - this needs to be before
|
|
258 |
create(); // the 'setAsInput' because this has side-effects!
|
|
334 | 259 |
|
335 |
if( ren.setAsInput() )
|
|
260 |
if( surface.setAsInput() )
|
|
336 | 261 |
{ |
337 | 262 |
DistortedRenderable.deleteAllMarked(); |
338 |
effects.drawPriv(ren.getWidth()/2.0f, ren.getHeight()/2.0f, mesh, this, time);
|
|
263 |
effects.drawPriv(surface.getWidth()/2.0f, surface.getHeight()/2.0f, mesh, this, time);
|
|
339 | 264 |
} |
340 | 265 |
} |
341 | 266 |
|
... | ... | |
365 | 290 |
*/ |
366 | 291 |
public void setProjection(float fov, float x, float y) |
367 | 292 |
{ |
368 |
mFOV = fov; |
|
369 |
mX = x; |
|
370 |
mY = y; |
|
371 |
|
|
372 |
createProjection(); |
|
293 |
mProjection.set(fov,x,y); |
|
294 |
mProjection.createProjection(); |
|
373 | 295 |
} |
374 | 296 |
|
375 | 297 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
376 | 298 |
/** |
377 | 299 |
* Resize the underlying Framebuffer. |
378 | 300 |
* |
379 |
* As the Framebuffer is not created until the first render, typical usage of this API is actually |
|
380 |
* to set the size of an not-yet-created Framebuffer of an object that has been created with the |
|
381 |
* second constructor. |
|
382 |
* <p> |
|
383 |
* Fully creating an object, rendering to it, then resizing mid-render is also possible. Actual |
|
384 |
* resize takes place on the next render. |
|
385 |
* |
|
386 | 301 |
* @param width The new width. |
387 | 302 |
* @param height The new height. |
388 | 303 |
*/ |
389 | 304 |
public void resize(int width, int height) |
390 | 305 |
{ |
391 |
if( mWidth!=width || mHeight!=height )
|
|
306 |
if( mProjection.resize(width,height) )
|
|
392 | 307 |
{ |
393 |
mWidth = width; |
|
394 |
mHeight= height; |
|
395 | 308 |
mSizeX = width; |
396 | 309 |
mSizeY = height; |
397 |
|
|
398 |
createProjection(); |
|
399 |
|
|
400 | 310 |
if( mColorH[0]>0 ) markForDeletion(); |
401 | 311 |
} |
402 | 312 |
} |
... | ... | |
427 | 337 |
{ |
428 | 338 |
return mDepthEnabled; |
429 | 339 |
} |
340 |
|
|
341 |
////////////////////////////////////////////////////////////////////////////////////////////////// |
|
342 |
/** |
|
343 |
* Return Projection stored in this Framebuffer. |
|
344 |
* |
|
345 |
* @return DistortedProjection object. |
|
346 |
*/ |
|
347 |
public DistortedProjection getProjection() |
|
348 |
{ |
|
349 |
return mProjection; |
|
350 |
} |
|
430 | 351 |
} |
Also available in: Unified diff
Major: change of API.
Split DFramebuffer into Framebuffer and Screen; introduce the 'DistortedInputSurface' and 'DistortedOutputSurface' interfaces.