commit d236a443562443950317c6a10c22261760300613
Author: LeszekKoltunski <leszek@koltunski.pl>
Date:   Fri May 30 15:15:18 2025 +0200

    transition the second half of the 'main' package to Kotlin. Compiles, but fails to run.

diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.kt b/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.kt
index 3cf8d54..887ca18 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.kt
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.kt
@@ -41,7 +41,9 @@ class PostprocessEffectBlur(haloAndRadius: Data2D) : PostprocessEffectBlurred(Ef
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////
         // Clean up of static variables on exit. Called by reflection from super class.
-        @Suppress("unused") fun destroyStatics() = PostprocessEffectBlurred.destroyStatics()
+        @Suppress("unused")
+        @JvmStatic
+        fun destroyStatics() = PostprocessEffectBlurred.destroyStatics()
     }
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectBlurred.kt b/src/main/java/org/distorted/library/effect/PostprocessEffectBlurred.kt
index e064441..964a124 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectBlurred.kt
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectBlurred.kt
@@ -206,7 +206,7 @@ abstract class PostprocessEffectBlurred(name: EffectName) : PostprocessEffect(na
         private var mIndex2 = 0
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////
-        fun destroyStatics()
+        @JvmStatic fun destroyStatics()
         {
             mProgram1 = null
             mProgram2 = null
@@ -221,7 +221,7 @@ abstract class PostprocessEffectBlurred(name: EffectName) : PostprocessEffect(na
         /**
          * Have to call this before the shaders get compiled (i.e before DistortedLibrary.onCreate()) for the Effect to work.
          */
-        fun enable(prog1: String, prog2: String)
+        @JvmStatic fun enable(prog1: String, prog2: String)
         {
             val vertex =
                 """ precision lowp float;     
diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectBorder.kt b/src/main/java/org/distorted/library/effect/PostprocessEffectBorder.kt
index b1222c4..2ed892d 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectBorder.kt
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectBorder.kt
@@ -39,11 +39,7 @@ class PostprocessEffectBorder(halo: Data1D, color: Data4D) : PostprocessEffect(E
     {
         ///////////////////////////////////////////////////////////////////////////////////////////
         // Clean up of static variables on exit. Called by reflection from super class.
-        @Suppress("unused") fun destroyStatics() { }
-
-        ///////////////////////////////////////////////////////////////////////////////////////////
-        // PUBLIC API
-        ///////////////////////////////////////////////////////////////////////////////////////////
+        @Suppress("unused") @JvmStatic fun destroyStatics() { }
         /**
          * No local programs; we do not postprocess anything here. No need to do anything
          */
diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.kt b/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.kt
index d5c07b9..b36ecdf 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.kt
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.kt
@@ -35,9 +35,6 @@ class PostprocessEffectGlow(haloAndRadius: Data2D, color: Data4D) : PostprocessE
 
     companion object
     {
-        ///////////////////////////////////////////////////////////////////////////////////////////////////
-        // PUBLIC API
-        ///////////////////////////////////////////////////////////////////////////////////////////////////
         /**
          * Have to call this before the shaders get compiled (i.e before DistortedLibrary.onCreate()) for the Effect to work.
          */
@@ -45,8 +42,7 @@ class PostprocessEffectGlow(haloAndRadius: Data2D, color: Data4D) : PostprocessE
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////
         // Clean up of static variables on exit. Called by reflection from super class.
-        @Suppress("unused")
-        fun destroyStatics() = PostprocessEffectBlurred.destroyStatics()
+        @Suppress("unused") @JvmStatic fun destroyStatics() = PostprocessEffectBlurred.destroyStatics()
     }
     ///////////////////////////////////////////////////////////////////////////////////////////////////
     override fun compute(uniforms: FloatArray, index: Int, currentDuration: Long, step: Long): Boolean
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
index 05d4afe..355b87a 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
@@ -65,7 +65,7 @@ abstract class EffectQueue : Slave
 
         ///////////////////////////////////////////////////////////////////////////////////////////
         @JvmStatic
-        fun createQueues() : Array<EffectQueue?>
+        fun createQueues() : Array<EffectQueue>
         {
             val queues: Array<EffectQueue?> = arrayOfNulls(EffectType.LENGTH)
 
@@ -74,12 +74,12 @@ abstract class EffectQueue : Slave
             queues[2] = EffectQueueFragment()
             queues[3] = EffectQueuePostprocess()
 
-            return queues
+            return queues.requireNoNulls()
         }
 
         ///////////////////////////////////////////////////////////////////////////////////////////
         @JvmStatic
-        fun createQueues(from: Array<EffectQueue?>, flags: Int) : Array<EffectQueue?>
+        fun createQueues(from: Array<EffectQueue>, flags: Int) : Array<EffectQueue>
         {
             val queues: Array<EffectQueue?> = arrayOfNulls(EffectType.LENGTH)
 
@@ -88,7 +88,7 @@ abstract class EffectQueue : Slave
             queues[2] = if ((flags and DistortedLibrary.CLONE_FRAGMENT   ) != 0) from[2] else EffectQueueFragment()
             queues[3] = if ((flags and DistortedLibrary.CLONE_POSTPROCESS) != 0) from[3] else EffectQueuePostprocess()
 
-            return queues
+            return queues.requireNoNulls()
         }
 
         ///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
index dc4852d..736f182 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
@@ -78,7 +78,7 @@ class EffectQueuePostprocess : EffectQueue
 
             mainVertHeader += "#define MAX_COMPON " + MeshBase.maxEffComponents + "\n"
             if (MeshBase.useCenters) mainVertHeader += "#define COMP_CENTERS\n"
-            if (DistortedLibrary.isUBOBuggy()) mainVertHeader += "#define BUGGY_UBOS\n"
+            if (DistortedLibrary.isUBOBuggy) mainVertHeader += "#define BUGGY_UBOS\n"
             mainVertHeader += "#define POSTPROCESS\n"
 
             val enabledEffectV = VertexEffect.getGLSL()
@@ -159,8 +159,8 @@ class EffectQueuePostprocess : EffectQueue
     fun preprocess(buffer: InternalOutputSurface, node: DistortedNode, distance: Float,
                    mipmap: Float, projection: FloatArray): Int
     {
-        val mesh    = node.mesh
-        val effects = node.effects
+        val mesh    = node.mMesh
+        val effects = node.mEffects
         val width   = buffer.mWidth
         val height  = buffer.mHeight
 
diff --git a/src/main/java/org/distorted/library/main/DistortedEffects.kt b/src/main/java/org/distorted/library/main/DistortedEffects.kt
index e30ae49..5af8e00 100644
--- a/src/main/java/org/distorted/library/main/DistortedEffects.kt
+++ b/src/main/java/org/distorted/library/main/DistortedEffects.kt
@@ -17,219 +17,210 @@
 // License along with this library; if not, write to the Free Software                           //
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+package org.distorted.library.main
 
-package org.distorted.library.main;
-
-import org.distorted.library.effect.Effect;
-import org.distorted.library.effect.EffectName;
-import org.distorted.library.effectqueue.EffectQueue;
-import org.distorted.library.effect.EffectType;
+import org.distorted.library.effect.Effect
+import org.distorted.library.effect.EffectName
+import org.distorted.library.effect.EffectType
+import org.distorted.library.effectqueue.EffectQueue
+import org.distorted.library.effectqueue.EffectQueue.Companion.createQueues
+import org.distorted.library.main.InternalStackFrameList.nextEffectsID
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Class containing Matrix, Vertex, Fragment and Postprocessing effect queues.
- * <p>
+ *
  * The queues hold actual effects to be applied to a given (InputSurface,MeshBase) combo.
  */
-public class DistortedEffects
-  {
-  private final long mID;
-  private final EffectQueue[] mQueues;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @y.exclude
- */
-  public EffectQueue[] getQueues()
+class DistortedEffects
+{
+    /**
+     * Returns unique ID of this instance.
+     *
+     * @return ID of the object.
+     */
+    val iD: Long
+    val queues: Array<EffectQueue>
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Create empty effect queue.
+     */
+    constructor()
     {
-    return mQueues;
+        iD = nextEffectsID
+        queues = createQueues()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create empty effect queue.
- */
- public DistortedEffects()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     *
+     * Whatever we do not clone gets created just like in the default constructor.
+     *
+     * @param dc    Source object to create our object from
+     * @param flags A bitmask of values specifying what to copy.
+     * For example, CLONE_VERTEX | CLONE_MATRIX.
+     */
+    constructor(dc: DistortedEffects, flags: Int)
     {
-    mID = InternalStackFrameList.getNextEffectsID();
-    mQueues = EffectQueue.createQueues();
+        iD = nextEffectsID
+        queues = createQueues(dc.queues, flags)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- * <p>
- * Whatever we do not clone gets created just like in the default constructor.
- *
- * @param dc    Source object to create our object from
- * @param flags A bitmask of values specifying what to copy.
- *              For example, CLONE_VERTEX | CLONE_MATRIX.
- */
-  public DistortedEffects(DistortedEffects dc, int flags)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Return if this queue contains effect with a given ID.
+     */
+    fun exists(id: Long): Boolean
     {
-    mID = InternalStackFrameList.getNextEffectsID();
-    mQueues = EffectQueue.createQueues(dc.getQueues(),flags);
+        val num = (id and EffectType.MASK.toLong()).toInt()
+        return queues[num].exists(id)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns unique ID of this instance.
- *
- * @return ID of the object.
- */
-  public long getID()
-      {
-      return mID;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return if this queue contains effect with a given ID.
- */
-  public boolean exists(long id)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Aborts all Effects.
+     * @return Number of effects aborted.
+     */
+    fun abortAllEffects(): Int
     {
-    int num = (int)(id&EffectType.MASK);
-    return mQueues[num].exists(id);
-    }
+        var aborted = 0
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Aborts all Effects.
- * @return Number of effects aborted.
- */
-  public int abortAllEffects()
-    {
-    int aborted = 0;
-
-    for( int i=0; i<EffectType.LENGTH; i++)
-      {
-      aborted += mQueues[i].removeAll(true);
-      }
+        for (i in 0 until EffectType.LENGTH)
+        {
+            aborted += queues[i].removeAll(true)
+        }
 
-    return aborted;
+        return aborted
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Aborts all Effects of a given type, for example all MATRIX Effects.
- * 
- * @param type one of the constants defined in {@link EffectType}
- * @return Number of effects aborted.
- */
-  public int abortByType(EffectType type)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Aborts all Effects of a given type, for example all MATRIX Effects.
+     *
+     * @param type one of the constants defined in [EffectType]
+     * @return Number of effects aborted.
+     */
+    fun abortByType(type: EffectType): Int
     {
-    int num = type.ordinal();
-    return mQueues[num].removeAll(true);
+        val num = type.ordinal
+        return queues[num].removeAll(true)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Aborts an Effect by its ID.
- *
- * @param id the Id of the Effect to be removed, as returned by getID().
- * @return Number of effects aborted.
- */
-  public int abortById(long id)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Aborts an Effect by its ID.
+     *
+     * @param id the Id of the Effect to be removed, as returned by getID().
+     * @return Number of effects aborted.
+     */
+    fun abortById(id: Long): Int
     {
-    int num = (int)(id&EffectType.MASK);
-    return mQueues[num].removeById(id);
+        val num = (id and EffectType.MASK.toLong()).toInt()
+        return queues[num].removeById(id)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Aborts a single Effect.
- * 
- * @param effect the Effect we want to abort.
- * @return number of Effects aborted. Always either 0 or 1.
- */
-  public int abortEffect(Effect effect)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Aborts a single Effect.
+     *
+     * @param effect the Effect we want to abort.
+     * @return number of Effects aborted. Always either 0 or 1.
+     */
+    fun abortEffect(effect: Effect): Int
     {
-    int num = effect.getType().ordinal();
-    return mQueues[num].removeEffect(effect);
+        val num = effect.type.ordinal
+        return queues[num].removeEffect(effect)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Abort all Effects of a given name, for example all rotations.
- * 
- * @param name one of the constants defined in {@link EffectName}
- * @return number of Effects aborted.
- */
-  public int abortByName(EffectName name)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Abort all Effects of a given name, for example all rotations.
+     *
+     * @param name one of the constants defined in [EffectName]
+     * @return number of Effects aborted.
+     */
+    fun abortByName(name: EffectName): Int
     {
-    int num = name.getType().ordinal();
-    return mQueues[num].removeByName(name);
+        val num = name.type.ordinal
+        return queues[num].removeByName(name)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Add a new Effect to the tail of our queue.
- *
- * @param effect The Effect to add.
- * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
- */
-  public boolean apply(Effect effect)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Add a new Effect to the tail of our queue.
+     *
+     * @param effect The Effect to add.
+     * @return `true` if operation was successful, `false` otherwise.
+     */
+    fun apply(effect: Effect): Boolean
     {
-    int num = effect.getType().ordinal();
-    return mQueues[num].add(effect);
+        val num = effect.type.ordinal
+        return queues[num].add(effect)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Add a new Effect to our queue at a specified position.
- *
- * @param effect The Effect to add.
- * @param position the place in the effects queue where to add the new effect.
- * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
- */
-  public boolean apply(Effect effect, int position)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Add a new Effect to our queue at a specified position.
+     *
+     * @param effect The Effect to add.
+     * @param position the place in the effects queue where to add the new effect.
+     * @return `true` if operation was successful, `false` otherwise.
+     */
+    fun apply(effect: Effect, position: Int): Boolean
     {
-    int num = effect.getType().ordinal();
-    return mQueues[num].add(effect,position);
+        val num = effect.type.ordinal
+        return queues[num].add(effect, position)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
- */
-  public void markForDeletion()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
+     */
+    fun markForDeletion()
     {
-    for( int i=0; i<EffectType.LENGTH; i++)
-      {
-      mQueues[i].markForDeletion();
-      }
+        for (i in 0 until EffectType.LENGTH)
+        {
+            queues[i].markForDeletion()
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return number of effects of the given type currently in the Queue.
- *
- * @param type The EffectType.
- * @return Number of effects of the given type currently in the Queue.
- */
-  public int getNumEffects(EffectType type)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Return number of effects of the given type currently in the Queue.
+     *
+     * @param type The EffectType.
+     * @return Number of effects of the given type currently in the Queue.
+     */
+    fun getNumEffects(type: EffectType): Int
     {
-    int num = type.ordinal();
-    return mQueues[num].getNumEffects();
+        val num = type.ordinal
+        return queues[num].numEffects
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return a string describing all effects in the queues.
- */
-  public String debug(int depth)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Return a string describing all effects in the queues.
+     */
+    fun debug(depth: Int): String
     {
-    StringBuilder s = new StringBuilder();
-    for(int i=0; i<depth; i++) s.append(" ");
-    String space = s.toString();
-
-    String mat = mQueues[0].retEffects();
-    String ver = mQueues[1].retEffects();
-    String fra = mQueues[2].retEffects();
-    String pos = mQueues[3].retEffects();
-
-    return space+"MAT: "+mat+"\n"+space+"VER: "+ver+"\n"+space+"FRA: "+fra+"\n"+space+"POS: "+pos;
+        val s = StringBuilder()
+        for (i in 0 until depth) s.append(" ")
+        val space = s.toString()
+
+        val mat = queues[0].retEffects()
+        val ver = queues[1].retEffects()
+        val fra = queues[2].retEffects()
+        val pos = queues[3].retEffects()
+
+        return """
+         ${space}MAT: $mat
+         ${space}VER: $ver
+         ${space}FRA: $fra
+         ${space}POS: $pos
+         """.trimIndent()
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/DistortedFramebuffer.kt b/src/main/java/org/distorted/library/main/DistortedFramebuffer.kt
index 674f884..88aec5b 100644
--- a/src/main/java/org/distorted/library/main/DistortedFramebuffer.kt
+++ b/src/main/java/org/distorted/library/main/DistortedFramebuffer.kt
@@ -17,350 +17,346 @@
 // License along with this library; if not, write to the Free Software                           //
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+package org.distorted.library.main
 
-package org.distorted.library.main;
-
-import android.opengl.GLES30;
+import android.opengl.GLES30
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Class which represents a OpenGL Framebuffer object.
- * <p>
+ *
  * User is able to create offscreen FBOs and both a) render to them b) use their COLOR0 attachment as
  * an input texture. Attaching Depths and/or Stencils is also possible.
  */
-public class DistortedFramebuffer extends InternalOutputSurface
-  {
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Must be called from a thread holding OpenGL Context
-
-  public void create()
+open class DistortedFramebuffer internal constructor(numfbos: Int, numcolors: Int, depthStencil: Int, type: Int, storage: Int, width: Int, height: Int) : InternalOutputSurface(width, height, NOT_CREATED_YET, numfbos, numcolors, depthStencil, NOT_CREATED_YET, type, storage)
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Must be called from a thread holding OpenGL Context
+    override fun create()
     {
-    if( mNumFBOs==DistortedLibrary.WAIT_FOR_FBO_QUEUE_SIZE )
-      {
-      // only now we know how many FBOs there should be
-      mNumFBOs = DistortedLibrary.getQueueSize();
-      allocateColor();
-      allocateStuffDependantOnNumFBOS();
-      }
-
-    //////////////////////////////////////////////////////////////
-    // COLOR
-
-    if( mColorCreated==NOT_CREATED_YET )
-      {
-      GLES30.glGenTextures( mNumFBOs*mNumColors, mColorH, 0);
-      GLES30.glGenFramebuffers(mNumFBOs, mFBOH, 0);
-
-      for(int i=0; i<mNumFBOs; i++)
+        if( mNumFBOs==DistortedLibrary.WAIT_FOR_FBO_QUEUE_SIZE )
         {
-        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[i]);
-
-        for(int j=0; j<mNumColors; j++)
-          {
-          GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH[i*mNumColors+j]);
-          GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_REPEAT);
-          GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_REPEAT);
-          GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
-          GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
-          GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, mRealWidth, mRealHeight, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
-          }
-
-        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mColorH[i*mNumColors], 0);
-        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
+            // only now we know how many FBOs there should be
+            mNumFBOs = DistortedLibrary.queueSize
+            allocateColor()
+            allocateStuffDependantOnNumFBOS()
         }
 
-      // TODO
-      mColorCreated = checkStatus("color");
-      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
-      }
-
-    //////////////////////////////////////////////////////////////
-    // DEPTH / STENCIL
-
-    if( mDepthStencilCreated==NOT_CREATED_YET ) // we need to create a new DEPTH or STENCIL attachment
-      {
-      GLES30.glGenTextures(mNumFBOs, mDepthStencilH, 0);
-
-      for(int i=0; i<mNumFBOs; i++)
+        //////////////////////////////////////////////////////////////
+        // COLOR
+        if( mColorCreated==NOT_CREATED_YET )
         {
-        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mDepthStencilH[i]);
-        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_REPEAT);
-        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_REPEAT);
-        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
-        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_NEAREST);
-
-        if (mDepthStencil == DEPTH_NO_STENCIL)
-          {
-          GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_DEPTH_COMPONENT, mRealWidth, mRealHeight, 0, GLES30.GL_DEPTH_COMPONENT, GLES30.GL_UNSIGNED_INT, null);
-          }
-        else if (mDepthStencil == BOTH_DEPTH_STENCIL)
-          {
-          GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_DEPTH24_STENCIL8, mRealWidth, mRealHeight, 0, GLES30.GL_DEPTH_STENCIL, GLES30.GL_UNSIGNED_INT_24_8, null);
-          }
+            GLES30.glGenTextures(mNumFBOs*mNumColors, mColorH, 0)
+            GLES30.glGenFramebuffers(mNumFBOs, mFBOH, 0)
+
+            for (i in 0 until mNumFBOs)
+            {
+                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[i])
+
+                for (j in 0 until mNumColors)
+                {
+                    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH!![i*mNumColors+j])
+                    GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_REPEAT)
+                    GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_REPEAT)
+                    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST.toFloat())
+                    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR.toFloat())
+                    GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, mRealWidth, mRealHeight, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null)
+                }
+
+                GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mColorH!![i*mNumColors], 0)
+                GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
+            }
+
+            // TODO
+            mColorCreated = checkStatus("color")
+            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
         }
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
 
-      for(int i=0; i<mNumFBOs; i++)
+        //////////////////////////////////////////////////////////////
+        // DEPTH / STENCIL
+        if( mDepthStencilCreated==NOT_CREATED_YET )  // we need to create a new DEPTH or STENCIL attachment
         {
-        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[i]);
-
-        if (mDepthStencil == DEPTH_NO_STENCIL)
-          {
-          GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT, GLES30.GL_TEXTURE_2D, mDepthStencilH[i], 0);
-          }
-        else if (mDepthStencil == BOTH_DEPTH_STENCIL)
-          {
-          GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_STENCIL_ATTACHMENT, GLES30.GL_TEXTURE_2D, mDepthStencilH[i], 0);
-          }
+            GLES30.glGenTextures(mNumFBOs, mDepthStencilH, 0)
+
+            for (i in 0 until mNumFBOs)
+            {
+                GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mDepthStencilH!![i])
+                GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_REPEAT)
+                GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_REPEAT)
+                GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
+                GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_NEAREST)
+
+                if (mDepthStencil==DEPTH_NO_STENCIL)
+                {
+                    GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_DEPTH_COMPONENT, mRealWidth, mRealHeight, 0, GLES30.GL_DEPTH_COMPONENT, GLES30.GL_UNSIGNED_INT, null)
+                }
+                else if (mDepthStencil==BOTH_DEPTH_STENCIL)
+                {
+                    GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_DEPTH24_STENCIL8, mRealWidth, mRealHeight, 0, GLES30.GL_DEPTH_STENCIL, GLES30.GL_UNSIGNED_INT_24_8, null)
+                }
+            }
+            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
+
+            for (i in 0 until mNumFBOs)
+            {
+                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[i])
+
+                if (mDepthStencil==DEPTH_NO_STENCIL)
+                {
+                    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT, GLES30.GL_TEXTURE_2D, mDepthStencilH!![i], 0)
+                }
+                else if (mDepthStencil==BOTH_DEPTH_STENCIL)
+                {
+                    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_STENCIL_ATTACHMENT, GLES30.GL_TEXTURE_2D, mDepthStencilH!![i], 0)
+                }
+            }
+
+            // TODO
+            mDepthStencilCreated = checkStatus("depth")
+            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
         }
 
-      // TODO
-      mDepthStencilCreated = checkStatus("depth");
-      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
-      }
-
-    //////////////////////////////////////////////////////////////
-    // DETACH
-
-    // TODO
-    if( mDepthStencilCreated==DONT_CREATE && mDepthStencilH[0]>0 ) // we need to detach and recreate the DEPTH attachment.
-      {
-      // OpenGL ES 3.0.5 spec, chapter 4.4.2.4 :
-      // "Note that the texture image is specifically not detached from any other framebuffer objects.
-      //  Detaching the texture image from any other framebuffer objects is the responsibility of the application."
+        //////////////////////////////////////////////////////////////
+        // DETACH
 
-      for(int i=0; i<mNumFBOs; i++)
+        // TODO
+        if( mDepthStencilCreated==DONT_CREATE && mDepthStencilH!![0]>0 )  // we need to detach and recreate the DEPTH attachment.
         {
-        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[i]);
-        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT, GLES30.GL_TEXTURE_2D, 0, 0);
-        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_STENCIL_ATTACHMENT, GLES30.GL_TEXTURE_2D, 0, 0);
-        mDepthStencilH[i]=0;
+            // OpenGL ES 3.0.5 spec, chapter 4.4.2.4 :
+            // "Note that the texture image is specifically not detached from any other framebuffer objects.
+            //  Detaching the texture image from any other framebuffer objects is the responsibility of the application."
+
+            for (i in 0 until mNumFBOs)
+            {
+                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[i])
+                GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT, GLES30.GL_TEXTURE_2D, 0, 0)
+                GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_STENCIL_ATTACHMENT, GLES30.GL_TEXTURE_2D, 0, 0)
+                mDepthStencilH!![i] = 0
+            }
+
+            GLES30.glDeleteTextures(mNumFBOs, mDepthStencilH, 0)
+            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
         }
-
-      GLES30.glDeleteTextures(mNumFBOs, mDepthStencilH, 0);
-      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO
-
-  private int checkStatus(String message)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // TODO
+    private fun checkStatus(message: String): Int
     {
-    int status = GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER);
+        val status = GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER)
 
-    if(status != GLES30.GL_FRAMEBUFFER_COMPLETE)
-      {
-      DistortedLibrary.logMessage("DistortedFramebuffer: FRAMEBUFFER INCOMPLETE, "+message+" error="+status);
+        if (status!=GLES30.GL_FRAMEBUFFER_COMPLETE)
+        {
+            DistortedLibrary.logMessage("DistortedFramebuffer: FRAMEBUFFER INCOMPLETE, $message error=$status")
 
-      GLES30.glDeleteTextures(1, mColorH, 0);
-      GLES30.glDeleteTextures(1, mDepthStencilH, 0);
-      GLES30.glDeleteFramebuffers(1, mFBOH, 0);
-      mFBOH[0]= 0;
+            GLES30.glDeleteTextures(1, mColorH, 0)
+            GLES30.glDeleteTextures(1, mDepthStencilH, 0)
+            GLES30.glDeleteFramebuffers(1, mFBOH, 0)
+            mFBOH[0] = 0
 
-      return FAILED_TO_CREATE;
-      }
+            return FAILED_TO_CREATE
+        }
 
-    return CREATED;
+        return CREATED
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Must be called from a thread holding OpenGL Context
-
-  public void delete()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Must be called from a thread holding OpenGL Context
+    override fun delete()
     {
-    if( mColorH[0]>0 )
-      {
-      GLES30.glDeleteTextures(mNumFBOs*mNumColors, mColorH, 0);
-      mColorCreated = NOT_CREATED_YET;
-
-      for(int i=0; i<mNumFBOs*mNumColors; i++) mColorH[i] = 0;
-      }
-
-    if( mDepthStencilH[0]>0 )
-      {
-      GLES30.glDeleteTextures(mNumFBOs, mDepthStencilH, 0);
-      mDepthStencilCreated = NOT_CREATED_YET;
-
-      for(int i=0; i<mNumFBOs; i++) mDepthStencilH[i] = 0;
-      }
-
-    if( mNumFBOs>0 && mFBOH[0]>0 )
-      {
-      GLES30.glDeleteFramebuffers(mNumFBOs, mFBOH, 0);
-      }
-
-    for(int i=0; i<mNumFBOs; i++)
-      {
-      mFBOH[i] = 0;
-      }
-    }
+        val colorH = mColorH!!
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// called from onDestroy(); mark OpenGL assets as 'not created'
-
-  public void recreate()
-    {
-    if( mColorCreated!=DONT_CREATE )
-      {
-      mColorCreated = NOT_CREATED_YET;
-      mColorH[0] = 0;
-      }
-    if( mDepthStencilCreated!=DONT_CREATE )
-      {
-      mDepthStencilCreated = NOT_CREATED_YET;
-      mDepthStencilH[0] = 0;
-      }
-    for(int i=0; i<mNumFBOs; i++)
-      {
-      mFBOH[i] = 0;
-      }
-    }
+        if( colorH[0]>0 )
+        {
+            GLES30.glDeleteTextures(mNumFBOs*mNumColors, colorH, 0)
+            mColorCreated = NOT_CREATED_YET
+            for (i in 0 until mNumFBOs*mNumColors) colorH[i] = 0
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        val depthStencilH = mDepthStencilH!!
 
-  boolean setAsInput(int fbo, int texture)
-    {
-    if( texture>=0 && texture<mNumColors && fbo>=0 && fbo<mNumFBOs && mColorH[mNumColors*fbo + texture]>0 )
-      {
-      GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH[mNumColors*fbo + texture]);
-      return true;
-      }
-
-    return false;
-    }
+        if( depthStencilH[0]>0 )
+        {
+            GLES30.glDeleteTextures(mNumFBOs, depthStencilH, 0)
+            mDepthStencilCreated = NOT_CREATED_YET
+            for (i in 0 until mNumFBOs) depthStencilH[i] = 0
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// create a multi-framebuffer (1 object containing multiple FBOs)
+        if( mNumFBOs>0 && mFBOH[0]>0 )
+        {
+            GLES30.glDeleteFramebuffers(mNumFBOs, mFBOH, 0)
+        }
 
-  DistortedFramebuffer(int numfbos, int numcolors, int depthStencil, int type, int storage, int width, int height)
-    {
-    super(width,height,NOT_CREATED_YET,numfbos,numcolors,depthStencil,NOT_CREATED_YET, type, storage);
-    markForCreation();
+        for (i in 0 until mNumFBOs)
+        {
+            mFBOH[i] = 0
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// create SYSTEM or TREE framebuffers (those are just like normal FBOs, just hold information
-// that they were autocreated only for the Library's internal purposes (SYSTEM) or for using
-// inside a Tree of DistortedNodes (TREE)
-// SYSTEM surfaces do not get removed in onDestroy().
-
-  DistortedFramebuffer(int numcolors, int depthStencil, int type, int width, int height)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // called from onDestroy(); mark OpenGL assets as 'not created'
+    override fun recreate()
     {
-    this(1,numcolors,depthStencil,type,STORAGE_PRIVATE,width,height);
+        if (mColorCreated!=DONT_CREATE)
+        {
+            mColorCreated = NOT_CREATED_YET
+            mColorH!![0] = 0
+        }
+        if (mDepthStencilCreated!=DONT_CREATE)
+        {
+            mDepthStencilCreated = NOT_CREATED_YET
+            mDepthStencilH!![0] = 0
+        }
+        for (i in 0 until mNumFBOs)
+        {
+            mFBOH[i] = 0
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create new offscreen Framebuffer with configurable number of COLOR, DEPTH and STENCIL attachments.
- *
- * @param width        Width of all the COLOR attachments.
- * @param height       Height of all the COLOR attachments.
- * @param numcolors    How many COLOR attachments to create?
- * @param depthStencil Add DEPTH or STENCIL attachment?
- *                     Valid values: NO_DEPTH_NO_STENCIL, DEPTH_NO_STENCIL, BOTH_DEPTH_STENCIL.
- */
-  @SuppressWarnings("unused")
-  public DistortedFramebuffer(int width, int height, int numcolors, int depthStencil)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setAsInput(fbo: Int, texture: Int): Boolean
     {
-    this(1,numcolors,depthStencil,TYPE_USER,STORAGE_PRIVATE,width,height);
-    }
+        if( (texture in 0..<mNumColors) && (fbo in 0..<mNumFBOs) )
+        {
+            val colorH = mColorH!![mNumColors*fbo+texture]
+
+            if( colorH>0 )
+            {
+                GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
+                GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, colorH)
+                return true
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Bind the underlying rectangle of pixels as a OpenGL Texture.
- *
- * @param texture The Texture number to bind (and thus read from).
- * @return <code>true</code> if successful.
- */
-  public boolean setAsInput(int texture)
-    {
-    if( texture>=0 && texture<mNumColors && mColorH[texture]>0 )
-      {
-      GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH[2*mCurrFBO+texture]);
-      return true;
-      }
-
-    return false;
+        return false
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  /**
-   * Attach the texture'th Texture to COLOR0 attachment.
-   *
-   * @param texture The Texture number to attach (and subsequently use to render to)
-   */
-  public void bindForOutput(int texture)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // create a multi-framebuffer (1 object containing multiple FBOs)
+    init
     {
-    if( texture>=0 && texture<mNumColors && mColorH[texture]>0 )
-      {
-      GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mColorH[2*mCurrFBO+texture], 0);
-      }
+        markForCreation()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Enable.disable DEPTH and STENCIL buffers.
- *
- * @param depthStencil Valid values: NO_DEPTH_NO_STENCIL, DEPTH_NO_STENCIL, BOTH_DEPTH_STENCIL.
- */
-  public void enableDepthStencil(int depthStencil)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // create SYSTEM or TREE framebuffers (those are just like normal FBOs, just hold information
+    // that they were autocreated only for the Library's internal purposes (SYSTEM) or for using
+    // inside a Tree of DistortedNodes (TREE)
+    // SYSTEM surfaces do not get removed in onDestroy().
+    internal constructor(numcolors: Int, depthStencil: Int, type: Int, width: Int, height: Int)
+             : this(1, numcolors, depthStencil, type, STORAGE_PRIVATE, width, height)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Create new offscreen Framebuffer with configurable number of COLOR, DEPTH and STENCIL attachments.
+     *
+     * @param width        Width of all the COLOR attachments.
+     * @param height       Height of all the COLOR attachments.
+     * @param numcolors    How many COLOR attachments to create?
+     * @param depthStencil Add DEPTH or STENCIL attachment?
+     * Valid values: NO_DEPTH_NO_STENCIL, DEPTH_NO_STENCIL, BOTH_DEPTH_STENCIL.
+     */
+    @Suppress("unused")
+    constructor(width: Int, height: Int, numcolors: Int, depthStencil: Int)
+      : this(1, numcolors, depthStencil, TYPE_USER, STORAGE_PRIVATE, width, height)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Bind the underlying rectangle of pixels as a OpenGL Texture.
+     *
+     * @param texture The Texture number to bind (and thus read from).
+     * @return `true` if successful.
+     */
+    fun setAsInput(texture: Int): Boolean
     {
-    if( depthStencil != mDepthStencil )
-      {
-      mDepthStencil = depthStencil;
-
-      if( depthStencil!= NO_DEPTH_NO_STENCIL && mDepthStencilCreated==DONT_CREATE )
+        if( texture in 0..<mNumColors )
         {
-        mDepthStencilCreated = NOT_CREATED_YET;
-        markForCreation();
+            val colorH = mColorH!![2*mCurrFBO + texture]
+
+            if( colorH>0 )
+            {
+                GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
+                GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, colorH)
+                return true
+            }
         }
-      if( depthStencil== NO_DEPTH_NO_STENCIL && mDepthStencilCreated!=DONT_CREATE )
+
+        return false
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Attach the texture'th Texture to COLOR0 attachment.
+     *
+     * @param texture The Texture number to attach (and subsequently use to render to)
+     */
+    fun bindForOutput(texture: Int)
+    {
+        if( texture in 0..<mNumColors )
         {
-        mDepthStencilCreated = DONT_CREATE;
-        markForCreation();
+            val colorH = mColorH!![2*mCurrFBO + texture]
+
+            if( colorH>0 )
+                GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, colorH, 0)
         }
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the ID of the Texture (COLOR attachment 0) that's backing this FBO.
- * <p>
- * Catch: this will only work if the library has had time to actually create the texture. Remember
- * that the texture gets created only on first render, thus creating a Texture object and immediately
- * calling this method will return an invalid (negative) result.
- *
- * @return If there was not a single render between creation of the Object and calling this method on
- *         it, return a negative value. Otherwise, return ID of COLOR attachment 0.
- */
-  public int getTextureID()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Enable/disable DEPTH and STENCIL buffers.
+     *
+     * @param depthStencil Valid values: NO_DEPTH_NO_STENCIL, DEPTH_NO_STENCIL, BOTH_DEPTH_STENCIL.
+     */
+    fun enableDepthStencil(depthStencil: Int)
     {
-    return mColorH[0];
+        if( depthStencil!=mDepthStencil )
+        {
+            mDepthStencil = depthStencil
+
+            if( depthStencil!=NO_DEPTH_NO_STENCIL && mDepthStencilCreated==DONT_CREATE )
+            {
+                mDepthStencilCreated = NOT_CREATED_YET
+                markForCreation()
+            }
+            if( depthStencil==NO_DEPTH_NO_STENCIL && mDepthStencilCreated!=DONT_CREATE )
+            {
+                mDepthStencilCreated = DONT_CREATE
+                markForCreation()
+            }
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Bind one of the underlying FBOs to GL_READ_FRAMEBUFFER.
- * Useful for a subsequent glReadBuffer / glReadPixels.
- *
- * @param fbo which of the underlying FBOs to bind.
- * @return <code>true</code> if successful.
- */
-  public boolean setAsReadFramebuffer(int fbo)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Return the ID of the Texture (COLOR attachment 0) that's backing this FBO.
+     *
+     * Catch: this will only work if the library has had time to actually create the texture. Remember
+     * that the texture gets created only on first render, thus creating a Texture object and immediately
+     * calling this method will return an invalid (negative) result.
+     *
+     * @return If there was not a single render between creation of the Object and calling this method on
+     * it, return a negative value. Otherwise, return ID of COLOR attachment 0.
+     */
+    val textureID: Int get() = mColorH!![0]
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Bind one of the underlying FBOs to GL_READ_FRAMEBUFFER.
+     * Useful for a subsequent glReadBuffer / glReadPixels.
+     *
+     * @param fbo which of the underlying FBOs to bind.
+     * @return `true` if successful.
+     */
+    fun setAsReadFramebuffer(fbo: Int): Boolean
     {
-    if( fbo>=0 && fbo<mNumFBOs )
-      {
-      GLES30.glBindFramebuffer(GLES30.GL_READ_FRAMEBUFFER, mFBOH[fbo]);
-      return true;
-      }
+        if( fbo in 0..<mNumFBOs )
+        {
+            GLES30.glBindFramebuffer(GLES30.GL_READ_FRAMEBUFFER, mFBOH[fbo])
+            return true
+        }
 
-    return false;
+        return false
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/DistortedLibrary.kt b/src/main/java/org/distorted/library/main/DistortedLibrary.kt
index 4f5c52d..1ae3b0d 100644
--- a/src/main/java/org/distorted/library/main/DistortedLibrary.kt
+++ b/src/main/java/org/distorted/library/main/DistortedLibrary.kt
@@ -17,1345 +17,1239 @@
 // License along with this library; if not, write to the Free Software                           //
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.main;
-
-import android.opengl.GLES30;
-import android.opengl.GLES31;
-
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.distorted.library.R;
-import org.distorted.library.effect.Effect;
-import org.distorted.library.effectqueue.EffectQueue;
-import org.distorted.library.effectqueue.EffectQueuePostprocess;
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.effect.FragmentEffect;
-import org.distorted.library.effect.PostprocessEffect;
-import org.distorted.library.effect.VertexEffect;
-import org.distorted.library.effectqueue.EffectQueueVertex;
-import org.distorted.library.mesh.DeferredJobs;
-import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.message.EffectMessageSender;
-import org.distorted.library.program.DistortedProgram;
-import org.distorted.library.program.VertexCompilationException;
-import org.distorted.library.type.Dynamic;
+package org.distorted.library.main
+
+import android.opengl.GLES30
+import android.opengl.GLES31
+import org.distorted.library.R
+import org.distorted.library.effect.Effect
+import org.distorted.library.effect.EffectType
+import org.distorted.library.effect.FragmentEffect
+import org.distorted.library.effect.PostprocessEffect.Companion.createPrograms
+import org.distorted.library.effect.VertexEffect
+import org.distorted.library.effect.VertexEffect.Companion.allEnabled
+import org.distorted.library.effect.VertexEffect.Companion.allGLSL
+import org.distorted.library.effectqueue.EffectQueue
+import org.distorted.library.effectqueue.EffectQueue.Companion.compute
+import org.distorted.library.effectqueue.EffectQueue.Companion.getMax
+import org.distorted.library.effectqueue.EffectQueue.Companion.send
+import org.distorted.library.effectqueue.EffectQueue.Companion.setMax
+import org.distorted.library.effectqueue.EffectQueuePostprocess.Companion.createPrograms
+import org.distorted.library.effectqueue.EffectQueueVertex
+import org.distorted.library.main.InternalRenderState.Companion.restoreDrawing
+import org.distorted.library.main.InternalRenderState.Companion.switchOffDrawing
+import org.distorted.library.main.InternalStackFrameList.isInitialized
+import org.distorted.library.mesh.DeferredJobs
+import org.distorted.library.mesh.MeshBase
+import org.distorted.library.mesh.MeshBase.Companion.maxEffComponents
+import org.distorted.library.mesh.MeshBase.Companion.useCenters
+import org.distorted.library.message.EffectMessageSender.Companion.startSending
+import org.distorted.library.program.DistortedProgram
+import org.distorted.library.program.VertexCompilationException
+import org.distorted.library.type.Dynamic
+import java.io.InputStream
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.nio.FloatBuffer
+import java.util.regex.Pattern
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * A singleton class used to control various global dialog_settings.
+ * A singleton class used to control various global settings.
  */
-public class DistortedLibrary
-  {
-  /**
-   * When creating an instance of a DistortedTexture from another instance, clone the Bitmap that's
-   * backing up our DistortedTexture.
-   * <p>
-   * This way we can have two DistortedTextures, both backed up by the same Bitmap, to which we can
-   * apply different effects. Used in the copy constructor.
-   */
-  public static final int CLONE_SURFACE = 0x1;
-  /**
-   * When creating an instance of a DistortedEffects from another instance, clone the Matrix Effects.
-   * <p>
-   * This way we can have two different DistortedEffects sharing the MATRIX queue.
-   */
-  public static final int CLONE_MATRIX = 0x2;
-  /**
-   * When creating an instance of a DistortedEffects from another instance, clone the Vertex Effects.
-   * <p>
-   * This way we can have two different DistortedEffects sharing the VERTEX queue.
-   */
-  public static final int CLONE_VERTEX  = 0x4;
-  /**
-   * When creating an instance of a DistortedEffects from another instance, clone the Fragment Effects.
-   * <p>
-   * This way we can have two different DistortedEffects sharing the FRAGMENT queue.
-   */
-  public static final int CLONE_FRAGMENT= 0x8;
-   /**
-   * When creating an instance of a DistortedEffects from another instance, clone the PostProcess Effects.
-   * <p>
-   * This way we can have two different DistortedEffects sharing the POSTPROCESS queue.
-   */
-  public static final int CLONE_POSTPROCESS= 0x10;
-  /**
-   * When creating an instance of a DistortedNode from another instance, clone the children Nodes.
-   * <p>
-   * This is mainly useful for creating many similar sub-trees and rendering then at different places
-   * on the screen with (optionally) different Effects.
-   */
-  public static final int CLONE_CHILDREN= 0x20;
-
-  /**
-   * When creating a DistortedScreen (which needs to have mFBOQueueSize FBOs attached), pass this
-   * constant for 'numOfFBOs' and the number of backing FBOs will be taken from 'mFBOQueueSize'
-   * (the value of which is most likely unknown at the time of creation of the Screen)
-   */
-  public static final int WAIT_FOR_FBO_QUEUE_SIZE = -1;
-  /**
-   * Work around bugs in ARM Mali driver by, instead to a single FBO, rendering to a circular queue
-   * of mFBOQueueSize FBOs. (otherwise we sometimes get a 'full pipeline flush' and the end result
-   * might be missing part of the Objects)
-   * <p>
-   * This bug only exists on Mali driver r12. (or more precisely it's there in r12 but fixed in r22)
-   * <p>
-   * <a href="https://community.arm.com/graphics/f/discussions/10285/opengl-es-3-1-on-mali-t880-flashes">...</a>
-   */
-  private static int mFBOQueueSize;
-  private static int mGLSL;
-  private static String mGLSL_VERSION;
-  private static boolean mOITCompilationAttempted, mNeedsTransformFeedback;
-
-  private static int mMaxTextureSize         = Integer.MAX_VALUE;
-  private static int mMaxNumberOfVerUniforms = Integer.MAX_VALUE;
-  private static int mMaxNumberOfFraUniforms = Integer.MAX_VALUE;
-
-  private static boolean mBuggyUBOs;
-  private static String mVendor, mVersion, mRenderer;
-  private static boolean mFastCompilationTF;
-
-  //////////////////////////////////////////////////////////////////////////////////////////////
-  /// MAIN PROGRAM ///
-  private static DistortedProgram mMainProgram;
-  private static int mMainTextureH;
-  private static int mTransformFeedbackH;
-
-  /// NORMAL PROGRAM /////
-  private static DistortedProgram mNormalProgram;
-  private static int mNormalProjectionH;
-
-  /// MAIN OIT PROGRAM ///
-  private static DistortedProgram mMainOITProgram;
-  private static int mMainOITTextureH;
-  private static int mMainOITSizeH;
-  private static int mMainOITNumRecordsH;
-
-  /// BLIT PROGRAM ///
-  private static DistortedProgram mBlitProgram;
-  private static int mBlitTextureH;
-  private static int mBlitDepthH;
-  private static final FloatBuffer mQuadPositions;
-
-  /// FULL PROGRAM ///
-  private static DistortedProgram mFullProgram;
-
-  static
-    {
-    float[] positionData= { -0.5f, -0.5f,  -0.5f, 0.5f,  0.5f,-0.5f,  0.5f, 0.5f };
-    mQuadPositions = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder()).asFloatBuffer();
-    mQuadPositions.put(positionData).position(0);
-    }
-
-  /// BLIT DEPTH PROGRAM ///
-  private static DistortedProgram mBlitDepthProgram;
-  private static int mBlitDepthTextureH;
-  private static int mBlitDepthDepthTextureH;
-  private static int mBlitDepthTexCorrH;
-
-  /// Program Handles ///
-  private static int mMainProgramH, mFullProgramH, mMainOITProgramH;
-
-  /// OIT SSBO BUFFER ///
-  private static final int[] mLinkedListSSBO = new int[1];
-  private static int[] mAtomicCounter;
-  private static int   mCurrBuffer;
-
-  static
-    {
-    mLinkedListSSBO[0]= -1;
-    mCurrBuffer       =  0;
-    }
-
-  ///////////////////////////////////////////////////////////////
-  // meaning: allocate 1.0 screenful of places for transparent
-  // fragments in the SSBO backing up the OIT render method.
-  private static float mBufferSize=1.0f;
-
-  /// OIT CLEAR PROGRAM ///
-  private static DistortedProgram mOITClearProgram;
-  private static int mOITClearDepthH;
-  private static int mOITClearTexCorrH;
-  private static int mOITClearSizeH;
-
-  /// OIT BUILD PROGRAM ///
-  private static DistortedProgram mOITBuildProgram;
-  private static int mOITBuildTextureH;
-  private static int mOITBuildDepthTextureH;
-  private static int mOITBuildDepthH;
-  private static int mOITBuildTexCorrH;
-  private static int mOITBuildSizeH;
-  private static int mOITBuildNumRecordsH;
-
-  /// OIT COLLAPSE PROGRAM ///
-  private static DistortedProgram mOITCollapseProgram;
-  private static int mOITCollapseDepthTextureH;
-  private static int mOITCollapseDepthH;
-  private static int mOITCollapseTexCorrH;
-  private static int mOITCollapseSizeH;
-
-  /// OIT RENDER PROGRAM ///
-  private static DistortedProgram mOITRenderProgram;
-  private static int mOITRenderDepthH;
-  private static int mOITRenderTexCorrH;
-  private static int mOITRenderSizeH;
-
-  /// END PROGRAMS //////
-
-  /**
-   * Every application using the library must implement this interface so that the library can send
-   * it exceptions that arise. The exceptions may come at any time, for example the library will
-   * compile its OIT problem only on the first attempt to use the OIT
-   * Those will mainly be hardware-related: shaders do not compile on particular hardware, the required
-   * OpenGL ES 3.0 is not supported, etc.
-   * <p>
-   * Additionally, the User must be able to provide version of the OpenGL supported by the underlying
-   * hardware and InputStreams to given local files.
-   */
-  public interface LibraryUser
+object DistortedLibrary
+{
+    /**
+     * When creating an instance of a DistortedTexture from another instance, clone the Bitmap that's
+     * backing up our DistortedTexture.
+     *
+     * This way we can have two DistortedTextures, both backed up by the same Bitmap, to which we can
+     * apply different effects. Used in the copy constructor.
+     */
+    const val CLONE_SURFACE: Int = 0x1
+
+    /**
+     * When creating an instance of a DistortedEffects from another instance, clone the Matrix Effects.
+     *
+     * This way we can have two different DistortedEffects sharing the MATRIX queue.
+     */
+    const val CLONE_MATRIX: Int = 0x2
+
+    /**
+     * When creating an instance of a DistortedEffects from another instance, clone the Vertex Effects.
+     *
+     * This way we can have two different DistortedEffects sharing the VERTEX queue.
+     */
+    const val CLONE_VERTEX: Int = 0x4
+
+    /**
+     * When creating an instance of a DistortedEffects from another instance, clone the Fragment Effects.
+     *
+     * This way we can have two different DistortedEffects sharing the FRAGMENT queue.
+     */
+    const val CLONE_FRAGMENT: Int = 0x8
+
+    /**
+     * When creating an instance of a DistortedEffects from another instance, clone the PostProcess Effects.
+     *
+     * This way we can have two different DistortedEffects sharing the POSTPROCESS queue.
+     */
+    const val CLONE_POSTPROCESS: Int = 0x10
+
+    /**
+     * When creating an instance of a DistortedNode from another instance, clone the children Nodes.
+     *
+     * This is mainly useful for creating many similar sub-trees and rendering then at different places
+     * on the screen with (optionally) different Effects.
+     */
+    const val CLONE_CHILDREN: Int = 0x20
+
+    /**
+     * When creating a DistortedScreen (which needs to have mFBOQueueSize FBOs attached), pass this
+     * constant for 'numOfFBOs' and the number of backing FBOs will be taken from 'mFBOQueueSize'
+     * (the value of which is most likely unknown at the time of creation of the Screen)
+     */
+    const val WAIT_FOR_FBO_QUEUE_SIZE: Int = -1
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Work around bugs in ARM Mali driver by, instead to a single FBO, rendering to a circular queue
+     * of mFBOQueueSize FBOs. (otherwise we sometimes get a 'full pipeline flush' and the end result
+     * might be missing part of the Objects)
+     *
+     * This bug only exists on Mali driver r12. (or more precisely it's there in r12 but fixed in r22)
+     *
+     * [...](https://community.arm.com/graphics/f/discussions/10285/opengl-es-3-1-on-mali-t880-flashes)
+     */
+    var queueSize: Int = 0
+        private set
+
+    /**
+     * Return OpenGL ES version supported by the hardware we are running on.
+     * There are only three possibilities: 300 (OpenGL ES 3.0) or 310 (at least OpenGL ES 3.1)
+     * or 200 (OpenGL ES 2.0)
+     */
+    @JvmStatic var gLSL: Int = 0
+        private set
+    private var mGLSL_VERSION: String? = null
+    private var mOITCompilationAttempted = false
+    private var mNeedsTransformFeedback = false
+
+    /**
+     * Return the maximum size of the texture supported by the driver.
+     */
+    @JvmStatic var maxTextureSize: Int = Int.MAX_VALUE
+        private set
+
+    /**
+     * Return an interger - max number of uniforms one can upload to a Vertex Shader.
+     */
+    var maxVertexUniforms: Int = Int.MAX_VALUE
+        private set
+
+    /**
+     * Return an interger - max number of uniforms one can upload to a Fragment Shader.
+     */
+    var maxFragmentUniforms: Int = Int.MAX_VALUE
+        private set
+
+    /**
+     * Are we running this on hardware where UBOs are buggy?
+     */
+    var isUBOBuggy: Boolean = false
+        private set
+
+    /**
+     * Return a String defining the vendor of the graphics driver.
+     */
+    var driverVendor: String? = null
+        private set
+
+    /**
+     * Return a String defining the version of the graphics driver.
+     */
+    var driverVersion: String? = null
+        private set
+
+    /**
+     * Return a String defining the renderer of the graphics driver.
+     */
+    var driverRenderer: String? = null
+        private set
+    private var mFastCompilationTF = false
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    /** MAIN PROGRAM /// */
+    private var mMainProgram: DistortedProgram? = null
+    private var mMainTextureH = 0
+    private var mTransformFeedbackH = 0
+
+    /** NORMAL PROGRAM ///// */
+    private var mNormalProgram: DistortedProgram? = null
+    private var mNormalProjectionH = 0
+
+    /** MAIN OIT PROGRAM /// */
+    private var mMainOITProgram: DistortedProgram? = null
+    private var mMainOITTextureH = 0
+    private var mMainOITSizeH = 0
+    private var mMainOITNumRecordsH = 0
+
+    /** BLIT PROGRAM /// */
+    private var mBlitProgram: DistortedProgram? = null
+    private var mBlitTextureH = 0
+    private var mBlitDepthH = 0
+    private var mQuadPositions: FloatBuffer
+
+    /** FULL PROGRAM /// */
+    private var mFullProgram: DistortedProgram? = null
+
+    init
     {
-    void distortedException(Exception ex);
-    InputStream localFile(int fileID);
-    void logMessage(String message);
+        val positionData = floatArrayOf(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f)
+        mQuadPositions = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder()).asFloatBuffer()
+        mQuadPositions.put(positionData).position(0)
     }
 
-  private static LibraryUser mUser;
+    /** BLIT DEPTH PROGRAM /// */
+    private var mBlitDepthProgram: DistortedProgram? = null
+    private var mBlitDepthTextureH = 0
+    private var mBlitDepthDepthTextureH = 0
+    private var mBlitDepthTexCorrH = 0
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// private: hide this from Javadoc
-
-  private DistortedLibrary()
-    {
-
-    }
+    /** Program Handles /// */
+    private var mMainProgramH = 0
+    private var mFullProgramH = 0
+    private var mMainOITProgramH = 0
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    /** OIT SSBO BUFFER /// */
+    private val mLinkedListSSBO = IntArray(1)
+    private var mAtomicCounter: IntArray? = null
+    private var mCurrBuffer = 0
 
-  private static void createMainProgram()
+    init
     {
-    // MAIN PROGRAM ////////////////////////////////////
-    final InputStream mainVertStream = mUser.localFile(R.raw.main_vertex_shader);
-    final InputStream mainFragStream = mUser.localFile(R.raw.main_fragment_shader);
-
-    int numF = FragmentEffect.getNumEnabled();
-    int numV = VertexEffect.getNumEnabled();
-
-    String mainVertHeader= mGLSL_VERSION + ("#define NUM_VERTEX "   + ( numV>0 ? getMax(EffectType.VERTEX  ) : 0 ) + "\n");
-    String mainFragHeader= mGLSL_VERSION + ("#define NUM_FRAGMENT " + ( numF>0 ? getMax(EffectType.FRAGMENT) : 0 ) + "\n");
-
-    mainVertHeader += "#define MAX_COMPON " + MeshBase.getMaxEffComponents() + "\n";
-    if( MeshBase.getUseCenters() ) mainVertHeader += "#define COMP_CENTERS\n";
-    if( mBuggyUBOs )               mainVertHeader += "#define BUGGY_UBOS\n";
-
-    String enabledEffectV= VertexEffect.getGLSL();
-    String enabledEffectF= FragmentEffect.getGLSL();
-
-    String[] feedback = { "v_Position", "v_endPosition" };
-
-    try
-      {
-      mMainProgram = new DistortedProgram(mainVertStream, mainFragStream, mainVertHeader,
-                                          mainFragHeader, enabledEffectV, enabledEffectF,
-                                          mGLSL, mNeedsTransformFeedback ? feedback : null );
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile MAIN program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    mMainProgramH = mMainProgram.getProgramHandle();
-    EffectQueue.getUniforms(mMainProgramH,0);
-    MeshBase.getUniforms(mMainProgramH,0);
-    mMainTextureH= GLES30.glGetUniformLocation( mMainProgramH, "u_Texture");
-    mTransformFeedbackH= GLES30.glGetUniformLocation( mMainProgramH, "u_TransformFeedback");
-
-    // BLIT PROGRAM ////////////////////////////////////
-    final InputStream blitVertStream = mUser.localFile(R.raw.blit_vertex_shader);
-    final InputStream blitFragStream = mUser.localFile(R.raw.blit_fragment_shader);
-
-    try
-      {
-      mBlitProgram = new DistortedProgram(blitVertStream,blitFragStream, mGLSL_VERSION, mGLSL_VERSION, mGLSL);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile BLIT program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    int blitProgramH = mBlitProgram.getProgramHandle();
-    mBlitTextureH  = GLES30.glGetUniformLocation( blitProgramH, "u_Texture");
-    mBlitDepthH    = GLES30.glGetUniformLocation( blitProgramH, "u_Depth");
-
-    // BLIT DEPTH PROGRAM ////////////////////////////////////
-    final InputStream blitDepthVertStream = mUser.localFile(R.raw.blit_depth_vertex_shader);
-    final InputStream blitDepthFragStream = mUser.localFile(R.raw.blit_depth_fragment_shader);
-
-    try
-      {
-      mBlitDepthProgram = new DistortedProgram(blitDepthVertStream,blitDepthFragStream, mGLSL_VERSION, mGLSL_VERSION, mGLSL);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile BLIT DEPTH program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    int blitDepthProgramH   = mBlitDepthProgram.getProgramHandle();
-    mBlitDepthTextureH      = GLES30.glGetUniformLocation( blitDepthProgramH, "u_Texture");
-    mBlitDepthDepthTextureH = GLES30.glGetUniformLocation( blitDepthProgramH, "u_DepthTexture");
-    mBlitDepthTexCorrH      = GLES30.glGetUniformLocation( blitDepthProgramH, "u_TexCorr");
+        mLinkedListSSBO[0] = -1
+        mCurrBuffer = 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void createNormalProgram()
+    ///////////////////////////////////////////////////////////////
+    // meaning: allocate 1.0 screenful of places for transparent
+    // fragments in the SSBO backing up the OIT render method.
+    private var mBufferSize = 1.0f
+
+    /** OIT CLEAR PROGRAM /// */
+    private var mOITClearProgram: DistortedProgram? = null
+    private var mOITClearDepthH = 0
+    private var mOITClearTexCorrH = 0
+    private var mOITClearSizeH = 0
+
+    /** OIT BUILD PROGRAM /// */
+    private var mOITBuildProgram: DistortedProgram? = null
+    private var mOITBuildTextureH = 0
+    private var mOITBuildDepthTextureH = 0
+    private var mOITBuildDepthH = 0
+    private var mOITBuildTexCorrH = 0
+    private var mOITBuildSizeH = 0
+    private var mOITBuildNumRecordsH = 0
+
+    /** OIT COLLAPSE PROGRAM /// */
+    private var mOITCollapseProgram: DistortedProgram? = null
+    private var mOITCollapseDepthTextureH = 0
+    private var mOITCollapseDepthH = 0
+    private var mOITCollapseTexCorrH = 0
+    private var mOITCollapseSizeH = 0
+
+    /** OIT RENDER PROGRAM /// */
+    private var mOITRenderProgram: DistortedProgram? = null
+    private var mOITRenderDepthH = 0
+    private var mOITRenderTexCorrH = 0
+    private var mOITRenderSizeH = 0
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /** END PROGRAMS ////// */
+    /**
+     * Every application using the library must implement this interface so that the library can send
+     * it exceptions that arise. The exceptions may come at any time, for example the library will
+     * compile its OIT problem only on the first attempt to use the OIT
+     * Those will mainly be hardware-related: shaders do not compile on particular hardware, the required
+     * OpenGL ES 3.0 is not supported, etc.
+     *
+     *
+     * Additionally, the User must be able to provide version of the OpenGL supported by the underlying
+     * hardware and InputStreams to given local files.
+     */
+    interface LibraryUser
     {
-    // NORMAL PROGRAM //////////////////////////////////////
-    final InputStream normalVertexStream   = mUser.localFile(R.raw.normal_vertex_shader);
-    final InputStream normalFragmentStream = mUser.localFile(R.raw.normal_fragment_shader);
-
-    try
-      {
-      mNormalProgram = new DistortedProgram(normalVertexStream,normalFragmentStream, mGLSL_VERSION, mGLSL_VERSION, mGLSL);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile NORMAL program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    int normalProgramH = mNormalProgram.getProgramHandle();
-    mNormalProjectionH = GLES30.glGetUniformLocation( normalProgramH, "u_Projection");
+        fun distortedException(ex: Exception?)
+        fun localFile(fileID: Int): InputStream
+        fun logMessage(message: String?)
     }
+    private lateinit var mUser: LibraryUser
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void createFullProgram()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun createMainProgram()
     {
-    final InputStream fullVertStream = mUser.localFile(R.raw.main_vertex_shader);
-    final InputStream fullFragStream = mUser.localFile(R.raw.main_fragment_shader);
+        // MAIN PROGRAM ////////////////////////////////////
+        val mainVertStream = mUser.localFile(R.raw.main_vertex_shader)
+        val mainFragStream = mUser.localFile(R.raw.main_fragment_shader)
 
-    int numV = VertexEffect.getAllEnabled();
+        val numF = FragmentEffect.getNumEnabled()
+        val numV = VertexEffect.getNumEnabled()
 
-    String fullVertHeader= mGLSL_VERSION + ("#define NUM_VERTEX "   + ( numV>0 ? getMax(EffectType.VERTEX ) : 0 ) + "\n");
-    String fullFragHeader= mGLSL_VERSION + ("#define NUM_FRAGMENT " +                                         0   + "\n");
+        var mainVertHeader = mGLSL_VERSION+("#define NUM_VERTEX "+(if (numV>0) getMax(EffectType.VERTEX) else 0)+"\n")
+        val mainFragHeader = mGLSL_VERSION+("#define NUM_FRAGMENT "+(if (numF>0) getMax(EffectType.FRAGMENT) else 0)+"\n")
 
-    fullVertHeader += "#define MAX_COMPON " + MeshBase.getMaxEffComponents() + "\n";
-    if( MeshBase.getUseCenters() ) fullVertHeader += "#define COMP_CENTERS\n";
-    if( mBuggyUBOs )               fullVertHeader += "#define BUGGY_UBOS\n";
+        mainVertHeader += "#define MAX_COMPON $maxEffComponents\n"
+        if (useCenters) mainVertHeader += "#define COMP_CENTERS\n"
+        if (isUBOBuggy) mainVertHeader += "#define BUGGY_UBOS\n"
 
-    String enabledEffectV= VertexEffect.getAllGLSL();
-    String enabledEffectF= "{}";
+        val enabledEffectV = VertexEffect.getGLSL()
+        val enabledEffectF = FragmentEffect.getGLSL()
 
-    fullVertHeader += "#define PREAPPLY\n";
+        val feedback = arrayOf("v_Position", "v_endPosition")
 
-    String[] feedback = { "v_Position", "v_endPosition" };
+        try
+        {
+            mMainProgram = DistortedProgram(mainVertStream, mainFragStream, mainVertHeader,
+                                            mainFragHeader, enabledEffectV, enabledEffectF,
+                                            gLSL, if (mNeedsTransformFeedback) feedback else null)
+        }
+        catch (e: Exception)
+        {
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile MAIN program: "+e.message)
+            throw RuntimeException(e.message)
+        }
 
-    try
-      {
-      mFullProgram = new DistortedProgram(fullVertStream, fullFragStream, fullVertHeader, fullFragHeader,
-                                          enabledEffectV, enabledEffectF, mGLSL, feedback);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile FULL program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
+        mMainProgramH = mMainProgram!!.programHandle
+        EffectQueue.getUniforms(mMainProgramH, 0)
+        MeshBase.getUniforms(mMainProgramH, 0)
+        mMainTextureH = GLES30.glGetUniformLocation(mMainProgramH, "u_Texture")
+        mTransformFeedbackH = GLES30.glGetUniformLocation(mMainProgramH, "u_TransformFeedback")
 
-    mFullProgramH = mFullProgram.getProgramHandle();
-    EffectQueue.getUniforms(mFullProgramH,3);
-    MeshBase.getUniforms(mFullProgramH,3);
-    }
+        // BLIT PROGRAM ////////////////////////////////////
+        val blitVertStream = mUser.localFile(R.raw.blit_vertex_shader)
+        val blitFragStream = mUser.localFile(R.raw.blit_fragment_shader)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        try
+        {
+            mBlitProgram = DistortedProgram(blitVertStream, blitFragStream, mGLSL_VERSION!!, mGLSL_VERSION!!, gLSL)
+        }
+        catch (e: Exception)
+        {
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile BLIT program: "+e.message)
+            throw RuntimeException(e.message)
+        }
 
-  private static void createOITProgram()
-    {
-    // MAIN OIT PROGRAM ////////////////////////////////
-    final InputStream mainVertStream = mUser.localFile(R.raw.main_vertex_shader);
-    final InputStream mainFragStream = mUser.localFile(R.raw.main_fragment_shader);
-
-    int numF = FragmentEffect.getNumEnabled();
-    int numV = VertexEffect.getNumEnabled();
-
-    String mainVertHeader= mGLSL_VERSION + ("#define NUM_VERTEX "   + ( numV>0 ? getMax(EffectType.VERTEX  ) : 0 ) + "\n") + ("#define OIT\n");
-    String mainFragHeader= mGLSL_VERSION + ("#define NUM_FRAGMENT " + ( numF>0 ? getMax(EffectType.FRAGMENT) : 0 ) + "\n") + ("#define OIT\n");
-
-    mainVertHeader += "#define MAX_COMPON " + MeshBase.getMaxEffComponents() + "\n";
-    if( MeshBase.getUseCenters() ) mainVertHeader += "#define COMP_CENTERS\n";
-    if( mBuggyUBOs )               mainVertHeader += "#define BUGGY_UBOS\n";
-
-    String enabledEffectV= VertexEffect.getGLSL();
-    String enabledEffectF= FragmentEffect.getGLSL();
-
-    try
-      {
-      mMainOITProgram = new DistortedProgram(mainVertStream, mainFragStream, mainVertHeader, mainFragHeader,
-                                             enabledEffectV, enabledEffectF, mGLSL, null);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile MAIN OIT program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    mMainOITProgramH = mMainOITProgram.getProgramHandle();
-    EffectQueue.getUniforms(mMainOITProgramH,1);
-    MeshBase.getUniforms(mMainOITProgramH,1);
-    mMainOITTextureH    = GLES30.glGetUniformLocation( mMainOITProgramH, "u_Texture");
-    mMainOITSizeH       = GLES30.glGetUniformLocation( mMainOITProgramH, "u_Size");
-    mMainOITNumRecordsH = GLES30.glGetUniformLocation( mMainOITProgramH, "u_numRecords");
-
-    // OIT CLEAR PROGRAM ////////////////////////////////////
-    final InputStream oitClearVertStream = mUser.localFile(R.raw.oit_vertex_shader);
-    final InputStream oitClearFragStream = mUser.localFile(R.raw.oit_clear_fragment_shader);
-
-    try
-      {
-      mOITClearProgram = new DistortedProgram(oitClearVertStream,oitClearFragStream, mGLSL_VERSION, mGLSL_VERSION, mGLSL);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile OIT CLEAR program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    int oitClearProgramH   = mOITClearProgram.getProgramHandle();
-    mOITClearDepthH        = GLES30.glGetUniformLocation( oitClearProgramH, "u_Depth");
-    mOITClearTexCorrH      = GLES30.glGetUniformLocation( oitClearProgramH, "u_TexCorr");
-    mOITClearSizeH         = GLES30.glGetUniformLocation( oitClearProgramH, "u_Size");
-
-    // OIT BUILD PROGRAM ////////////////////////////////////
-    final InputStream oitBuildVertStream = mUser.localFile(R.raw.oit_vertex_shader);
-    final InputStream oitBuildFragStream = mUser.localFile(R.raw.oit_build_fragment_shader);
-
-    try
-      {
-      mOITBuildProgram = new DistortedProgram(oitBuildVertStream,oitBuildFragStream, mGLSL_VERSION, mGLSL_VERSION, mGLSL);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile OIT BUILD program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    int oitBuildProgramH   = mOITBuildProgram.getProgramHandle();
-    mOITBuildTextureH      = GLES30.glGetUniformLocation( oitBuildProgramH, "u_Texture");
-    mOITBuildDepthTextureH = GLES30.glGetUniformLocation( oitBuildProgramH, "u_DepthTexture");
-    mOITBuildDepthH        = GLES30.glGetUniformLocation( oitBuildProgramH, "u_Depth");
-    mOITBuildTexCorrH      = GLES30.glGetUniformLocation( oitBuildProgramH, "u_TexCorr");
-    mOITBuildSizeH         = GLES30.glGetUniformLocation( oitBuildProgramH, "u_Size");
-    mOITBuildNumRecordsH   = GLES30.glGetUniformLocation( oitBuildProgramH, "u_numRecords");
-
-    // OIT COLLAPSE PROGRAM ///////////////////////////
-    final InputStream oitCollapseVertStream = mUser.localFile(R.raw.oit_vertex_shader);
-    final InputStream oitCollapseFragStream = mUser.localFile(R.raw.oit_collapse_fragment_shader);
-
-    try
-      {
-      mOITCollapseProgram = new DistortedProgram(oitCollapseVertStream,oitCollapseFragStream, mGLSL_VERSION, mGLSL_VERSION, mGLSL);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile OIT COLLAPSE program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    int oitCollapseProgramH   = mOITCollapseProgram.getProgramHandle();
-    mOITCollapseDepthTextureH = GLES30.glGetUniformLocation( oitCollapseProgramH, "u_DepthTexture");
-    mOITCollapseDepthH        = GLES30.glGetUniformLocation( oitCollapseProgramH, "u_Depth");
-    mOITCollapseTexCorrH      = GLES30.glGetUniformLocation( oitCollapseProgramH, "u_TexCorr");
-    mOITCollapseSizeH         = GLES30.glGetUniformLocation( oitCollapseProgramH, "u_Size");
-
-    // OIT RENDER PROGRAM ///////////////////////////
-    final InputStream oitRenderVertStream = mUser.localFile(R.raw.oit_vertex_shader);
-    final InputStream oitRenderFragStream = mUser.localFile(R.raw.oit_render_fragment_shader);
-
-    try
-      {
-      mOITRenderProgram = new DistortedProgram(oitRenderVertStream,oitRenderFragStream, mGLSL_VERSION, mGLSL_VERSION, mGLSL);
-      }
-    catch(Exception e)
-      {
-      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile OIT RENDER program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    int oitRenderProgramH   = mOITRenderProgram.getProgramHandle();
-    mOITRenderDepthH        = GLES30.glGetUniformLocation( oitRenderProgramH, "u_Depth");
-    mOITRenderTexCorrH      = GLES30.glGetUniformLocation( oitRenderProgramH, "u_TexCorr");
-    mOITRenderSizeH         = GLES30.glGetUniformLocation( oitRenderProgramH, "u_Size");
-    }
+        val blitProgramH = mBlitProgram!!.programHandle
+        mBlitTextureH = GLES30.glGetUniformLocation(blitProgramH, "u_Texture")
+        mBlitDepthH = GLES30.glGetUniformLocation(blitProgramH, "u_Depth")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        // BLIT DEPTH PROGRAM ////////////////////////////////////
+        val blitDepthVertStream = mUser.localFile(R.raw.blit_depth_vertex_shader)
+        val blitDepthFragStream = mUser.localFile(R.raw.blit_depth_fragment_shader)
 
-  private static void displayNormals(float[] projection, MeshBase mesh)
-    {
-    if( mNormalProgram==null )
-      {
-      try
+        try
         {
-        createNormalProgram();
+            mBlitDepthProgram = DistortedProgram(blitDepthVertStream, blitDepthFragStream, mGLSL_VERSION!!, mGLSL_VERSION!!, gLSL)
         }
-      catch(Exception ex)
+        catch (e: Exception)
         {
-        mUser.distortedException(ex);
-        return;
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile BLIT DEPTH program: "+e.message)
+            throw RuntimeException(e.message)
         }
-      }
-
-    int num = mesh.getNumVertices();
-    int tfo = mesh.getTFO();
-
-    GLES30.glUniform1i(DistortedLibrary.mTransformFeedbackH, 1);
-    GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfo );
-    GLES30.glBeginTransformFeedback( GLES30.GL_POINTS);
-    InternalRenderState.switchOffDrawing();
-    GLES30.glDrawArrays( GLES30.GL_POINTS, 0, num );
-    InternalRenderState.restoreDrawing();
-    GLES30.glEndTransformFeedback();
-    GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
-    GLES30.glUniform1i(DistortedLibrary.mTransformFeedbackH, 0);
-
-    mNormalProgram.useProgram();
-    GLES30.glUniformMatrix4fv(mNormalProjectionH, 1, false, projection, 0);
-    mesh.bindTransformAttribs(mNormalProgram);
-    GLES30.glLineWidth(8.0f);
-    GLES30.glDrawArrays(GLES30.GL_LINES, 0, 2*num);
-    mNormalProgram.stopUsingProgram();
+
+        val blitDepthProgramH = mBlitDepthProgram!!.programHandle
+        mBlitDepthTextureH = GLES30.glGetUniformLocation(blitDepthProgramH, "u_Texture")
+        mBlitDepthDepthTextureH = GLES30.glGetUniformLocation(blitDepthProgramH, "u_DepthTexture")
+        mBlitDepthTexCorrH = GLES30.glGetUniformLocation(blitDepthProgramH, "u_TexCorr")
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Execute all VertexEffects and adjust all vertices
- */
-  public static void adjustVertices(MeshBase mesh, EffectQueueVertex queue)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun createNormalProgram()
     {
-    if( mFullProgram==null )
-      {
-      try
+        val normalVertexStream = mUser.localFile(R.raw.normal_vertex_shader)
+        val normalFragmentStream = mUser.localFile(R.raw.normal_fragment_shader)
+
+        try
         {
-        createFullProgram();
+            mNormalProgram = DistortedProgram(normalVertexStream, normalFragmentStream, mGLSL_VERSION!!, mGLSL_VERSION!!, gLSL)
         }
-      catch(Exception ex)
+        catch (e: Exception)
         {
-        mUser.distortedException(ex);
-        return;
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile NORMAL program: "+e.message)
+            throw RuntimeException(e.message)
         }
-      }
-
-    int num = mesh.getNumVertices();
-    int tfo = mesh.getTFO();
-
-    mFullProgram.useProgram();
-    mesh.bindVertexAttribs(mFullProgram);
-    queue.compute(1,0);
-    queue.send(0.0f,mFullProgramH,3);
-    mesh.send(mFullProgramH,3);
-
-    GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfo );
-    GLES30.glBeginTransformFeedback( GLES30.GL_POINTS);
-    InternalRenderState.switchOffDrawing();
-    GLES30.glDrawArrays( GLES30.GL_POINTS, 0, num );
-    InternalRenderState.restoreDrawing();
-    GLES30.glEndTransformFeedback();
-    mesh.copyTransformToVertex();
-    GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
-    mFullProgram.stopUsingProgram();
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        val normalProgramH = mNormalProgram!!.programHandle
+        mNormalProjectionH = GLES30.glGetUniformLocation(normalProgramH, "u_Projection")
+    }
 
-  static void drawPrivOIT(DistortedEffects effects, MeshBase mesh, InternalOutputSurface surface, long currTime, long step)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun createFullProgram()
     {
-    if( mMainOITProgram!=null )
-      {
-      int w = surface.getWidth();
-      int h = surface.getHeight();
-      EffectQueue[] queues = effects.getQueues();
-
-      EffectQueue.compute(queues, currTime, step);
-      GLES30.glViewport(0, 0, w, h );
-
-      mMainOITProgram.useProgram();
-      GLES30.glUniform1i(mMainOITTextureH, 0);
-      GLES30.glUniform2ui(mMainOITSizeH, w, h );
-      GLES30.glUniform1ui(mMainOITNumRecordsH, (int)(mBufferSize*w*h) );
-      mesh.bindVertexAttribs(mMainOITProgram);
-      mesh.send(mMainOITProgramH,1);
-
-      float inflate     = mesh.getInflate();
-      float distance    = surface.mDistance;
-      float mipmap      = surface.mMipmap;
-      float[] projection= surface.mProjectionMatrix;
-
-      EffectQueue.send(queues, mMainOITProgramH, distance, mipmap, projection, inflate, 1 );
-      GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mesh.getNumVertices() );
-      if( mesh.getShowNormals() ) displayNormals(projection,mesh);
-      mMainOITProgram.stopUsingProgram();
-      }
-    }
+        val fullVertStream = mUser.localFile(R.raw.main_vertex_shader)
+        val fullFragStream = mUser.localFile(R.raw.main_fragment_shader)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        val numV = allEnabled
 
-  static void drawPriv(DistortedEffects effects, MeshBase mesh, InternalOutputSurface surface, long currTime, long step)
-    {
-    if( mMainProgram!=null )
-      {
-      EffectQueue[] queues = effects.getQueues();
-      int w = surface.getWidth();
-      int h = surface.getHeight();
-      EffectQueue.compute(queues, currTime, step);
-      GLES30.glViewport(0, 0, w, h);
-
-      mMainProgram.useProgram();
-      GLES30.glUniform1i(mMainTextureH, 0);
-      mesh.bindVertexAttribs(mMainProgram);
-      mesh.send(mMainProgramH,0);
-
-      float inflate     = mesh.getInflate();
-      float distance    = surface.mDistance;
-      float mipmap      = surface.mMipmap;
-      float[] projection= surface.mProjectionMatrix;
-
-      EffectQueue.send(queues, mMainProgramH, distance, mipmap, projection, inflate, 0 );
-      GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mesh.getNumVertices() );
-      if( mesh.getShowNormals() ) displayNormals(projection,mesh);
-      mMainProgram.stopUsingProgram();
-      }
-    }
+        var fullVertHeader = mGLSL_VERSION+("#define NUM_VERTEX "+(if (numV>0) getMax(EffectType.VERTEX) else 0)+"\n")
+        val fullFragHeader = mGLSL_VERSION+("#define NUM_FRAGMENT "+0+"\n")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        fullVertHeader += "#define MAX_COMPON $maxEffComponents\n"
+        if (useCenters) fullVertHeader += "#define COMP_CENTERS\n"
+        if (isUBOBuggy) fullVertHeader += "#define BUGGY_UBOS\n"
 
-  static void blitPriv(InternalOutputSurface surface)
-    {
-    if( mBlitProgram!=null )
-      {
-      int w = surface.getWidth();
-      int h = surface.getHeight();
-      float n = surface.getNear();
-      mBlitProgram.useProgram();
-      GLES30.glViewport(0, 0, w, h);
-      GLES30.glUniform1i(mBlitTextureH, 0);
-      GLES30.glUniform1f( mBlitDepthH , 1.0f-n);
-      GLES30.glVertexAttribPointer(mBlitProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
-      GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
-      mBlitProgram.stopUsingProgram();
-      }
-    }
+        val enabledEffectV = allGLSL
+        val enabledEffectF = "{}"
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        fullVertHeader += "#define PREAPPLY\n"
 
-  static void blitDepthPriv(InternalOutputSurface surface, float corrW, float corrH)
-    {
-    if( mBlitDepthProgram!=null )
-      {
-      mBlitDepthProgram.useProgram();
-      int w = surface.getWidth();
-      int h = surface.getHeight();
-      GLES30.glViewport(0, 0, w, h);
-      GLES30.glUniform1i(mBlitDepthTextureH, 0);
-      GLES30.glUniform1i(mBlitDepthDepthTextureH, 1);
-      GLES30.glUniform2f(mBlitDepthTexCorrH, corrW, corrH );
-      GLES30.glVertexAttribPointer(mBlitDepthProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
-      GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
-      mBlitDepthProgram.stopUsingProgram();
-      }
-    }
+        val feedback = arrayOf("v_Position", "v_endPosition")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// yes it is safe to be mixing 3.0 and 3.1 like that, senior members of the OpenGL discussions forum assert
+        try
+        {
+            mFullProgram = DistortedProgram(fullVertStream, fullFragStream, fullVertHeader, fullFragHeader,
+                enabledEffectV, enabledEffectF, gLSL, feedback)
+        }
+        catch (e: Exception)
+        {
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile FULL program: "+e.message)
+            throw RuntimeException(e.message)
+        }
 
-  private static int printPreviousBuffer()
-    {
-    int counter = 0;
-
-    ByteBuffer atomicBuf = (ByteBuffer)GLES30.glMapBufferRange( GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, 4,
-                                                                GLES30.GL_MAP_READ_BIT);
-    if( atomicBuf!=null )
-      {
-      IntBuffer atomicIntBuf = atomicBuf.order(ByteOrder.nativeOrder()).asIntBuffer();
-      counter = atomicIntBuf.get(0);
-      }
-    else
-      {
-      mUser.logMessage("printPreviousBuffer: failed to map atomic buffer");
-      }
-
-    GLES30.glUnmapBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER);
-
-    return counter;
+        mFullProgramH = mFullProgram!!.programHandle
+        EffectQueue.getUniforms(mFullProgramH, 3)
+        MeshBase.getUniforms(mFullProgramH, 3)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void zeroBuffer()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun createOITProgram()
     {
-    ByteBuffer atomicBuf = (ByteBuffer)GLES30.glMapBufferRange( GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, 4,
-                                                                GLES30.GL_MAP_WRITE_BIT|GLES30.GL_MAP_INVALIDATE_BUFFER_BIT);
-    if( atomicBuf!=null )
-      {
-      IntBuffer atomicIntBuf = atomicBuf.order(ByteOrder.nativeOrder()).asIntBuffer();
-      atomicIntBuf.put(0,0);
-      }
-    else
-      {
-      mUser.logMessage("zeroBuffer: failed to map atomic buffer");
-      }
-
-    GLES30.glUnmapBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER);
-    }
+        // MAIN OIT PROGRAM ////////////////////////////////
+        val mainVertStream = mUser.localFile(R.raw.main_vertex_shader)
+        val mainFragStream = mUser.localFile(R.raw.main_fragment_shader)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// reset atomic counter to 0
+        val numF = FragmentEffect.getNumEnabled()
+        val numV = VertexEffect.getNumEnabled()
 
-  static int zeroOutAtomic()
-    {
-    int counter = 0;
+        var mainVertHeader = mGLSL_VERSION+("#define NUM_VERTEX "+(if (numV>0) getMax(EffectType.VERTEX) else 0)+"\n")+("#define OIT\n")
+        val mainFragHeader = mGLSL_VERSION+("#define NUM_FRAGMENT "+(if (numF>0) getMax(EffectType.FRAGMENT) else 0)+"\n")+("#define OIT\n")
 
-    if( mAtomicCounter==null )
-      {
-      mAtomicCounter = new int[mFBOQueueSize];
+        mainVertHeader += "#define MAX_COMPON $maxEffComponents\n"
+        if (useCenters) mainVertHeader += "#define COMP_CENTERS\n"
+        if (isUBOBuggy) mainVertHeader += "#define BUGGY_UBOS\n"
 
-      GLES30.glGenBuffers(mFBOQueueSize,mAtomicCounter,0);
+        val enabledEffectV = VertexEffect.getGLSL()
+        val enabledEffectF = FragmentEffect.getGLSL()
 
-      for(int i=0; i<mFBOQueueSize; i++)
+        try
+        {
+            mMainOITProgram = DistortedProgram(mainVertStream, mainFragStream, mainVertHeader, mainFragHeader,
+                enabledEffectV, enabledEffectF, gLSL, null)
+        }
+        catch (e: Exception)
         {
-        GLES30.glBindBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER, mAtomicCounter[i]);
-        GLES30.glBufferData(GLES31.GL_ATOMIC_COUNTER_BUFFER, 4, null, GLES30.GL_DYNAMIC_DRAW);
-        zeroBuffer();
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile MAIN OIT program: "+e.message)
+            throw RuntimeException(e.message)
         }
-      }
 
-    // reading the value of the buffer on every frame would slow down rendering by
-    // about 3%; doing it only once every 5 frames affects speed by less than 1%.
-    if( mCurrBuffer==0 )
-      {
-      GLES30.glBindBufferBase(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, mAtomicCounter[mCurrBuffer]);
-      counter = printPreviousBuffer();
-      }
+        mMainOITProgramH = mMainOITProgram!!.programHandle
+        EffectQueue.getUniforms(mMainOITProgramH, 1)
+        MeshBase.getUniforms(mMainOITProgramH, 1)
+        mMainOITTextureH = GLES30.glGetUniformLocation(mMainOITProgramH, "u_Texture")
+        mMainOITSizeH = GLES30.glGetUniformLocation(mMainOITProgramH, "u_Size")
+        mMainOITNumRecordsH = GLES30.glGetUniformLocation(mMainOITProgramH, "u_numRecords")
 
-    if( ++mCurrBuffer>=mFBOQueueSize ) mCurrBuffer = 0;
+        // OIT CLEAR PROGRAM ////////////////////////////////////
+        val oitClearVertStream = mUser.localFile(R.raw.oit_vertex_shader)
+        val oitClearFragStream = mUser.localFile(R.raw.oit_clear_fragment_shader)
 
-    GLES30.glBindBufferBase(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, mAtomicCounter[mCurrBuffer]);
-    zeroBuffer();
+        try
+        {
+            mOITClearProgram = DistortedProgram(oitClearVertStream, oitClearFragStream, mGLSL_VERSION!!, mGLSL_VERSION!!, gLSL)
+        }
+        catch (e: Exception)
+        {
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile OIT CLEAR program: "+e.message)
+            throw RuntimeException(e.message)
+        }
 
-    return counter;
-    }
+        val oitClearProgramH = mOITClearProgram!!.programHandle
+        mOITClearDepthH = GLES30.glGetUniformLocation(oitClearProgramH, "u_Depth")
+        mOITClearTexCorrH = GLES30.glGetUniformLocation(oitClearProgramH, "u_TexCorr")
+        mOITClearSizeH = GLES30.glGetUniformLocation(oitClearProgramH, "u_Size")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Pass1 of the OIT algorithm. Clear per-pixel head-pointers.
+        // OIT BUILD PROGRAM ////////////////////////////////////
+        val oitBuildVertStream = mUser.localFile(R.raw.oit_vertex_shader)
+        val oitBuildFragStream = mUser.localFile(R.raw.oit_build_fragment_shader)
 
-  static void oitClear(InternalOutputSurface surface, int counter)
-    {
-    if( mOITClearProgram==null )
-      {
-      if( mGLSL>=310 && !mOITCompilationAttempted )
+        try
+        {
+            mOITBuildProgram = DistortedProgram(oitBuildVertStream, oitBuildFragStream, mGLSL_VERSION!!, mGLSL_VERSION!!, gLSL)
+        }
+        catch (e: Exception)
         {
-        mOITCompilationAttempted = true;
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile OIT BUILD program: "+e.message)
+            throw RuntimeException(e.message)
+        }
+
+        val oitBuildProgramH = mOITBuildProgram!!.programHandle
+        mOITBuildTextureH      = GLES30.glGetUniformLocation(oitBuildProgramH, "u_Texture")
+        mOITBuildDepthTextureH = GLES30.glGetUniformLocation(oitBuildProgramH, "u_DepthTexture")
+        mOITBuildDepthH        = GLES30.glGetUniformLocation(oitBuildProgramH, "u_Depth")
+        mOITBuildTexCorrH      = GLES30.glGetUniformLocation(oitBuildProgramH, "u_TexCorr")
+        mOITBuildSizeH         = GLES30.glGetUniformLocation(oitBuildProgramH, "u_Size")
+        mOITBuildNumRecordsH   = GLES30.glGetUniformLocation(oitBuildProgramH, "u_numRecords")
+
+        // OIT COLLAPSE PROGRAM ///////////////////////////
+        val oitCollapseVertStream = mUser.localFile(R.raw.oit_vertex_shader)
+        val oitCollapseFragStream = mUser.localFile(R.raw.oit_collapse_fragment_shader)
 
         try
-          {
-          createOITProgram();
-          }
-        catch(Exception ex)
-          {
-          mUser.distortedException(ex);
-          return;
-          }
+        {
+            mOITCollapseProgram = DistortedProgram(oitCollapseVertStream, oitCollapseFragStream, mGLSL_VERSION!!, mGLSL_VERSION!!, gLSL)
         }
-      else
+        catch (e: Exception)
         {
-        return;
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile OIT COLLAPSE program: "+e.message)
+            throw RuntimeException(e.message)
         }
-      }
-
-
-    int w = surface.getWidth();
-    int h = surface.getHeight();
-
-    if( mLinkedListSSBO[0]<0 )
-      {
-      GLES30.glGenBuffers(1,mLinkedListSSBO,0);
-
-      int size = (int)(w*h*(3*mBufferSize+1)*4);
-      GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0]);
-      GLES30.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, size, null, GLES30.GL_DYNAMIC_READ|GLES30.GL_DYNAMIC_DRAW);
-      GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0);
-
-      GLES30.glBindBufferBase(GLES31.GL_SHADER_STORAGE_BUFFER, 1, mLinkedListSSBO[0]);
-      }
-
-    // See if we have overflown the SSBO in one of the previous frames.
-    // If yes, assume we need to make the SSBO larger.
-    float overflow = counter/(mBufferSize*w*h);
-
-    if( overflow>1.0f )
-      {
-      mBufferSize *= (int)(overflow+1.0f);
-      int size = (int)(w*h*(3*mBufferSize+1)*4);
-      GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0]);
-      GLES30.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, size, null, GLES30.GL_DYNAMIC_READ|GLES30.GL_DYNAMIC_DRAW);
-      GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0);
-      }
-
-    mOITClearProgram.useProgram();
-    GLES30.glViewport(0, 0, w, h);
-    GLES30.glUniform2f(mOITClearTexCorrH, 1.0f, 1.0f );   // corrections do not really matter here - only present because of common vertex shader.
-    GLES30.glUniform1f( mOITClearDepthH , 1.0f);          // likewise depth
-    GLES30.glUniform2ui(mOITClearSizeH, w, h);
-    GLES30.glVertexAttribPointer(mOITClearProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
-    GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
-    mOITClearProgram.stopUsingProgram();
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Pass2 of the OIT algorithm - build per-pixel linked lists.
+        val oitCollapseProgramH = mOITCollapseProgram!!.programHandle
+        mOITCollapseDepthTextureH = GLES30.glGetUniformLocation(oitCollapseProgramH, "u_DepthTexture")
+        mOITCollapseDepthH        = GLES30.glGetUniformLocation(oitCollapseProgramH, "u_Depth")
+        mOITCollapseTexCorrH      = GLES30.glGetUniformLocation(oitCollapseProgramH, "u_TexCorr")
+        mOITCollapseSizeH         = GLES30.glGetUniformLocation(oitCollapseProgramH, "u_Size")
 
-  static void oitBuild(InternalOutputSurface surface, float corrW, float corrH)
-    {
-    if( mOITBuildProgram!=null )
-      {
-      int w = surface.getWidth();
-      int h = surface.getHeight();
-      float n = surface.getNear();
-      mOITBuildProgram.useProgram();
-      GLES30.glViewport(0, 0, w, h);
-      GLES30.glUniform1i(mOITBuildTextureH, 0);
-      GLES30.glUniform1i(mOITBuildDepthTextureH, 1);
-      GLES30.glUniform2f(mOITBuildTexCorrH, corrW, corrH );
-      GLES30.glUniform2ui(mOITBuildSizeH, w, h);
-      GLES30.glUniform1ui(mOITBuildNumRecordsH, (int)(mBufferSize*w*h) );
-      GLES30.glUniform1f(mOITBuildDepthH , 1.0f-n);
-      GLES30.glVertexAttribPointer(mOITBuildProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
-      GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
-      mOITBuildProgram.stopUsingProgram();
-      }
-    }
+        // OIT RENDER PROGRAM ///////////////////////////
+        val oitRenderVertStream = mUser.localFile(R.raw.oit_vertex_shader)
+        val oitRenderFragStream = mUser.localFile(R.raw.oit_render_fragment_shader)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Pass3 of the OIT algorithm. Cut occluded parts of the linked list.
+        try
+        {
+            mOITRenderProgram = DistortedProgram(oitRenderVertStream, oitRenderFragStream, mGLSL_VERSION!!, mGLSL_VERSION!!, gLSL)
+        }
+        catch (e: Exception)
+        {
+            mUser.logMessage(e.javaClass.simpleName+" trying to compile OIT RENDER program: "+e.message)
+            throw RuntimeException(e.message)
+        }
 
-  static void oitCollapse(InternalOutputSurface surface, float corrW, float corrH)
-    {
-    if( mOITCollapseProgram!=null )
-      {
-      int w = surface.getWidth();
-      int h = surface.getHeight();
-      float n = surface.getNear();
-      mOITCollapseProgram.useProgram();
-      GLES30.glViewport(0, 0, w, h);
-      GLES30.glUniform1i(mOITCollapseDepthTextureH, 1);
-      GLES30.glUniform2f(mOITCollapseTexCorrH, corrW, corrH );
-      GLES30.glUniform2ui(mOITCollapseSizeH, w, h);
-      GLES30.glUniform1f( mOITCollapseDepthH , 1.0f-n);
-      GLES30.glVertexAttribPointer(mOITCollapseProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
-      GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
-      mOITCollapseProgram.stopUsingProgram();
-      }
+        val oitRenderProgramH = mOITRenderProgram!!.programHandle
+        mOITRenderDepthH   = GLES30.glGetUniformLocation(oitRenderProgramH, "u_Depth")
+        mOITRenderTexCorrH = GLES30.glGetUniformLocation(oitRenderProgramH, "u_TexCorr")
+        mOITRenderSizeH    = GLES30.glGetUniformLocation(oitRenderProgramH, "u_Size")
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Pass4 of the OIT algorithm. Render all the transparent pixels from the per-pixel linked lists.
-
-  static void oitRender(InternalOutputSurface surface, float corrW, float corrH)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun displayNormals(projection: FloatArray, mesh: MeshBase)
     {
-    if( mOITRenderProgram!=null )
-      {
-      int w = surface.getWidth();
-      int h = surface.getHeight();
-      float n = surface.getNear();
-      mOITRenderProgram.useProgram();
-      GLES30.glViewport(0, 0, w, h);
-      GLES30.glUniform2f(mOITRenderTexCorrH, corrW, corrH );
-      GLES30.glUniform2ui(mOITRenderSizeH, w, h);
-      GLES30.glUniform1f( mOITRenderDepthH , 1.0f-n);
-      GLES30.glVertexAttribPointer(mOITRenderProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
-      GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
-      mOITRenderProgram.stopUsingProgram();
-      }
-    }
+        if (mNormalProgram==null)
+        {
+            try
+            {
+                createNormalProgram()
+            }
+            catch (ex: Exception)
+            {
+                mUser.distortedException(ex)
+                return
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        val num = mesh.getNumVertices()
+        val tfo = mesh.tFO
+
+        GLES30.glUniform1i(mTransformFeedbackH, 1)
+        GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfo)
+        GLES30.glBeginTransformFeedback(GLES30.GL_POINTS)
+        switchOffDrawing()
+        GLES30.glDrawArrays(GLES30.GL_POINTS, 0, num)
+        restoreDrawing()
+        GLES30.glEndTransformFeedback()
+        GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0)
+        GLES30.glUniform1i(mTransformFeedbackH, 0)
+
+        mNormalProgram!!.useProgram()
+        GLES30.glUniformMatrix4fv(mNormalProjectionH, 1, false, projection, 0)
+        mesh.bindTransformAttribs(mNormalProgram!!)
+        GLES30.glLineWidth(8.0f)
+        GLES30.glDrawArrays(GLES30.GL_LINES, 0, 2*num)
+        mNormalProgram!!.stopUsingProgram()
+    }
 
-  static void setSSBOSize(float size)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Execute all VertexEffects and adjust all vertices
+     */
+    fun adjustVertices(mesh: MeshBase, queue: EffectQueueVertex)
     {
-    mBufferSize = size;
-    }
+        if (mFullProgram==null)
+        {
+            try
+            {
+                createFullProgram()
+            }
+            catch (ex: Exception)
+            {
+                mUser.distortedException(ex)
+                return
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        val num = mesh.getNumVertices()
+        val tfo = mesh.tFO
+
+        mFullProgram!!.useProgram()
+        mesh.bindVertexAttribs(mFullProgram!!)
+        queue.compute(1, 0)
+        queue.send(0.0f, mFullProgramH, 3)
+        mesh.send(mFullProgramH, 3)
+
+        GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfo)
+        GLES30.glBeginTransformFeedback(GLES30.GL_POINTS)
+        switchOffDrawing()
+        GLES30.glDrawArrays(GLES30.GL_POINTS, 0, num)
+        restoreDrawing()
+        GLES30.glEndTransformFeedback()
+        mesh.copyTransformToVertex()
+        GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0)
+        mFullProgram!!.stopUsingProgram()
+    }
 
-  static int getQueueSize()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun drawPrivOIT(effects: DistortedEffects, mesh: MeshBase, surface: InternalOutputSurface, currTime: Long, step: Long)
     {
-    return mFBOQueueSize;
+        if (mMainOITProgram!=null)
+        {
+            val w = surface.getWidth()
+            val h = surface.getHeight()
+            val queues = effects.queues
+
+            compute(queues, currTime, step)
+            GLES30.glViewport(0, 0, w, h)
+
+            mMainOITProgram!!.useProgram()
+            GLES30.glUniform1i(mMainOITTextureH, 0)
+            GLES30.glUniform2ui(mMainOITSizeH, w, h)
+            GLES30.glUniform1ui(mMainOITNumRecordsH, (mBufferSize*w*h).toInt())
+            mesh.bindVertexAttribs(mMainOITProgram!!)
+            mesh.send(mMainOITProgramH, 1)
+
+            val inflate    = mesh.inflate
+            val distance   = surface.mDistance
+            val mipmap     = surface.mMipmap
+            val projection = surface.mProjectionMatrix
+
+            send(queues, mMainOITProgramH, distance, mipmap, projection, inflate, 1)
+            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mesh.getNumVertices())
+            if (mesh.showNormals) displayNormals(projection, mesh)
+            mMainOITProgram!!.stopUsingProgram()
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static int parseQualcommDriverVersion(String version)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun drawPriv(effects: DistortedEffects, mesh: MeshBase, surface: InternalOutputSurface, currTime: Long, step: Long)
     {
-    int at = version.indexOf('@');
-
-    if( at>=0 )
-      {
-      int dot = version.indexOf('.',at);
+        if( mMainProgram!=null )
+        {
+            val queues = effects.queues
+            val w = surface.getWidth()
+            val h = surface.getHeight()
+            compute(queues, currTime, step)
+            GLES30.glViewport(0, 0, w, h)
+
+            mMainProgram!!.useProgram()
+            GLES30.glUniform1i(mMainTextureH, 0)
+            mesh.bindVertexAttribs(mMainProgram!!)
+            mesh.send(mMainProgramH, 0)
+
+            val inflate = mesh.inflate
+            val distance = surface.mDistance
+            val mipmap = surface.mMipmap
+            val projection = surface.mProjectionMatrix
+
+            send(queues, mMainProgramH, distance, mipmap, projection, inflate, 0)
+            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mesh.getNumVertices())
+            if (mesh.showNormals) displayNormals(projection, mesh)
+            mMainProgram!!.stopUsingProgram()
+        }
+    }
 
-      if( dot>at+1 )
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun blitPriv(surface: InternalOutputSurface)
+    {
+        if( mBlitProgram!=null )
         {
-        String ver = version.substring(at+1,dot);
-        return Integer.parseInt(ver);
+            val w = surface.getWidth()
+            val h = surface.getHeight()
+            val n = surface.getNear()
+            mBlitProgram!!.useProgram()
+            GLES30.glViewport(0, 0, w, h)
+            GLES30.glUniform1i(mBlitTextureH, 0)
+            GLES30.glUniform1f(mBlitDepthH, 1.0f-n)
+            GLES30.glVertexAttribPointer(mBlitProgram!!.mAttribute!![0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions)
+            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
+            mBlitProgram!!.stopUsingProgram()
         }
-      }
+    }
 
-    return 0;
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun blitDepthPriv(surface: InternalOutputSurface, corrW: Float, corrH: Float)
+    {
+        if (mBlitDepthProgram!=null)
+        {
+            mBlitDepthProgram!!.useProgram()
+            val w = surface.getWidth()
+            val h = surface.getHeight()
+            GLES30.glViewport(0, 0, w, h)
+            GLES30.glUniform1i(mBlitDepthTextureH, 0)
+            GLES30.glUniform1i(mBlitDepthDepthTextureH, 1)
+            GLES30.glUniform2f(mBlitDepthTexCorrH, corrW, corrH)
+            GLES30.glVertexAttribPointer(mBlitDepthProgram!!.mAttribute!![0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions)
+            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
+            mBlitDepthProgram!!.stopUsingProgram()
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// ARM Mali driver r12 has problems when we keep swapping many FBOs (fixed in r22)
-// PowerVR GE8100 / GE8300 compiler fails to compile OIT programs.
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // yes it is safe to be mixing 3.0 and 3.1 like that, senior members of the OpenGL discussions forum assert
+    private fun printPreviousBuffer(): Int
+    {
+        val atomicBuf = GLES30.glMapBufferRange(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, 4, GLES30.GL_MAP_READ_BIT) as ByteBuffer
+        val atomicIntBuf = atomicBuf.order(ByteOrder.nativeOrder()).asIntBuffer()
+        val counter = atomicIntBuf[0]
+
+        GLES30.glUnmapBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER)
+
+        return counter
+    }
 
-  private static void detectBuggyDriversAndSetQueueSize(int queueSize)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun zeroBuffer()
     {
-    mVendor  = GLES30.glGetString(GLES30.GL_VENDOR);
-    mVersion = GLES30.glGetString(GLES30.GL_VERSION);
-    mRenderer= GLES30.glGetString(GLES30.GL_RENDERER);
+        val atomicBuf = GLES30.glMapBufferRange(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, 4, GLES30.GL_MAP_WRITE_BIT or GLES30.GL_MAP_INVALIDATE_BUFFER_BIT) as ByteBuffer
+        val atomicIntBuf = atomicBuf.order(ByteOrder.nativeOrder()).asIntBuffer()
+        atomicIntBuf.put(0, 0)
 
-    mFBOQueueSize = 1;
-    mFastCompilationTF = true;
+        GLES30.glUnmapBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // reset atomic counter to 0
+    fun zeroOutAtomic(): Int
+    {
+        var counter = 0
 
-    if( mVendor.contains("ARM") )
-      {
-      try
+        if( mAtomicCounter==null )
         {
-        String regex = ".*r(\\d+)p\\d.*";
-        Pattern pattern = Pattern.compile(regex);
-        Matcher matcher = pattern.matcher(mVersion);
+            mAtomicCounter = IntArray(queueSize)
 
-        if( matcher.find() )
-          {
-          String driverVersion = matcher.group(1);
+            GLES30.glGenBuffers(queueSize, mAtomicCounter, 0)
 
-          if( driverVersion!=null )
+            for (i in 0 until queueSize)
             {
-            int drvVersion = Integer.parseInt(driverVersion);
-
-            if( drvVersion<22 )
-              {
-              mUser.logMessage("You are running this on a ARM Mali driver r"+driverVersion+".\n" +
-                    "This is a buggy driver, please update to r22. Inserting workaround which uses a lot of memory.");
-
-              mFBOQueueSize = queueSize;
-              }
+                GLES30.glBindBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER, mAtomicCounter!![i])
+                GLES30.glBufferData(GLES31.GL_ATOMIC_COUNTER_BUFFER, 4, null, GLES30.GL_DYNAMIC_DRAW)
+                zeroBuffer()
             }
-          }
         }
-      catch(Exception ex)
+
+        // reading the value of the buffer on every frame would slow down rendering by
+        // about 3%; doing it only once every 5 frames affects speed by less than 1%.
+        if (mCurrBuffer==0)
         {
-        mUser.logMessage("DistortedLibrary: exception trying to pattern match version: "+ ex);
+            GLES30.glBindBufferBase(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, mAtomicCounter!![mCurrBuffer])
+            counter = printPreviousBuffer()
         }
-      }
-    else if( mVendor.contains("Imagination") )
-      {
-      if( mRenderer.contains("GE8") )
+
+        if (++mCurrBuffer>=queueSize) mCurrBuffer = 0
+
+        GLES30.glBindBufferBase(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, mAtomicCounter!![mCurrBuffer])
+        zeroBuffer()
+
+        return counter
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Pass1 of the OIT algorithm. Clear per-pixel head-pointers.
+    fun oitClear(surface: InternalOutputSurface, counter: Int)
+    {
+        if( mOITClearProgram==null )
         {
-        mUser.logMessage("You are running this on a PowerVR GE8XXX.\nDue to a buggy compiler OIT rendering will not work");
-        mUser.logMessage("GLSL Version "+GLES30.glGetString(GLES31.GL_SHADING_LANGUAGE_VERSION));
+            if( gLSL>=310 && !mOITCompilationAttempted )
+            {
+                mOITCompilationAttempted = true
+
+                try
+                {
+                    createOITProgram()
+                }
+                catch (ex: Exception)
+                {
+                    mUser.distortedException(ex)
+                    return
+                }
+            }
+            else
+            {
+                return
+            }
         }
-      }
-    else if( mVendor.contains("Qualcomm"))
-      {
-      int driverVersion = parseQualcommDriverVersion(mVersion);
 
-      if( mRenderer.contains("308") && (driverVersion==331 || driverVersion==415) )
+        val w = surface.getWidth()
+        val h = surface.getHeight()
+
+        if (mLinkedListSSBO[0]<0)
         {
-        mUser.logMessage("You are running this on an Adreno 308 driver 331 or 415.\nStrange shit might happen.");
-        mBuggyUBOs = true;
+            GLES30.glGenBuffers(1, mLinkedListSSBO, 0)
+
+            val size = (w*h*(3*mBufferSize+1)*4).toInt()
+            GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0])
+            GLES30.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, size, null, GLES30.GL_DYNAMIC_READ or GLES30.GL_DYNAMIC_DRAW)
+            GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0)
+
+            GLES30.glBindBufferBase(GLES31.GL_SHADER_STORAGE_BUFFER, 1, mLinkedListSSBO[0])
         }
-      if( mGLSL<=300 && driverVersion<415 ) // This is only on old enough drivers, 84,95,100,104,140 and known bad, 415 is known good.
+
+        // See if we have overflown the SSBO in one of the previous frames.
+        // If yes, assume we need to make the SSBO larger.
+        val overflow = counter/(mBufferSize*w*h)
+
+        if (overflow>1.0f)
         {
-        mUser.logMessage("Slow compilation of Transform Feedback programs!");
-        mFastCompilationTF = false;
+            mBufferSize *= (overflow+1.0f).toInt().toFloat()
+            val size = (w*h*(3*mBufferSize+1)*4).toInt()
+            GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0])
+            GLES30.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, size, null, GLES30.GL_DYNAMIC_READ or GLES30.GL_DYNAMIC_DRAW)
+            GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0)
         }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static int getVersion()
-    {
-    int[] major = new int[1];
-    int[] minor = new int[1];
-    GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, major, 0);
-    GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, minor, 0);
-    return major[0] * 100 + minor[0] * 10;
+        mOITClearProgram!!.useProgram()
+        GLES30.glViewport(0, 0, w, h)
+        GLES30.glUniform2f(mOITClearTexCorrH, 1.0f, 1.0f) // corrections do not really matter here - only present because of common vertex shader.
+        GLES30.glUniform1f(mOITClearDepthH, 1.0f) // likewise depth
+        GLES30.glUniform2ui(mOITClearSizeH, w, h)
+        GLES30.glVertexAttribPointer(mOITClearProgram!!.mAttribute!![0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions)
+        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
+        mOITClearProgram!!.stopUsingProgram()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  public static void logMessage(String message)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Pass2 of the OIT algorithm - build per-pixel linked lists.
+    fun oitBuild(surface: InternalOutputSurface, corrW: Float, corrH: Float)
     {
-    mUser.logMessage(message);
+        if( mOITBuildProgram!=null )
+        {
+            val w = surface.getWidth()
+            val h = surface.getHeight()
+            val n = surface.getNear()
+            mOITBuildProgram!!.useProgram()
+            GLES30.glViewport(0, 0, w, h)
+            GLES30.glUniform1i(mOITBuildTextureH, 0)
+            GLES30.glUniform1i(mOITBuildDepthTextureH, 1)
+            GLES30.glUniform2f(mOITBuildTexCorrH, corrW, corrH)
+            GLES30.glUniform2ui(mOITBuildSizeH, w, h)
+            GLES30.glUniform1ui(mOITBuildNumRecordsH, (mBufferSize*w*h).toInt())
+            GLES30.glUniform1f(mOITBuildDepthH, 1.0f-n)
+            GLES30.glVertexAttribPointer(mOITBuildProgram!!.mAttribute!![0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions)
+            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
+            mOITBuildProgram!!.stopUsingProgram()
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return OpenGL ES version supported by the hardware we are running on.
- * There are only three possibilities: 300 (OpenGL ES 3.0) or 310 (at least OpenGL ES 3.1)
- * or 200 (OpenGL ES 2.0)
- */
-  public static int getGLSL()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Pass3 of the OIT algorithm. Cut occluded parts of the linked list.
+    fun oitCollapse(surface: InternalOutputSurface, corrW: Float, corrH: Float)
     {
-    return mGLSL;
+        if( mOITCollapseProgram!=null )
+        {
+            val w = surface.getWidth()
+            val h = surface.getHeight()
+            val n = surface.getNear()
+            mOITCollapseProgram!!.useProgram()
+            GLES30.glViewport(0, 0, w, h)
+            GLES30.glUniform1i(mOITCollapseDepthTextureH, 1)
+            GLES30.glUniform2f(mOITCollapseTexCorrH, corrW, corrH)
+            GLES30.glUniform2ui(mOITCollapseSizeH, w, h)
+            GLES30.glUniform1f(mOITCollapseDepthH, 1.0f-n)
+            GLES30.glVertexAttribPointer(mOITCollapseProgram!!.mAttribute!![0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions)
+            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
+            mOITCollapseProgram!!.stopUsingProgram()
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Are we running this on hardware where UBOs are buggy?
- */
-  public static boolean isUBOBuggy()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Pass4 of the OIT algorithm. Render all the transparent pixels from the per-pixel linked lists.
+    fun oitRender(surface: InternalOutputSurface, corrW: Float, corrH: Float)
     {
-    return mBuggyUBOs;
+        if( mOITRenderProgram!=null )
+        {
+            val w = surface.getWidth()
+            val h = surface.getHeight()
+            val n = surface.getNear()
+            mOITRenderProgram!!.useProgram()
+            GLES30.glViewport(0, 0, w, h)
+            GLES30.glUniform2f(mOITRenderTexCorrH, corrW, corrH)
+            GLES30.glUniform2ui(mOITRenderSizeH, w, h)
+            GLES30.glUniform1f(mOITRenderDepthH, 1.0f-n)
+            GLES30.glVertexAttribPointer(mOITRenderProgram!!.mAttribute!![0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions)
+            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
+            mOITRenderProgram!!.stopUsingProgram()
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When OpenGL context gets created, call this method so that the library can initialise its internal data structures.
- * I.e. best called from GLSurfaceView.Renderer.onSurfaceCreated().
- * <p>
- * Needs to be called from a thread holding the OpenGL context.
- *
- * @param user The Code which will be using this library, which must implement the LibraryUser interface.
- */
-  public static void onSurfaceCreated(final LibraryUser user)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setSSBOSize(size: Float)
     {
-    onSurfaceCreated(user,4);
+        mBufferSize = size
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When OpenGL context gets created, call this method so that the library can initialise its internal data structures.
- * I.e. best called from GLSurfaceView.Renderer.onSurfaceCreated().
- * <p>
- * Needs to be called from a thread holding the OpenGL context.
- *   
- * @param user The Code which will be using this library, which must implement the LibraryUser interface.
- * @param queueSize the size of the FBO queue, a workaround for the bug on Mali drivers. Use a small integer - 1,...,4
- */
-  public static void onSurfaceCreated(final LibraryUser user, int queueSize)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun parseQualcommDriverVersion(version: String): Int
     {
-    mUser = user;
-    int version = getVersion();
-    int major = version/100;
-    int minor = ((version/10)%10);
-
-    if( major< 3 )
-      {
-      mGLSL = 100*major + 10*minor;
-      VertexCompilationException ex = new VertexCompilationException("at least OpenGL ES 3.0 required, this device supports only "+major+"."+minor);
-      mUser.distortedException(ex);
-      }
-    else
-      {
-      mGLSL = (major==3 && minor==0) ? 300 : 310;
-      }
-
-    int[] tmp = new int[1];
-    GLES30.glGetIntegerv(GLES30.GL_MAX_TEXTURE_SIZE, tmp, 0);
-    mMaxTextureSize = tmp[0];
-    GLES30.glGetIntegerv(GLES30.GL_MAX_VERTEX_UNIFORM_VECTORS  , tmp, 0);
-    mMaxNumberOfVerUniforms = tmp[0];
-    GLES30.glGetIntegerv(GLES30.GL_MAX_FRAGMENT_UNIFORM_VECTORS, tmp, 0);
-    mMaxNumberOfFraUniforms = tmp[0];
-
-    mUser.logMessage("Using OpenGL ES "+major+"."+minor+" texSize="+mMaxTextureSize+" maxVerUniforms: "+mMaxNumberOfVerUniforms+" maxFraUniforms: "+mMaxNumberOfFraUniforms);
-
-    mGLSL_VERSION = "#version "+mGLSL+" es\n";
-
-    InternalStackFrameList.setInitialized(true);
-    mOITCompilationAttempted = false;
-
-    detectBuggyDriversAndSetQueueSize(queueSize);
-    EffectMessageSender.startSending();
-
-    try
-      {
-      createMainProgram();
-      }
-    catch(Exception ex)
-      {
-      mUser.distortedException(ex);
-      }
-
-    try
-      {
-      final InputStream vertStream = mUser.localFile(R.raw.main_vertex_shader);
-      final InputStream fragStream = mUser.localFile(R.raw.preprocess_fragment_shader);
-      EffectQueuePostprocess.createPrograms(vertStream, fragStream, mGLSL);
-      }
-    catch(Exception ex)
-      {
-      mUser.distortedException(ex);
-      }
-
-    try
-      {
-      PostprocessEffect.createPrograms(mGLSL);
-      }
-    catch(Exception ex)
-      {
-      mUser.distortedException(ex);
-      }
-    }
+        val at = version.indexOf('@')
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Switch face culling on/off
- */
-  public static void setCull(boolean on)
-    {
-    if( on )
-      {
-      GLES30.glEnable(GLES30.GL_CULL_FACE);
-      GLES30.glCullFace(GLES30.GL_FRONT);
-      }
-    else
-      {
-      GLES30.glDisable(GLES30.GL_CULL_FACE);
-      }
-    }
+        if (at>=0)
+        {
+            val dot = version.indexOf('.', at)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this so that the Library can initialize its internal data structures.
- * Must be called from Activity.onCreate().
- */
-  public static void onCreate()
-    {
-    onCreate(0);
-    }
+            if (dot>at+1)
+            {
+                val ver = version.substring(at+1, dot)
+                return ver.toInt()
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this so that the Library can initialize its internal data structures.
- * Must be called from Activity.onCreate().
- *
- * @param id id of an Activity that is using the library; anything unique so that the Library can
- *           tell between Activities in case you're going to be using it from more than one.
- */
-  public static void onCreate(long id)
-    {
-    InternalStackFrameList.onCreate(id);
+        return 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this so that the Library can resume its operations.
- * Must be called from Activity.onResume().
- */
-  public static void onResume()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // ARM Mali driver r12 has problems when we keep swapping many FBOs (fixed in r22)
+    // PowerVR GE8100 / GE8300 compiler fails to compile OIT programs.
+    private fun detectBuggyDriversAndSetQueueSize(queueSize: Int)
     {
-    onResume(0);
-    }
+        driverVendor   = GLES30.glGetString(GLES30.GL_VENDOR)
+        driverVersion  = GLES30.glGetString(GLES30.GL_VERSION)
+        driverRenderer = GLES30.glGetString(GLES30.GL_RENDERER)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this so that the Library can resume its operations.
- * Must be called from Activity.onResume().
- *
- * @param id id of an Activity that is using the library; anything unique so that the Library can
- *           tell between Activities in case you're going to be using it from more than one.
- */
-  public static void onResume(long id)
-    {
-    InternalStackFrameList.onResume(id);
-    }
+        DistortedLibrary.queueSize = 1
+        mFastCompilationTF = true
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this so that the Library can release the OpenGL related data that needs to be recreated.
- * Must be called from Activity.onPause().
- */
-  public static void onPause()
-    {
-    onPause(0);
-    }
+        if( driverVendor!!.contains("ARM") )
+        {
+            try
+            {
+                val regex = ".*r(\\d+)p\\d.*"
+                val pattern = Pattern.compile(regex)
+                val matcher = pattern.matcher(driverVersion!!)
+
+                if (matcher.find())
+                {
+                    val driverVersion = matcher.group(1)
+
+                    if (driverVersion!=null)
+                    {
+                        val drvVersion = driverVersion.toInt()
+
+                        if (drvVersion<22)
+                        {
+                            mUser.logMessage("""
+    You are running this on a ARM Mali driver r$driverVersion.
+    This is a buggy driver, please update to r22. Inserting workaround which uses a lot of memory.
+    """.trimIndent())
+
+                            DistortedLibrary.queueSize = queueSize
+                        }
+                    }
+                }
+            }
+            catch (ex: Exception)
+            {
+                mUser.logMessage("DistortedLibrary: exception trying to pattern match version: $ex")
+            }
+        }
+        else if ( driverVendor!!.contains("Imagination") )
+        {
+            if ( driverRenderer!!.contains("GE8") )
+            {
+                mUser.logMessage("You are running this on a PowerVR GE8XXX.\nDue to a buggy compiler OIT rendering will not work")
+                mUser.logMessage("GLSL Version "+GLES30.glGetString(GLES31.GL_SHADING_LANGUAGE_VERSION))
+            }
+        }
+        else if (driverVendor!=null && driverVendor!!.contains("Qualcomm"))
+        {
+            val driverVersion = parseQualcommDriverVersion(driverVersion!!)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this so that the Library can release the OpenGL related data that needs to be recreated.
- * Must be called from Activity.onPause().
- *
- * @param id id of an Activity that is using the library; anything unique so that the Library can
- *           tell between Activities in case you're going to be using it from more than one.
- */
-  public static void onPause(long id)
-    {
-    InternalStackFrameList.onPause(id);
-
-    Dynamic.onPause();  // common for all frames
-    InternalOutputSurface.onPause();
-    Effect.onPause();
-    DeferredJobs.onPause();
-
-    mOITCompilationAttempted = false;
-    mNeedsTransformFeedback  = false;
-
-    mLinkedListSSBO[0]= -1;
-    mAtomicCounter = null;
-
-    mNormalProgram     = null;
-    mMainOITProgram    = null;
-    mMainProgram       = null;
-    mFullProgram       = null;
-    mOITClearProgram   = null;
-    mOITBuildProgram   = null;
-    mOITCollapseProgram= null;
-    mOITRenderProgram  = null;
-    mBlitDepthProgram  = null;
-    mBlitProgram       = null;
+            if ( driverRenderer!=null && driverRenderer!!.contains("308") && (driverVersion==331||driverVersion==415) )
+            {
+                mUser.logMessage("You are running this on an Adreno 308 driver 331 or 415.\nStrange shit might happen.")
+                isUBOBuggy = true
+            }
+            if ( gLSL<=300 && driverVersion<415 )  // This is only on old enough drivers, 84,95,100,104,140 and known bad, 415 is known good.
+            {
+                mUser.logMessage("Slow compilation of Transform Feedback programs!")
+                mFastCompilationTF = false
+            }
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this so that the Library can release its internal data structures.
- * Must be called from Activity.onDestroy().
- */
-  public static void onDestroy()
-    {
-    onDestroy(0);
-    }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private val version: Int
+        get()
+        {
+            val major = IntArray(1)
+            val minor = IntArray(1)
+            GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, major, 0)
+            GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, minor, 0)
+            return major[0]*100+minor[0]*10
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this so that the Library can release its internal data structures.
- * Must be called from Activity.onDestroy().
- *
- * @param id id of an Activity that is using the library; anything unique so that the Library can
- *           tell between Activities in case you're going to be using it from more than one.
- */
-  public static void onDestroy(long id)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun logMessage(message: String?)
     {
-    if( InternalStackFrameList.isInitialized() )
-      {
-      InternalStackFrameList.onDestroy(id);
-      }
+        mUser.logMessage(message)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Some devices - Qualcomm's Adreno 3xx with drivers v. 84,95,100,104,140, and possibly more -
- * suffer from a very slow compilation of GLSL program if said program includes Transform Feedback.
- * Return true if the platform we are running on does not suffer from this problem.
- */
-  public static boolean fastCompilationTF()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When OpenGL context gets created, call this method so that the library can initialise its internal data structures.
+     * I.e. best called from GLSurfaceView.Renderer.onSurfaceCreated().
+     *
+     * Needs to be called from a thread holding the OpenGL context.
+     *
+     * @param user The Code which will be using this library, which must implement the LibraryUser interface.
+     * @param queueSize the size of the FBO queue, a workaround for the bug on Mali drivers. Use a small integer - 1,...,4
+     */
+    @JvmStatic
+    @JvmOverloads
+    fun onSurfaceCreated(user: LibraryUser, queueSize: Int = 4)
     {
-    return mFastCompilationTF;
+        mUser = user
+        val version = version
+        val major = version/100
+        val minor = ((version/10)%10)
+
+        if (major<3)
+        {
+            gLSL = 100*major+10*minor
+            val ex = VertexCompilationException("at least OpenGL ES 3.0 required, this device supports only $major.$minor")
+            mUser.distortedException(ex)
+        }
+        else
+        {
+            gLSL = if ( (major==3 && minor==0) ) 300 else 310
+        }
+
+        val tmp = IntArray(1)
+        GLES30.glGetIntegerv(GLES30.GL_MAX_TEXTURE_SIZE, tmp, 0)
+        maxTextureSize = tmp[0]
+        GLES30.glGetIntegerv(GLES30.GL_MAX_VERTEX_UNIFORM_VECTORS, tmp, 0)
+        maxVertexUniforms = tmp[0]
+        GLES30.glGetIntegerv(GLES30.GL_MAX_FRAGMENT_UNIFORM_VECTORS, tmp, 0)
+        maxFragmentUniforms = tmp[0]
+
+        mUser.logMessage("Using OpenGL ES $major.$minor texSize=$maxTextureSize maxVerUniforms: $maxVertexUniforms maxFraUniforms: $maxFragmentUniforms")
+
+        mGLSL_VERSION = "#version $gLSL es\n"
+
+        isInitialized = true
+        mOITCompilationAttempted = false
+
+        detectBuggyDriversAndSetQueueSize(queueSize)
+        startSending()
+
+        try
+        {
+            createMainProgram()
+        }
+        catch (ex: Exception)
+        {
+            mUser.distortedException(ex)
+        }
+
+        try
+        {
+            val vertStream = mUser.localFile(R.raw.main_vertex_shader)
+            val fragStream = mUser.localFile(R.raw.preprocess_fragment_shader)
+            createPrograms(vertStream, fragStream, gLSL)
+        }
+        catch (ex: Exception)
+        {
+            mUser.distortedException(ex)
+        }
+
+        try
+        {
+            createPrograms(gLSL)
+        }
+        catch (ex: Exception)
+        {
+            mUser.distortedException(ex)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the maximum size of the texture supported by the driver.
- */
-  public static int getMaxTextureSize()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Switch face culling on/off
+     */
+    @JvmStatic
+    fun setCull(on: Boolean)
     {
-    return mMaxTextureSize;
+        if (on)
+        {
+            GLES30.glEnable(GLES30.GL_CULL_FACE)
+            GLES30.glCullFace(GLES30.GL_FRONT)
+        }
+        else
+        {
+            GLES30.glDisable(GLES30.GL_CULL_FACE)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Call this before calling onSurfaceCreated() if you want to access normal vectors in CPU.
- */
-  public static void needTransformFeedback()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Call this so that the Library can initialize its internal data structures.
+     * Must be called from Activity.onCreate().
+     *
+     * @param id id of an Activity that is using the library; anything unique so that the Library can
+     * tell between Activities in case you're going to be using it from more than one.
+     */
+    @JvmStatic
+    @JvmOverloads
+    fun onCreate(id: Long = 0)
     {
-    mNeedsTransformFeedback = true;
+        InternalStackFrameList.onCreate(id)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the maximum number of effects of a given type that can be simultaneously applied to a
- * single (InputSurface,MeshBase) combo.
- *
- * @param type {@link EffectType}
- * @return The maximum number of effects of a given type.
- */
-  @SuppressWarnings("unused")
-  public static int getMax(EffectType type)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Call this so that the Library can resume its operations.
+     * Must be called from Activity.onResume().
+     *
+     * @param id id of an Activity that is using the library; anything unique so that the Library can
+     * tell between Activities in case you're going to be using it from more than one.
+     */
+    @JvmStatic
+    @JvmOverloads
+    fun onResume(id: Long = 0)
     {
-    return EffectQueue.getMax(type.ordinal());
+        InternalStackFrameList.onResume(id)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the maximum number of effects that can be stored in a single EffectQueue at one time.
- * This can fail if:
- * <ul>
- * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
- * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
- *     before the Vertex Shader gets compiled, i.e. before the call to {@link DistortedLibrary#onSurfaceCreated}. After this
- *     time only decreasing the value of 'max' is permitted.
- * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
- * </ul>
- *
- * @param type {@link EffectType}
- * @param max new maximum number of simultaneous effects. Has to be a non-negative number not greater
- *            than Byte.MAX_VALUE
- * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
- */
-  @SuppressWarnings("unused")
-  public static boolean setMax(EffectType type, int max)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Call this so that the Library can release the OpenGL related data that needs to be recreated.
+     * Must be called from Activity.onPause().
+     *
+     * @param id id of an Activity that is using the library; anything unique so that the Library can
+     * tell between Activities in case you're going to be using it from more than one.
+     */
+    @JvmStatic
+    @JvmOverloads
+    fun onPause(id: Long = 0)
     {
-    return EffectQueue.setMax(type.ordinal(),max);
+        InternalStackFrameList.onPause(id)
+
+        Dynamic.onPause() // common for all frames
+        InternalOutputSurface.onPause()
+        Effect.onPause()
+        DeferredJobs.onPause()
+
+        mOITCompilationAttempted = false
+        mNeedsTransformFeedback = false
+
+        mLinkedListSSBO[0] = -1
+        mAtomicCounter = null
+
+        mNormalProgram = null
+        mMainOITProgram = null
+        mMainProgram = null
+        mFullProgram = null
+        mOITClearProgram = null
+        mOITBuildProgram = null
+        mOITCollapseProgram = null
+        mOITRenderProgram = null
+        mBlitDepthProgram = null
+        mBlitProgram = null
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return an interger - max number of uniforms one can upload to a Vertex Shader.
- */
-  public static int getMaxVertexUniforms()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Call this so that the Library can release its internal data structures.
+     * Must be called from Activity.onDestroy().
+     *
+     * @param id id of an Activity that is using the library; anything unique so that the Library can
+     * tell between Activities in case you're going to be using it from more than one.
+     */
+    @JvmStatic
+    @JvmOverloads
+    fun onDestroy(id: Long = 0)
     {
-    return mMaxNumberOfVerUniforms;
+        if (isInitialized)
+        {
+            InternalStackFrameList.onDestroy(id)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return an interger - max number of uniforms one can upload to a Fragment Shader.
- */
-  public static int getMaxFragmentUniforms()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Some devices - Qualcomm's Adreno 3xx with drivers v. 84,95,100,104,140, and possibly more -
+     * suffer from a very slow compilation of GLSL program if said program includes Transform Feedback.
+     * Return true if the platform we are running on does not suffer from this problem.
+     */
+    @JvmStatic
+    fun fastCompilationTF(): Boolean
     {
-    return mMaxNumberOfFraUniforms;
+        return mFastCompilationTF
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return a String defining the vendor of the graphics driver.
- */
-  public static String getDriverVendor()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Call this before calling onSurfaceCreated() if you want to access normal vectors in CPU.
+     */
+    @JvmStatic
+    fun needTransformFeedback()
     {
-    return mVendor;
+        mNeedsTransformFeedback = true
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return a String defining the version of the graphics driver.
- */
-  public static String getDriverVersion()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Returns the maximum number of effects of a given type that can be simultaneously applied to a
+     * single (InputSurface,MeshBase) combo.
+     *
+     * @param type [EffectType]
+     * @return The maximum number of effects of a given type.
+     */
+    @Suppress("unused")
+    fun getMax(type: EffectType): Int
     {
-    return mVersion;
+        return getMax(type.ordinal)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return a String defining the renderer of the graphics driver.
- */
-  public static String getDriverRenderer()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Sets the maximum number of effects that can be stored in a single EffectQueue at one time.
+     * This can fail if:
+     *
+     *  * the value of 'max' is outside permitted range (0  max  Byte.MAX_VALUE)
+     *  * We try to increase the value of 'max' when it is too late to do so already. It needs to be called
+     * before the Vertex Shader gets compiled, i.e. before the call to [DistortedLibrary.onSurfaceCreated]. After this
+     * time only decreasing the value of 'max' is permitted.
+     *  * Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
+     *
+     *
+     * @param type [EffectType]
+     * @param max new maximum number of simultaneous effects. Has to be a non-negative number not greater
+     * than Byte.MAX_VALUE
+     * @return `true` if operation was successful, `false` otherwise.
+     */
+    @JvmStatic
+    @Suppress("unused")
+    fun setMax(type: EffectType, max: Int): Boolean
     {
-    return mRenderer;
+        return setMax(type.ordinal, max)
     }
-  }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/main/DistortedNode.kt b/src/main/java/org/distorted/library/main/DistortedNode.kt
index fe9957f..2ed5a2e 100644
--- a/src/main/java/org/distorted/library/main/DistortedNode.kt
+++ b/src/main/java/org/distorted/library/main/DistortedNode.kt
@@ -17,783 +17,740 @@
 // License along with this library; if not, write to the Free Software                           //
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+package org.distorted.library.main
 
-package org.distorted.library.main;
-
-import android.opengl.GLES30;
-
-import org.distorted.library.mesh.MeshBase;
-
-import java.util.ArrayList;
-import java.util.Collections;
+import android.opengl.GLES30
+import org.distorted.library.main.InternalNodeData.Companion.returnData
+import org.distorted.library.mesh.MeshBase
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Class which represents a Node in a Tree of (InputSurface,Mesh,Effects) triplets.
- * <p>
+ *
  * Having organized such sets into a Tree, we can then render any Node to any OutputSurface.
  * That recursively renders the set held in the Node and all its children.
- * <p>
+ *
  * The class takes special care to only render identical sub-trees once. Each Node holds a reference
  * to sub-class 'NodeData'. Two identical sub-trees attached at different points of the main tree
  * will point to the same NodeData; only the first of this is rendered (mData.numRender!).
  */
-public class DistortedNode implements InternalChildrenList.Parent
-  {
-  private static final int DEFAULT_FBO_SIZE = 100;
-
-  private final DistortedEffects mEffects;
-  private final InternalRenderState mState;
-  private final InternalChildrenList mChildren;
-  private InternalChildrenList.Parent mParent;
-  private InternalSurface mSurface;
-  private InternalNodeData mData;
-  private MeshBase mMesh;
-
-  private int mFboW, mFboH, mFboDepthStencil;
-  private boolean mRenderWayOIT;
-  private float mFOV, mNear;
-  private long mLastTime;
+open class DistortedNode : InternalChildrenList.Parent
+{
+    val mEffects: DistortedEffects
+    var mMesh: MeshBase
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    private val mState: InternalRenderState
+    private val mChildren: InternalChildrenList
+    private var mParent: InternalChildrenList.Parent?
+    private var mData: InternalNodeData
+    private var mFboW: Int
+    private var mFboH: Int
+    private var mFboDepthStencil: Int
+    private var mRenderWayOIT: Boolean
+    private var mFOV: Float
+    private var mNear: Float
+    private var mLastTime: Long
 
-  public void markForDeletion()
+    companion object
     {
-    if( mData.removeData() )
-      {
-      mData.mFBO.markForDeletion();
-      mData.mFBO = null;
-      }
+        private const val DEFAULT_FBO_SIZE = 100
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// [3] --> the postprocessing queue. See EffectType.
-
-  long getBucket()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun markForDeletion()
     {
-    return mEffects.getQueues()[3].getID();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private ArrayList<Long> generateIDList()
-    {
-    ArrayList<Long> ret = new ArrayList<>();
-    int numChildren = mChildren.getNumChildren();
-
-    if( numChildren==0 )
-      {
-      // add a negative number so this leaf never gets confused with a internal node
-      // with a single child that happens to have ID identical to some leaf's Effects ID.
-      ret.add(-mEffects.getID());
-      }
-    else
-      {
-      DistortedNode node;
-   
-      for(int i=0; i<numChildren; i++)
+        if (mData.removeData())
         {
-        node = mChildren.getChild(i);
-        ret.add(node.mData.ID);
+            mData.mFBO!!.markForDeletion()
+            mData.mFBO = null
         }
-
-      // A bit questionable decision here - we are sorting the children IDs, which means
-      // that order in which we draw the children is going to be undefined (well, this is not
-      // strictly speaking true - when rendering, if no postprocessing and isomorphism are
-      // involved, we *DO* render the children in order they were added; if however there
-      // are two internal nodes with the same list of identical children, just added in a
-      // different order each time, then we consider them isomorphic, i.e. identical and only
-      // render the first one. If then two children of such 'pseudo-isomorphic' nodes are at
-      // exactly the same Z-height this might result in some unexpected sights).
-      //
-      // Reason: with the children being sorted by postprocessing buckets, the order is
-      // undefined anyway (although only when postprocessing is applied).
-      //
-      // See the consequences in the 'Olympic' app - remove a few leaves and add them back in
-      // different order. You will see the number of renders go back to the original 15.
-      Collections.sort(ret);
-      }
-
-    ret.add( 0, mSurface.getID() );
-
-    return ret;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * This is not really part of the public API. Has to be public only because it is a part of the
- * InternalChildrenList.Parent interface.
- *
- * @y.exclude
- */
-  public void adjustIsomorphism()
-    {
-    InternalNodeData newData = InternalNodeData.returnData(generateIDList());
-    boolean deleteOldFBO = mData.removeData();
-    boolean createNewFBO = (mChildren.getNumChildren()>0 && newData.mFBO==null);
-
-    if( deleteOldFBO && createNewFBO )
-      {
-      newData.mFBO = mData.mFBO;
-      }
-    else if( deleteOldFBO )
-      {
-      mData.mFBO.markForDeletion();
-      mData.mFBO = null;
-      }
-    else if( createNewFBO )
-      {
-      newData.mFBO = allocateNewFBO();
-      }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    val mBucket: Long get() = mEffects.queues[3].iD
 
-    mData = newData;
-
-    if( mParent!=null ) mParent.adjustIsomorphism();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return the total number of render calls issued
-
-  int drawNoBlend(long currTime, InternalOutputSurface surface)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setMesh(mesh: MeshBase)
     {
-    InternalSurface input = getSurface();
-
-    if( input.setAsInput() )
-      {
-      mState.apply();
-      GLES30.glDisable(GLES30.GL_BLEND);
-      if( mLastTime==0 ) mLastTime=currTime;
-      DistortedLibrary.drawPriv(mEffects, mMesh, surface, currTime, (currTime-mLastTime));
-      mLastTime = currTime;
-      GLES30.glEnable(GLES30.GL_BLEND);
-      return 1;
-      }
-
-    return 0;
+        mMesh = mesh
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Use the Order Independent Transparency method to draw a non-postprocessed child.
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getMesh(): MeshBase = mMesh
 
-  int drawOIT(long currTime, InternalOutputSurface surface)
-    {
-    InternalSurface input = getSurface();
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getEffects(): DistortedEffects = mEffects
 
-    if( input.setAsInput() )
-      {
-      mState.apply();
-      if( mLastTime==0 ) mLastTime=currTime;
-      DistortedLibrary.drawPrivOIT(mEffects, mMesh, surface, currTime, (currTime-mLastTime));
-      mLastTime = currTime;
-      return 1;
-      }
-
-    return 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return the total number of render calls issued
-
-  int draw(long currTime, InternalOutputSurface surface)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun generateIDList(): ArrayList<Long>
     {
-    InternalSurface input = getSurface();
-
-    if( input.setAsInput() )
-      {
-      mState.apply();
-      if( mLastTime==0 ) mLastTime=currTime;
-      DistortedLibrary.drawPriv(mEffects, mMesh, surface, currTime, (currTime-mLastTime));
-      mLastTime = currTime;
-      return 1;
-      }
-
-    return 0;
-    }
+        val ret = ArrayList<Long>()
+        val numChildren = mChildren.numChildren
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return the total number of render calls issued
-
-  int renderRecursive(long currTime)
-    {
-    int numRenders = 0;
-    int numChildren = mChildren.getNumChildren();
-
-    if( numChildren>0 && mData.notRenderedYetAtThisTime(currTime) )
-      {
-      DistortedNode node;
-      long oldBucket=0, newBucket;
-
-      for (int i=0; i<numChildren; i++)
+        if (numChildren==0)
         {
-        node = mChildren.getChild(i);
-        newBucket = node.getBucket();
-        numRenders += node.renderRecursive(currTime);
-        if( newBucket<oldBucket ) mChildren.rearrangeByBuckets(i,newBucket);
-        else oldBucket=newBucket;
+            // add a negative number so this leaf never gets confused with a internal node
+            // with a single child that happens to have ID identical to some leaf's Effects ID.
+            ret.add(-mEffects.iD)
         }
-
-      if( mData.mFBO==null ) mData.mFBO = allocateNewFBO();
-      mData.mFBO.setAsOutput(currTime);
-
-      if( mSurface.setAsInput() )
+        else
         {
-        numRenders++;
-        DistortedLibrary.blitPriv(mData.mFBO);
+            var node: DistortedNode
+
+            for (i in 0 until numChildren)
+            {
+                node = mChildren.getChild(i)
+                ret.add(node.mData.ID)
+            }
+
+            // A bit questionable decision here - we are sorting the children IDs, which means
+            // that order in which we draw the children is going to be undefined (well, this is not
+            // strictly speaking true - when rendering, if no postprocessing and isomorphism are
+            // involved, we *DO* render the children in order they were added; if however there
+            // are two internal nodes with the same list of identical children, just added in a
+            // different order each time, then we consider them isomorphic, i.e. identical and only
+            // render the first one. If then two children of such 'pseudo-isomorphic' nodes are at
+            // exactly the same Z-height this might result in some unexpected sights).
+            //
+            // Reason: with the children being sorted by postprocessing buckets, the order is
+            // undefined anyway (although only when postprocessing is applied).
+            //
+            // See the consequences in the 'Olympic' app - remove a few leaves and add them back in
+            // different order. You will see the number of renders go back to the original 15.
+            ret.sort()
         }
 
-      numRenders += mData.mFBO.renderChildren(currTime,numChildren,mChildren,0, mRenderWayOIT);
-      }
+        ret.add(0, mSurface!!.iD)
 
-    return numRenders;
+        return ret
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private DistortedFramebuffer allocateNewFBO()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * This is not really part of the public API. Has to be public only because it is a part of the
+     * InternalChildrenList.Parent interface.
+     *
+     * @y.exclude
+     */
+    override fun adjustIsomorphism()
     {
-    int width, height;
+        val newData = returnData(generateIDList())
+        val deleteOldFBO = mData.removeData()
+        val createNewFBO = (mChildren.numChildren>0 && newData.mFBO==null)
 
-    if( mFboW>0 && mFboH>0 )
-      {
-      width = mFboW;
-      height= mFboH;
-      }
-    else
-      {
-      if( mSurface instanceof DistortedFramebuffer )
+        if (deleteOldFBO&&createNewFBO)
         {
-        DistortedFramebuffer fbo = (DistortedFramebuffer)mSurface;
-        width = fbo.getWidth();
-        height= fbo.getHeight();
+            newData.mFBO = mData.mFBO
         }
-      else
+        else if (deleteOldFBO)
         {
-        width = DEFAULT_FBO_SIZE;
-        height= DEFAULT_FBO_SIZE;
+            mData.mFBO!!.markForDeletion()
+            mData.mFBO = null
+        }
+        else if (createNewFBO)
+        {
+            newData.mFBO = allocateNewFBO()
         }
-      }
-
-    DistortedFramebuffer fbo = new DistortedFramebuffer(1,mFboDepthStencil, InternalSurface.TYPE_TREE, width, height);
 
-    if( mFOV!=InternalOutputSurface.DEFAULT_FOV || mNear!=InternalOutputSurface.DEFAULT_NEAR )
-      {
-      fbo.setProjection(mFOV,mNear);
-      }
+        mData = newData
 
-    return fbo;
+        if (mParent!=null) mParent!!.adjustIsomorphism()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void resetLastTime()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // return the total number of render calls issued
+    fun drawNoBlend(currTime: Long, surface: InternalOutputSurface): Int
     {
-    mLastTime = 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        if ( mSurface!!.setAsInput() )
+        {
+            mState.apply()
+            GLES30.glDisable(GLES30.GL_BLEND)
+            if (mLastTime==0L) mLastTime = currTime
+            DistortedLibrary.drawPriv(mEffects, mMesh, surface, currTime, (currTime-mLastTime))
+            mLastTime = currTime
+            GLES30.glEnable(GLES30.GL_BLEND)
+            return 1
+        }
 
-  void setParent(InternalChildrenList.Parent parent)
-    {
-    mParent = parent;
+        return 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructs new Node.
- *     
- * @param surface InputSurface to put into the new Node.
- * @param effects DistortedEffects to put into the new Node.
- * @param mesh MeshBase to put into the new Node.
- */
-  public DistortedNode(InternalSurface surface, DistortedEffects effects, MeshBase mesh)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Use the Order Independent Transparency method to draw a non-postprocessed child.
+    fun drawOIT(currTime: Long, surface: InternalOutputSurface): Int
     {
-    mLastTime      = 0;
-    mSurface       = surface;
-    mEffects       = effects;
-    mMesh          = mesh;
-    mState         = new InternalRenderState();
-    mChildren      = new InternalChildrenList(this);
-    mParent        = null;
-    mRenderWayOIT  = false;
-
-    mFOV = InternalOutputSurface.DEFAULT_FOV;
-    mNear= InternalOutputSurface.DEFAULT_NEAR;
-
-    mFboW            = 0;  // i.e. take this from
-    mFboH            = 0;  // mEffects's stretch{X,Y}
-    mFboDepthStencil = DistortedFramebuffer.DEPTH_NO_STENCIL;
-
-    mData = InternalNodeData.returnData(generateIDList());
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////  
-/**
- * Copy-constructs new Node from another Node.
- *     
- * @param node The DistortedNode to copy data from.
- * @param flags bit field composed of a subset of the following:
- *        {@link DistortedLibrary#CLONE_SURFACE},  {@link DistortedLibrary#CLONE_MATRIX}, {@link DistortedLibrary#CLONE_VERTEX},
- *        {@link DistortedLibrary#CLONE_FRAGMENT} and {@link DistortedLibrary#CLONE_CHILDREN}.
- *        For example flags = CLONE_SURFACE | CLONE_CHILDREN.
- */
-  public DistortedNode(DistortedNode node, int flags)
-    {
-    mLastTime     = 0;
-    mEffects      = new DistortedEffects(node.mEffects,flags);
-    mMesh         = node.mMesh;
-    mState        = new InternalRenderState();
-    mParent       = null;
-    mRenderWayOIT = false;
-
-    mFOV = InternalOutputSurface.DEFAULT_FOV;
-    mNear= InternalOutputSurface.DEFAULT_NEAR;
-
-    mFboW            = node.mFboW;
-    mFboH            = node.mFboH;
-    mFboDepthStencil = node.mFboDepthStencil;
-
-    if( (flags & DistortedLibrary.CLONE_SURFACE) != 0 )
-      {
-      mSurface = node.mSurface;
-      }
-    else
-      {
-      if( node.mSurface instanceof DistortedTexture )
-        {
-        mSurface = new DistortedTexture(InternalSurface.TYPE_TREE);
-        }
-      else if( node.mSurface instanceof DistortedFramebuffer )
+        if (mSurface!!.setAsInput())
         {
-        DistortedFramebuffer fbo = (DistortedFramebuffer)node.mSurface;
-
-        int w = fbo.getWidth();
-        int h = fbo.getHeight();
-        int depthStencil = DistortedFramebuffer.NO_DEPTH_NO_STENCIL;
-
-        if( fbo.hasDepth() )
-          {
-          boolean hasStencil = fbo.hasStencil();
-          depthStencil = (hasStencil ? DistortedFramebuffer.BOTH_DEPTH_STENCIL:DistortedFramebuffer.DEPTH_NO_STENCIL);
-          }
-
-        mSurface = new DistortedFramebuffer(1,depthStencil, InternalSurface.TYPE_TREE,w,h);
+            mState.apply()
+            if (mLastTime==0L) mLastTime = currTime
+            DistortedLibrary.drawPrivOIT( mEffects, mMesh, surface, currTime, (currTime-mLastTime))
+            mLastTime = currTime
+            return 1
         }
-      }
 
-    if( (flags & DistortedLibrary.CLONE_CHILDREN) != 0 )
-      {
-      mChildren = node.mChildren;
-      }
-    else
-      {
-      mChildren = new InternalChildrenList(this);
-      }
-
-    mData = InternalNodeData.returnData(generateIDList());
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  /**
-   * Change the input surface while keeping everything else about the Node the same.
-   *
-   * @param surface The new input surface.
-   */
-  public void changeInputSurface(InternalSurface surface)
-    {
-    mSurface = surface;
+        return 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  /**
-   * When rendering this Node, should we use the Order Independent Transparency render more?
-   * <p>
-   * There are two modes of rendering: the fast 'normal' way, which however renders transparent
-   * fragments in different ways depending on which fragments get rendered first, or the slower
-   * 'oit' way, which renders transparent fragments correctly regardless of their order.
-   *
-   * @param oit True if we want to render more slowly, but in a way which accounts for transparency.
-   */
-  public void setOrderIndependentTransparency(boolean oit)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // return the total number of render calls issued
+    fun draw(currTime: Long, surface: InternalOutputSurface): Int
     {
-    mRenderWayOIT = oit;
-    }
+        if (mSurface!!.setAsInput())
+        {
+            mState.apply()
+            if (mLastTime==0L) mLastTime = currTime
+            DistortedLibrary.drawPriv(mEffects, mMesh, surface, currTime, (currTime-mLastTime))
+            mLastTime = currTime
+            return 1
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  /**
-   * When rendering this Node, should we use the Order Independent Transparency render more?
-   * <p>
-   * There are two modes of rendering: the fast 'normal' way, which however renders transparent
-   * fragments in different ways depending on which fragments get rendered first, or the slower
-   * 'oit' way, which renders transparent fragments correctly regardless of their order.
-   *
-   * @param oit True if we want to render more slowly, but in a way which accounts for transparency.
-   * @param initialSize Initial number of transparent fragments we expect, in screenfulls.
-   *                    I.e '1.0' means 'the scene we are going to render contains dialog_about 1 screen
-   *                    worth of transparent fragments'. Valid values: 0.0 &lt; initialSize &lt; 10.0
-   *                    Even if you get this wrong, the library will detect that there are more
-   *                    transparent fragments than it has space for and readjust its internal buffers,
-   *                    but only after a few frames during which one will probably see missing objects.
-   */
-  public void setOrderIndependentTransparency(boolean oit, float initialSize)
-    {
-    mRenderWayOIT = oit;
-
-    if( initialSize>0.0f && initialSize<10.0f )
-      DistortedLibrary.setSSBOSize(initialSize);
+        return 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new child to the last position in the list of our Node's children.
- * <p>
- * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * InternalMaster (by calling doWork())
- *
- * @param node The new Node to add.
- */
-  public void attach(DistortedNode node)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // return the total number of render calls issued
+    fun renderRecursive(currTime: Long): Int
     {
-    mChildren.attach(node);
-    }
+        var numRenders = 0
+        val numChildren = mChildren.numChildren
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new child to the last position in the list of our Node's children.
- * <p>
- * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * InternalMaster (by calling doWork())
- *
- * @param surface InputSurface to initialize our child Node with.
- * @param effects DistortedEffects to initialize our child Node with.
- * @param mesh MeshBase to initialize our child Node with.
- * @return the newly constructed child Node, or null if we couldn't allocate resources.
- */
-  public DistortedNode attach(InternalSurface surface, DistortedEffects effects, MeshBase mesh)
-    {
-    return mChildren.attach(surface,effects,mesh);
-    }
+        if (numChildren>0&&mData.notRenderedYetAtThisTime(currTime))
+        {
+            var node: DistortedNode
+            var oldBucket: Long = 0
+            var newBucket: Long
+
+            for (i in 0 until numChildren)
+            {
+                node = mChildren.getChild(i)
+                newBucket = node.mBucket
+                numRenders += node.renderRecursive(currTime)
+                if (newBucket<oldBucket) mChildren.rearrangeByBuckets(i, newBucket)
+                else oldBucket = newBucket
+            }
+
+            if (mData.mFBO==null) mData.mFBO = allocateNewFBO()
+            mData.mFBO!!.setAsOutput(currTime)
+
+            if (mSurface!!.setAsInput())
+            {
+                numRenders++
+                DistortedLibrary.blitPriv(mData.mFBO!!)
+            }
+
+            numRenders += mData.mFBO!!.renderChildren(currTime, numChildren, mChildren, 0, mRenderWayOIT)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes the first occurrence of a specified child from the list of children of our Node.
- * <p>
- * We cannot do this mid-render - actual detachment will be done just before the next render, by the
- * InternalMaster (by calling doWork())
- *
- * @param node The Node to remove.
- */
-  public void detach(DistortedNode node)
-    {
-    mChildren.detach(node);
+        return numRenders
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes the first occurrence of a specified child from the list of children of our Node.
- * <p>
- * A bit questionable method as there can be many different Nodes attached as children, some
- * of them having the same Effects but - for instance - different Mesh. Use with care.
- * <p>
- * We cannot do this mid-render - actual detachment will be done just before the next render, by the
- * InternalMaster (by calling doWork())
- *
- * @param effects DistortedEffects to remove.
- */
-  public void detach(DistortedEffects effects)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun allocateNewFBO(): DistortedFramebuffer
     {
-    mChildren.detach(effects);
-    }
+        val width: Int
+        val height: Int
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all children Nodes.
- * <p>
- * We cannot do this mid-render - actual detachment will be done just before the next render, by the
- * InternalMaster (by calling doWork())
- */
-  public void detachAll()
-    {
-    mChildren.detachAll();
-    }
+        if( mFboW>0 && mFboH>0 )
+        {
+            width = mFboW
+            height = mFboH
+        }
+        else
+        {
+            if (mSurface is DistortedFramebuffer)
+            {
+                val fbo = mSurface as DistortedFramebuffer
+                width = fbo.getWidth()
+                height = fbo.getHeight()
+            }
+            else
+            {
+                width = DEFAULT_FBO_SIZE
+                height = DEFAULT_FBO_SIZE
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the DistortedEffects object that's in the Node.
- * 
- * @return The DistortedEffects contained in the Node.
- */
-  public DistortedEffects getEffects()
-    {
-    return mEffects;
-    }
+        val fbo = DistortedFramebuffer(1, mFboDepthStencil, InternalObject.TYPE_TREE, width, height)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  /**
-   * Returns the surface this object gets rendered to.
-   *
-   * @return The InternalSurface contained in the Node (if a leaf), or the FBO (if an internal Node)
-   */
-  public InternalSurface getSurface()
-    {
-    return mChildren.getNumChildren()==0 ? mSurface : mData.mFBO;
-    }
+        if ( mFOV!=InternalOutputSurface.DEFAULT_FOV || mNear!=InternalOutputSurface.DEFAULT_NEAR )
+        {
+            fbo.setProjection(mFOV, mNear)
+        }
 
-//////////////////////////////////////////////////////////////////////////////////////////////////
-  /**
-   * Returns the FBO contained in this object, even if it is a leaf.
-   *
-   * @return The DistortedFramebuffer.
-   */
-  public DistortedFramebuffer getFramebuffer()
-    {
-    return mData.mFBO;
+        return fbo
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the Mesh object that's in the Node.
- *
- * @return Mesh contained in the Node.
- */
-  public MeshBase getMesh()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun resetLastTime()
     {
-    return mMesh;
+        mLastTime = 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Set a new Mesh.
- */
-  public void setMesh(MeshBase mesh)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setParent(parent: InternalChildrenList.Parent?)
     {
-    mMesh = mesh;
+        mParent = parent
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resizes the DistortedFramebuffer object that we render this Node to.
- */
-  public void resizeFBO(int width, int height)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Constructs new Node.
+     *
+     * @param surface InputSurface to put into the new Node.
+     * @param effects DistortedEffects to put into the new Node.
+     * @param mesh MeshBase to put into the new Node.
+     */
+    constructor(surface: InternalSurface?, effects: DistortedEffects, mesh: MeshBase)
     {
-    mFboW = width;
-    mFboH = height;
+        mLastTime = 0
+        mSurface = surface
+        mEffects = effects
+        mMesh = mesh
+        mState = InternalRenderState()
+        mChildren = InternalChildrenList(this)
+        mParent = null
+        mRenderWayOIT = false
 
-    if ( mData.mFBO !=null )
-      {
-      // TODO: potentially allocate a new NodeData if we have to
-      mData.mFBO.resize(width,height);
-      }
-    }
+        mFOV = InternalOutputSurface.DEFAULT_FOV
+        mNear = InternalOutputSurface.DEFAULT_NEAR
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Set Projection Matrix for the Framebuffer contained in this Node.
- * <p>
- * If this Node is a Leaf and has no Framebuffer in it, this call does nothing.
- *
- * @param fov Vertical 'field of view' of the Projection frustrum (in degrees).
- *            Valid values: 0<=fov<180. FOV==0 means 'parallel projection'.
- * @param near The Near plane.
- */
-  public void setProjection(float fov, float near)
-    {
-    if( fov < 180.0f && fov >=0.0f )
-      {
-      mFOV = fov;
-      }
-
-    if( near<   1.0f && near> 0.0f )
-      {
-      mNear= near;
-      }
-    else if( near<=0.0f )
-      {
-      mNear = 0.01f;
-      }
-    else if( near>=1.0f )
-      {
-      mNear=0.99f;
-      }
-
-    if( mData.mFBO!=null )
-      {
-      mData.mFBO.setProjection(mFOV,mNear);
-      }
-    }
+        mFboW = 0 // i.e. take this from
+        mFboH = 0 // mEffects stretch{X,Y}
+        mFboDepthStencil = InternalOutputSurface.DEPTH_NO_STENCIL
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Enables/disables DEPTH and STENCIL buffers in the Framebuffer object that we render this Node to.
- */
-  public void enableDepthStencil(int depthStencil)
-    {
-    mFboDepthStencil = depthStencil;
-
-    if ( mData.mFBO !=null )
-      {
-      // TODO: potentially allocate a new NodeData if we have to
-      mData.mFBO.enableDepthStencil(depthStencil);
-      }
+        mData = returnData(generateIDList())
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// APIs that control how to set the OpenGL state just before rendering this Node.
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, use ColorMask (r,g,b,a).
- *
- * @param r Write to the RED color channel when rendering this Node?
- * @param g Write to the GREEN color channel when rendering this Node?
- * @param b Write to the BLUE color channel when rendering this Node?
- * @param a Write to the ALPHA channel when rendering this Node?
- */
-  @SuppressWarnings("unused")
-  public void glColorMask(boolean r, boolean g, boolean b, boolean a)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////  
+    /**
+     * Copy-constructs new Node from another Node.
+     *
+     * @param node The DistortedNode to copy data from.
+     * @param flags bit field composed of a subset of the following:
+     * [DistortedLibrary.CLONE_SURFACE],  [DistortedLibrary.CLONE_MATRIX], [DistortedLibrary.CLONE_VERTEX],
+     * [DistortedLibrary.CLONE_FRAGMENT] and [DistortedLibrary.CLONE_CHILDREN].
+     * For example flags = CLONE_SURFACE | CLONE_CHILDREN.
+     */
+    constructor(node: DistortedNode, flags: Int)
     {
-    mState.glColorMask(r,g,b,a);
-    }
+        mLastTime = 0
+        mEffects = DistortedEffects(node.mEffects, flags)
+        mMesh = node.mMesh
+        mState = InternalRenderState()
+        mParent = null
+        mRenderWayOIT = false
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, switch on writing to Depth buffer?
- *
- * @param mask Write to the Depth buffer when rendering this Node?
- */
-  @SuppressWarnings("unused")
-  public void glDepthMask(boolean mask)
-    {
-    mState.glDepthMask(mask);
-    }
+        mFOV = InternalOutputSurface.DEFAULT_FOV
+        mNear = InternalOutputSurface.DEFAULT_NEAR
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, which bits of the Stencil buffer to write to?
- *
- * @param mask Marks the bits of the Stencil buffer we will write to when rendering this Node.
- */
-  @SuppressWarnings("unused")
-  public void glStencilMask(int mask)
-    {
-    mState.glStencilMask(mask);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, which Tests to enable?
- *
- * @param test Valid values: GL_DEPTH_TEST, GL_STENCIL_TEST, GL_BLEND
- */
-  @SuppressWarnings("unused")
-  public void glEnable(int test)
-    {
-    mState.glEnable(test);
-    }
+        mFboW = node.mFboW
+        mFboH = node.mFboH
+        mFboDepthStencil = node.mFboDepthStencil
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, which Tests to enable?
- *
- * @param test Valid values: GL_DEPTH_TEST, GL_STENCIL_TEST, GL_BLEND
- */
-  @SuppressWarnings("unused")
-  public void glDisable(int test)
-    {
-    mState.glDisable(test);
-    }
+        if ((flags and DistortedLibrary.CLONE_SURFACE)!=0)
+        {
+            mSurface = node.mSurface
+        }
+        else
+        {
+            if (node.mSurface is DistortedTexture)
+            {
+                mSurface = DistortedTexture(InternalObject.TYPE_TREE)
+            }
+            else if (node.mSurface is DistortedFramebuffer)
+            {
+                val fbo = node.mSurface as DistortedFramebuffer
+
+                val w = fbo.getWidth()
+                val h = fbo.getHeight()
+                var depthStencil = InternalOutputSurface.NO_DEPTH_NO_STENCIL
+
+                if (fbo.hasDepth())
+                {
+                    val hasStencil = fbo.hasStencil()
+                    depthStencil = (if (hasStencil) InternalOutputSurface.BOTH_DEPTH_STENCIL else InternalOutputSurface.DEPTH_NO_STENCIL)
+                }
+
+                mSurface = DistortedFramebuffer(1, depthStencil, InternalObject.TYPE_TREE, w, h)
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, use the following StencilFunc.
- *
- * @param func Valid values: GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL
- * @param ref  Reference valut to compare our stencil with.
- * @param mask Mask used when comparing.
- */
-  @SuppressWarnings("unused")
-  public void glStencilFunc(int func, int ref, int mask)
-    {
-    mState.glStencilFunc(func,ref,mask);
-    }
+        mChildren = if ((flags and DistortedLibrary.CLONE_CHILDREN)!=0)
+        {
+            node.mChildren
+        }
+        else
+        {
+            InternalChildrenList(this)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, use the following StencilOp.
- * <p>
- * Valid values of all 3 parameters: GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
- *
- * @param sfail  What to do when Stencil Test fails.
- * @param dpfail What to do when Depth Test fails.
- * @param dppass What to do when Depth Test passes.
- */
-  @SuppressWarnings("unused")
-  public void glStencilOp(int sfail, int dpfail, int dppass)
-    {
-    mState.glStencilOp(sfail,dpfail,dppass);
+        mData = returnData(generateIDList())
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Change the input surface while keeping everything else about the Node the same.
+     *
+     * @param surface The new input surface.
+     */
+    fun changeInputSurface(surface: InternalSurface?)
+    {
+        mSurface = surface
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, should we use the Order Independent Transparency render more?
+     *
+     * There are two modes of rendering: the fast 'normal' way, which however renders transparent
+     * fragments in different ways depending on which fragments get rendered first, or the slower
+     * 'oit' way, which renders transparent fragments correctly regardless of their order.
+     *
+     * @param oit True if we want to render more slowly, but in a way which accounts for transparency.
+     */
+    fun setOrderIndependentTransparency(oit: Boolean)
+    {
+        mRenderWayOIT = oit
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, should we use the Order Independent Transparency render more?
+     *
+     * There are two modes of rendering: the fast 'normal' way, which however renders transparent
+     * fragments in different ways depending on which fragments get rendered first, or the slower
+     * 'oit' way, which renders transparent fragments correctly regardless of their order.
+     *
+     * @param oit True if we want to render more slowly, but in a way which accounts for transparency.
+     * @param initialSize Initial number of transparent fragments we expect, in screenfulls.
+     * I.e '1.0' means 'the scene we are going to render contains dialog_about 1 screen
+     * worth of transparent fragments'. Valid values: 0.0 &lt; initialSize &lt; 10.0
+     * Even if you get this wrong, the library will detect that there are more
+     * transparent fragments than it has space for and readjust its internal buffers,
+     * but only after a few frames during which one will probably see missing objects.
+     */
+    fun setOrderIndependentTransparency(oit: Boolean, initialSize: Float)
+    {
+        mRenderWayOIT = oit
+
+        if( initialSize>0.0f && initialSize<10.0f ) DistortedLibrary.setSSBOSize(initialSize)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Adds a new child to the last position in the list of our Node's children.
+     *
+     * We cannot do this mid-render - actual attachment will be done just before the next render, by the
+     * InternalMaster (by calling doWork())
+     *
+     * @param node The new Node to add.
+     */
+    fun attach(node: DistortedNode)
+    {
+        mChildren.attach(node)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Adds a new child to the last position in the list of our Node's children.
+     *
+     * We cannot do this mid-render - actual attachment will be done just before the next render, by the
+     * InternalMaster (by calling doWork())
+     *
+     * @param surface InputSurface to initialize our child Node with.
+     * @param effects DistortedEffects to initialize our child Node with.
+     * @param mesh MeshBase to initialize our child Node with.
+     * @return the newly constructed child Node, or null if we couldn't allocate resources.
+     */
+    fun attach(surface: InternalSurface, effects: DistortedEffects, mesh: MeshBase): DistortedNode
+    {
+        return mChildren.attach(surface, effects, mesh)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Removes the first occurrence of a specified child from the list of children of our Node.
+     *
+     * We cannot do this mid-render - actual detachment will be done just before the next render, by the
+     * InternalMaster (by calling doWork())
+     *
+     * @param node The Node to remove.
+     */
+    fun detach(node: DistortedNode)
+    {
+        mChildren.detach(node)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Removes the first occurrence of a specified child from the list of children of our Node.
+     *
+     * A bit questionable method as there can be many different Nodes attached as children, some
+     * of them having the same Effects but - for instance - different Mesh. Use with care.
+     *
+     * We cannot do this mid-render - actual detachment will be done just before the next render, by the
+     * InternalMaster (by calling doWork())
+     *
+     * @param effects DistortedEffects to remove.
+     */
+    fun detach(effects: DistortedEffects)
+    {
+        mChildren.detach(effects)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Removes all children Nodes.
+     *
+     * We cannot do this mid-render - actual detachment will be done just before the next render, by the
+     * InternalMaster (by calling doWork())
+     */
+    fun detachAll()
+    {
+        mChildren.detachAll()
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Returns the surface this object gets rendered to.
+     *
+     * @return The InternalSurface contained in the Node (if a leaf), or the FBO (if an internal Node)
+     */
+    var mSurface: InternalSurface? = null
+        get() = if (mChildren.numChildren==0) field else mData.mFBO
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////
+     /**
+     * Returns the FBO contained in this object, even if it is a leaf.
+     *
+     * @return The DistortedFramebuffer.
+     */
+    fun getFramebuffer(): DistortedFramebuffer? = mData.mFBO
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Resizes the DistortedFramebuffer object that we render this Node to.
+     */
+    fun resizeFBO(width: Int, height: Int)
+    {
+        mFboW = width
+        mFboH = height
+
+        if (mData.mFBO!=null)
+        {
+            // TODO: potentially allocate a new NodeData if we have to
+            mData.mFBO!!.resize(width, height)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, use the following DepthFunc.
- *
- * @param func Valid values: GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL
- */
-  @SuppressWarnings("unused")
-  public void glDepthFunc(int func)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Set Projection Matrix for the Framebuffer contained in this Node.
+     *
+     * If this Node is a Leaf and has no Framebuffer in it, this call does nothing.
+     *
+     * @param fov Vertical 'field of view' of the Projection frustrum (in degrees).
+     * Valid values: 0<=fov<180. FOV==0 means 'parallel projection'.
+     * @param near The Near plane.
+     */
+    fun setProjection(fov: Float, near: Float)
     {
-    mState.glDepthFunc(func);
-    }
+        if (fov<180.0f&&fov>=0.0f)
+        {
+            mFOV = fov
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, use the following Blending mode.
- * <p>
- * Valid values: GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
- *               GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR,
- *               GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_SRC_ALPHA_SATURATE
- *
- * @param src Source Blend function
- * @param dst Destination Blend function
- */
-  @SuppressWarnings("unused")
-  public void glBlendFunc(int src, int dst)
-    {
-    mState.glBlendFunc(src,dst);
-    }
+        if (near<1.0f&&near>0.0f)
+        {
+            mNear = near
+        }
+        else if (near<=0.0f)
+        {
+            mNear = 0.01f
+        }
+        else if (near>=1.0f)
+        {
+            mNear = 0.99f
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Before rendering this Node, clear the following buffers.
- * <p>
- * Valid values: 0, or bitwise OR of one or more values from the set GL_COLOR_BUFFER_BIT,
- *               GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT.
- * Default: 0
- *
- * @param mask bitwise OR of BUFFER_BITs to clear.
- */
-  @SuppressWarnings("unused")
-  public void glClear(int mask)
-    {
-    mState.glClear(mask);
+        if (mData.mFBO!=null)
+        {
+            mData.mFBO!!.setProjection(mFOV, mNear)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Recursively print all the effect queues attached to the children Nodes and to this Node.
- */
-  public void debug(int depth)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Enables/disables DEPTH and STENCIL buffers in the Framebuffer object that we render this Node to.
+     */
+    fun enableDepthStencil(depthStencil: Int)
     {
-    String dbg = mEffects.debug(depth);
-    DistortedLibrary.logMessage(dbg);
+        mFboDepthStencil = depthStencil
 
-    int numChildren = mChildren.getNumChildren();
+        if (mData.mFBO!=null)
+        {
+            // TODO: potentially allocate a new NodeData if we have to
+            mData.mFBO!!.enableDepthStencil(depthStencil)
+        }
+    }
 
-    for(int i=0; i<numChildren; i++)
-      {
-      DistortedNode node = mChildren.getChild(i);
-      node.debug(depth+1);
-      }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // APIs that control how to set the OpenGL state just before rendering this Node.
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, use ColorMask (r,g,b,a).
+     *
+     * @param r Write to the RED color channel when rendering this Node?
+     * @param g Write to the GREEN color channel when rendering this Node?
+     * @param b Write to the BLUE color channel when rendering this Node?
+     * @param a Write to the ALPHA channel when rendering this Node?
+     */
+    @Suppress("unused")
+    fun glColorMask(r: Boolean, g: Boolean, b: Boolean, a: Boolean)
+    {
+        mState.glColorMask(r, g, b, a)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, switch on writing to Depth buffer?
+     *
+     * @param mask Write to the Depth buffer when rendering this Node?
+     */
+    @Suppress("unused")
+    fun glDepthMask(mask: Boolean)
+    {
+        mState.glDepthMask(mask)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, which bits of the Stencil buffer to write to?
+     *
+     * @param mask Marks the bits of the Stencil buffer we will write to when rendering this Node.
+     */
+    @Suppress("unused")
+    fun glStencilMask(mask: Int)
+    {
+        mState.glStencilMask(mask)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, which Tests to enable?
+     *
+     * @param test Valid values: GL_DEPTH_TEST, GL_STENCIL_TEST, GL_BLEND
+     */
+    @Suppress("unused")
+    fun glEnable(test: Int)
+    {
+        mState.glEnable(test)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, which Tests to enable?
+     *
+     * @param test Valid values: GL_DEPTH_TEST, GL_STENCIL_TEST, GL_BLEND
+     */
+    @Suppress("unused")
+    fun glDisable(test: Int)
+    {
+        mState.glDisable(test)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, use the following StencilFunc.
+     *
+     * @param func Valid values: GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL
+     * @param ref  Reference valut to compare our stencil with.
+     * @param mask Mask used when comparing.
+     */
+    @Suppress("unused")
+    fun glStencilFunc(func: Int, ref: Int, mask: Int)
+    {
+        mState.glStencilFunc(func, ref, mask)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, use the following StencilOp.
+     *
+     * Valid values of all 3 parameters: GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
+     *
+     * @param sfail  What to do when Stencil Test fails.
+     * @param dpfail What to do when Depth Test fails.
+     * @param dppass What to do when Depth Test passes.
+     */
+    @Suppress("unused")
+    fun glStencilOp(sfail: Int, dpfail: Int, dppass: Int)
+    {
+        mState.glStencilOp(sfail, dpfail, dppass)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, use the following DepthFunc.
+     *
+     * @param func Valid values: GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL
+     */
+    @Suppress("unused")
+    fun glDepthFunc(func: Int)
+    {
+        mState.glDepthFunc(func)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, use the following Blending mode.
+     *
+     * Valid values: GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
+     * GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR,
+     * GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_SRC_ALPHA_SATURATE
+     *
+     * @param src Source Blend function
+     * @param dst Destination Blend function
+     */
+    @Suppress("unused")
+    fun glBlendFunc(src: Int, dst: Int)
+    {
+        mState.glBlendFunc(src, dst)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Before rendering this Node, clear the following buffers.
+     *
+     * Valid values: 0, or bitwise OR of one or more values from the set GL_COLOR_BUFFER_BIT,
+     * GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT.
+     * Default: 0
+     *
+     * @param mask bitwise OR of BUFFER_BITs to clear.
+     */
+    @Suppress("unused")
+    fun glClear(mask: Int)
+    {
+        mState.glClear(mask)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Recursively print all the effect queues attached to the children Nodes and to this Node.
+     */
+    fun debug(depth: Int)
+    {
+        val dbg = mEffects.debug(depth)
+        DistortedLibrary.logMessage(dbg)
+
+        val numChildren = mChildren.numChildren
+
+        for (i in 0 until numChildren)
+        {
+            val node = mChildren.getChild(i)
+            node.debug(depth+1)
+        }
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/DistortedScreen.kt b/src/main/java/org/distorted/library/main/DistortedScreen.kt
index 3109243..7511412 100644
--- a/src/main/java/org/distorted/library/main/DistortedScreen.kt
+++ b/src/main/java/org/distorted/library/main/DistortedScreen.kt
@@ -17,196 +17,196 @@
 // License along with this library; if not, write to the Free Software                           //
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+package org.distorted.library.main
 
-package org.distorted.library.main;
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.opengl.GLES30
+import org.distorted.library.effect.MatrixEffectMove
+import org.distorted.library.effect.MatrixEffectScale
+import org.distorted.library.mesh.MeshQuad
+import org.distorted.library.type.Static3D
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.opengl.GLES30;
-
-import org.distorted.library.effect.MatrixEffectMove;
-import org.distorted.library.effect.MatrixEffectScale;
-import org.distorted.library.mesh.MeshQuad;
-import org.distorted.library.type.Static3D;
-
 /**
  * Class which represents the Screen.
- * <p>
+ *
  * User is able to render to it just like to a DistortedFramebuffer.
  */
-public class DistortedScreen extends DistortedFramebuffer
-  {
-  ///// DEBUGGING ONLY /////////////////////////
-  private static final int NUM_FRAMES  = 80;
-  private static final float DEBUG_SCR_FRAC = 0.15f;
-  private static final float DEBUG_FRAC     = 0.5f;
-
-  private static final int DEBUG_MODE_NONE = 0;
-  private static final int DEBUG_MODE_FPS  = 1;
-  private static final int DEBUG_MODE_FRAME= 2;
-
-  private int mDebugMode;
-  private boolean mDebugAllocated;
-
-  private MeshQuad debugMesh;
-  private DistortedTexture debugTexture;
-  private DistortedEffects debugEffects;
-  private Canvas debugCanvas;
-  private Bitmap debugBitmap;
-  private Paint mPaint;
-  private String debugString;
-  private long lastTime=0;
-  private long[] durations;
-  private int currDuration;
-  private int frameNumber;
-  private static final Static3D mMoveVector = new Static3D(0,0,0);
-  private static final MatrixEffectMove mMoveEffect = new MatrixEffectMove(mMoveVector);
-  ///// END DEBUGGING //////////////////////////
-
-  private int mQueueSize;
-  private int mCurRenderedFBO;    // During the first FBO_QUEUE_SIZE frames, we blit the very first
-  private int mToBeBlittedFBO;    // FBO one we have rendered. Then, we keep blitting the one we
-  private boolean mFirstCircle;   // rendered FBO_QUEUE_SIZE ago.
-
-  private int mDebugWidth, mDebugHeight, mDebugGap, mDebugTextColor, mDebugBackColor;
+class DistortedScreen : DistortedFramebuffer(DistortedLibrary.WAIT_FOR_FBO_QUEUE_SIZE, 1, BOTH_DEPTH_STENCIL, TYPE_SYST, STORAGE_PRIVATE, 1, 1)
+{
+    private var mDebugMode: Int
+    private var mDebugAllocated = false
+
+    private var debugMesh: MeshQuad? = null
+    private var debugTexture: DistortedTexture? = null
+    private var debugEffects: DistortedEffects? = null
+    private var debugCanvas: Canvas? = null
+    private var debugBitmap: Bitmap? = null
+    private var mPaint: Paint? = null
+    private var debugString: String? = null
+    private var lastTime: Long = 0
+    private var durations: LongArray? = null
+    private var currDuration = 0
+    private var frameNumber = 0
+
+    ///// END DEBUGGING //////////////////////////
+    private var mQueueSize: Int
+    private var mCurRenderedFBO = 0 // During the first FBO_QUEUE_SIZE frames, we blit the very first
+    private var mToBeBlittedFBO = 0 // FBO one we have rendered. Then, we keep blitting the one we
+    private var mFirstCircle = true // rendered FBO_QUEUE_SIZE ago.
+
+    private var mDebugWidth = 0
+    private var mDebugHeight = 0
+    private var mDebugGap = 0
+    private var mDebugTextColor = 0
+    private var mDebugBackColor = 0
+
+    companion object
+    {
+        ///// DEBUGGING ONLY /////////////////////////
+        private const val NUM_FRAMES = 80
+        private const val DEBUG_SCR_FRAC = 0.15f
+        private const val DEBUG_FRAC = 0.5f
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create a new Screen. Initially 1x1 in size.
- * <p>
- * Has to be followed by a 'resizeFBO()' to set the size.
- */
-  public DistortedScreen()
+        private const val DEBUG_MODE_NONE = 0
+        private const val DEBUG_MODE_FPS = 1
+        private const val DEBUG_MODE_FRAME = 2
+
+        private val mMoveVector = Static3D(0f, 0f, 0f)
+        private val mMoveEffect = MatrixEffectMove(mMoveVector)
+    }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Create a new Screen. Initially 1x1 in size.
+     *
+     * Has to be followed by a 'resizeFBO()' to set the size.
+     */
+    init
     {
-    super(DistortedLibrary.WAIT_FOR_FBO_QUEUE_SIZE,1,BOTH_DEPTH_STENCIL, TYPE_SYST, STORAGE_PRIVATE,1,1);
-    mDebugMode = DEBUG_MODE_NONE;
-    mCurRenderedFBO = 0;
-    mToBeBlittedFBO = 0;
-    mFirstCircle = true;
-    mDebugAllocated = false;
-    mQueueSize = -1;
+        mDebugMode = DEBUG_MODE_NONE
+        mQueueSize = -1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Draws all the attached children to this OutputSurface.
- * <p>
- * Must be called from a thread holding OpenGL Context.
- *
- * @param time Current time, in milliseconds. This will be passed to all the Effects stored in the children Nodes.
- * @return Number of objects rendered.
- */
-  public int render(long time)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Draws all the attached children to this OutputSurface.
+     *
+     * Must be called from a thread holding OpenGL Context.
+     *
+     * @param time Current time, in milliseconds. This will be passed to all the Effects stored in the children Nodes.
+     * @return Number of objects rendered.
+     */
+    override fun render(time: Long): Int
     {
-    if( mDebugMode!=DEBUG_MODE_NONE )
-      {
-      int w = getWidth();
-      int h = getHeight();
-
-      if( lastTime==0 ) lastTime = time;
-
-      if( mDebugMode==DEBUG_MODE_FPS )
+        if (mDebugMode!=DEBUG_MODE_NONE)
         {
-        currDuration++;
-        if (currDuration >= NUM_FRAMES) currDuration = 0;
-        durations[NUM_FRAMES] += ((time - lastTime) - durations[currDuration]);
-        durations[currDuration] = time - lastTime;
+            val w = getWidth()
+            val h = getHeight()
 
-        debugString = "" + ((int)(10000.0f*NUM_FRAMES/durations[NUM_FRAMES]))/10.0f;
-        }
-      else if( mDebugMode==DEBUG_MODE_FRAME )
-        {
-        debugString = "" + frameNumber;
-        frameNumber++;
-        }
+            if (lastTime==0L) lastTime = time
 
-      if( !mDebugAllocated )
-        {
-        mDebugAllocated = true;
-        debugString = "";
+            if (mDebugMode==DEBUG_MODE_FPS)
+            {
+                val dur = durations!!
+                currDuration++
+                if (currDuration>=NUM_FRAMES) currDuration = 0
+                dur[NUM_FRAMES] += ((time-lastTime)-dur[currDuration])
+                dur[currDuration] = time-lastTime
 
-        if( mDebugWidth<=0 || mDebugHeight<=0 )
-          {
-          mDebugWidth = (int)(w*DEBUG_SCR_FRAC);
-          mDebugHeight= (int)(w*DEBUG_SCR_FRAC*DEBUG_FRAC);
+                debugString = ""+((10000.0f*NUM_FRAMES/dur[NUM_FRAMES]).toInt())/10.0f
+            }
+            else if (mDebugMode==DEBUG_MODE_FRAME)
+            {
+                debugString = ""+frameNumber
+                frameNumber++
+            }
 
-          if( mDebugWidth<=0 || mDebugHeight<=0 )
+            if (!mDebugAllocated)
             {
-            int width = 100;
-            mDebugWidth = (int)(width*DEBUG_SCR_FRAC);
-            mDebugHeight= (int)(width*DEBUG_SCR_FRAC*DEBUG_FRAC);
+                mDebugAllocated = true
+                debugString = ""
+
+                if (mDebugWidth<=0||mDebugHeight<=0)
+                {
+                    mDebugWidth = (w*DEBUG_SCR_FRAC).toInt()
+                    mDebugHeight = (w*DEBUG_SCR_FRAC*DEBUG_FRAC).toInt()
+
+                    if (mDebugWidth<=0||mDebugHeight<=0)
+                    {
+                        val width = 100
+                        mDebugWidth = (width*DEBUG_SCR_FRAC).toInt()
+                        mDebugHeight = (width*DEBUG_SCR_FRAC*DEBUG_FRAC).toInt()
+                    }
+                }
+
+                debugBitmap = Bitmap.createBitmap(mDebugWidth, mDebugHeight, Bitmap.Config.ARGB_8888)
+                debugMesh = MeshQuad()
+                debugTexture = DistortedTexture()
+                debugTexture!!.setTexture(debugBitmap!!)
+                debugCanvas = Canvas(debugBitmap!!)
+                debugEffects = DistortedEffects()
+                debugEffects!!.apply(MatrixEffectScale(Static3D(mDebugWidth.toFloat(), mDebugHeight.toFloat(), 1f)))
+                debugEffects!!.apply(mMoveEffect)
+
+                mPaint = Paint()
+                mPaint!!.isAntiAlias = true
+                mPaint!!.textAlign = Paint.Align.CENTER
+                mPaint!!.textSize = 0.7f*mDebugHeight
             }
-          }
-
-        debugBitmap = Bitmap.createBitmap( mDebugWidth, mDebugHeight, Bitmap.Config.ARGB_8888);
-        debugMesh = new MeshQuad();
-        debugTexture = new DistortedTexture();
-        debugTexture.setTexture(debugBitmap);
-        debugCanvas = new Canvas(debugBitmap);
-        debugEffects = new DistortedEffects();
-        debugEffects.apply( new MatrixEffectScale( new Static3D(mDebugWidth,mDebugHeight,1) ) );
-        debugEffects.apply(mMoveEffect);
-
-        mPaint = new Paint();
-        mPaint.setAntiAlias(true);
-        mPaint.setTextAlign(Paint.Align.CENTER);
-        mPaint.setTextSize(0.7f*mDebugHeight);
-        }
 
-      mPaint.setColor(mDebugBackColor);
-      debugCanvas.drawRect(0, 0, mDebugWidth, mDebugHeight, mPaint);
-      mPaint.setColor(mDebugTextColor);
-      debugCanvas.drawText(debugString, 0.5f*mDebugWidth, 0.75f*mDebugHeight, mPaint);
-      debugTexture.setTexture(debugBitmap);
+            mPaint!!.color = mDebugBackColor
+            debugCanvas!!.drawRect(0f, 0f, mDebugWidth.toFloat(), mDebugHeight.toFloat(), mPaint!!)
+            mPaint!!.color = mDebugTextColor
+            debugCanvas!!.drawText(debugString!!, 0.5f*mDebugWidth, 0.75f*mDebugHeight, mPaint!!)
+            debugTexture!!.setTexture(debugBitmap!!)
 
-      mMoveVector.set( (-w+mDebugWidth)*0.5f +mDebugGap, (h-mDebugHeight)*0.5f -mDebugGap, 0);
+            mMoveVector.set((-w+mDebugWidth)*0.5f+mDebugGap, (h-mDebugHeight)*0.5f-mDebugGap, 0f)
 
-      lastTime = time;
-      }
+            lastTime = time
+        }
 
-    int numrender = super.render(time,mCurRenderedFBO);
+        val numrender = super.render(time, mCurRenderedFBO)
 
-    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
+        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
 
-    // workaround for the Mali issues: blit the framebuffer we have computed DistortedLibrary.FBO_QUEUE_SIZE
-    // frames ago. Looks like FBO_QUEUE_SIZE=2 solves the issue already but I decided to play it safe and
-    // make it equal to 3.
-    // This of course introduces a delay and uses more memory, but it does not appear to have any effect
-    // on speed. Maybe a slight positive effect if any!
-    setAsInput(mToBeBlittedFBO,0);
+        // workaround for the Mali issues: blit the framebuffer we have computed DistortedLibrary.FBO_QUEUE_SIZE
+        // frames ago. Looks like FBO_QUEUE_SIZE=2 solves the issue already but I decided to play it safe and
+        // make it equal to 3.
+        // This of course introduces a delay and uses more memory, but it does not appear to have any effect
+        // on speed. Maybe a slight positive effect if any!
+        setAsInput(mToBeBlittedFBO, 0)
 
-    GLES30.glColorMask(true,true,true,true);
-    GLES30.glDepthMask(false);
-    GLES30.glDisable(GLES30.GL_STENCIL_TEST);
-    GLES30.glDisable(GLES30.GL_DEPTH_TEST);
-    GLES30.glDisable(GLES30.GL_BLEND);
+        GLES30.glColorMask(true, true, true, true)
+        GLES30.glDepthMask(false)
+        GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+        GLES30.glDisable(GLES30.GL_DEPTH_TEST)
+        GLES30.glDisable(GLES30.GL_BLEND)
 
-    DistortedLibrary.blitPriv(this);
+        DistortedLibrary.blitPriv(this)
 
-    if( mDebugMode!=DEBUG_MODE_NONE && debugTexture.setAsInput())
-      {
-      DistortedLibrary.drawPriv(debugEffects, debugMesh, this, time,0);
-      }
+        if (mDebugMode!=DEBUG_MODE_NONE&&debugTexture!!.setAsInput())
+        {
+            DistortedLibrary.drawPriv(debugEffects!!, debugMesh!!, this, time, 0)
+        }
 
-    if( mQueueSize<=0 )
-      {
-      mQueueSize = DistortedLibrary.getQueueSize();
-      }
+        if (mQueueSize<=0)
+        {
+            mQueueSize = DistortedLibrary.queueSize
+        }
 
-    if( ++mCurRenderedFBO>=mQueueSize )
-      {
-      mCurRenderedFBO = 0;
-      if (mFirstCircle) mFirstCircle = false;
-      }
-    if( !mFirstCircle && ++mToBeBlittedFBO>=mQueueSize )
-      {
-      mToBeBlittedFBO=0;
-      }
+        if (++mCurRenderedFBO>=mQueueSize)
+        {
+            mCurRenderedFBO = 0
+            if (mFirstCircle) mFirstCircle = false
+        }
+        if (!mFirstCircle&&++mToBeBlittedFBO>=mQueueSize)
+        {
+            mToBeBlittedFBO = 0
+        }
 /*
     int err = GLES30.glGetError();
 
@@ -215,63 +215,62 @@ public class DistortedScreen extends DistortedFramebuffer
       DistortedLibrary.logMessage("DistortedScreen: OpenGL error "+err);
       }
 */
-    return numrender+1;
+        return numrender+1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Make the library show Frames Per Second in the upper-left corner.
- * <p>
- */
-  public void showFPS()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Make the library show Frames Per Second in the upper-left corner.
+     */
+    fun showFPS()
     {
-    int w         = getWidth();
-    int width     = (int)(w*DEBUG_SCR_FRAC);
-    int height    = (int)(w*DEBUG_SCR_FRAC*DEBUG_FRAC);
-    int gap       = 5;
-    int textColor = 0xffffffff;
-    int backColor = 0xff000000;
-
-    showFPS(width,height,gap,textColor,backColor);
+        val w = getWidth()
+        val width = (w*DEBUG_SCR_FRAC).toInt()
+        val height = (w*DEBUG_SCR_FRAC*DEBUG_FRAC).toInt()
+        val gap = 5
+        val textColor = -0x1
+        val backColor = -0x1000000
+
+        showFPS(width, height, gap, textColor, backColor)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Make the library show Frames Per Second in the upper-left corner.
- * Set appropriate params.
- * <p>
- */
-  public void showFPS(int width, int height, int gap, int textColor, int backColor)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Make the library show Frames Per Second in the upper-left corner.
+     * Set appropriate params.
+     */
+    fun showFPS(width: Int, height: Int, gap: Int, textColor: Int, backColor: Int)
     {
-    mDebugWidth     = width;
-    mDebugHeight    = height;
-    mDebugGap       = gap;
-    mDebugBackColor = backColor;
-    mDebugTextColor = textColor;
+        mDebugWidth = width
+        mDebugHeight = height
+        mDebugGap = gap
+        mDebugBackColor = backColor
+        mDebugTextColor = textColor
 
-    if( mDebugMode!=DEBUG_MODE_FPS )
-      {
-      mDebugMode = DEBUG_MODE_FPS;
+        if (mDebugMode!=DEBUG_MODE_FPS)
+        {
+            mDebugMode = DEBUG_MODE_FPS
 
-      durations = new long[NUM_FRAMES + 1];
-      currDuration = 0;
+            durations = LongArray(NUM_FRAMES+1)
+            currDuration = 0
+            val dur = durations!!
 
-      for (int i=0; i<NUM_FRAMES+1; i++) durations[i] = 16;  // Assume FPS will be
-      durations[NUM_FRAMES] = NUM_FRAMES * 16;               // close to 1000/16 ~ 60
-      }
+            for (i in 0 until NUM_FRAMES+1) dur[i] = 16 // Assume FPS will be
+
+            dur[NUM_FRAMES] = (NUM_FRAMES*16).toLong() // close to 1000/16 ~ 60
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Make the library show current frame number in the upper-left corner.
- * <p>
- */
-  public void showFrameNumber()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Make the library show current frame number in the upper-left corner.
+     */
+    fun showFrameNumber()
     {
-    if( mDebugMode!=DEBUG_MODE_FRAME )
-      {
-      mDebugMode = DEBUG_MODE_FRAME;
-      frameNumber = 0;
-      }
+        if (mDebugMode!=DEBUG_MODE_FRAME)
+        {
+            mDebugMode = DEBUG_MODE_FRAME
+            frameNumber = 0
+        }
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/DistortedTexture.kt b/src/main/java/org/distorted/library/main/DistortedTexture.kt
index bfcee89..c069af8 100644
--- a/src/main/java/org/distorted/library/main/DistortedTexture.kt
+++ b/src/main/java/org/distorted/library/main/DistortedTexture.kt
@@ -17,195 +17,175 @@
 // License along with this library; if not, write to the Free Software                           //
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+package org.distorted.library.main
 
-package org.distorted.library.main;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.opengl.GLES30;
-import android.opengl.GLUtils;
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.opengl.GLES30
+import android.opengl.GLUtils
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Class which represents a OpenGL Texture object.
- * <p>
+ *
  * Create a Texture of arbitrary size and feed some pixels to it via the setTexture() method.
  */
-public class DistortedTexture extends InternalSurface
-  {
-  private Bitmap mBmp;
-  private boolean mBitmapInverted;
+class DistortedTexture
+@JvmOverloads constructor(type: Int = TYPE_USER) : InternalSurface(NOT_CREATED_YET, 1, 1, type, STORAGE_PRIVATE)
+{
+    private var mBmp: Bitmap? = null
+    private var mBitmapInverted = false
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// We have to vertically flip all the bitmaps passed here via setTexture().
-//
-// Reason: textures read from files are the only objects in OpenGL which have their origins at the
-// upper-left corner. Everywhere else the origin is in the lower-left corner. Thus we have to flip.
-// The alternative solution, namely inverting the y-coordinate of the TexCoord does not really work-
-// i.e. it works only in case of rendering directly to the screen, but if we render to an FBO and
-// then take the FBO and render to screen, (DistortedNode does so!) things get inverted as textures
-// created from FBO have their origins in the lower-left...
-
-  private static Bitmap flipBitmap(Bitmap src)
+    companion object
     {
-    Matrix matrix = new Matrix();
-    matrix.preScale(1.0f,-1.0f);
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        // We have to vertically flip all the bitmaps passed here via setTexture().
+        //
+        // Reason: textures read from files are the only objects in OpenGL which have their origins at the
+        // upper-left corner. Everywhere else the origin is in the lower-left corner. Thus we have to flip.
+        // The alternative solution, namely inverting the y-coordinate of the TexCoord does not really work-
+        // i.e. it works only in case of rendering directly to the screen, but if we render to an FBO and
+        // then take the FBO and render to screen, (DistortedNode does so!) things get inverted as textures
+        // created from FBO have their origins in the lower-left...
+        private fun flipBitmap(src: Bitmap): Bitmap
+        {
+            val matrix = Matrix()
+            matrix.preScale(1.0f, -1.0f)
 
-    return Bitmap.createBitmap(src,0,0,src.getWidth(),src.getHeight(), matrix,true);
+            return Bitmap.createBitmap(src, 0, 0, src.width, src.height, matrix, true)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// must be called from a thread holding OpenGL Context
-
-  public void create()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // must be called from a thread holding OpenGL Context
+    override fun create()
     {
-    if( mBmp!=null )
-      {
-      if( mColorCreated==NOT_CREATED_YET )
+        if (mBmp!=null)
         {
-        mColorCreated = CREATED;
-        GLES30.glGenTextures(1, mColorH, 0);
+            if (mColorCreated==NOT_CREATED_YET)
+            {
+                mColorCreated = CREATED
+                GLES30.glGenTextures(1, mColorH, 0)
+            }
+
+            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH!![0])
+            GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR)
+            GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
+            GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
+            GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
+            GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, if (mBitmapInverted) mBmp else flipBitmap(mBmp!!), 0)
+            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
+
+            mBmp = null
         }
-
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH[0]);
-      GLES30.glTexParameteri ( GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR );
-      GLES30.glTexParameteri ( GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR );
-      GLES30.glTexParameteri ( GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE );
-      GLES30.glTexParameteri ( GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE );
-      GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, mBitmapInverted ? mBmp : flipBitmap(mBmp), 0);
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
-
-      mBmp = null;
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// must be called from a thread holding OpenGL Context
-
-  public void delete()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // must be called from a thread holding OpenGL Context
+    override fun delete()
     {
-    if( mColorH[0]>0 )
-      {
-      GLES30.glDeleteTextures(1, mColorH, 0);
-      mColorH[0] = 0;
-      mColorCreated = NOT_CREATED_YET;
-      }
+        if (mColorH!![0]>0)
+        {
+            GLES30.glDeleteTextures(1, mColorH, 0)
+            mColorH!![0] = 0
+            mColorCreated = NOT_CREATED_YET
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// called from onDestroy(); mark OpenGL assets as 'not created'
-
-  public void recreate()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // called from onDestroy(); mark OpenGL assets as 'not created'
+    override fun recreate()
     {
-    if( mColorCreated!=DONT_CREATE )
-      {
-      mColorCreated = NOT_CREATED_YET;
-      mColorH[0] = 0;
-      }
+        if (mColorCreated!=DONT_CREATE)
+        {
+            mColorCreated = NOT_CREATED_YET
+            mColorH!![0] = 0
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// create SYSTEM or TREE textures (those are just like normal Textures, just hold information
-// that they were autocreated only for the Library's internal purposes (SYSTEM) or for using
-// inside a Tree of DistortedNodes (TREE)
-// SYSTEM surfaces do not get removed in onDestroy().
-
-  public DistortedTexture(int type)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // create SYSTEM or TREE textures (those are just like normal Textures, just hold information
+    // that they were autocreated only for the Library's internal purposes (SYSTEM) or for using
+    // inside a Tree of DistortedNodes (TREE)
+    // SYSTEM surfaces do not get removed in onDestroy().
+    /**
+     * Sets the underlying android.graphics.Bitmap object.
+     *
+     * You can only recycle() the passed Bitmap once the OpenGL context gets created (i.e. after call
+     * to GLSurfaceView.onSurfaceCreated) because only after this point can the Library upload it to the GPU!
+     *
+     * @param bmp The android.graphics.Bitmap object to apply effects to and display.
+     * @return true if successful, false if texture too large.
+     */
+    fun setTexture(bmp: Bitmap): Boolean
     {
-    super(NOT_CREATED_YET,1,1,type,InternalObject.STORAGE_PRIVATE);
-    mBmp= null;
-    }
+        val width = bmp.width
+        val height = bmp.height
+        val max = DistortedLibrary.maxTextureSize
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create an empty texture.
- */
-  public DistortedTexture()
-    {
-    this(TYPE_USER);
+        if( width>max || height>max )
+        {
+            DistortedLibrary.logMessage("DistortedTexture: error, trying to upload too large texture of size $width x $height. Max is $max")
+            return false
+        }
+        else
+        {
+            mBitmapInverted = false
+            mBmp = bmp
+            markForCreation()
+            return true
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the underlying android.graphics.Bitmap object.
- * <p>
- * You can only recycle() the passed Bitmap once the OpenGL context gets created (i.e. after call
- * to GLSurfaceView.onSurfaceCreated) because only after this point can the Library upload it to the GPU!
- *
- * @param bmp The android.graphics.Bitmap object to apply effects to and display.
- * @return true if successful, false if texture too large.
- */
-  public boolean setTexture(Bitmap bmp)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Sets the underlying android.graphics.Bitmap object - this version assumes the object is already
+     * flipped upside down.
+     *
+     * You can only recycle() the passed Bitmap once the OpenGL context gets created (i.e. after call
+     * to GLSurfaceView.onSurfaceCreated) because only after this point can the Library upload it to the GPU!
+     *
+     * @param bmp The (vertically flipped!) android.graphics.Bitmap object to apply effects to and display.
+     * @return true if successful, false if texture too large.
+     */
+    fun setTextureAlreadyInverted(bmp: Bitmap): Boolean
     {
-    int width = bmp.getWidth();
-    int height= bmp.getHeight();
-    int max   = DistortedLibrary.getMaxTextureSize();
-
-    if( width>max || height>max )
-      {
-      DistortedLibrary.logMessage("DistortedTexture: error, trying to upload too large texture of size "+width+" x "+height+". Max is "+max);
-      return false;
-      }
-    else
-      {
-      mBitmapInverted = false;
-      mBmp= bmp;
-      markForCreation();
-      return true;
-      }
-    }
+        val width = bmp.width
+        val height = bmp.height
+        val max = DistortedLibrary.maxTextureSize
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the underlying android.graphics.Bitmap object - this version assumes the object is already
- * flipped upside down.
- * <p>
- * You can only recycle() the passed Bitmap once the OpenGL context gets created (i.e. after call
- * to GLSurfaceView.onSurfaceCreated) because only after this point can the Library upload it to the GPU!
- *
- * @param bmp The (vertically flipped!) android.graphics.Bitmap object to apply effects to and display.
- * @return true if successful, false if texture too large.
- */
-  public boolean setTextureAlreadyInverted(Bitmap bmp)
-    {
-    int width = bmp.getWidth();
-    int height= bmp.getHeight();
-    int max   = DistortedLibrary.getMaxTextureSize();
-
-    if( width>max || height>max )
-      {
-      DistortedLibrary.logMessage("DistortedTexture: error, trying to upload too large texture of size "+width+" x "+height+". Max is "+max);
-      return false;
-      }
-    else
-      {
-      mBitmapInverted = true;
-      mBmp= bmp;
-      markForCreation();
-      return true;
-      }
+        if( width>max || height>max )
+        {
+            DistortedLibrary.logMessage("DistortedTexture: error, trying to upload too large texture of size $width x $height. Max is $max")
+            return false
+        }
+        else
+        {
+            mBitmapInverted = true
+            mBmp = bmp
+            markForCreation()
+            return true
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Paints the Texture with solid color.
- *
- * @param argb The color to paint the Texture with. Example: 0xffff0000 - red.
- */
-  public void setColorARGB(int argb)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Paints the Texture with solid color.
+     *
+     * @param argb The color to paint the Texture with. Example: 0xffff0000 - red.
+     */
+    fun setColorARGB(argb: Int)
     {
-    Paint paint = new Paint();
-    paint.setColor(argb);
-    paint.setStyle(Paint.Style.FILL);
+        val paint = Paint()
+        paint.color = argb
+        paint.style = Paint.Style.FILL
 
-    mBmp = Bitmap.createBitmap(1,1, Bitmap.Config.ARGB_8888);
-    Canvas canvas = new Canvas(mBmp);
-    canvas.drawRect(0,0,1,1,paint);
+        mBmp = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+        val canvas = Canvas(mBmp!!)
+        canvas.drawRect(0f, 0f, 1f, 1f, paint)
 
-    markForCreation();
+        markForCreation()
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/InternalChildrenList.kt b/src/main/java/org/distorted/library/main/InternalChildrenList.kt
index 4d13739..abb3da7 100644
--- a/src/main/java/org/distorted/library/main/InternalChildrenList.kt
+++ b/src/main/java/org/distorted/library/main/InternalChildrenList.kt
@@ -62,7 +62,7 @@ class InternalChildrenList(private val mParent: Parent) : Slave
 
         while( i<index )
         {
-            if( mChildren!![i].bucket>bucket ) break
+            if( mChildren!![i].mBucket > bucket ) break
             i++
         }
 
@@ -78,14 +78,14 @@ class InternalChildrenList(private val mParent: Parent) : Slave
     // we don't need it (given the fixes to renderChildren() )
     private fun addSortingByBuckets(newChild: DistortedNode)
     {
-        val bucket = newChild.bucket
+        val bucket = newChild.mBucket
         var thisSame: Boolean
         var lastSame = false
 
         var i = 0
         while (i<numChildren)
         {
-            thisSame = (mChildren!![i].bucket==bucket)
+            thisSame = (mChildren!![i].mBucket == bucket)
             if( lastSame&&!thisSame ) break
             lastSame = thisSame
             i++
@@ -122,7 +122,7 @@ class InternalChildrenList(private val mParent: Parent) : Slave
     ///////////////////////////////////////////////////////////////////////////////////////////////////
     fun detach(effects: DistortedEffects)
     {
-        val id = effects.id
+        val id = effects.iD
         var node: DistortedNode
         var detached = false
 
@@ -130,7 +130,7 @@ class InternalChildrenList(private val mParent: Parent) : Slave
         {
             node = mChildren!![i]
 
-            if( node.effects.id==id )
+            if( node.mEffects.iD == id )
             {
                 detached = true
                 mJobs.add(Job(DETACH, node))
@@ -150,7 +150,7 @@ class InternalChildrenList(private val mParent: Parent) : Slave
             {
                 job = mJobs[i]
 
-                if( job.type==ATTACH && job.node?.effects===effects )
+                if( job.type==ATTACH && job.node?.mEffects===effects )
                 {
                     mJobs.removeAt(i)
                     break
diff --git a/src/main/java/org/distorted/library/main/InternalOutputSurface.kt b/src/main/java/org/distorted/library/main/InternalOutputSurface.kt
index 1ee0b63..5339a3c 100644
--- a/src/main/java/org/distorted/library/main/InternalOutputSurface.kt
+++ b/src/main/java/org/distorted/library/main/InternalOutputSurface.kt
@@ -91,7 +91,7 @@ abstract class InternalOutputSurface
             val CLEAR_D = 1.0f
             val CLEAR_S = 0
 
-            val queueSize = DistortedLibrary.getQueueSize()
+            val queueSize = DistortedLibrary.queueSize
             val mipmap = getMipmap(quality)
 
             mBuffer[quality] = DistortedFramebuffer(queueSize, 2, BOTH_DEPTH_STENCIL, TYPE_SYST, STORAGE_COMMON, (width*mipmap).toInt(), (height*mipmap).toInt())
@@ -352,7 +352,7 @@ abstract class InternalOutputSurface
         {
             val node = children.getChild(j)
 
-            if (node.surface.setAsInput())
+            if (node.mSurface!!.setAsInput())
             {
                 buffer.setAsOutput()
                 numRenders += queue.preprocess(buffer, node, buffer.mDistance, buffer.mMipmap, buffer.mProjectionMatrix)
@@ -477,7 +477,7 @@ abstract class InternalOutputSurface
         for (i in 0 until numChildren)
         {
             child = children.getChild(i)
-            currQueue = child.effects.queues[3] as EffectQueuePostprocess
+            currQueue = child.mEffects.queues[3] as EffectQueuePostprocess
             currBucket = currQueue.iD
 
             if( currBucket!=0L && lastBucket!=currBucket )
@@ -583,7 +583,7 @@ abstract class InternalOutputSurface
         for (i in 0 until numChildren)
         {
             node = mChildren.getChild(i)
-            newBucket = node.bucket
+            newBucket = node.mBucket
             numRenders += node.renderRecursive(time)
             if (newBucket<oldBucket) mChildren.rearrangeByBuckets(i, newBucket)
             else oldBucket = newBucket
@@ -803,7 +803,7 @@ abstract class InternalOutputSurface
      */
     fun setOrderIndependentTransparency(oit: Boolean)
     {
-        if (DistortedLibrary.getGLSL()>=310)
+        if (DistortedLibrary.gLSL >= 310)
         {
             mRenderWayOIT = oit
         }
@@ -831,7 +831,7 @@ abstract class InternalOutputSurface
      */
     fun setOrderIndependentTransparency(oit: Boolean, initialSize: Float)
     {
-        if (DistortedLibrary.getGLSL()>=310)
+        if (DistortedLibrary.gLSL >= 310)
         {
             mRenderWayOIT = oit
 
diff --git a/src/main/java/org/distorted/library/mesh/DeferredJobs.kt b/src/main/java/org/distorted/library/mesh/DeferredJobs.kt
index dd5ed75..f75ba9a 100644
--- a/src/main/java/org/distorted/library/mesh/DeferredJobs.kt
+++ b/src/main/java/org/distorted/library/mesh/DeferredJobs.kt
@@ -287,7 +287,7 @@ object DeferredJobs
         {
             when (mType)
             {
-                JOB_TYPE_VERTEX        -> DistortedLibrary.adjustVertices(mTarget, mVertexEffects)
+                JOB_TYPE_VERTEX        -> DistortedLibrary.adjustVertices(mTarget, mVertexEffects!!)
                 JOB_TYPE_MATRIX        -> mTarget.applyMatrix(mMatrixEffect!!, mAndAssoc, mEquAssoc)
                 JOB_TYPE_MERGE_TEX     -> mTarget.mergeTexComponentsNow()
                 JOB_TYPE_MERGE_EFF     -> mTarget.mergeEffComponentsNow()
diff --git a/src/main/java/org/distorted/library/program/DistortedProgram.kt b/src/main/java/org/distorted/library/program/DistortedProgram.kt
index c22abcf..1fa24e6 100644
--- a/src/main/java/org/distorted/library/program/DistortedProgram.kt
+++ b/src/main/java/org/distorted/library/program/DistortedProgram.kt
@@ -61,9 +61,69 @@ class DistortedProgram
      */
     @JvmField var mUniform: IntArray? = null
 
+    companion object
+    {
+        const val ATTR_LAYOUT_PNTC: Int = 0
+        const val ATTR_LAYOUT_PTC : Int = 1
+        const val ATTR_LAYOUT_PNT : Int = 2
+        const val ATTR_LAYOUT_PNC : Int = 3
+        const val ATTR_LAYOUT_PT  : Int = 4
+        const val ATTR_LAYOUT_P   : Int = 5
+        const val ATTR_LAYOUT_UNK : Int = 6
+
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        @Throws(FragmentCompilationException::class, VertexCompilationException::class)
+        private fun compileShader(shaderType: Int, shaderSource: String): Int
+        {
+            val shaderHandle = GLES30.glCreateShader(shaderType)
+
+            if (shaderHandle != 0)
+            {
+                GLES30.glShaderSource(shaderHandle, shaderSource)
+                GLES30.glCompileShader(shaderHandle)
+                val compileStatus = IntArray(1)
+                GLES30.glGetShaderiv(shaderHandle, GLES30.GL_COMPILE_STATUS, compileStatus, 0)
+
+                if (compileStatus[0] != GLES30.GL_TRUE)
+                {
+                    val error = GLES30.glGetShaderInfoLog(shaderHandle)
+
+                    GLES30.glDeleteShader(shaderHandle)
+
+                    when (shaderType)
+                    {
+                        GLES30.GL_VERTEX_SHADER   -> throw VertexCompilationException(error)
+                        GLES30.GL_FRAGMENT_SHADER -> throw FragmentCompilationException(error)
+                        else                      -> throw RuntimeException(error)
+                    }
+                }
+            }
+
+            return shaderHandle
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        private fun insertEnabledEffects(code: String, effects: String): String?
+        {
+            val marker = "// ENABLED EFFECTS WILL BE INSERTED HERE"
+            val length = marker.length
+            val place = code.indexOf(marker)
+
+            if (place >= 0)
+            {
+                val beg = code.substring(0, place-1)
+                val end = code.substring(place+length)
+                return beg+effects+end
+            }
+            else DistortedLibrary.logMessage("DistortedProgram: Error: marker string not found in SHADER!")
+
+            return null
+        }
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     @Throws(LinkingException::class)
-    private fun createAndLinkProgram(verHandle: Int, fraHandle: Int, attributes: Array<String>?, feedbackVaryings: Array<String?>?): Int
+    private fun createAndLinkProgram(verHandle: Int, fraHandle: Int, attributes: Array<String>?, feedbackVaryings: Array<String>?): Int
     {
         val programHandle = GLES30.glCreateProgram()
 
@@ -396,7 +456,7 @@ class DistortedProgram
     * @y.exclude
     */
     constructor(vert: InputStream, frag: InputStream, vertHeader: String,
-                fragHeader: String, glslVersion: Int, feedback: Array<String?>?)
+                fragHeader: String, glslVersion: Int, feedback: Array<String>?)
     {
         init(glslVersion)
 
@@ -423,7 +483,7 @@ class DistortedProgram
     * @y.exclude
     */
     constructor(vert: InputStream, frag: InputStream, vertHeader: String, fragHeader: String,
-                enabledVert: String?, enabledFrag: String?, glslVersion: Int, feedback: Array<String?>?)
+                enabledVert: String?, enabledFrag: String?, glslVersion: Int, feedback: Array<String>?)
     {
         init(glslVersion)
 
@@ -508,66 +568,6 @@ class DistortedProgram
         for (i in 0..<mNumAttributes)
             GLES30.glDisableVertexAttribArray(mAttribute!![i])
     }
-
-    companion object
-    {
-        const val ATTR_LAYOUT_PNTC: Int = 0
-        const val ATTR_LAYOUT_PTC : Int = 1
-        const val ATTR_LAYOUT_PNT : Int = 2
-        const val ATTR_LAYOUT_PNC : Int = 3
-        const val ATTR_LAYOUT_PT  : Int = 4
-        const val ATTR_LAYOUT_P   : Int = 5
-        const val ATTR_LAYOUT_UNK : Int = 6
-
-        ///////////////////////////////////////////////////////////////////////////////////////////
-        @Throws(FragmentCompilationException::class, VertexCompilationException::class)
-        private fun compileShader(shaderType: Int, shaderSource: String): Int
-        {
-            val shaderHandle = GLES30.glCreateShader(shaderType)
-
-            if (shaderHandle != 0)
-            {
-                GLES30.glShaderSource(shaderHandle, shaderSource)
-                GLES30.glCompileShader(shaderHandle)
-                val compileStatus = IntArray(1)
-                GLES30.glGetShaderiv(shaderHandle, GLES30.GL_COMPILE_STATUS, compileStatus, 0)
-
-                if (compileStatus[0] != GLES30.GL_TRUE)
-                {
-                    val error = GLES30.glGetShaderInfoLog(shaderHandle)
-
-                    GLES30.glDeleteShader(shaderHandle)
-
-                    when (shaderType)
-                    {
-                        GLES30.GL_VERTEX_SHADER   -> throw VertexCompilationException(error)
-                        GLES30.GL_FRAGMENT_SHADER -> throw FragmentCompilationException(error)
-                        else                      -> throw RuntimeException(error)
-                    }
-                }
-            }
-
-            return shaderHandle
-        }
-
-        ///////////////////////////////////////////////////////////////////////////////////////////
-        private fun insertEnabledEffects(code: String, effects: String): String?
-        {
-            val marker = "// ENABLED EFFECTS WILL BE INSERTED HERE"
-            val length = marker.length
-            val place = code.indexOf(marker)
-
-            if (place >= 0)
-            {
-                val beg = code.substring(0, place-1)
-                val end = code.substring(place+length)
-                return beg+effects+end
-            }
-            else DistortedLibrary.logMessage("DistortedProgram: Error: marker string not found in SHADER!")
-
-            return null
-        }
-    }
 }
 
 
