commit f6fb3c6d76c2df896e872cf2a7bd1ae6b77f47d4
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Fri Dec 2 16:02:03 2016 +0000

    New DistortedRenderTarget - preparation for being able to render to any FBO (screen, offscreen pixmaps)

diff --git a/src/main/java/org/distorted/library/Distorted.java b/src/main/java/org/distorted/library/Distorted.java
index 9d3f2f7..4f45532 100644
--- a/src/main/java/org/distorted/library/Distorted.java
+++ b/src/main/java/org/distorted/library/Distorted.java
@@ -317,24 +317,10 @@ public class Distorted
   public static void onSurfaceCreated(final Context context) throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
     { 
     mInitialized = true;  
-     
-// String ver;  
-    
+
     final String vertexShader   = Distorted.getVertexShader(context);
     final String fragmentShader = Distorted.getFragmentShader(context);
-/*
-    ver = GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION);    
-    Log.d(TAG, "GLSL version: "+(ver==null ? "null" : ver) );
-    ver = GLES20.glGetString(GLES20.GL_VERSION);   
-    Log.d(TAG, "GL version: "+(ver==null ? "null" : ver) );
-    ver = GLES20.glGetString(GLES20.GL_VENDOR);    
-    Log.d(TAG, "GL vendor: "+(ver==null ? "null" : ver) );
-    ver = GLES20.glGetString(GLES20.GL_RENDERER);  
-    Log.d(TAG, "GL renderer: "+(ver==null ? "null" : ver) );
-*/
-    //ver = GLES20.glGetString(GLES20.GL_EXTENSIONS);    
-    //Log.d(TAG, "GL extensions: "+(ver==null ? "null" : ver) );
-    
+
     sanitizeMaxValues();
     
     final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );     
diff --git a/src/main/java/org/distorted/library/DistortedProjection.java b/src/main/java/org/distorted/library/DistortedProjection.java
index e04f831..7f71151 100644
--- a/src/main/java/org/distorted/library/DistortedProjection.java
+++ b/src/main/java/org/distorted/library/DistortedProjection.java
@@ -32,7 +32,7 @@ class DistortedProjection
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
-  public DistortedProjection()
+  DistortedProjection()
    {
    projectionMatrix = new float[16];   
    }
@@ -70,7 +70,4 @@ class DistortedProjection
    
     depth = (int)((far-near)/2);
     }
-  }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-//
+  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/DistortedRenderTarget.java b/src/main/java/org/distorted/library/DistortedRenderTarget.java
new file mode 100644
index 0000000..8575023
--- /dev/null
+++ b/src/main/java/org/distorted/library/DistortedRenderTarget.java
@@ -0,0 +1,196 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library;
+
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DistortedRenderTarget
+  {
+  private static final int TEXTURE_FAILED_TO_CREATE = -1;
+  private static final int TEXTURE_NOT_CREATED_YET  = -2;
+
+  private int mFramebufferID, mTextureID;
+  private float mX, mY;
+  private float mFOV;
+
+  int mWidth,mHeight,mDepth,mDistance;
+  float[] mProjectionMatrix;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createProjection()
+    {
+    float ratio  = (float) mWidth / mHeight;
+    float left   =-ratio;          //
+    float right  = ratio;          // Create a new perspective projection matrix.
+    float bottom = -1.0f;          //
+    float top    =  1.0f;          // any change to those values will have serious consequences!
+    float near, far;
+
+    if( mFOV>0.0f )  // perspective projection
+      {
+      near= (float)(top / Math.tan(mFOV*Math.PI/360));
+      mDistance = (int)(mHeight*near/(top-bottom));
+      far = 2*mDistance-near;
+
+      Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
+      }
+    else                      // parallel projection
+      {
+      near= (float)(top / Math.tan(Math.PI/360));
+      mDistance = (int)(mHeight*near/(top-bottom));
+      far = 2*mDistance-near;
+
+      Matrix.orthoM(mProjectionMatrix, 0, -mWidth/2, mWidth/2,-mHeight/2, mHeight/2, near, far);
+      }
+
+    mDepth = (int)((far-near)/2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// must be called form a thread holding OpenGL Context
+
+  private boolean createFBO()
+    {
+    int[] textureIds = new int[1];
+    GLES20.glGenTextures(1, textureIds, 0);
+    mTextureID = textureIds[0];
+    int[] mFBORenderToTexture = new int[1];
+
+    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
+    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
+    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
+    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mWidth, mHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
+
+    GLES20.glGenFramebuffers(1, mFBORenderToTexture, 0);
+    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFBORenderToTexture[0]);
+    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mTextureID, 0);
+    int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
+
+    if(status != GLES20.GL_FRAMEBUFFER_COMPLETE)
+      {
+      android.util.Log.e("RenderTarget", "failed to create framebuffer, error="+status);
+
+      GLES20.glDeleteTextures(1, textureIds, 0);
+      GLES20.glDeleteFramebuffers(1, mFBORenderToTexture, 0);
+      mFramebufferID = 0;
+      mTextureID = TEXTURE_FAILED_TO_CREATE;
+      return false;
+      }
+
+    mFramebufferID = mFBORenderToTexture[0];
+
+    return true;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void deleteFBO()
+    {
+    int[] textureIds = new int[1];
+    int[] mFBORenderToTexture = new int[1];
+
+    textureIds[0] = mTextureID;
+    mFBORenderToTexture[0] = mFramebufferID;
+
+    GLES20.glDeleteTextures(1, textureIds, 0);
+    GLES20.glDeleteFramebuffers(1, mFBORenderToTexture, 0);
+
+    mFramebufferID = 0;
+    mTextureID = TEXTURE_NOT_CREATED_YET;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+
+  public DistortedRenderTarget(int width, int height)
+    {
+    mProjectionMatrix = new float[16];
+
+    mHeight        = height;
+    mWidth         = width;
+    mFramebufferID = 0;
+    mTextureID     = TEXTURE_NOT_CREATED_YET;
+    mFOV           = 6.0f;
+    mX             = 0.0f;
+    mY             = 0.0f;
+
+    createProjection();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void release()
+    {
+    if( mTextureID>=0 ) deleteFBO();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setProjection(float FOV, float x, float y)
+    {
+    mFOV = FOV;
+    mX   = x;
+    mY   = y;
+
+    createProjection();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void resize(int width, int height)
+    {
+    mWidth = width;
+    mHeight= height;
+
+    createProjection();
+
+    if( mTextureID>0 ) deleteFBO();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getFBO()
+    {
+    return mFramebufferID;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getTexture()
+    {
+    return mTextureID;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// set this as Render Target. Must be called from a thread holding OpenGL context.
+
+  public void use()
+    {
+    if( mTextureID==TEXTURE_NOT_CREATED_YET ) createFBO();
+
+
+    }
+  }
