commit 766d5faa124d5407f04d3c333f7aec2be5a0c15a
Author: LeszekKoltunski <leszek@koltunski.pl>
Date:   Fri May 30 10:38:42 2025 +0200

    transition half of the 'main' package to Kotlin.

diff --git a/src/main/java/org/distorted/library/effect/Effect.kt b/src/main/java/org/distorted/library/effect/Effect.kt
index 6b04355..8ca9314 100644
--- a/src/main/java/org/distorted/library/effect/Effect.kt
+++ b/src/main/java/org/distorted/library/effect/Effect.kt
@@ -128,7 +128,7 @@ abstract class Effect internal constructor (val name: EffectName)
         val l = u.size
         System.arraycopy(u, 0, mUnity, MAX_UNITY_DIM*n, l)
         mUnityDim[n] = l
-        iD = (InternalStackFrameList.getNextEffectID() shl EffectType.LENGTH)+type.ordinal
+        iD = (InternalStackFrameList.nextEffectID shl EffectType.LENGTH)+type.ordinal
     }
 
     ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectBlurred.kt b/src/main/java/org/distorted/library/effect/PostprocessEffectBlurred.kt
index 1581f93..e064441 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectBlurred.kt
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectBlurred.kt
@@ -100,9 +100,9 @@ abstract class PostprocessEffectBlurred(name: EffectName) : PostprocessEffect(na
 
         buffer.setAsOutput()
 
-        val w = buffer.width.toFloat()
-        val h = buffer.height.toFloat()
-        val n = 1.0f-buffer.near
+        val w = buffer.mWidth.toFloat()
+        val h = buffer.mHeight.toFloat()
+        val n = 1.0f-buffer.mNear
 
         val corrW = buffer.widthCorrection
         val corrH = buffer.heightCorrection
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
index c2e2e33..05d4afe 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
@@ -202,7 +202,7 @@ abstract class EffectQueue : Slave
     {
         if (numEffects > 0)
         {
-            val map = InternalStackFrameList.getMap()
+            val map = InternalStackFrameList.map
             val list = ArrayList<Long>()
             for (i in 0..<numEffects) list.add(mEffects[i]!!.iD)
             val id = map[list]
@@ -213,7 +213,7 @@ abstract class EffectQueue : Slave
             }
             else
             {
-                iD = InternalStackFrameList.getNextQueueID()
+                iD = InternalStackFrameList.nextQueueID
                 map[list] = iD
             }
         }
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
index 25548cf..dc4852d 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
@@ -161,8 +161,8 @@ class EffectQueuePostprocess : EffectQueue
     {
         val mesh    = node.mesh
         val effects = node.effects
-        val width   = buffer.width
-        val height  = buffer.height
+        val width   = buffer.mWidth
+        val height  = buffer.mHeight
 
         InternalRenderState.setUpStencilMark(mA != 0.0f)
         InternalRenderState.disableBlending()
diff --git a/src/main/java/org/distorted/library/main/DistortedFramebuffer.java b/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
index e60a3d5..674f884 100644
--- a/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
@@ -35,7 +35,7 @@ public class DistortedFramebuffer extends InternalOutputSurface
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Must be called from a thread holding OpenGL Context
 
-  void create()
+  public void create()
     {
     if( mNumFBOs==DistortedLibrary.WAIT_FOR_FBO_QUEUE_SIZE )
       {
@@ -169,7 +169,7 @@ public class DistortedFramebuffer extends InternalOutputSurface
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Must be called from a thread holding OpenGL Context
 
-  void delete()
+  public void delete()
     {
     if( mColorH[0]>0 )
       {
@@ -201,7 +201,7 @@ public class DistortedFramebuffer extends InternalOutputSurface
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // called from onDestroy(); mark OpenGL assets as 'not created'
 
-  void recreate()
+  public void recreate()
     {
     if( mColorCreated!=DONT_CREATE )
       {
diff --git a/src/main/java/org/distorted/library/main/DistortedLibrary.java b/src/main/java/org/distorted/library/main/DistortedLibrary.java
index 2be608a..4f5c52d 100644
--- a/src/main/java/org/distorted/library/main/DistortedLibrary.java
+++ b/src/main/java/org/distorted/library/main/DistortedLibrary.java
@@ -578,15 +578,17 @@ public class DistortedLibrary
     {
     if( mMainOITProgram!=null )
       {
+      int w = surface.getWidth();
+      int h = surface.getHeight();
       EffectQueue[] queues = effects.getQueues();
 
       EffectQueue.compute(queues, currTime, step);
-      GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
+      GLES30.glViewport(0, 0, w, h );
 
       mMainOITProgram.useProgram();
       GLES30.glUniform1i(mMainOITTextureH, 0);
-      GLES30.glUniform2ui(mMainOITSizeH, surface.mWidth, surface.mHeight);
-      GLES30.glUniform1ui(mMainOITNumRecordsH, (int)(mBufferSize*surface.mWidth*surface.mHeight) );
+      GLES30.glUniform2ui(mMainOITSizeH, w, h );
+      GLES30.glUniform1ui(mMainOITNumRecordsH, (int)(mBufferSize*w*h) );
       mesh.bindVertexAttribs(mMainOITProgram);
       mesh.send(mMainOITProgramH,1);
 
@@ -609,9 +611,10 @@ public class DistortedLibrary
     if( mMainProgram!=null )
       {
       EffectQueue[] queues = effects.getQueues();
-
+      int w = surface.getWidth();
+      int h = surface.getHeight();
       EffectQueue.compute(queues, currTime, step);
-      GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
+      GLES30.glViewport(0, 0, w, h);
 
       mMainProgram.useProgram();
       GLES30.glUniform1i(mMainTextureH, 0);
@@ -636,10 +639,13 @@ public class DistortedLibrary
     {
     if( mBlitProgram!=null )
       {
+      int w = surface.getWidth();
+      int h = surface.getHeight();
+      float n = surface.getNear();
       mBlitProgram.useProgram();
-      GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
+      GLES30.glViewport(0, 0, w, h);
       GLES30.glUniform1i(mBlitTextureH, 0);
-      GLES30.glUniform1f( mBlitDepthH , 1.0f-surface.mNear);
+      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();
@@ -653,7 +659,9 @@ public class DistortedLibrary
     if( mBlitDepthProgram!=null )
       {
       mBlitDepthProgram.useProgram();
-      GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
+      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 );
@@ -770,11 +778,15 @@ public class DistortedLibrary
         }
       }
 
+
+    int w = surface.getWidth();
+    int h = surface.getHeight();
+
     if( mLinkedListSSBO[0]<0 )
       {
       GLES30.glGenBuffers(1,mLinkedListSSBO,0);
 
-      int size = (int)(surface.mWidth*surface.mHeight*(3*mBufferSize+1)*4);
+      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);
@@ -784,22 +796,22 @@ public class DistortedLibrary
 
     // 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*surface.mWidth*surface.mHeight);
+    float overflow = counter/(mBufferSize*w*h);
 
     if( overflow>1.0f )
       {
       mBufferSize *= (int)(overflow+1.0f);
-      int size = (int)(surface.mWidth*surface.mHeight*(3*mBufferSize+1)*4);
+      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, surface.mWidth, surface.mHeight );
+    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, surface.mWidth, surface.mHeight);
+    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();
@@ -812,14 +824,17 @@ public class DistortedLibrary
     {
     if( mOITBuildProgram!=null )
       {
+      int w = surface.getWidth();
+      int h = surface.getHeight();
+      float n = surface.getNear();
       mOITBuildProgram.useProgram();
-      GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
+      GLES30.glViewport(0, 0, w, h);
       GLES30.glUniform1i(mOITBuildTextureH, 0);
       GLES30.glUniform1i(mOITBuildDepthTextureH, 1);
       GLES30.glUniform2f(mOITBuildTexCorrH, corrW, corrH );
-      GLES30.glUniform2ui(mOITBuildSizeH, surface.mWidth, surface.mHeight);
-      GLES30.glUniform1ui(mOITBuildNumRecordsH, (int)(mBufferSize*surface.mWidth*surface.mHeight) );
-      GLES30.glUniform1f(mOITBuildDepthH , 1.0f-surface.mNear);
+      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();
@@ -833,12 +848,15 @@ public class DistortedLibrary
     {
     if( mOITCollapseProgram!=null )
       {
+      int w = surface.getWidth();
+      int h = surface.getHeight();
+      float n = surface.getNear();
       mOITCollapseProgram.useProgram();
-      GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
+      GLES30.glViewport(0, 0, w, h);
       GLES30.glUniform1i(mOITCollapseDepthTextureH, 1);
       GLES30.glUniform2f(mOITCollapseTexCorrH, corrW, corrH );
-      GLES30.glUniform2ui(mOITCollapseSizeH, surface.mWidth, surface.mHeight);
-      GLES30.glUniform1f( mOITCollapseDepthH , 1.0f-surface.mNear);
+      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();
@@ -852,11 +870,14 @@ public class DistortedLibrary
     {
     if( mOITRenderProgram!=null )
       {
+      int w = surface.getWidth();
+      int h = surface.getHeight();
+      float n = surface.getNear();
       mOITRenderProgram.useProgram();
-      GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
+      GLES30.glViewport(0, 0, w, h);
       GLES30.glUniform2f(mOITRenderTexCorrH, corrW, corrH );
-      GLES30.glUniform2ui(mOITRenderSizeH, surface.mWidth, surface.mHeight);
-      GLES30.glUniform1f( mOITRenderDepthH , 1.0f-surface.mNear);
+      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();
diff --git a/src/main/java/org/distorted/library/main/DistortedNode.java b/src/main/java/org/distorted/library/main/DistortedNode.java
index baf3a7e..fe9957f 100644
--- a/src/main/java/org/distorted/library/main/DistortedNode.java
+++ b/src/main/java/org/distorted/library/main/DistortedNode.java
@@ -263,8 +263,8 @@ public class DistortedNode implements InternalChildrenList.Parent
       if( mSurface instanceof DistortedFramebuffer )
         {
         DistortedFramebuffer fbo = (DistortedFramebuffer)mSurface;
-        width = fbo.mWidth;
-        height= fbo.mHeight;
+        width = fbo.getWidth();
+        height= fbo.getHeight();
         }
       else
         {
diff --git a/src/main/java/org/distorted/library/main/DistortedScreen.java b/src/main/java/org/distorted/library/main/DistortedScreen.java
index bbc2deb..3109243 100644
--- a/src/main/java/org/distorted/library/main/DistortedScreen.java
+++ b/src/main/java/org/distorted/library/main/DistortedScreen.java
@@ -105,6 +105,9 @@ public class DistortedScreen extends DistortedFramebuffer
     {
     if( mDebugMode!=DEBUG_MODE_NONE )
       {
+      int w = getWidth();
+      int h = getHeight();
+
       if( lastTime==0 ) lastTime = time;
 
       if( mDebugMode==DEBUG_MODE_FPS )
@@ -129,8 +132,8 @@ public class DistortedScreen extends DistortedFramebuffer
 
         if( mDebugWidth<=0 || mDebugHeight<=0 )
           {
-          mDebugWidth = (int)(mWidth*DEBUG_SCR_FRAC);
-          mDebugHeight= (int)(mWidth*DEBUG_SCR_FRAC*DEBUG_FRAC);
+          mDebugWidth = (int)(w*DEBUG_SCR_FRAC);
+          mDebugHeight= (int)(w*DEBUG_SCR_FRAC*DEBUG_FRAC);
 
           if( mDebugWidth<=0 || mDebugHeight<=0 )
             {
@@ -161,7 +164,7 @@ public class DistortedScreen extends DistortedFramebuffer
       debugCanvas.drawText(debugString, 0.5f*mDebugWidth, 0.75f*mDebugHeight, mPaint);
       debugTexture.setTexture(debugBitmap);
 
-      mMoveVector.set( (-mWidth+mDebugWidth)*0.5f +mDebugGap, (mHeight-mDebugHeight)*0.5f -mDebugGap, 0);
+      mMoveVector.set( (-w+mDebugWidth)*0.5f +mDebugGap, (h-mDebugHeight)*0.5f -mDebugGap, 0);
 
       lastTime = time;
       }
@@ -222,8 +225,9 @@ public class DistortedScreen extends DistortedFramebuffer
  */
   public void showFPS()
     {
-    int width     = (int)(mWidth*DEBUG_SCR_FRAC);
-    int height    = (int)(mWidth*DEBUG_SCR_FRAC*DEBUG_FRAC);
+    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;
diff --git a/src/main/java/org/distorted/library/main/DistortedTexture.java b/src/main/java/org/distorted/library/main/DistortedTexture.java
index 98ff9d7..bfcee89 100644
--- a/src/main/java/org/distorted/library/main/DistortedTexture.java
+++ b/src/main/java/org/distorted/library/main/DistortedTexture.java
@@ -59,7 +59,7 @@ public class DistortedTexture extends InternalSurface
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // must be called from a thread holding OpenGL Context
 
-  void create()
+  public void create()
     {
     if( mBmp!=null )
       {
@@ -84,7 +84,7 @@ public class DistortedTexture extends InternalSurface
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // must be called from a thread holding OpenGL Context
 
-  void delete()
+  public void delete()
     {
     if( mColorH[0]>0 )
       {
@@ -97,7 +97,7 @@ public class DistortedTexture extends InternalSurface
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // called from onDestroy(); mark OpenGL assets as 'not created'
 
-  void recreate()
+  public void recreate()
     {
     if( mColorCreated!=DONT_CREATE )
       {
diff --git a/src/main/java/org/distorted/library/main/InternalBuffer.kt b/src/main/java/org/distorted/library/main/InternalBuffer.kt
index 904f81e..3e86361 100644
--- a/src/main/java/org/distorted/library/main/InternalBuffer.kt
+++ b/src/main/java/org/distorted/library/main/InternalBuffer.kt
@@ -17,207 +17,193 @@
 // 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 java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
+import android.opengl.GLES30
+import java.nio.Buffer
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.nio.FloatBuffer
+import java.nio.IntBuffer
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Implements OpenGL buffer object such as GL_ARRAY_BUFFER or GL_TRANSFORM_FEEDBACK_BUFFER.
  * Main point: deal with Android lifecycle and recreate the buffer on loss of OpenGL context.
- * <p>
+ *
  * Not part of public API, do not document (public only because has to be used in Meshes)
  *
  * @y.exclude
  */
-public class InternalBuffer extends InternalObject
-  {
-  private static final int DONE     = 0;
-  private static final int RECREATE = 1;
-  private static final int UPDATE   = 2;
-
-  private int mStatus, mSize;
-  private final int[] mIndex;
-  private final int mTarget, mUsage;
-  private Buffer mBuffer;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public InternalBuffer()
+class InternalBuffer : InternalObject
+{
+    private var mStatus: Int
+    private var mSize  : Int
+    private val mIndex : IntArray
+    private val mTarget: Int
+    private val mUsage : Int
+    private var mBuffer: Buffer?
+
+    companion object
     {
-    super(InternalObject.TYPE_USER, InternalObject.STORAGE_PRIVATE );
-
-    mIndex  = new int[1];
-    mTarget = GLES30.GL_UNIFORM_BUFFER;
-    mUsage  = GLES30.GL_STATIC_DRAW;
-    mBuffer = null;
-    mSize   = 0;
-    mStatus = RECREATE;
+        private const val DONE     = 0
+        private const val RECREATE = 1
+        private const val UPDATE   = 2
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public InternalBuffer(int target, int usage)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    constructor() : super(TYPE_USER, STORAGE_PRIVATE)
     {
-    super(InternalObject.TYPE_USER, InternalObject.STORAGE_PRIVATE );
-
-    mIndex  = new int[1];
-    mTarget = target;
-    mUsage  = usage;
-    mBuffer = null;
-    mSize   = 0;
-    mStatus = RECREATE;
+        mIndex  = IntArray(1)
+        mTarget = GLES30.GL_UNIFORM_BUFFER
+        mUsage  = GLES30.GL_STATIC_DRAW
+        mBuffer = null
+        mSize   = 0
+        mStatus = RECREATE
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// must be called from a thread holding OpenGL Context.
-
-  public int createImmediatelyFloat(int size, float[] buffer)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    constructor(target: Int, usage: Int) : super(TYPE_USER, STORAGE_PRIVATE)
     {
-    if( (mStatus & RECREATE) != 0 )
-      {
-      mSize= size;
+        mIndex  = IntArray(1)
+        mTarget = target
+        mUsage  = usage
+        mBuffer = null
+        mSize   = 0
+        mStatus = RECREATE
+    }
 
-      if( buffer!=null )
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // must be called from a thread holding OpenGL Context.
+    fun createImmediatelyFloat(size: Int, buffer: FloatArray?): Int
+    {
+        if( (mStatus and RECREATE)!=0 )
         {
-        FloatBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asFloatBuffer();
-        buf.put(buffer).position(0);
-        mBuffer = buf;
+            mSize = size
+
+            if (buffer!=null)
+            {
+                val buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asFloatBuffer()
+                buf.put(buffer).position(0)
+                mBuffer = buf
+            }
+            else
+            {
+                mBuffer = null
+            }
+
+            GLES30.glGenBuffers(1, mIndex, 0)
+            GLES30.glBindBuffer(mTarget, mIndex[0])
+            GLES30.glBufferData(mTarget, mSize, mBuffer, mUsage)
+            GLES30.glBindBuffer(mTarget, 0)
+
+            markWasCreatedImmediately()
         }
-      else
+        else if ((mStatus and UPDATE)!=0)
         {
-        mBuffer = null;
+            updateFloat(buffer)
         }
 
-      GLES30.glGenBuffers( 1, mIndex, 0);
-      GLES30.glBindBuffer( mTarget, mIndex[0]);
-      GLES30.glBufferData( mTarget, mSize, mBuffer, mUsage);
-      GLES30.glBindBuffer( mTarget, 0);
+        mStatus = DONE
 
-      markWasCreatedImmediately();
-      }
-    else if( (mStatus & UPDATE) != 0 )
-      {
-      updateFloat(buffer);
-      }
-
-    mStatus = DONE;
-
-    return mIndex[0];
+        return mIndex[0]
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// must be called from a thread holding OpenGL Context.
-
-  public int createImmediatelyInt(int size, int[] buffer)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // must be called from a thread holding OpenGL Context.
+    fun createImmediatelyInt(size: Int, buffer: IntArray?): Int
     {
-    if( (mStatus & RECREATE) != 0 )
-      {
-      mSize= size;
-
-      if( buffer!=null )
+        if ((mStatus and RECREATE)!=0)
         {
-        IntBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asIntBuffer();
-        buf.put(buffer).position(0);
-        mBuffer = buf;
+            mSize = size
+
+            if (buffer!=null)
+            {
+                val buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asIntBuffer()
+                buf.put(buffer).position(0)
+                mBuffer = buf
+            }
+            else
+            {
+                mBuffer = null
+            }
+
+            GLES30.glGenBuffers(1, mIndex, 0)
+            GLES30.glBindBuffer(mTarget, mIndex[0])
+            GLES30.glBufferData(mTarget, mSize, mBuffer, mUsage)
+            GLES30.glBindBuffer(mTarget, 0)
+
+            markWasCreatedImmediately()
         }
-      else
+        else if ((mStatus and UPDATE)!=0)
         {
-        mBuffer = null;
+            updateInt(buffer)
         }
 
-      GLES30.glGenBuffers( 1, mIndex, 0);
-      GLES30.glBindBuffer( mTarget,  mIndex[0]);
-      GLES30.glBufferData( mTarget, mSize, mBuffer, mUsage);
-      GLES30.glBindBuffer( mTarget,  0);
-
-      markWasCreatedImmediately();
-      }
-    else if( (mStatus & UPDATE) != 0  )
-      {
-      updateInt(buffer);
-      }
-
-    mStatus = DONE;
+        mStatus = DONE
 
-    return mIndex[0];
+        return mIndex[0]
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// buffer non-null!!
-
-  public void updateFloat(float[] buffer)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // buffer non-null!!
+    fun updateFloat(buffer: FloatArray?)
     {
-    ((FloatBuffer)mBuffer).put(buffer).position(0);
+        (mBuffer as FloatBuffer).put(buffer).position(0)
 
-    GLES30.glBindBuffer( mTarget, mIndex[0]);
-    GLES30.glBufferData( mTarget, mSize, mBuffer, mUsage);
-    GLES30.glBindBuffer( mTarget, 0);
+        GLES30.glBindBuffer(mTarget, mIndex[0])
+        GLES30.glBufferData(mTarget, mSize, mBuffer, mUsage)
+        GLES30.glBindBuffer(mTarget, 0)
 
-    mStatus &= (~UPDATE);
+        mStatus = mStatus and (UPDATE.inv())
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// buffer non-null!!
-
-  public void updateInt(int[] buffer)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // buffer non-null!!
+    fun updateInt(buffer: IntArray?)
     {
-    ((IntBuffer)mBuffer).put(buffer).position(0);
+        (mBuffer as IntBuffer).put(buffer).position(0)
 
-    GLES30.glBindBuffer( mTarget, mIndex[0]);
-    GLES30.glBufferData( mTarget, mSize, mBuffer, mUsage);
-    GLES30.glBindBuffer( mTarget, 0);
+        GLES30.glBindBuffer(mTarget, mIndex[0])
+        GLES30.glBufferData(mTarget, mSize, mBuffer, mUsage)
+        GLES30.glBindBuffer(mTarget, 0)
 
-    mStatus &= (~UPDATE);
+        mStatus = mStatus and (UPDATE.inv())
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void invalidate()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun invalidate()
     {
-    mStatus |= UPDATE;
+        mStatus = mStatus or UPDATE
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Intentionally empty, no need to do anything here since it will be done in createImmediatelyXXX().
-// In fact, recreating a Mesh's mVBO1 here - rather than in createImmediatelyFloat - was the reason
-// of the 'disappearing cube after the mesh has changed from nice to simple' bug. I don't quite
-// understand why TBH.
-
-  void create()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Intentionally empty, no need to do anything here since it will be done in createImmediatelyXXX().
+    // In fact, recreating a Mesh's mVBO1 here - rather than in createImmediatelyFloat - was the reason
+    // of the 'disappearing cube after the mesh has changed from nice to simple' bug. I don't quite
+    // understand why TBH.
+    override fun create()
     {
-
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// must be called from a thread holding OpenGL Context
-
-  void delete()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // must be called from a thread holding OpenGL Context
+    override fun delete()
     {
-    GLES30.glDeleteBuffers(1, mIndex, 0);
-    mStatus |= RECREATE;
-    removeFromDone();
+        GLES30.glDeleteBuffers(1, mIndex, 0)
+        mStatus = mStatus or RECREATE
+        removeFromDone()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void recreate()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    override fun recreate()
     {
-    mStatus |= RECREATE;
+        mStatus = mStatus or RECREATE
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// debugging only
-
-  String printDetails()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // debugging only
+    override fun printDetails(): String
     {
-    return getClass().getSimpleName();
+        return javaClass.simpleName
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/InternalChildrenList.kt b/src/main/java/org/distorted/library/main/InternalChildrenList.kt
index 96fcf10..4d13739 100644
--- a/src/main/java/org/distorted/library/main/InternalChildrenList.kt
+++ b/src/main/java/org/distorted/library/main/InternalChildrenList.kt
@@ -17,241 +17,224 @@
 // 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.mesh.MeshBase;
-
-import java.util.ArrayList;
+import org.distorted.library.main.InternalMaster.Slave
+import org.distorted.library.mesh.MeshBase
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class InternalChildrenList implements InternalMaster.Slave
-  {
-  private static final int ATTACH = 0;
-  private static final int DETACH = 1;
-  private static final int DETALL = 2;
-  private static final int SORT   = 3;
-
-  private static class Job
+class InternalChildrenList(private val mParent: Parent) : Slave
+{
+    private class Job(var type: Int, n: DistortedNode?)
     {
-    int type;
-    DistortedNode node;
-
-    Job(int t, DistortedNode n)
-      {
-      type = t;
-      node = n;
-      }
+        var node: DistortedNode? = n
     }
 
-  private final ArrayList<Job> mJobs;
-  private final InternalChildrenList.Parent mParent;
-  private ArrayList<DistortedNode> mChildren;
-  private int mNumChildren;
+    private val mJobs = ArrayList<Job>()
+    private var mChildren: ArrayList<DistortedNode>? = null
 
-  public interface Parent
-    {
-    void adjustIsomorphism();
-    }
+    var numChildren: Int = 0
+        private set
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  InternalChildrenList(InternalChildrenList.Parent parent)
+    interface Parent
     {
-    mParent = parent;
-    mJobs = new ArrayList<>();
-    mChildren = null;
-    mNumChildren = 0;
+        fun adjustIsomorphism()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getNumChildren()
+    companion object
     {
-    return mNumChildren;
+        private const val ATTACH = 0
+        private const val DETACH = 1
+        private const val DETALL = 2
+        private const val SORT   = 3
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  DistortedNode getChild(int index)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getChild(index: Int): DistortedNode
     {
-    return mChildren.get(index);
+        return mChildren!![index]
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void rearrangeByBuckets(int index,long bucket)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun rearrangeByBuckets(index: Int, bucket: Long)
     {
-    DistortedNode child = mChildren.remove(index);
-    int i;
+        val child = mChildren!!.removeAt(index)
+        var i = 0
 
-    for(i=0; i<index; i++)
-      {
-      if( mChildren.get(i).getBucket() > bucket ) break;
-      }
+        while( i<index )
+        {
+            if( mChildren!![i].bucket>bucket ) break
+            i++
+        }
 
-    mChildren.add(i,child);
+        mChildren!!.add(i, child)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Can make this logarithmic but the typical number of children is very small anyway.
-//
-// We want to keep same buckets next to each other, while avoiding changes in order of the children
-// (if possible!)
-// 2022/10/25: removed keeping bucket 0 (i.e. non-postprocessed children) always in the front -
-// we don't need it (given the fixes to renderChildren() )
-
-  private void addSortingByBuckets(DistortedNode newChild)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Can make this logarithmic but the typical number of children is very small anyway.
+    //
+    // We want to keep same buckets next to each other, while avoiding changes in order of the children
+    // (if possible!)
+    // 2022/10/25: removed keeping bucket 0 (i.e. non-postprocessed children) always in the front -
+    // we don't need it (given the fixes to renderChildren() )
+    private fun addSortingByBuckets(newChild: DistortedNode)
     {
-    int i;
-    long bucket = newChild.getBucket();
-    boolean thisSame,lastSame = false;
-
-    for(i=0; i<mNumChildren; i++)
-      {
-      thisSame= (mChildren.get(i).getBucket()==bucket);
-      if( lastSame && !thisSame ) break;
-      lastSame = thisSame;
-      }
-
-    mChildren.add(i,newChild);
-    mNumChildren++;
-    }
+        val bucket = newChild.bucket
+        var thisSame: Boolean
+        var lastSame = false
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        var i = 0
+        while (i<numChildren)
+        {
+            thisSame = (mChildren!![i].bucket==bucket)
+            if( lastSame&&!thisSame ) break
+            lastSame = thisSame
+            i++
+        }
 
-  void attach(DistortedNode node)
-    {
-    node.resetLastTime();
-    mJobs.add(new Job(ATTACH,node));
-    InternalMaster.newSlave(this);
+        mChildren!!.add(i, newChild)
+        numChildren++
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  DistortedNode attach(InternalSurface surface, DistortedEffects effects, MeshBase mesh)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun attach(node: DistortedNode)
     {
-    DistortedNode node = new DistortedNode(surface,effects,mesh);
-    mJobs.add(new Job(ATTACH,node));
-    InternalMaster.newSlave(this);
-    return node;
+        node.resetLastTime()
+        mJobs.add(Job(ATTACH, node))
+        InternalMaster.newSlave(this)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void detach(DistortedNode node)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun attach(surface: InternalSurface, effects: DistortedEffects, mesh: MeshBase): DistortedNode
     {
-    mJobs.add(new Job(DETACH,node));
-    InternalMaster.newSlave(this);
+        val node = DistortedNode(surface, effects, mesh)
+        mJobs.add(Job(ATTACH, node))
+        InternalMaster.newSlave(this)
+        return node
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void detach(DistortedEffects effects)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun detach(node: DistortedNode)
     {
-    long id = effects.getID();
-    DistortedNode node;
-    boolean detached = false;
+        mJobs.add(Job(DETACH, node))
+        InternalMaster.newSlave(this)
+    }
 
-    for(int i=0; i<mNumChildren; i++)
-      {
-      node = mChildren.get(i);
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun detach(effects: DistortedEffects)
+    {
+        val id = effects.id
+        var node: DistortedNode
+        var detached = false
 
-      if( node.getEffects().getID()==id )
+        for (i in 0 until numChildren)
         {
-        detached = true;
-        mJobs.add(new Job(DETACH,node));
-        InternalMaster.newSlave(this);
-        break;
+            node = mChildren!![i]
+
+            if( node.effects.id==id )
+            {
+                detached = true
+                mJobs.add(Job(DETACH, node))
+                InternalMaster.newSlave(this)
+                break
+            }
         }
-      }
 
-    if( !detached )
-      {
-      // if we failed to detach any, it still might be the case that
-      // there's an ATTACH job that we need to cancel.
-      int num = mJobs.size();
-      Job job;
-
-      for(int i=0; i<num; i++)
+        if (!detached)
         {
-        job = mJobs.get(i);
-
-        if( job.type==ATTACH && job.node.getEffects()==effects )
-          {
-          mJobs.remove(i);
-          break;
-          }
+            // if we failed to detach any, it still might be the case that
+            // there's an ATTACH job that we need to cancel.
+            val num = mJobs.size
+            var job: Job
+
+            for (i in 0 until num)
+            {
+                job = mJobs[i]
+
+                if( job.type==ATTACH && job.node?.effects===effects )
+                {
+                    mJobs.removeAt(i)
+                    break
+                }
+            }
         }
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void detachAll()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun detachAll()
     {
-    mJobs.add(new Job(DETALL,null));
-    InternalMaster.newSlave(this);
+        mJobs.add(Job(DETALL, null))
+        InternalMaster.newSlave(this)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * This is not really part of the public API. Has to be public only because it is a part of the
- * DistortedSlave interface, which should really be a class that we extend here instead but
- * Java has no multiple inheritance.
- *
- * @y.exclude
- */
-  public void doWork()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * This is not really part of the public API. Has to be public only because it is a part of the
+     * DistortedSlave interface, which should really be a class that we extend here instead but
+     * Java has no multiple inheritance.
+     *
+     * @y.exclude
+     */
+    override fun doWork()
     {
-    int num = mJobs.size();
-
-    if( num>0 )
-      {
-      Job job;
-      int numChanges=0;
+        val num = mJobs.size
 
-      for(int i=0; i<num; i++)
+        if( num>0 )
         {
-        job = mJobs.remove(0);
-
-        switch(job.type)
-          {
-          case ATTACH: numChanges++;
-                       if( mChildren==null ) mChildren = new ArrayList<>(2);
-                       job.node.setParent(mParent);
-                       addSortingByBuckets(job.node);
-                       break;
-          case DETACH: numChanges++;
-                       if( mNumChildren>0 && mChildren.remove(job.node) )
-                         {
-                         job.node.setParent(null);
-                         mNumChildren--;
-                         }
-                       break;
-          case DETALL: numChanges++;
-                       if( mNumChildren>0 )
-                         {
-                         DistortedNode tmp;
-
-                         for(int j=mNumChildren-1; j>=0; j--)
-                           {
-                           tmp = mChildren.remove(j);
-                           tmp.setParent(null);
-                           }
-
-                         mNumChildren = 0;
-                         }
-                       break;
-          case SORT  : mChildren.remove(job.node);
-                       addSortingByBuckets(job.node);
-                       break;
-          }
+            var job: Job
+            var numChanges = 0
+
+            for (i in 0 until num)
+            {
+                job = mJobs.removeAt(0)
+
+                when( job.type )
+                {
+                    ATTACH ->
+                    {
+                        numChanges++
+                        if( mChildren==null ) mChildren = ArrayList(2)
+                        job.node?.setParent(mParent)
+                        addSortingByBuckets(job.node!!)
+                    }
+
+                    DETACH ->
+                    {
+                        numChanges++
+                        if( numChildren>0 && mChildren!!.remove(job.node) )
+                        {
+                            job.node?.setParent(null)
+                            numChildren--
+                        }
+                    }
+
+                    DETALL ->
+                    {
+                        numChanges++
+                        if( numChildren>0 )
+                        {
+                            var tmp: DistortedNode
+
+                            var j = numChildren-1
+                            while( j>=0 )
+                            {
+                                tmp = mChildren!!.removeAt(j)
+                                tmp.setParent(null)
+                                j--
+                            }
+
+                            numChildren = 0
+                        }
+                    }
+
+                    SORT ->
+                    {
+                        mChildren!!.remove(job.node)
+                        addSortingByBuckets(job.node!!)
+                    }
+                }
+            }
+            if( numChanges>0 ) mParent.adjustIsomorphism()
         }
-      if( numChanges>0 ) mParent.adjustIsomorphism();
-      }
     }
-  }
+}
 
diff --git a/src/main/java/org/distorted/library/main/InternalMaster.kt b/src/main/java/org/distorted/library/main/InternalMaster.kt
index d313190..3dbc645 100644
--- a/src/main/java/org/distorted/library/main/InternalMaster.kt
+++ b/src/main/java/org/distorted/library/main/InternalMaster.kt
@@ -17,90 +17,57 @@
 // 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 java.util.ArrayList;
+package org.distorted.library.main
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * This static class handles assigning jobs to other classes. It does it once, at the beginning of
  * each frame.
- * <p>
+ *
  * Not part of public API, do not document (public only because has to be used in PostprocessEffects)
  *
  * @y.exclude
  */
-public class InternalMaster
-  {
-  /**
-   * Not part of public API, do not document (public only because has to be used in PostprocessEffects)
-   *
-   * @y.exclude
-   */
-  public interface Slave
-    {
-    void doWork();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private InternalMaster()
+object InternalMaster
+{
+    interface Slave
     {
-
+        fun doWork()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static boolean toDo()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun toDo(): Boolean
     {
-    Slave slave;
-    ArrayList<Slave> list = InternalStackFrameList.getSet();
-    int numSlaves = list.size();
+        val list = InternalStackFrameList.mSet
+        val numSlaves = list.size
 
-    try
-      {
-      for(int i=0; i<numSlaves; i++)
+        try
         {
-        slave = list.remove(0);
-        if( slave!=null ) slave.doWork();
+            for (i in 0 until numSlaves) list.removeAt(0).doWork()
         }
-      }
-    catch(IndexOutOfBoundsException ie)
-      {
-      // onDestroy must have been called, ignore
-      }
+        catch (_: IndexOutOfBoundsException) {} // onDestroy must have been called, ignore
 
-    return numSlaves>0;
+        return numSlaves>0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static void newSlave(Slave s)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun newSlave(s: Slave)
     {
-    ArrayList<Slave> list = InternalStackFrameList.getSet();
-    int num = list.size();
-    boolean found = false;
-    Slave tmp;
+        val list = InternalStackFrameList.mSet
+        val num = list.size
+        var found = false
 
-    try
-      {
-      for(int i=0; i<num; i++)
+        try
         {
-        tmp = list.get(i);
-
-        if( tmp==s )
-          {
-          found = true;
-          break;
-          }
+            for (i in 0 until num)
+                if( list[i]===s )
+                {
+                    found = true
+                    break
+                }
         }
-      }
-    catch(IndexOutOfBoundsException ie)
-      {
-      // onDestroy must have been called, ignore
-      }
+        catch (_: IndexOutOfBoundsException) {} // onDestroy must have been called, ignore
 
-    if( !found ) list.add(s);
+        if( !found ) list.add(s)
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/InternalNodeData.kt b/src/main/java/org/distorted/library/main/InternalNodeData.kt
index 42192c4..2a7ba6d 100644
--- a/src/main/java/org/distorted/library/main/InternalNodeData.kt
+++ b/src/main/java/org/distorted/library/main/InternalNodeData.kt
@@ -17,77 +17,52 @@
 // 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 java.util.ArrayList;
+package org.distorted.library.main
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * This is a member of DistortedNode. Makes sure two isomorphic Nodes only get rendered once.
  */
-class InternalNodeData
-  {
-  private final ArrayList<Long> mKey;
-  private int numPointingNodes;
-  private long currTime;
-
-  final long ID;
-  DistortedFramebuffer mFBO;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  InternalNodeData(long id, ArrayList<Long> k)
-    {
-    ID              = id;
-    mKey            = k;
-    numPointingNodes= 1;
-    currTime        =-1;
-    mFBO            = null;
-    }
+class InternalNodeData(@JvmField val ID: Long, private val mKey: ArrayList<Long>)
+{
+    private var numPointingNodes = 1
+    private var currTime: Long
+    @JvmField var mFBO: DistortedFramebuffer? = null
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    init { currTime = -1 }
 
-  static InternalNodeData returnData(ArrayList<Long> list)
+    companion object
     {
-    InternalNodeData data = InternalStackFrameList.getMapID(list);
-
-    if( data!=null )
-      {
-      data.numPointingNodes++;
-      }
-    else
-      {
-      data = InternalStackFrameList.putNewDataToMap(list);
-      }
-
-    return data;
+        @JvmStatic fun returnData(list: ArrayList<Long>): InternalNodeData
+        {
+            var data = InternalStackFrameList.getMapID(list)
+            if (data!=null) data.numPointingNodes++
+            else data = InternalStackFrameList.putNewDataToMap(list)
+            return data
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean removeData()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun removeData(): Boolean
     {
-    if( --numPointingNodes==0 )
-      {
-      InternalStackFrameList.removeKeyFromMap(mKey);
-
-        return mFBO != null;
-      }
+        if( --numPointingNodes==0 )
+        {
+            InternalStackFrameList.removeKeyFromMap(mKey)
+            return mFBO!=null
+        }
 
-    return false;
+        return false
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean notRenderedYetAtThisTime(long time)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun notRenderedYetAtThisTime(time: Long): Boolean
     {
-    if( currTime!=time )
-      {
-      currTime = time;
-      return true;
-      }
+        if( currTime!=time )
+        {
+            currTime = time
+            return true
+        }
 
-    return false;
+        return false
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/InternalObject.kt b/src/main/java/org/distorted/library/main/InternalObject.kt
index b1f042a..478d67b 100644
--- a/src/main/java/org/distorted/library/main/InternalObject.kt
+++ b/src/main/java/org/distorted/library/main/InternalObject.kt
@@ -17,112 +17,91 @@
 // 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
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Any Object which gets uploaded to GPU memory and thus needs to be re-created (transparently to
  * applications!) whenever we lose OpenGL context.
- * <p>
+ *
  * Keep all objects created in a static LinkedList. The point: we need to be able to mark
  * Objects for deletion, and delete all marked Objects later at a convenient time (that's
  * because we can only delete from a thread that holds the OpenGL context so here we provide a
  * framework where one is able to mark for deletion at any time and actual deletion takes place
  * on the next render).
-*/
-abstract class InternalObject
+ */
+abstract class InternalObject(private val mType: Int, private val mStorage: Int)
 {
-  static final int FAILED_TO_CREATE = 1;
-  static final int NOT_CREATED_YET  = 2;
-  static final int DONT_CREATE      = 3;
-  static final int CREATED          = 4;
-
-  static final int TYPE_USER = 0;
-  static final int TYPE_TREE = 1;
-  static final int TYPE_SYST = 2;
-
-  static final int STORAGE_COMMON  = 0;
-  static final int STORAGE_PRIVATE = 1;
-
-  static final int JOB_CREATE = 0;
-  static final int JOB_DELETE = 1;
-
-  private final long mID;
-  private final int mType;
-  private final int mStorage;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  abstract void create();
-  abstract void delete();
-  abstract void recreate();
-  abstract String printDetails();
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Return unique ID of this Object.
+     */
+    val iD: Long = InternalStackFrameList.currentFrame!!.generateID(mType, mStorage)
 
-  void print(String msg)
+    companion object
     {
-    String str = "ID:"+mID;
+        const val FAILED_TO_CREATE: Int = 1
+        const val NOT_CREATED_YET : Int = 2
+        const val DONT_CREATE     : Int = 3
+        const val CREATED         : Int = 4
 
-    switch(mType)
-      {
-      case TYPE_SYST: str+=" SYSTEM "; break;
-      case TYPE_USER: str+=" USER   "; break;
-      case TYPE_TREE: str+=" TREE   "; break;
-      default       : str+=" ERROR? ";
-      }
+        const val TYPE_USER: Int = 0
+        const val TYPE_TREE: Int = 1
+        const val TYPE_SYST: Int = 2
 
-    DistortedLibrary.logMessage("InternalObject: "+str+printDetails()+msg);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        const val STORAGE_COMMON : Int = 0
+        const val STORAGE_PRIVATE: Int = 1
 
-  InternalObject(int type, int storage)
-    {
-    mType    = type;
-    mStorage = storage;
-    mID      = InternalStackFrameList.getCurrentFrame().generateID(mType,mStorage);
+        const val JOB_CREATE: Int = 0
+        const val JOB_DELETE: Int = 1
     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    abstract fun create()
+    abstract fun delete()
+    abstract fun recreate()
+    abstract fun printDetails(): String
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun print(msg: String)
+    {
+        var str = "ID:$iD"
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        str += when (mType)
+        {
+            TYPE_SYST -> " SYSTEM "
+            TYPE_USER -> " USER   "
+            TYPE_TREE -> " TREE   "
+            else      -> " ERROR? "
+        }
 
-  void markWasCreatedImmediately()
-    {
-    InternalStackFrameList.getCurrentFrame().addToDoneList(this,mStorage);
+        DistortedLibrary.logMessage("InternalObject: "+str+printDetails()+msg)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void markForCreation()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun markWasCreatedImmediately()
     {
-    InternalStackFrameList.markFor(this,mID,mStorage,JOB_CREATE);
+        InternalStackFrameList.currentFrame!!.addToDoneList(this, mStorage)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void removeFromDone()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun markForCreation()
     {
-    InternalStackFrameList.removeFromDone(this,mStorage);
+        InternalStackFrameList.markFor(this, iD, mStorage, JOB_CREATE)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
- */
-  public void markForDeletion()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun removeFromDone()
     {
-    InternalStackFrameList.markFor(this,mID,mStorage,JOB_DELETE);
+        InternalStackFrameList.removeFromDone(this, mStorage)
     }
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return unique ID of this Object.
- */
-  public long getID()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
+     */
+    fun markForDeletion()
     {
-    return mID;
+        InternalStackFrameList.markFor(this, iD, mStorage, JOB_DELETE)
     }
 }
diff --git a/src/main/java/org/distorted/library/main/InternalOutputSurface.kt b/src/main/java/org/distorted/library/main/InternalOutputSurface.kt
index 5d47cfc..1ee0b63 100644
--- a/src/main/java/org/distorted/library/main/InternalOutputSurface.kt
+++ b/src/main/java/org/distorted/library/main/InternalOutputSurface.kt
@@ -17,16 +17,18 @@
 // 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.GLES31;
-
-import org.distorted.library.effect.EffectQuality;
-import org.distorted.library.effectqueue.EffectQueuePostprocess;
-import org.distorted.library.helpers.MatrixHelper;
-import org.distorted.library.mesh.MeshBase;
+import android.opengl.GLES30
+import android.opengl.GLES31
+import org.distorted.library.effect.EffectQuality
+import org.distorted.library.effect.EffectQuality.Companion.getMipmap
+import org.distorted.library.effectqueue.EffectQueuePostprocess
+import org.distorted.library.helpers.MatrixHelper.frustum
+import org.distorted.library.helpers.MatrixHelper.ortho
+import org.distorted.library.mesh.MeshBase
+import kotlin.math.max
+import kotlin.math.tan
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -34,947 +36,883 @@ import org.distorted.library.mesh.MeshBase;
  *
  * @y.exclude
  */
-public abstract class InternalOutputSurface extends InternalSurface implements InternalChildrenList.Parent
+abstract class InternalOutputSurface
+    internal constructor(var mWidth: Int, var mHeight: Int, createColor: Int, numfbos: Int, numcolors: Int,
+                         @JvmField var mDepthStencil: Int, fbo: Int, type: Int, storage: Int)
+    : InternalSurface(createColor, numfbos, numcolors, type, storage), InternalChildrenList.Parent
 {
-  public static final int NO_DEPTH_NO_STENCIL = 0;
-  public static final int DEPTH_NO_STENCIL    = 1;
-  public static final int BOTH_DEPTH_STENCIL  = 2;
-
-  static final float DEFAULT_FOV = 60.0f;
-  static final float DEFAULT_NEAR=  0.1f;
-
-  private float mFOV;
-  private final int mTmpFBO;
-
-  private long[] mTime;
-  private float mClearR, mClearG, mClearB, mClearA, mClearDepth;
-  private int mClear, mClearStencil;
-  private boolean mRenderWayOIT;
-  private final InternalChildrenList mChildren;
-
-  // Global buffers used for postprocessing
-  private final static DistortedFramebuffer[] mBuffer= new DistortedFramebuffer[EffectQuality.LENGTH];
-  private final boolean[] mBufferInitialized;
-
-  float mDistance, mNear, mMipmap;
-  float[] mProjectionMatrix;
-  int mDepthStencilCreated, mDepthStencil;
-  int[] mDepthStencilH, mFBOH;
-  int mRealWidth;   // the Surface can be backed up by a texture larger than the viewport we have to it.
-  int mRealHeight;  // mWidth,mHeight are the sizes of the Viewport, those - sizes of the backing up texture.
-  int mCurrFBO;     // internal current FBO (see DistortedLibrary.FBO_QUEUE_SIZE)
-  int mWidth, mHeight;
+    var fOV: Float
+        private set
+    private val mTmpFBO: Int
+
+    private var mTime: LongArray
+    private var mClearR = 0.0f
+    private var mClearG = 0.0f
+    private var mClearB = 0.0f
+    private var mClearA = 0.0f
+    private var mClearDepth = 1.0f
+    private var mClear: Int
+    private var mClearStencil = 0
+    private var mRenderWayOIT = false
+    private val mChildren: InternalChildrenList
+    private val mBufferInitialized: BooleanArray
+
+    @JvmField var mDistance: Float = 0f
+    @JvmField var mNear: Float
+    @JvmField var mMipmap: Float = 1.0f
+    @JvmField var mProjectionMatrix: FloatArray
+    @JvmField var mDepthStencilCreated: Int
+    @JvmField var mDepthStencilH: IntArray? = null
+    @JvmField var mFBOH: IntArray
+    @JvmField var mRealWidth: Int // the Surface can be backed up by a texture larger than the viewport we have to it.
+    @JvmField var mRealHeight: Int // mWidth,mHeight are the sizes of the Viewport, those - sizes of the backing up texture.
+    @JvmField var mCurrFBO: Int = 0 // internal current FBO (see DistortedLibrary.FBO_QUEUE_SIZE)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    companion object
+    {
+        const val NO_DEPTH_NO_STENCIL: Int = 0
+        const val DEPTH_NO_STENCIL   : Int = 1
+        const val BOTH_DEPTH_STENCIL : Int = 2
+
+        const val DEFAULT_FOV : Float = 60.0f
+        const val DEFAULT_NEAR: Float = 0.1f
+
+        // Global buffers used for postprocessing
+        private val mBuffer = arrayOfNulls<DistortedFramebuffer>(EffectQuality.LENGTH)
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun createPostprocessingBuffers(quality: Int, width: Int, height: Int, near: Float)
+        {
+            val CLEAR_R = 1.0f
+            val CLEAR_G = 1.0f
+            val CLEAR_B = 1.0f
+            val CLEAR_A = 0.0f
+            val CLEAR_D = 1.0f
+            val CLEAR_S = 0
+
+            val queueSize = DistortedLibrary.getQueueSize()
+            val mipmap = getMipmap(quality)
+
+            mBuffer[quality] = DistortedFramebuffer(queueSize, 2, BOTH_DEPTH_STENCIL, TYPE_SYST, STORAGE_COMMON, (width*mipmap).toInt(), (height*mipmap).toInt())
+            mBuffer[quality]!!.mMipmap = mipmap
+            mBuffer[quality]!!.mNear = near // copy mNear as well (for blitting- see PostprocessEffect.apply() )
+            mBuffer[quality]!!.glClearColor(CLEAR_R, CLEAR_G, CLEAR_B, CLEAR_A)
+
+            InternalStackFrameList.toDo() // create the FBOs immediately. This is safe as we must be holding the OpenGL context now.
+
+            InternalRenderState.colorDepthStencilOn()
+            GLES30.glClearColor(CLEAR_R, CLEAR_G, CLEAR_B, CLEAR_A)
+            GLES30.glClearDepthf(CLEAR_D)
+            GLES30.glClearStencil(CLEAR_S)
+
+            val colorH = mBuffer[quality]!!.mColorH!!
+
+            for (k in 0 until queueSize)
+            {
+                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mBuffer[quality]!!.mFBOH[k])
+                GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, colorH[2*k+1], 0)
+                GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT or GLES30.GL_STENCIL_BUFFER_BIT)
+                GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, colorH[2*k], 0)
+                GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
+            }
+
+            InternalRenderState.colorDepthStencilRestore()
+
+            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        @JvmStatic
+        @Synchronized
+        fun onPause()
+        {
+            for (j in 0 until EffectQuality.LENGTH) if (mBuffer[j]!=null)
+            {
+                mBuffer[j]!!.markForDeletion()
+                mBuffer[j] = null
+            }
+        }
 
-  InternalOutputSurface(int width, int height, int createColor, int numfbos, int numcolors, int depthStencil, int fbo, int type, int storage)
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun oitClear(buffer: InternalOutputSurface)
+        {
+            val counter = DistortedLibrary.zeroOutAtomic()
+            DistortedLibrary.oitClear(buffer, counter)
+            GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT or GLES31.GL_ATOMIC_COUNTER_BARRIER_BIT)
+        }
+    }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    init
     {
-    super(createColor,numfbos,numcolors,type,storage);
-
-    mRenderWayOIT = false;
-    mCurrFBO      = 0;
-
-    mRealWidth = mWidth = width;
-    mRealHeight= mHeight= height;
-
-    mProjectionMatrix = new float[16];
+        mRealWidth  = mWidth
+        mRealHeight = mHeight
 
-    mFOV = DEFAULT_FOV;
-    mNear= DEFAULT_NEAR;
+        mProjectionMatrix = FloatArray(16)
 
-    mDepthStencilCreated= (depthStencil== NO_DEPTH_NO_STENCIL ? DONT_CREATE:NOT_CREATED_YET);
-    mDepthStencil = depthStencil;
+        fOV = DEFAULT_FOV
+        mNear = DEFAULT_NEAR
 
-    mClearR = 0.0f;
-    mClearG = 0.0f;
-    mClearB = 0.0f;
-    mClearA = 0.0f;
+        mDepthStencilCreated = (if (mDepthStencil==NO_DEPTH_NO_STENCIL) DONT_CREATE else NOT_CREATED_YET)
 
-    mClearDepth = 1.0f;
-    mClearStencil = 0;
-    mClear = GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT;
+        mClear = GLES30.GL_DEPTH_BUFFER_BIT or GLES30.GL_COLOR_BUFFER_BIT
 
-    mMipmap = 1.0f;
+        mChildren = InternalChildrenList(this)
 
-    mChildren = new InternalChildrenList(this);
+        mTmpFBO = fbo
 
-    mTmpFBO = fbo;
+        mFBOH = IntArray(10) // Crashlytics shows the library occasionally crashing in setAsOutput()
+        mTime = LongArray(10) // when trying to read from 'null array' mFBOH. Probably sometimes a
 
-    mFBOH = new int[10];  // Crashlytics shows the library occasionally crashing in setAsOutput()
-    mTime = new long[10]; // when trying to read from 'null array' mFBOH. Probably sometimes a
-                          // a Framebuffer gets created in the wrong moment, just after we did a
-                          // round of create(), but before we start rendering.
-                          // Create an empty FBO and Time here so that setAsOutput() is always safe to call.
+        // a Framebuffer gets created in the wrong moment, just after we did a
+        // round of create(), but before we start rendering.
+        // Create an empty FBO and Time here so that setAsOutput() is always safe to call.
+        mBufferInitialized = BooleanArray(EffectQuality.LENGTH)
 
-    mBufferInitialized = new boolean[EffectQuality.LENGTH];
-
-    allocateStuffDependantOnNumFBOS();
-    createProjection();
+        allocateStuffDependantOnNumFBOS()
+        createProjection()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getWidth()  : Int   = mWidth
+    fun getHeight() : Int   = mHeight
+    fun getNear()   : Float = mNear
 
-  void allocateStuffDependantOnNumFBOS()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun allocateStuffDependantOnNumFBOS()
     {
-    if( mNumFBOs>0 )
-      {
-      mDepthStencilH   = new int[mNumFBOs];
-      mDepthStencilH[0]= 0;
-
-      mFBOH   = new int[mNumFBOs];
-      mFBOH[0]= mTmpFBO;
-
-      mTime = new long[mNumFBOs];
-      for(int i=0; i<mNumFBOs;i++) mTime[i]=0;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createProjection()
-    {
-    if( mWidth>0 && mHeight>1 )
-      {
-      if( mFOV>0.0f )  // perspective projection
+        if (mNumFBOs>0)
         {
-        float a = 2.0f*(float)Math.tan(mFOV*Math.PI/360);
-        float q = mWidth*mNear;
-        float c = mHeight*mNear;
-
-        float left   = -q/2;
-        float right  =  q/2;
-        float bottom = -c/2;
-        float top    =  c/2;
-        float near   =  c/a;
+            mDepthStencilH = IntArray(mNumFBOs)
+            mDepthStencilH!![0] = 0
 
-        mDistance    = mHeight/a;
-        float far    = 2*mDistance-near;
+            mFBOH = IntArray(mNumFBOs)
+            mFBOH[0] = mTmpFBO
 
-        MatrixHelper.frustum(mProjectionMatrix, left, right, bottom, top, near, far);
-        }
-      else             // parallel projection
-        {
-        float left   = -mWidth/2.0f;
-        float right  =  mWidth/2.0f;
-        float bottom = -mHeight/2.0f;
-        float top    =  mHeight/2.0f;
-        float near   = mWidth+mHeight-mHeight*(1.0f-mNear);
-        mDistance    = mWidth+mHeight;
-        float far    = mWidth+mHeight+mHeight*(1.0f-mNear);
-
-        MatrixHelper.ortho(mProjectionMatrix, left, right, bottom, top, near, far);
+            mTime = LongArray(mNumFBOs)
+            for (i in 0 until mNumFBOs) mTime[i] = 0
         }
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void createPostprocessingBuffers(int quality, int width, int height, float near)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun createProjection()
     {
-    final float CLEAR_R = 1.0f;
-    final float CLEAR_G = 1.0f;
-    final float CLEAR_B = 1.0f;
-    final float CLEAR_A = 0.0f;
-    final float CLEAR_D = 1.0f;
-    final int   CLEAR_S = 0;
-
-    final int queueSize = DistortedLibrary.getQueueSize();
-    float mipmap= EffectQuality.getMipmap(quality);
-
-    mBuffer[quality] = new DistortedFramebuffer(queueSize,2,BOTH_DEPTH_STENCIL,TYPE_SYST, STORAGE_COMMON, (int)(width*mipmap), (int)(height*mipmap) );
-    mBuffer[quality].mMipmap = mipmap;
-    mBuffer[quality].mNear = near;  // copy mNear as well (for blitting- see PostprocessEffect.apply() )
-    mBuffer[quality].glClearColor(CLEAR_R, CLEAR_G, CLEAR_B, CLEAR_A);
-
-    InternalStackFrameList.toDo(); // create the FBOs immediately. This is safe as we must be holding the OpenGL context now.
-
-    InternalRenderState.colorDepthStencilOn();
-    GLES30.glClearColor(CLEAR_R, CLEAR_G, CLEAR_B, CLEAR_A);
-    GLES30.glClearDepthf(CLEAR_D);
-    GLES30.glClearStencil(CLEAR_S);
-
-    for(int k=0; k<queueSize; k++)
-      {
-      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mBuffer[quality].mFBOH[k]);
-      GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mBuffer[quality].mColorH[2*k+1], 0);
-      GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_STENCIL_BUFFER_BIT);
-      GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mBuffer[quality].mColorH[2*k  ], 0);
-      GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
-      }
-
-    InternalRenderState.colorDepthStencilRestore();
-
-    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static synchronized void onPause()
-    {
-    for (int j=0; j<EffectQuality.LENGTH; j++)
-      if( mBuffer[j]!=null )
+        if( mWidth>0 && mHeight>1 )
         {
-        mBuffer[j].markForDeletion();
-        mBuffer[j] = null;
+            if (fOV>0.0f)  // perspective projection
+            {
+                val a = 2.0f*tan(fOV*Math.PI/360).toFloat()
+                val q = mWidth*mNear
+                val c = mHeight*mNear
+
+                val left   = -q/2
+                val right  = q/2
+                val bottom = -c/2
+                val top    = c/2
+                val near   = c/a
+
+                mDistance = mHeight/a
+                val far = 2*mDistance-near
+
+                frustum(mProjectionMatrix, left, right, bottom, top, near, far)
+            }
+            else  // parallel projection
+            {
+                val left   =-mWidth/2.0f
+                val right  = mWidth/2.0f
+                val bottom =-mHeight/2.0f
+                val top    = mHeight/2.0f
+                val near   = mWidth+mHeight-mHeight*(1.0f-mNear)
+                mDistance  = (mWidth+mHeight).toFloat()
+                val far    = mWidth+mHeight+mHeight*(1.0f-mNear)
+
+                ortho(mProjectionMatrix, left, right, bottom, top, near, far)
+            }
         }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int blitWithDepth(long currTime, InternalOutputSurface buffer, int fbo)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun blitWithDepth(currTime: Long, buffer: InternalOutputSurface, fbo: Int): Int
     {
-    GLES30.glViewport(0, 0, mWidth, mHeight);
-    setAsOutput(currTime);
-    GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
-    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, buffer.mColorH[2*fbo]);
-    GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
-    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, buffer.mDepthStencilH[fbo]);
+        val colorH = buffer.mColorH!!
 
-    GLES30.glDisable(GLES30.GL_STENCIL_TEST);
-    GLES30.glStencilMask(0x00);
+        GLES30.glViewport(0, 0, mWidth, mHeight)
+        setAsOutput(currTime)
+        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
+        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, colorH[2*fbo])
+        GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
+        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, buffer.mDepthStencilH!![fbo])
 
-    DistortedLibrary.blitDepthPriv(this, buffer.getWidthCorrection(), buffer.getHeightCorrection() );
-    GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
-    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
-    GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
-    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
+        GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+        GLES30.glStencilMask(0x00)
 
-    // clear buffers
-    GLES30.glStencilMask(0xff);
-    GLES30.glDepthMask(true);
-    GLES30.glColorMask(true,true,true,true);
-    GLES30.glClearColor(buffer.mClearR,buffer.mClearG,buffer.mClearB,buffer.mClearA);
-    GLES30.glClearDepthf(buffer.mClearDepth);
-    GLES30.glClearStencil(buffer.mClearStencil);
+        DistortedLibrary.blitDepthPriv(this, buffer.widthCorrection, buffer.heightCorrection)
+        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
+        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
+        GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
+        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
 
-    buffer.setAsOutput();
-    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, buffer.mColorH[2*fbo+1], 0);
-    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT|GLES30.GL_DEPTH_BUFFER_BIT|GLES30.GL_STENCIL_BUFFER_BIT);
-    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, buffer.mColorH[2*fbo  ], 0);
-    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
+        // clear buffers
+        GLES30.glStencilMask(0xff)
+        GLES30.glDepthMask(true)
+        GLES30.glColorMask(true, true, true, true)
+        GLES30.glClearColor(buffer.mClearR, buffer.mClearG, buffer.mClearB, buffer.mClearA)
+        GLES30.glClearDepthf(buffer.mClearDepth)
+        GLES30.glClearStencil(buffer.mClearStencil)
 
-    return 1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        buffer.setAsOutput()
+        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, colorH[2*fbo+1], 0)
+        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT or GLES30.GL_STENCIL_BUFFER_BIT)
+        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, colorH[2*fbo], 0)
+        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
 
-  private static void oitClear(InternalOutputSurface buffer)
-    {
-    int counter = DistortedLibrary.zeroOutAtomic();
-    DistortedLibrary.oitClear(buffer,counter);
-    GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT|GLES31.GL_ATOMIC_COUNTER_BARRIER_BIT);
+        return 1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int oitBuild(long time, InternalOutputSurface buffer, int fbo)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun oitBuild(time: Long, buffer: InternalOutputSurface, fbo: Int): Int
     {
-    GLES30.glViewport(0, 0, mWidth, mHeight);
-    setAsOutput(time);
-    GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
-    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, buffer.mColorH[2*fbo]);
-    GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
-    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, buffer.mDepthStencilH[fbo]);
+        GLES30.glViewport(0, 0, mWidth, mHeight)
+        setAsOutput(time)
+        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
+        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, buffer.mColorH!![2*fbo])
+        GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
+        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, buffer.mDepthStencilH!![fbo])
 
-    InternalRenderState.colorDepthStencilOn();
-    InternalRenderState.enableDepthTest();
+        InternalRenderState.colorDepthStencilOn()
+        InternalRenderState.enableDepthTest()
 
-    DistortedLibrary.oitBuild(this, buffer.getWidthCorrection(), buffer.getHeightCorrection() );
-    GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
-    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
-    GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
-    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
+        DistortedLibrary.oitBuild(this, buffer.widthCorrection, buffer.heightCorrection)
+        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
+        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
+        GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
+        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
 
-    InternalRenderState.colorDepthStencilRestore();
-    InternalRenderState.restoreDepthTest();
+        InternalRenderState.colorDepthStencilRestore()
+        InternalRenderState.restoreDepthTest()
 
-    return 1;
+        return 1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// two phases: 1. collapse the SSBO 2. blend the ssbo's color
-
-  private int oitRender(long currTime, int fbo)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // two phases: 1. collapse the SSBO 2. blend the ssbo's color
+    private fun oitRender(currTime: Long, fbo: Int): Int
     {
-    float corrW = getWidthCorrection();
-    float corrH = getHeightCorrection();
+        val corrW = widthCorrection
+        val corrH = heightCorrection
+        val depthStencil = mDepthStencilH!!
 
-    // Do the Collapse Pass only if we do have a Depth attachment.
-    // Otherwise there's no point (in fact we then would create a feedback loop!)
-
-    if( mDepthStencilH[fbo] != 0 )
-      {
-      GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
-      GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mDepthStencilH[fbo]);
-      InternalRenderState.switchOffColorDepthStencil();
-      DistortedLibrary.oitCollapse(this, corrW, corrH);
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
-      }
+        // Do the Collapse Pass only if we do have a Depth attachment.
+        // Otherwise there's no point (in fact we then would create a feedback loop!)
+        if( depthStencil[fbo]!=0 )
+        {
+            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
+            GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
+            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, depthStencil[fbo])
+            InternalRenderState.switchOffColorDepthStencil()
+            DistortedLibrary.oitCollapse(this, corrW, corrH)
+            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
+        }
 
-    setAsOutput(currTime);
-    InternalRenderState.switchColorDepthOnStencilOff();
-    DistortedLibrary.oitRender(this, corrW, corrH);
-    InternalRenderState.restoreColorDepthStencil();
+        setAsOutput(currTime)
+        InternalRenderState.switchColorDepthOnStencilOff()
+        DistortedLibrary.oitRender(this, corrW, corrH)
+        InternalRenderState.restoreColorDepthStencil()
 
-    return 1;
+        return 1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void clear()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun clear()
     {
-    InternalRenderState.colorDepthStencilOn();
-    GLES30.glClearColor(mClearR, mClearG, mClearB, mClearA);
-    GLES30.glClearDepthf(mClearDepth);
-    GLES30.glClearStencil(mClearStencil);
-    GLES30.glClear(mClear);
-    InternalRenderState.colorDepthStencilRestore();
+        InternalRenderState.colorDepthStencilOn()
+        GLES30.glClearColor(mClearR, mClearG, mClearB, mClearA)
+        GLES30.glClearDepthf(mClearDepth)
+        GLES30.glClearStencil(mClearStencil)
+        GLES30.glClear(mClear)
+        InternalRenderState.colorDepthStencilRestore()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setCurrFBO(int fbo)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setCurrFBO(fbo: Int)
     {
-    mCurrFBO = fbo;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Render all children from the current bucket to the buffer, apply the postprocessing once to the
-// whole buffer (queue.postprocess) and merge it to 'this' (oitBuild or blitWithDepth depending on
-// the type of rendering)
-
-  private int accumulateAndBlit(EffectQueuePostprocess queue, InternalChildrenList children, DistortedFramebuffer buffer,
-                                int begIndex, int endIndex, boolean isFinal, long time, int fbo, boolean oit )
-    {
-    int numRenders = 0;
-
-    for(int j=begIndex; j<endIndex; j++)
-       {
-       DistortedNode node = children.getChild(j);
-
-       if( node.getSurface().setAsInput() )
-         {
-         buffer.setAsOutput();
-         numRenders += queue.preprocess( buffer, node, buffer.mDistance, buffer.mMipmap, buffer.mProjectionMatrix );
-         }
-       }
-    numRenders += queue.postprocess(buffer);
-
-    if( oit )
-      {
-      numRenders += oitBuild(time, buffer, fbo);
-      GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT | GLES31.GL_ATOMIC_COUNTER_BARRIER_BIT);
-      buffer.clearBuffer(fbo);
-      }
-    else
-      {
-      numRenders += blitWithDepth(time, buffer, fbo);
-      if( !isFinal ) buffer.clearBuffer(fbo);
-      }
-
-    return numRenders;
+        mCurrFBO = fbo
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int renderChildToThisOrToBuffer(DistortedNode child, DistortedFramebuffer buffer, long time, boolean oit, boolean toThis)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Render all children from the current bucket to the buffer, apply the postprocessing once to the
+    // whole buffer (queue.postprocess) and merge it to 'this' (oitBuild or blitWithDepth depending on
+    // the type of rendering)
+    private fun accumulateAndBlit(queue: EffectQueuePostprocess, children: InternalChildrenList, buffer: DistortedFramebuffer,
+                                  begIndex: Int, endIndex: Int, isFinal: Boolean, time: Long, fbo: Int, oit: Boolean): Int
     {
-    int numRenders;
+        var numRenders = 0
 
-    if( toThis )
-      {
-      setAsOutput(time);
-
-      if( oit )
+        for (j in begIndex until endIndex)
         {
-        numRenders = child.drawOIT(time, this);
-        GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT | GLES31.GL_ATOMIC_COUNTER_BARRIER_BIT);
+            val node = children.getChild(j)
+
+            if (node.surface.setAsInput())
+            {
+                buffer.setAsOutput()
+                numRenders += queue.preprocess(buffer, node, buffer.mDistance, buffer.mMipmap, buffer.mProjectionMatrix)
+            }
         }
-      else
+        numRenders += queue.postprocess(buffer)
+
+        if (oit)
         {
-        numRenders = child.draw(time, this);
+            numRenders += oitBuild(time, buffer, fbo)
+            GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT or GLES31.GL_ATOMIC_COUNTER_BARRIER_BIT)
+            buffer.clearBuffer(fbo)
         }
-      }
-    else
-      {
-      buffer.setAsOutput(time);
-      numRenders = child.drawNoBlend(time, buffer);
-      }
-
-    return numRenders;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// The postprocessing buffers mBuffer[] are generally speaking too large (there's just one static
-// set of them) so before we use them for output, we need to adjust the Viewport as if they were
-// smaller. That takes care of outputting pixels to them. When we use them as input, we have to
-// adjust the texture coords - see the get{Width|Height}Correction functions.
-//
-// Also, adjust the Buffers so their Projection is the same like the surface we are supposed to be
-// rendering to.
-
-  private void clonePostprocessingViewportAndProjection(InternalOutputSurface surface, InternalOutputSurface from)
-    {
-    if( surface.mWidth != from.mWidth || surface.mHeight != from.mHeight ||
-        surface.mFOV   != from.mFOV   || surface.mNear   != from.mNear    )
-      {
-      surface.mWidth  = (int)(from.mWidth *surface.mMipmap);
-      surface.mHeight = (int)(from.mHeight*surface.mMipmap);
-      surface.mFOV    = from.mFOV;
-      surface.mNear   = from.mNear;  // Near plane is independent of the mipmap level
-
-      surface.createProjection();
-
-      int maxw = Math.max(surface.mWidth , surface.mRealWidth );
-      int maxh = Math.max(surface.mHeight, surface.mRealHeight);
-
-      if (maxw > surface.mRealWidth || maxh > surface.mRealHeight)
+        else
         {
-        surface.mRealWidth = maxw;
-        surface.mRealHeight = maxh;
-
-        surface.recreate();
-        surface.create();
+            numRenders += blitWithDepth(time, buffer, fbo)
+            if (!isFinal) buffer.clearBuffer(fbo)
         }
-      }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        return numRenders
+    }
 
-  private DistortedFramebuffer initializeBuffer(EffectQueuePostprocess queue, int fbo )
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun renderChildToThisOrToBuffer(child: DistortedNode, buffer: DistortedFramebuffer?, time: Long, oit: Boolean, toThis: Boolean): Int
     {
-    int currQuality = queue.getQuality();
-    if( mBuffer[currQuality]==null ) createPostprocessingBuffers(currQuality, mWidth, mHeight, mNear);
-    mBuffer[currQuality].setCurrFBO(fbo);
+        val numRenders: Int
 
-    if( !mBufferInitialized[currQuality] )
-      {
-      mBufferInitialized[currQuality] = true;
-      clonePostprocessingViewportAndProjection(mBuffer[currQuality],this);
-      }
-
-    return mBuffer[currQuality];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Render all children, one by one. If there are no postprocessing effects, just render to THIS.
-// Otherwise, render to a buffer and on each change of Postprocessing Bucket, apply the postprocessing
-// to a whole buffer (lastQueue.postprocess) and merge it (this.oitBuild or blitWithDepth - depending
-// on the type of rendering)
-
-  int renderChildren(long time, int numChildren, InternalChildrenList children, int fbo, boolean oit)
-    {
-    int numRenders=0, bucketChange=0;
-    DistortedNode child;
-    DistortedFramebuffer buffer=null;
-    EffectQueuePostprocess lastQueue=null, currQueue;
-    long lastBucket=0, currBucket;
-    boolean toThis=false;
-
-    setCurrFBO(fbo);
-    if( numChildren==0 ) setAsOutput(time);
-    if( oit && numChildren>0 ) oitClear(this);
-    for(int i=0; i<EffectQuality.LENGTH; i++) mBufferInitialized[i]=false;
-
-    for(int i=0; i<numChildren; i++)
-      {
-      child = children.getChild(i);
-      currQueue = (EffectQueuePostprocess)child.getEffects().getQueues()[3];
-      currBucket= currQueue.getID();
-
-      if( currBucket!=0 && lastBucket!=currBucket )
+        if (toThis)
         {
-        buffer = initializeBuffer(currQueue,fbo);
-        if( lastBucket!=0 ) numRenders += accumulateAndBlit(lastQueue,children,buffer,bucketChange,i,false,time,fbo,oit);
-        bucketChange= i;
-        toThis = currQueue.getRenderDirectly();
+            setAsOutput(time)
+
+            if (oit)
+            {
+                numRenders = child.drawOIT(time, this)
+                GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT or GLES31.GL_ATOMIC_COUNTER_BARRIER_BIT)
+            }
+            else
+            {
+                numRenders = child.draw(time, this)
+            }
+        }
+        else
+        {
+            buffer!!.setAsOutput(time)
+            numRenders = child.drawNoBlend(time, buffer)
         }
-      numRenders += renderChildToThisOrToBuffer(child,buffer,time,oit,currBucket==0 || toThis);
-      if( currBucket!=0 && i==numChildren-1 ) numRenders += accumulateAndBlit(currQueue,children,buffer,bucketChange,numChildren,true,time,fbo,oit);
-
-      lastQueue = currQueue;
-      lastBucket= currBucket;
-      }
-
-    if( oit && numChildren>0 ) numRenders += oitRender(time, fbo);  // merge the OIT linked list
 
-    return numRenders;
+        return numRenders
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of the public API.
- *
- * @y.exclude
- */
-  public void adjustIsomorphism() { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of the Public API.
- *
- * @y.exclude
- */
-  public float getWidthCorrection()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // The postprocessing buffers mBuffer[] are generally speaking too large (there's just one static
+    // set of them) so before we use them for output, we need to adjust the Viewport as if they were
+    // smaller. That takes care of outputting pixels to them. When we use them as input, we have to
+    // adjust the texture coords - see the get{Width|Height}Correction functions.
+    //
+    // Also, adjust the Buffers so their Projection is the same like the surface we are supposed to be
+    // rendering to.
+    private fun clonePostprocessingViewportAndProjection(surface: InternalOutputSurface, from: InternalOutputSurface)
     {
-    return (float)mWidth/mRealWidth;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of the Public API.
- *
- * @y.exclude
- */
-  public float getHeightCorrection()
-    {
-    return (float)mHeight/mRealHeight;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        if( surface.mWidth!=from.mWidth || surface.mHeight!=from.mHeight || surface.fOV!=from.fOV || surface.mNear!=from.mNear )
+        {
+            surface.mWidth = (from.mWidth*surface.mMipmap).toInt()
+            surface.mHeight= (from.mHeight*surface.mMipmap).toInt()
+            surface.fOV    = from.fOV
+            surface.mNear  = from.mNear // Near plane is independent of the mipmap level
 
-  void clearBuffer(int fbo)
-    {
-    InternalRenderState.colorDepthStencilOn();
+            surface.createProjection()
 
-    GLES30.glClearColor(mClearR, mClearG, mClearB, mClearA);
-    GLES30.glClearDepthf(mClearDepth);
-    GLES30.glClearStencil(mClearStencil);
+            val maxw = max(surface.mWidth.toDouble(), surface.mRealWidth.toDouble()).toInt()
+            val maxh = max(surface.mHeight.toDouble(), surface.mRealHeight.toDouble()).toInt()
 
-    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[fbo]);
-    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mColorH[2*fbo+1], 0);
-    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT|GLES30.GL_DEPTH_BUFFER_BIT|GLES30.GL_STENCIL_BUFFER_BIT);
-    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mColorH[2*fbo  ], 0);
-    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
+            if( maxw>surface.mRealWidth || maxh>surface.mRealHeight )
+            {
+                surface.mRealWidth = maxw
+                surface.mRealHeight = maxh
 
-    InternalRenderState.colorDepthStencilRestore();
+                surface.recreate()
+                surface.create()
+            }
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setAsOutput(long time)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun initializeBuffer(queue: EffectQueuePostprocess, fbo: Int): DistortedFramebuffer?
     {
-    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[mCurrFBO]);
+        val currQuality = queue.quality
+        if (mBuffer[currQuality]==null) createPostprocessingBuffers(currQuality, mWidth, mHeight, mNear)
+        mBuffer[currQuality]!!.setCurrFBO(fbo)
 
-    if( mTime[mCurrFBO]!=time )
-      {
-      mTime[mCurrFBO] = time;
-      clear();
-      }
-    }
+        if (!mBufferInitialized[currQuality])
+        {
+            mBufferInitialized[currQuality] = true
+            clonePostprocessingViewportAndProjection(mBuffer[currQuality]!!, this)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Draws all the attached children to this OutputSurface's 0th FBO.
- * <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)
-    {
-    return render(time,0);
+        return mBuffer[currQuality]
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * 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.
- * @param fbo The surface can have many FBOs backing it up - render this to FBO number 'fbo'.
- * @return Number of objects rendered.
- */
-  public int render(long time, int fbo)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Render all children, one by one. If there are no postprocessing effects, just render to THIS.
+    // Otherwise, render to a buffer and on each change of Postprocessing Bucket, apply the postprocessing
+    // to a whole buffer (lastQueue.postprocess) and merge it (this.oitBuild or blitWithDepth - depending
+    // on the type of rendering)
+    fun renderChildren(time: Long, numChildren: Int, children: InternalChildrenList, fbo: Int, oit: Boolean): Int
     {
-    InternalMaster.toDo();
-    InternalStackFrameList.toDo();
-    InternalRenderState.reset();
+        var numRenders = 0
+        var bucketChange = 0
+        var child: DistortedNode?
+        var buffer: DistortedFramebuffer? = null
+        var lastQueue: EffectQueuePostprocess? = null
+        var currQueue: EffectQueuePostprocess
+        var lastBucket: Long = 0
+        var currBucket: Long
+        var toThis = false
 
-    int numRenders=0, numChildren = mChildren.getNumChildren();
-    DistortedNode node;
-    long oldBucket=0, newBucket;
+        setCurrFBO(fbo)
+        if (numChildren==0) setAsOutput(time)
+        if (oit&&numChildren>0) oitClear(this)
+        for (i in 0 until EffectQuality.LENGTH) mBufferInitialized[i] = false
 
-    for(int i=0; i<numChildren; i++)
-      {
-      node = mChildren.getChild(i);
-      newBucket = node.getBucket();
-      numRenders += node.renderRecursive(time);
-      if( newBucket<oldBucket ) mChildren.rearrangeByBuckets(i,newBucket);
-      else oldBucket=newBucket;
-      }
+        for (i in 0 until numChildren)
+        {
+            child = children.getChild(i)
+            currQueue = child.effects.queues[3] as EffectQueuePostprocess
+            currBucket = currQueue.iD
+
+            if( currBucket!=0L && lastBucket!=currBucket )
+            {
+                buffer = initializeBuffer(currQueue, fbo)
+                if (lastBucket!=0L) numRenders += accumulateAndBlit(lastQueue!!, children, buffer!!, bucketChange, i, false, time, fbo, oit)
+                bucketChange = i
+                toThis = currQueue.renderDirectly
+            }
+            numRenders += renderChildToThisOrToBuffer(child, buffer, time, oit, currBucket==0L||toThis)
+            if( currBucket!=0L && i==numChildren-1 ) numRenders += accumulateAndBlit(currQueue, children, buffer!!, bucketChange, numChildren, true, time, fbo, oit)
+
+            lastQueue = currQueue
+            lastBucket = currBucket
+        }
 
-    numRenders += renderChildren(time,numChildren,mChildren,fbo, mRenderWayOIT);
+        if( oit && numChildren>0 ) numRenders += oitRender(time, fbo) // merge the OIT linked list
 
-    return numRenders;
+        return numRenders
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Recursively print all the effect queues attached to the children Nodes and to this Node.
- */
-  public void debug()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of the public API.
+     *
+     * @y.exclude
+     */
+    override fun adjustIsomorphism()
     {
-    int numChildren = mChildren.getNumChildren();
-
-    for(int i=0; i<numChildren; i++)
-      {
-      DistortedNode node = mChildren.getChild(i);
-      node.debug(0);
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Bind this Surface as a Framebuffer we can render to.
- * <p>
- * This version does not attempt to clear anything.
- */
-  public void setAsOutput()
-    {
-    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[mCurrFBO]);
-    }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    val widthCorrection:  Float get() = mWidth.toFloat()/mRealWidth
+    val heightCorrection: Float get() = mHeight.toFloat()/mRealHeight
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the Near plane of the Projection included in the Surface.
- *
- * @return the Near plane.
- */
-  public float getNear()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun clearBuffer(fbo: Int)
     {
-    return mNear;
-    }
+        val colorH = mColorH!!
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Set mipmap level.
- * <p>
- * Trick for speeding up your renders - one can create a pyramid of OutputSurface objects, each next
- * one some constant FACTOR smaller than the previous (0.5 is the common value), then set the Mipmap
- * Level of the i-th object to be FACTOR^i (we start counting from 0). When rendering any scene into
- * such prepared OutputSurface, the library will make sure to scale any Effects used so that the end
- * scene will end up looking identical no matter which object we render to. Identical, that is, except
- * for the loss of quality and gain in speed associated with rendering to a smaller Surface.
- * <p>
- * Example: if you create two FBOs, one 1000x1000 and another 500x500 in size, and set the second one
- * mipmap to 0.5 (the first one's is 1.0 by default), define Effects to be a single move by (100,100),
- * and render a skinned Mesh into both FBO, the end result will look proportionally the same, because
- * in the second case the move vector (100,100) will be auto-scaled to (50,50).
- *
- * @param mipmap The mipmap level. Acceptable range: 0&lt;mipmap&lt;infinity, although mipmap&gt;1
- *               does not make any sense (that would result in loss of speed and no gain in quality)
- */
-  public void setMipmap(float mipmap)
-    {
-    mMipmap = mipmap;
-    }
+        InternalRenderState.colorDepthStencilOn()
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Set the (R,G,B,A) values of GLES31.glClearColor() to set up color with which to clear
- * this Surface at the beginning of each frame.
- *
- * @param r the Red component. Default: 0.0f
- * @param g the Green component. Default: 0.0f
- * @param b the Blue component. Default: 0.0f
- * @param a the Alpha component. Default: 0.0f
- */
-  public void glClearColor(float r, float g, float b, float a)
-    {
-    mClearR = r;
-    mClearG = g;
-    mClearB = b;
-    mClearA = a;
-    }
+        GLES30.glClearColor(mClearR, mClearG, mClearB, mClearA)
+        GLES30.glClearDepthf(mClearDepth)
+        GLES30.glClearStencil(mClearStencil)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Uses glClearDepthf() to set up a value with which to clear
- * the Depth buffer of our Surface at the beginning of each frame.
- *
- * @param d the Depth. Default: 1.0f
- */
-  public void glClearDepthf(float d)
-    {
-    mClearDepth = d;
-    }
+        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[fbo])
+        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, colorH[2*fbo+1], 0)
+        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT or GLES30.GL_STENCIL_BUFFER_BIT)
+        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, colorH[2*fbo], 0)
+        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Uses glClearStencil() to set up a value with which to clear the
- * Stencil buffer of our Surface at the beginning of each frame.
- *
- * @param s the Stencil. Default: 0
- */
-  public void glClearStencil(int s)
-    {
-    mClearStencil = s;
+        InternalRenderState.colorDepthStencilRestore()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Which buffers to Clear at the beginning of each frame?
- * <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: GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT.
- *
- * @param mask bitwise OR of BUFFER_BITs to clear.
- */
-  public void glClear(int mask)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setAsOutput(time: Long)
     {
-    mClear = mask;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create new Projection matrix.
- *
- * @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;
-      }
-
-    for(int j=0; j<EffectQuality.LENGTH; j++)
-      {
-      if( mBuffer[j]!=null ) mBuffer[j].mNear = mNear;
-      }
-
-    createProjection();
-    }
+        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[mCurrFBO])
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the vertical field of view angle.
- *
- * @return Vertival Field of View Angle, in degrees.
- */
-  public float getFOV()
-    {
-    return mFOV;
+        if (mTime[mCurrFBO]!=time)
+        {
+            mTime[mCurrFBO] = time
+            clear()
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Resize the underlying Framebuffer.
- * <p>
- * This method can be safely called mid-render as it doesn't interfere with rendering.
- *
- * @param width The new width.
- * @param height The new height.
- */
-  public void resize(int width, int height)
-    {
-    if( mWidth!=width || mHeight!=height )
-      {
-      mWidth = mRealWidth = width;
-      mHeight= mRealHeight= height;
-
-      createProjection();
-
-      if( mColorCreated==CREATED )
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Draws all the attached children to this OutputSurface's 0th FBO.
+     *
+     * 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.
+     */
+    open fun render(time: Long): Int = render(time,0)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * 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.
+     * @param fbo The surface can have many FBOs backing it up - render this to FBO number 'fbo'.
+     * @return Number of objects rendered.
+     */
+    fun render(time: Long, fbo: Int): Int
+    {
+        InternalMaster.toDo()
+        InternalStackFrameList.toDo()
+        InternalRenderState.reset()
+
+        var numRenders = 0
+        val numChildren = mChildren.numChildren
+        var node: DistortedNode?
+        var oldBucket: Long = 0
+        var newBucket: Long
+
+        for (i in 0 until numChildren)
         {
-        markForCreation();
-        recreate();
+            node = mChildren.getChild(i)
+            newBucket = node.bucket
+            numRenders += node.renderRecursive(time)
+            if (newBucket<oldBucket) mChildren.rearrangeByBuckets(i, newBucket)
+            else oldBucket = newBucket
         }
-      }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return true if the Surface contains a DEPTH attachment.
- *
- * @return <bold>true</bold> if the Surface contains a DEPTH attachment.
- */
-  public boolean hasDepth()
-    {
-    return mDepthStencilCreated==CREATED;
-    }
+        numRenders += renderChildren(time, numChildren, mChildren, fbo, mRenderWayOIT)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return true if the Surface contains a STENCIL attachment.
- *
- * @return <bold>true</bold> if the Surface contains a STENCIL attachment.
- */
-  public boolean hasStencil()
-    {
-    return (mDepthStencilCreated==CREATED && mDepthStencil==BOTH_DEPTH_STENCIL);
+        return numRenders
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, should we use the Order Independent Transparency render mode?
- * <p>
- * This feature requires OpenGL ES 3.1. If we are running on OpenGL 3.0, this will do nothing.
- * Also, if you are running on a buggy driver ( Imagination GE8100/8300 driver build 1.8@4490469 )
- * then do nothing.
- *
- * 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)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Recursively print all the effect queues attached to the children Nodes and to this Node.
+     */
+    fun debug()
     {
-    if( DistortedLibrary.getGLSL()>=310 )
-      {
-      mRenderWayOIT = oit;
-      }
+        val numChildren = mChildren.numChildren
+
+        for (i in 0 until numChildren)
+        {
+            val node = mChildren.getChild(i)
+            node.debug(0)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Node, should we use the Order Independent Transparency render mode?
- * <p>
- * This feature requires OpenGL ES 3.1. If we are running on OpenGL 3.0, this will do nothing.
- * Also, if you are running on a buggy driver ( Imagination GE8100/8300 driver build 1.8@4490469 )
- * then do nothing.
- *
- * 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 screenfuls.
- *                    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)
-    {
-    if( DistortedLibrary.getGLSL()>=310 )
-      {
-      mRenderWayOIT = oit;
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Bind this Surface as a Framebuffer we can render to.
+     *
+     * This version does not attempt to clear anything.
+     */
+    fun setAsOutput()
+    {
+        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBOH[mCurrFBO])
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Set mipmap level.
+     *
+     * Trick for speeding up your renders - one can create a pyramid of OutputSurface objects, each next
+     * one some constant FACTOR smaller than the previous (0.5 is the common value), then set the Mipmap
+     * Level of the i-th object to be FACTOR^i (we start counting from 0). When rendering any scene into
+     * such prepared OutputSurface, the library will make sure to scale any Effects used so that the end
+     * scene will end up looking identical no matter which object we render to. Identical, that is, except
+     * for the loss of quality and gain in speed associated with rendering to a smaller Surface.
+     *
+     * Example: if you create two FBOs, one 1000x1000 and another 500x500 in size, and set the second one
+     * mipmap to 0.5 (the first one's is 1.0 by default), define Effects to be a single move by (100,100),
+     * and render a skinned Mesh into both FBO, the end result will look proportionally the same, because
+     * in the second case the move vector (100,100) will be auto-scaled to (50,50).
+     *
+     * @param mipmap The mipmap level. Acceptable range: 0&lt;mipmap&lt;infinity, although mipmap&gt;1
+     * does not make any sense (that would result in loss of speed and no gain in quality)
+     */
+    fun setMipmap(mipmap: Float)
+    {
+        mMipmap = mipmap
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Set the (R,G,B,A) values of GLES31.glClearColor() to set up color with which to clear
+     * this Surface at the beginning of each frame.
+     *
+     * @param r the Red component. Default: 0.0f
+     * @param g the Green component. Default: 0.0f
+     * @param b the Blue component. Default: 0.0f
+     * @param a the Alpha component. Default: 0.0f
+     */
+    fun glClearColor(r: Float, g: Float, b: Float, a: Float)
+    {
+        mClearR = r
+        mClearG = g
+        mClearB = b
+        mClearA = a
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Uses glClearDepthf() to set up a value with which to clear
+     * the Depth buffer of our Surface at the beginning of each frame.
+     *
+     * @param d the Depth. Default: 1.0f
+     */
+    fun glClearDepthf(d: Float)
+    {
+        mClearDepth = d
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Uses glClearStencil() to set up a value with which to clear the
+     * Stencil buffer of our Surface at the beginning of each frame.
+     *
+     * @param s the Stencil. Default: 0
+     */
+    fun glClearStencil(s: Int)
+    {
+        mClearStencil = s
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Which buffers to Clear at the beginning of each frame?
+     *
+     * 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: GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT.
+     *
+     * @param mask bitwise OR of BUFFER_BITs to clear.
+     */
+    fun glClear(mask: Int)
+    {
+        mClear = mask
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Create new Projection matrix.
+     *
+     * @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)
+    {
+        if (fov<180.0f&&fov>=0.0f)
+        {
+            fOV = fov
+        }
 
-      if( initialSize>0.0f && initialSize<10.0f )
+        if (near<1.0f&&near>0.0f)
         {
-        DistortedLibrary.setSSBOSize(initialSize);
+            mNear = near
+        }
+        else if (near<=0.0f)
+        {
+            mNear = 0.01f
+        }
+        else if (near>=1.0f)
+        {
+            mNear = 0.99f
         }
-      }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new child to the last position in the list of our Surface'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)
-    {
-    mChildren.attach(node);
-    }
+        for (j in 0 until EffectQuality.LENGTH)
+        {
+            if (mBuffer[j]!=null) mBuffer[j]!!.mNear = mNear
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds a new child to the last position in the list of our Surface'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);
+        createProjection()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes the first occurrence of a specified child from the list of children of our Surface.
- * <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)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Resize the underlying Framebuffer.
+     *
+     * This method can be safely called mid-render as it doesn't interfere with rendering.
+     *
+     * @param width The new width.
+     * @param height The new height.
+     */
+    fun resize(width: Int, height: Int)
     {
-    mChildren.detach(effects);
+        if( mWidth!=width || mHeight!=height )
+        {
+            mRealWidth  = width
+            mWidth      = mRealWidth
+            mRealHeight = height
+            mHeight     = mRealHeight
+
+            createProjection()
+
+            if( mColorCreated==CREATED )
+            {
+                markForCreation()
+                recreate()
+            }
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes the first occurrence of a specified child from the list of children of our Surface.
- * <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 Node to remove.
- */
-  public void detach(DistortedNode node)
-    {
-    mChildren.detach(node);
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Return true if the Surface contains a DEPTH attachment.
+     *
+     * @return <bold>true</bold> if the Surface contains a DEPTH attachment.
+     */
+    fun hasDepth(): Boolean
+    {
+        return mDepthStencilCreated==CREATED
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Return true if the Surface contains a STENCIL attachment.
+     *
+     * @return <bold>true</bold> if the Surface contains a STENCIL attachment.
+     */
+    fun hasStencil(): Boolean
+    {
+        return (mDepthStencilCreated==CREATED && mDepthStencil==BOTH_DEPTH_STENCIL)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, should we use the Order Independent Transparency render mode?
+     *
+     * This feature requires OpenGL ES 3.1. If we are running on OpenGL 3.0, this will do nothing.
+     * Also, if you are running on a buggy driver ( Imagination GE8100/8300 driver build 1.8@4490469 )
+     * then do nothing.
+     *
+     * 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)
+    {
+        if (DistortedLibrary.getGLSL()>=310)
+        {
+            mRenderWayOIT = oit
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Removes all children Nodes.
- * <p>
- * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * InternalMaster (by calling doWork())
- */
-  public void detachAll()
-    {
-    mChildren.detachAll();
-    }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * When rendering this Node, should we use the Order Independent Transparency render mode?
+     *
+     * This feature requires OpenGL ES 3.1. If we are running on OpenGL 3.0, this will do nothing.
+     * Also, if you are running on a buggy driver ( Imagination GE8100/8300 driver build 1.8@4490469 )
+     * then do nothing.
+     *
+     * 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 screenfuls.
+     * 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)
+    {
+        if (DistortedLibrary.getGLSL()>=310)
+        {
+            mRenderWayOIT = oit
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the width of this Surface.
- *
- * @return width of the Object, in pixels.
- */
-  public int getWidth()
-    {
-    return mWidth;
+            if (initialSize>0.0f&&initialSize<10.0f)
+            {
+                DistortedLibrary.setSSBOSize(initialSize)
+            }
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the height of this Surface.
- *
- * @return height of the Object, in pixels.
- */
-  public int getHeight()
-    {
-    return mHeight;
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Adds a new child to the last position in the list of our Surface'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 Surface'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 Surface.
+     *
+     * 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 the first occurrence of a specified child from the list of children of our Surface.
+     *
+     * 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 Node to remove.
+     */
+    fun detach(node: DistortedNode)
+    {
+        mChildren.detach(node)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Removes all children Nodes.
+     *
+     * We cannot do this mid-render - actual attachment will be done just before the next render, by the
+     * InternalMaster (by calling doWork())
+     */
+    fun detachAll()
+    {
+        mChildren.detachAll()
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/main/InternalRenderState.kt b/src/main/java/org/distorted/library/main/InternalRenderState.kt
index ecaab47..80a2154 100644
--- a/src/main/java/org/distorted/library/main/InternalRenderState.kt
+++ b/src/main/java/org/distorted/library/main/InternalRenderState.kt
@@ -17,852 +17,847 @@
 // 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
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Remember the OpenGL state.
- * <p>
+ *
  * This is a member of DistortedNode. Remembers the OpenGL state we want to set just before rendering
  * the Node.
- * <p>
+ *
  * Only for use by the library itself.
  *
  * @y.exclude
  */
-public class InternalRenderState
+class InternalRenderState internal constructor()
 {
-  // TODO: figure this out dynamically; this assumes 8 bit stencil buffer.
-  private static final int STENCIL_MASK = (1<<8)-1;
-
-  private static class RenderState
+    private class RenderState
     {
-    private int colorMaskR, colorMaskG, colorMaskB, colorMaskA;
-    private int depthMask;
-    private int stencilMask;
-    private int depthTest;
-    private int stencilTest;
-    private int stencilFuncFunc, stencilFuncRef, stencilFuncMask;
-    private int stencilOpSfail, stencilOpDpfail, stencilOpDppass;
-    private int depthFunc;
-    private int blend;
-    private int blendSrc, blendDst;
+        var colorMaskR     : Int = 0
+        var colorMaskG     : Int = 0
+        var colorMaskB     : Int = 0
+        var colorMaskA     : Int = 0
+        var depthMask      : Int = 0
+        var stencilMask    : Int = 0
+        var depthTest      : Int = 0
+        var stencilTest    : Int = 0
+        var stencilFuncFunc: Int = 0
+        var stencilFuncRef : Int = 0
+        var stencilFuncMask: Int = 0
+        var stencilOpSfail : Int = 0
+        var stencilOpDpfail: Int = 0
+        var stencilOpDppass: Int = 0
+        var depthFunc      : Int = 0
+        var blend          : Int = 0
+        var blendSrc       : Int = 0
+        var blendDst       : Int = 0
     }
 
-  private final RenderState mState;          // state the current object wants to have
-  static private final RenderState cState = new RenderState();   // current OpenGL Stave
-  static private final RenderState sState = new RenderState();   // saved OpenGL state
-
-  private int mClear;
+    private val mState = RenderState() // state the current object wants to have
+    private var mClear: Int
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// default: color writes on, depth test and writes on, blending on, stencil off.
-
-  InternalRenderState()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // default: color writes on, depth test and writes on, blending on, stencil off.
+    init
     {
-    mState = new RenderState();
-
-    mState.colorMaskR = 1;
-    mState.colorMaskG = 1;
-    mState.colorMaskB = 1;
-    mState.colorMaskA = 1;
-
-    mState.depthTest  = 1;
-    mState.depthMask  = 1;
-    mState.depthFunc  = GLES30.GL_LEQUAL;
-
-    mState.blend      = 1;
-    mState.blendSrc   = GLES30.GL_SRC_ALPHA;
-    mState.blendDst   = GLES30.GL_ONE_MINUS_SRC_ALPHA;
-
-    mState.stencilTest     = 0;
-    mState.stencilMask     = STENCIL_MASK;
-    mState.stencilFuncFunc = GLES30.GL_NEVER;
-    mState.stencilFuncRef  = 0;
-    mState.stencilFuncMask = STENCIL_MASK;
-    mState.stencilOpSfail  = GLES30.GL_KEEP;
-    mState.stencilOpDpfail = GLES30.GL_KEEP;
-    mState.stencilOpDppass = GLES30.GL_KEEP;
-
-    mClear = 0;
+        mState.colorMaskR = 1
+        mState.colorMaskG = 1
+        mState.colorMaskB = 1
+        mState.colorMaskA = 1
+
+        mState.depthTest = 1
+        mState.depthMask = 1
+        mState.depthFunc = GLES30.GL_LEQUAL
+
+        mState.blend = 1
+        mState.blendSrc = GLES30.GL_SRC_ALPHA
+        mState.blendDst = GLES30.GL_ONE_MINUS_SRC_ALPHA
+
+        mState.stencilTest = 0
+        mState.stencilMask = STENCIL_MASK
+        mState.stencilFuncFunc = GLES30.GL_NEVER
+        mState.stencilFuncRef = 0
+        mState.stencilFuncMask = STENCIL_MASK
+        mState.stencilOpSfail = GLES30.GL_KEEP
+        mState.stencilOpDpfail = GLES30.GL_KEEP
+        mState.stencilOpDppass = GLES30.GL_KEEP
+
+        mClear = 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// reset state of everything to a known state.
-
-  static void reset()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun apply()
     {
-    cState.colorMaskR = 1;
-    cState.colorMaskG = 1;
-    cState.colorMaskB = 1;
-    cState.colorMaskA = 1;
-    GLES30.glColorMask(true,true,true,true);
-
-    cState.depthTest = 1;
-    cState.depthMask = 1;
-    cState.depthFunc = GLES30.GL_LEQUAL;
-    GLES30.glEnable(GLES30.GL_DEPTH_TEST);
-    GLES30.glDepthMask(true);
-    GLES30.glDepthFunc(cState.depthFunc);
-
-    cState.stencilTest     = 0;
-    cState.stencilMask     = STENCIL_MASK;
-    cState.stencilFuncFunc = GLES30.GL_NEVER;
-    cState.stencilFuncRef  = 0;
-    cState.stencilFuncMask = STENCIL_MASK;
-    cState.stencilOpSfail  = GLES30.GL_KEEP;
-    cState.stencilOpDpfail = GLES30.GL_KEEP;
-    cState.stencilOpDppass = GLES30.GL_KEEP;
-    GLES30.glDisable(GLES30.GL_STENCIL_TEST);
-    GLES30.glStencilMask(cState.stencilMask);
-
-    cState.blend      = 1;
-    cState.blendSrc   = GLES30.GL_SRC_ALPHA;
-    cState.blendDst   = GLES30.GL_ONE_MINUS_SRC_ALPHA;
-    GLES30.glEnable(GLES30.GL_BLEND);
-    GLES30.glBlendFunc(cState.blendSrc,cState.blendDst);
-    }
+        //DistortedLibrary.logMessage("InternalRenderState: APPLYING STATE");
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        /////////////////////////////////////////////////////
+        // 1. Write to color buffer?
 
-  static void colorDepthStencilOn()
-    {
-    sState.colorMaskR = cState.colorMaskR;
-    sState.colorMaskG = cState.colorMaskG;
-    sState.colorMaskB = cState.colorMaskB;
-    sState.colorMaskA = cState.colorMaskA;
-
-    if( cState.colorMaskR!=1 || cState.colorMaskG!=1 || cState.colorMaskB!=1 || cState.colorMaskA!=1 )
-      {
-      cState.colorMaskR = 1;
-      cState.colorMaskG = 1;
-      cState.colorMaskB = 1;
-      cState.colorMaskA = 1;
-      GLES30.glColorMask(true,true,true,true);
-      }
-
-    sState.depthMask = cState.depthMask;
-
-    if( cState.depthMask!=1 )
-      {
-      cState.depthMask = 1;
-      GLES30.glDepthMask(true);
-      }
-
-    sState.stencilMask = cState.stencilMask;
-
-    if( cState.stencilMask!= STENCIL_MASK )
-      {
-      cState.stencilMask = STENCIL_MASK;
-      GLES30.glStencilMask(cState.stencilMask);
-      }
-    }
+        if( mState.colorMaskR!=cState.colorMaskR || mState.colorMaskG!=cState.colorMaskG ||
+            mState.colorMaskB!=cState.colorMaskB || mState.colorMaskA!=cState.colorMaskA  )
+        {
+            //DistortedLibrary.logMessage("InternalRenderState: setting color mask");
+            cState.colorMaskR = mState.colorMaskR
+            cState.colorMaskG = mState.colorMaskG
+            cState.colorMaskB = mState.colorMaskB
+            cState.colorMaskA = mState.colorMaskA
+            GLES30.glColorMask(cState.colorMaskR==1, cState.colorMaskG==1, cState.colorMaskB==1, cState.colorMaskA==1)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        /////////////////////////////////////////////////////
+        // 2. Enable Depth test?
+        if( mState.depthTest!=cState.depthTest )
+        {
+            cState.depthTest = mState.depthTest
+
+            if( cState.depthTest==0 )
+            {
+                //DistortedLibrary.logMessage("InternalRenderState: disabling depth test");
+                GLES30.glDisable(GLES30.GL_DEPTH_TEST)
+            }
+            else
+            {
+                //DistortedLibrary.logMessage("InternalRenderState: enable depth test");
+                GLES30.glEnable(GLES30.GL_DEPTH_TEST)
+            }
+        }
 
-  static void colorDepthStencilRestore()
-    {
-    if( sState.colorMaskR!=cState.colorMaskR || sState.colorMaskG!=cState.colorMaskG || sState.colorMaskB!=cState.colorMaskB || sState.colorMaskA!=cState.colorMaskA)
-      {
-      cState.colorMaskR = sState.colorMaskR;
-      cState.colorMaskG = sState.colorMaskG;
-      cState.colorMaskB = sState.colorMaskB;
-      cState.colorMaskA = sState.colorMaskA;
-      GLES30.glColorMask(cState.colorMaskR==1,cState.colorMaskG==1,cState.colorMaskB==1,cState.colorMaskA==1);
-      }
-    if( sState.depthMask!=cState.depthMask )
-      {
-      cState.depthMask = sState.depthMask;
-      GLES30.glDepthMask(cState.depthMask==1);
-      }
-    if( sState.stencilMask!=cState.stencilMask )
-      {
-      cState.stencilMask = sState.stencilMask;
-      GLES30.glStencilMask(cState.stencilMask);
-      }
-    }
+        /////////////////////////////////////////////////////
+        // 3. Change Depth Function?
+        if( mState.depthFunc!=cState.depthFunc )
+        {
+            //DistortedLibrary.logMessage("InternalRenderState: setting depth func");
+            cState.depthFunc = mState.depthFunc
+            GLES30.glDepthFunc(cState.depthFunc)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        /////////////////////////////////////////////////////
+        // 4. Write to Depth buffer?
+        if( mState.depthMask!=cState.depthMask )
+        {
+            //DistortedLibrary.logMessage("InternalRenderState: setting depth mask");
+            cState.depthMask = mState.depthMask
+            GLES30.glDepthMask(cState.depthMask==1)
+        }
 
-  public static void disableBlending()
-    {
-    sState.blend = cState.blend;
+        /////////////////////////////////////////////////////
+        // 5. Enable Blending?
+        if( mState.blend!=cState.blend )
+        {
+            cState.blend = mState.blend
+
+            if (cState.blend==0)
+            {
+                //DistortedLibrary.logMessage("InternalRenderState: disabling blending");
+                GLES30.glDisable(GLES30.GL_BLEND)
+            }
+            else
+            {
+                //DistortedLibrary.logMessage("InternalRenderState: enabling blending");
+                GLES30.glEnable(GLES30.GL_BLEND)
+            }
+        }
 
-    if (cState.blend != 0)
-      {
-      cState.blend = 0;
-      GLES30.glDisable(GLES30.GL_BLEND);
-      }
-    }
+        /////////////////////////////////////////////////////
+        // 6. Change Blend function?
+        if( mState.blendSrc!=cState.blendSrc || mState.blendDst!=cState.blendDst )
+        {
+            //DistortedLibrary.logMessage("InternalRenderState: setting blend function");
+            cState.blendSrc = mState.blendSrc
+            cState.blendDst = mState.blendDst
+            GLES30.glBlendFunc(cState.blendSrc, cState.blendDst)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        /////////////////////////////////////////////////////
+        // 7. Enable/Disable Stencil Test?
+        if( mState.stencilTest!=cState.stencilTest )
+        {
+            cState.stencilTest = mState.stencilTest
+
+            if( cState.stencilTest==0 )
+            {
+                //DistortedLibrary.logMessage("InternalRenderState: disabling stencil test");
+                GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+            }
+            else
+            {
+                //DistortedLibrary.logMessage("InternalRenderState: enabling stencil test");
+                GLES30.glEnable(GLES30.GL_STENCIL_TEST)
+            }
+        }
 
-  public static void restoreBlending()
-    {
-    if (sState.blend != cState.blend)
-      {
-      cState.blend = sState.blend;
+        /////////////////////////////////////////////////////
+        // 8. Adjust Stencil function?
+        if( mState.stencilFuncFunc!=cState.stencilFuncFunc ||
+            mState.stencilFuncRef !=cState.stencilFuncRef  ||
+            mState.stencilFuncMask!=cState.stencilFuncMask  )
+        {
+            //DistortedLibrary.logMessage("InternalRenderState: setting stencil function");
+            cState.stencilFuncFunc = mState.stencilFuncFunc
+            cState.stencilFuncRef  = mState.stencilFuncRef
+            cState.stencilFuncMask = mState.stencilFuncMask
+            GLES30.glStencilFunc(cState.stencilFuncFunc, cState.stencilFuncRef, cState.stencilFuncMask)
+        }
 
-      if (cState.blend == 0)
+        /////////////////////////////////////////////////////
+        // 9. Adjust Stencil operation?
+        if( mState.stencilOpSfail !=cState.stencilOpSfail ||
+            mState.stencilOpDpfail!=cState.stencilOpDpfail||
+            mState.stencilOpDppass!=cState.stencilOpDppass )
         {
-        GLES30.glDisable(GLES30.GL_BLEND);
+            //DistortedLibrary.logMessage("InternalRenderState: setting stencil op");
+            cState.stencilOpSfail = mState.stencilOpSfail
+            cState.stencilOpDpfail = mState.stencilOpDpfail
+            cState.stencilOpDppass = mState.stencilOpDppass
+            GLES30.glStencilOp(cState.stencilOpSfail, cState.stencilOpDpfail, cState.stencilOpDppass)
         }
-      else
+
+        /////////////////////////////////////////////////////
+        // 10. Write to Stencil buffer?
+        if( mState.stencilMask!=cState.stencilMask )
         {
-        GLES30.glEnable(GLES30.GL_BLEND);
+            //DistortedLibrary.logMessage("InternalRenderState: setting stencil mask");
+            cState.stencilMask = mState.stencilMask
+            GLES30.glStencilMask(cState.stencilMask)
         }
-      }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        /////////////////////////////////////////////////////
+        // 11. Clear buffers?
+        if( mClear!=0 )
+        {
+            //DistortedLibrary.logMessage("InternalRenderState: clearing buffer");
+            GLES30.glClear(mClear)
+        }
+    }
 
-  static void enableDepthTest()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glColorMask(r: Boolean, g: Boolean, b: Boolean, a: Boolean)
     {
-    sState.depthTest = cState.depthTest;
-
-    if (cState.depthTest != 1)
-      {
-      cState.depthTest = 1;
-      GLES30.glEnable(GLES30.GL_DEPTH_TEST);
-      }
+        mState.colorMaskR = (if (r) 1 else 0)
+        mState.colorMaskG = (if (g) 1 else 0)
+        mState.colorMaskB = (if (b) 1 else 0)
+        mState.colorMaskA = (if (a) 1 else 0)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void restoreDepthTest()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glDepthMask(mask: Boolean)
     {
-    if (sState.depthTest != cState.depthTest)
-      {
-      cState.depthTest = sState.depthTest;
-
-      if (cState.depthTest == 0)
-        {
-        GLES30.glDisable(GLES30.GL_DEPTH_TEST);
-        }
-      else
-        {
-        GLES30.glEnable(GLES30.GL_DEPTH_TEST);
-        }
-      }
+        mState.depthMask = (if (mask) 1 else 0)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static void switchOffDrawing()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glStencilMask(mask: Int)
     {
-    GLES30.glEnable(GLES30.GL_SCISSOR_TEST);
-    GLES30.glScissor(0,0,0,0);
+        mState.stencilMask = mask
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glEnable(test: Int)
+    {
+             if (test==GLES30.GL_DEPTH_TEST  ) mState.depthTest   = 1
+        else if (test==GLES30.GL_STENCIL_TEST) mState.stencilTest = 1
+        else if (test==GLES30.GL_BLEND       ) mState.blend       = 1
+    }
 
-  public static void restoreDrawing()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glDisable(test: Int)
     {
-    GLES30.glDisable(GLES30.GL_SCISSOR_TEST);
+             if (test==GLES30.GL_DEPTH_TEST  ) mState.depthTest   = 0
+        else if (test==GLES30.GL_STENCIL_TEST) mState.stencilTest = 0
+        else if (test==GLES30.GL_BLEND       ) mState.blend       = 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glStencilFunc(func: Int, ref: Int, mask: Int)
+    {
+        mState.stencilFuncFunc = func
+        mState.stencilFuncRef  = ref
+        mState.stencilFuncMask = mask
+    }
 
-  static void switchOffColorDepthStencil()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glStencilOp(sfail: Int, dpfail: Int, dppass: Int)
     {
-    sState.stencilTest = cState.stencilTest;
-
-    if( cState.stencilTest!=0 )
-      {
-      cState.stencilTest = 0;
-      //DistortedLibrary.logMessage("InternalRenderState: stencil test off");
-      GLES30.glDisable(GLES30.GL_STENCIL_TEST);
-      }
-
-    sState.depthTest = cState.depthTest;
-
-    if( cState.depthTest!=0 )
-      {
-      cState.depthTest = 0;
-      //DistortedLibrary.logMessage("InternalRenderState: depth test off");
-      GLES30.glDisable(GLES30.GL_DEPTH_TEST);
-      }
-
-    sState.colorMaskR = cState.colorMaskR;
-    sState.colorMaskG = cState.colorMaskG;
-    sState.colorMaskB = cState.colorMaskB;
-    sState.colorMaskA = cState.colorMaskA;
-
-    if( cState.colorMaskR!=0 || cState.colorMaskG!=0 || cState.colorMaskB!=0 || cState.colorMaskA!=0 )
-      {
-      cState.colorMaskR = 0;
-      cState.colorMaskG = 0;
-      cState.colorMaskB = 0;
-      cState.colorMaskA = 0;
-      //DistortedLibrary.logMessage("InternalRenderState: switch off color writing");
-      GLES30.glColorMask(false,false,false,false);
-      }
-
-    sState.depthMask = cState.depthMask;
-
-    if( cState.depthMask!=0 )
-      {
-      cState.depthMask = 0;
-      //DistortedLibrary.logMessage("InternalRenderState: switch off depth writing");
-      GLES30.glDepthMask(false);
-      }
-
-    sState.stencilMask = cState.stencilMask;
-
-    if( cState.stencilMask!= 0x00 )
-      {
-      cState.stencilMask = 0x00;
-      //DistortedLibrary.logMessage("InternalRenderState: stencil mask off");
-      GLES30.glStencilMask(cState.stencilMask);
-      }
+        mState.stencilOpSfail  = sfail
+        mState.stencilOpDpfail = dpfail
+        mState.stencilOpDppass = dppass
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glDepthFunc(func: Int)
+    {
+        mState.depthFunc = func
+    }
 
-  static void switchColorDepthOnStencilOff()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glBlendFunc(src: Int, dst: Int)
     {
-    sState.stencilTest = cState.stencilTest;
-
-    if( cState.stencilTest!=0 )
-      {
-      cState.stencilTest = 0;
-      //DistortedLibrary.logMessage("InternalRenderState: stencil test off");
-      GLES30.glDisable(GLES30.GL_STENCIL_TEST);
-      }
-
-    sState.depthTest = cState.depthTest;
-
-    if( cState.depthTest!=0 )
-      {
-      cState.depthTest = 0;
-      //DistortedLibrary.logMessage("InternalRenderState: depth test off");
-      GLES30.glDisable(GLES30.GL_DEPTH_TEST);
-      }
-
-    sState.colorMaskR = cState.colorMaskR;
-    sState.colorMaskG = cState.colorMaskG;
-    sState.colorMaskB = cState.colorMaskB;
-    sState.colorMaskA = cState.colorMaskA;
-
-    if( cState.colorMaskR!=1 || cState.colorMaskG!=1 || cState.colorMaskB!=1 || cState.colorMaskA!=1 )
-      {
-      cState.colorMaskR = 1;
-      cState.colorMaskG = 1;
-      cState.colorMaskB = 1;
-      cState.colorMaskA = 1;
-      //DistortedLibrary.logMessage("InternalRenderState: switch on color writing");
-      GLES30.glColorMask(true,true,true,true);
-      }
-
-    sState.depthMask = cState.depthMask;
-
-    if( cState.depthMask!=1 )
-      {
-      cState.depthMask = 1;
-      //DistortedLibrary.logMessage("InternalRenderState: switch on depth writing");
-      GLES30.glDepthMask(true);
-      }
-
-    sState.stencilMask = cState.stencilMask;
-
-    if( cState.stencilMask!= 0x00 )
-      {
-      cState.stencilMask = 0x00;
-      //DistortedLibrary.logMessage("InternalRenderState: stencil mask off");
-      GLES30.glStencilMask(cState.stencilMask);
-      }
+        mState.blendSrc = src
+        mState.blendDst = dst
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun glClear(mask: Int)
+    {
+        mClear = mask
+    }
 
-  static void restoreColorDepthStencil()
+    companion object
     {
-    if( sState.stencilTest!=cState.stencilTest )
-      {
-      cState.stencilTest = sState.stencilTest;
+        // TODO: figure this out dynamically; this assumes 8 bit stencil buffer.
+        private const val STENCIL_MASK = (1 shl 8)-1
 
-      if (cState.stencilTest == 0)
-        {
-        GLES30.glDisable(GLES30.GL_STENCIL_TEST);
-        }
-      else
+        private val cState = RenderState() // current OpenGL Stave
+        private val sState = RenderState() // saved OpenGL state
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        // reset state of everything to a known state.
+        fun reset()
         {
-        GLES30.glEnable(GLES30.GL_STENCIL_TEST);
+            cState.colorMaskR = 1
+            cState.colorMaskG = 1
+            cState.colorMaskB = 1
+            cState.colorMaskA = 1
+            GLES30.glColorMask(true, true, true, true)
+
+            cState.depthTest = 1
+            cState.depthMask = 1
+            cState.depthFunc = GLES30.GL_LEQUAL
+            GLES30.glEnable(GLES30.GL_DEPTH_TEST)
+            GLES30.glDepthMask(true)
+            GLES30.glDepthFunc(cState.depthFunc)
+
+            cState.stencilTest = 0
+            cState.stencilMask = STENCIL_MASK
+            cState.stencilFuncFunc = GLES30.GL_NEVER
+            cState.stencilFuncRef = 0
+            cState.stencilFuncMask = STENCIL_MASK
+            cState.stencilOpSfail = GLES30.GL_KEEP
+            cState.stencilOpDpfail = GLES30.GL_KEEP
+            cState.stencilOpDppass = GLES30.GL_KEEP
+            GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+            GLES30.glStencilMask(cState.stencilMask)
+
+            cState.blend = 1
+            cState.blendSrc = GLES30.GL_SRC_ALPHA
+            cState.blendDst = GLES30.GL_ONE_MINUS_SRC_ALPHA
+            GLES30.glEnable(GLES30.GL_BLEND)
+            GLES30.glBlendFunc(cState.blendSrc, cState.blendDst)
         }
-      }
-    if( sState.depthTest!=cState.depthTest )
-      {
-      cState.depthTest = sState.depthTest;
 
-      if (cState.depthTest == 0)
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun colorDepthStencilOn()
         {
-        GLES30.glDisable(GLES30.GL_DEPTH_TEST);
+            sState.colorMaskR = cState.colorMaskR
+            sState.colorMaskG = cState.colorMaskG
+            sState.colorMaskB = cState.colorMaskB
+            sState.colorMaskA = cState.colorMaskA
+
+            if( cState.colorMaskR!=1 || cState.colorMaskG!=1 || cState.colorMaskB!=1 || cState.colorMaskA!=1 )
+            {
+                cState.colorMaskR = 1
+                cState.colorMaskG = 1
+                cState.colorMaskB = 1
+                cState.colorMaskA = 1
+                GLES30.glColorMask(true, true, true, true)
+            }
+
+            sState.depthMask = cState.depthMask
+
+            if( cState.depthMask!=1 )
+            {
+                cState.depthMask = 1
+                GLES30.glDepthMask(true)
+            }
+
+            sState.stencilMask = cState.stencilMask
+
+            if( cState.stencilMask!=STENCIL_MASK )
+            {
+                cState.stencilMask = STENCIL_MASK
+                GLES30.glStencilMask(cState.stencilMask)
+            }
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun colorDepthStencilRestore()
         {
-        GLES30.glEnable(GLES30.GL_DEPTH_TEST);
+            if( sState.colorMaskR!=cState.colorMaskR || sState.colorMaskG!=cState.colorMaskG ||
+                sState.colorMaskB!=cState.colorMaskB || sState.colorMaskA!=cState.colorMaskA )
+            {
+                cState.colorMaskR = sState.colorMaskR
+                cState.colorMaskG = sState.colorMaskG
+                cState.colorMaskB = sState.colorMaskB
+                cState.colorMaskA = sState.colorMaskA
+                GLES30.glColorMask(cState.colorMaskR==1, cState.colorMaskG==1, cState.colorMaskB==1, cState.colorMaskA==1)
+            }
+            if( sState.depthMask!=cState.depthMask )
+            {
+                cState.depthMask = sState.depthMask
+                GLES30.glDepthMask(cState.depthMask==1)
+            }
+            if( sState.stencilMask!=cState.stencilMask )
+            {
+                cState.stencilMask = sState.stencilMask
+                GLES30.glStencilMask(cState.stencilMask)
+            }
         }
-      }
-    if( sState.colorMaskR!=cState.colorMaskR || sState.colorMaskG!=cState.colorMaskG || sState.colorMaskB!=cState.colorMaskB || sState.colorMaskA!=cState.colorMaskA)
-      {
-      cState.colorMaskR = sState.colorMaskR;
-      cState.colorMaskG = sState.colorMaskG;
-      cState.colorMaskB = sState.colorMaskB;
-      cState.colorMaskA = sState.colorMaskA;
-      GLES30.glColorMask(cState.colorMaskR==1,cState.colorMaskG==1,cState.colorMaskB==1,cState.colorMaskA==1);
-      }
-    if( sState.depthMask!=cState.depthMask )
-      {
-      cState.depthMask = sState.depthMask;
-      GLES30.glDepthMask(cState.depthMask==1);
-      }
-    if( sState.stencilMask!=cState.stencilMask )
-      {
-      cState.stencilMask = sState.stencilMask;
-      GLES30.glStencilMask(cState.stencilMask);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static void setUpStencilMark(boolean color)
-    {
-    sState.stencilTest = cState.stencilTest;
-
-    if( cState.stencilTest!=1 )
-      {
-      cState.stencilTest = 1;
-      //DistortedLibrary.logMessage("InternalRenderState: stencil test on");
-      GLES30.glEnable(GLES30.GL_STENCIL_TEST);
-      }
-
-    sState.stencilFuncFunc = cState.stencilFuncFunc;
-    sState.stencilFuncRef  = cState.stencilFuncRef;
-    sState.stencilFuncMask = cState.stencilFuncMask;
-
-    if( cState.stencilFuncFunc!=GLES30.GL_ALWAYS || cState.stencilFuncRef!=1 || cState.stencilFuncMask!=STENCIL_MASK )
-      {
-      cState.stencilFuncFunc = GLES30.GL_ALWAYS;
-      cState.stencilFuncRef  = 1;
-      cState.stencilFuncMask = STENCIL_MASK;
-      //DistortedLibrary.logMessage("InternalRenderState: stencil func on");
-      GLES30.glStencilFunc(cState.stencilFuncFunc,cState.stencilFuncRef,cState.stencilFuncMask);
-      }
-
-    sState.stencilOpSfail = cState.stencilOpSfail;
-    sState.stencilOpDpfail= cState.stencilOpDpfail;
-    sState.stencilOpDppass= cState.stencilOpDppass;
-
-    if( cState.stencilOpSfail!=GLES30.GL_KEEP || cState.stencilOpDpfail!=GLES30.GL_KEEP || cState.stencilOpDppass!=GLES30.GL_REPLACE )
-      {
-      cState.stencilOpSfail = GLES30.GL_KEEP;
-      cState.stencilOpDpfail= GLES30.GL_KEEP;
-      cState.stencilOpDppass= GLES30.GL_REPLACE;
-      //DistortedLibrary.logMessage("InternalRenderState: stencil op on");
-      GLES30.glStencilOp(cState.stencilOpSfail,cState.stencilOpDpfail,cState.stencilOpDppass);
-      }
-
-    sState.colorMaskR = cState.colorMaskR;
-    sState.colorMaskG = cState.colorMaskG;
-    sState.colorMaskB = cState.colorMaskB;
-    sState.colorMaskA = cState.colorMaskA;
-
-    int clr = color ? 1:0;
-
-    if( cState.colorMaskR!=clr || cState.colorMaskG!=clr || cState.colorMaskB!=clr || cState.colorMaskA!=clr )
-      {
-      cState.colorMaskR = clr;
-      cState.colorMaskG = clr;
-      cState.colorMaskB = clr;
-      cState.colorMaskA = clr;
-      //DistortedLibrary.logMessage("InternalRenderState: switch off color writing");
-      GLES30.glColorMask(color,color,color,color);
-      }
-
-    sState.depthMask = cState.depthMask;
-
-    if( cState.depthMask!=1 )
-      {
-      cState.depthMask = 1;
-      //DistortedLibrary.logMessage("InternalRenderState: switch on depth writing");
-      GLES30.glDepthMask(true);
-      }
-
-    sState.stencilMask = cState.stencilMask;
-
-    if( cState.stencilMask!= STENCIL_MASK )
-      {
-      cState.stencilMask = STENCIL_MASK;
-      //DistortedLibrary.logMessage("InternalRenderState: stencil mask on");
-      GLES30.glStencilMask(cState.stencilMask);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static void unsetUpStencilMark()
-    {
-    if( sState.stencilTest!=cState.stencilTest )
-      {
-      cState.stencilTest = sState.stencilTest;
 
-      if (cState.stencilTest == 0)
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun disableBlending()
         {
-        GLES30.glDisable(GLES30.GL_STENCIL_TEST);
+            sState.blend = cState.blend
+
+            if (cState.blend!=0)
+            {
+                cState.blend = 0
+                GLES30.glDisable(GLES30.GL_BLEND)
+            }
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun restoreBlending()
         {
-        GLES30.glEnable(GLES30.GL_STENCIL_TEST);
+            if (sState.blend!=cState.blend)
+            {
+                cState.blend = sState.blend
+
+                if (cState.blend==0)
+                {
+                    GLES30.glDisable(GLES30.GL_BLEND)
+                }
+                else
+                {
+                    GLES30.glEnable(GLES30.GL_BLEND)
+                }
+            }
         }
-      }
-    if( sState.colorMaskR!=cState.colorMaskR || sState.colorMaskG!=cState.colorMaskG || sState.colorMaskB!=cState.colorMaskB || sState.colorMaskA!=cState.colorMaskA)
-      {
-      cState.colorMaskR = sState.colorMaskR;
-      cState.colorMaskG = sState.colorMaskG;
-      cState.colorMaskB = sState.colorMaskB;
-      cState.colorMaskA = sState.colorMaskA;
-      GLES30.glColorMask(cState.colorMaskR==1,cState.colorMaskG==1,cState.colorMaskB==1,cState.colorMaskA==1);
-      }
-    if( sState.depthMask!=cState.depthMask )
-      {
-      cState.depthMask = sState.depthMask;
-      GLES30.glDepthMask(cState.depthMask==1);
-      }
-    if( sState.stencilMask!=cState.stencilMask )
-      {
-      cState.stencilMask = sState.stencilMask;
-      GLES30.glStencilMask(cState.stencilMask);
-      }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Only for use by the library itself.
- *
- * @y.exclude
- */
-  public static void useStencilMark()
-    {
-    sState.stencilMask = cState.stencilMask;
-
-    if( cState.stencilTest!=1 )
-      {
-      cState.stencilTest = 1;
-      GLES30.glEnable(GLES30.GL_STENCIL_TEST);
-      }
-
-    sState.stencilFuncFunc = cState.stencilFuncFunc;
-    sState.stencilFuncRef  = cState.stencilFuncRef;
-    sState.stencilFuncMask = cState.stencilFuncMask;
-
-    if( cState.stencilFuncFunc!=GLES30.GL_EQUAL || cState.stencilFuncRef!=1 || cState.stencilFuncMask!=STENCIL_MASK )
-      {
-      cState.stencilFuncFunc = GLES30.GL_EQUAL;
-      cState.stencilFuncRef  = 1;
-      cState.stencilFuncMask = STENCIL_MASK;
-      GLES30.glStencilFunc(cState.stencilFuncFunc,cState.stencilFuncRef,cState.stencilFuncMask);
-      }
-
-    sState.stencilMask = cState.stencilMask;
-
-    if( cState.stencilMask!= 0x00 )
-      {
-      cState.stencilMask = 0x00;
-      GLES30.glStencilMask(cState.stencilMask);
-      }
-
-    sState.depthMask = cState.depthMask;
-
-    if( cState.depthMask!=0 )
-      {
-      cState.depthMask = 0;
-      GLES30.glDepthMask(false);
-      }
-
-    sState.depthTest = cState.depthTest;
-
-    if( cState.depthTest!=0 )
-      {
-      cState.depthTest = 0;
-      GLES30.glDisable(GLES30.GL_DEPTH_TEST);
-      }
-    }
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun enableDepthTest()
+        {
+            sState.depthTest = cState.depthTest
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Only for use by the library itself.
- *
- * @y.exclude
- */
-  public static void unuseStencilMark()
-    {
-    if( sState.stencilTest!=cState.stencilTest )
-      {
-      cState.stencilTest = sState.stencilTest;
+            if( cState.depthTest!=1 )
+            {
+                cState.depthTest = 1
+                GLES30.glEnable(GLES30.GL_DEPTH_TEST)
+            }
+        }
 
-      if (cState.stencilTest == 0)
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun restoreDepthTest()
         {
-        GLES30.glDisable(GLES30.GL_STENCIL_TEST);
+            if( sState.depthTest!=cState.depthTest )
+            {
+                cState.depthTest = sState.depthTest
+
+                if( cState.depthTest==0 )
+                {
+                    GLES30.glDisable(GLES30.GL_DEPTH_TEST)
+                }
+                else
+                {
+                    GLES30.glEnable(GLES30.GL_DEPTH_TEST)
+                }
+            }
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        @JvmStatic fun switchOffDrawing()
         {
-        GLES30.glEnable(GLES30.GL_STENCIL_TEST);
+            GLES30.glEnable(GLES30.GL_SCISSOR_TEST)
+            GLES30.glScissor(0, 0, 0, 0)
         }
-      }
-    if( sState.stencilFuncFunc!=cState.stencilFuncFunc || sState.stencilFuncRef!=cState.stencilFuncRef || sState.stencilFuncMask!=cState.stencilFuncMask )
-      {
-      cState.stencilFuncFunc = sState.stencilFuncFunc;
-      cState.stencilFuncRef  = sState.stencilFuncRef ;
-      cState.stencilFuncMask = sState.stencilFuncMask;
-      GLES30.glStencilFunc(cState.stencilFuncFunc,cState.stencilFuncRef,cState.stencilFuncMask);
-      }
-    if( sState.stencilMask!=cState.stencilMask )
-      {
-      cState.stencilMask = sState.stencilMask;
-      GLES30.glStencilMask(cState.stencilMask);
-      }
-    if( sState.depthMask!=cState.depthMask )
-      {
-      cState.depthMask = sState.depthMask;
-      GLES30.glDepthMask(cState.depthMask==1);
-      }
-    if( sState.depthTest!=cState.depthTest )
-      {
-      cState.depthTest = sState.depthTest;
-
-      if (cState.depthTest == 0)
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        @JvmStatic fun restoreDrawing()
         {
-        GLES30.glDisable(GLES30.GL_DEPTH_TEST);
+            GLES30.glDisable(GLES30.GL_SCISSOR_TEST)
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun switchOffColorDepthStencil()
         {
-        GLES30.glEnable(GLES30.GL_DEPTH_TEST);
+            sState.stencilTest = cState.stencilTest
+
+            if( cState.stencilTest!=0 )
+            {
+                cState.stencilTest = 0
+                //DistortedLibrary.logMessage("InternalRenderState: stencil test off");
+                GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+            }
+
+            sState.depthTest = cState.depthTest
+
+            if( cState.depthTest!=0 )
+            {
+                cState.depthTest = 0
+                //DistortedLibrary.logMessage("InternalRenderState: depth test off");
+                GLES30.glDisable(GLES30.GL_DEPTH_TEST)
+            }
+
+            sState.colorMaskR = cState.colorMaskR
+            sState.colorMaskG = cState.colorMaskG
+            sState.colorMaskB = cState.colorMaskB
+            sState.colorMaskA = cState.colorMaskA
+
+            if( cState.colorMaskR!=0 || cState.colorMaskG!=0 || cState.colorMaskB!=0 || cState.colorMaskA!=0)
+            {
+                cState.colorMaskR = 0
+                cState.colorMaskG = 0
+                cState.colorMaskB = 0
+                cState.colorMaskA = 0
+                //DistortedLibrary.logMessage("InternalRenderState: switch off color writing");
+                GLES30.glColorMask(false, false, false, false)
+            }
+
+            sState.depthMask = cState.depthMask
+
+            if( cState.depthMask!=0 )
+            {
+                cState.depthMask = 0
+                //DistortedLibrary.logMessage("InternalRenderState: switch off depth writing");
+                GLES30.glDepthMask(false)
+            }
+
+            sState.stencilMask = cState.stencilMask
+
+            if( cState.stencilMask!=0x00 )
+            {
+                cState.stencilMask = 0x00
+                //DistortedLibrary.logMessage("InternalRenderState: stencil mask off");
+                GLES30.glStencilMask(cState.stencilMask)
+            }
         }
-      }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void apply()
-    {
-    //DistortedLibrary.logMessage("InternalRenderState: APPLYING STATE");
-
-    /////////////////////////////////////////////////////
-    // 1. Write to color buffer?
-    if( mState.colorMaskR!=cState.colorMaskR || mState.colorMaskG!=cState.colorMaskG || mState.colorMaskB!=cState.colorMaskB || mState.colorMaskA!=cState.colorMaskA)
-      {
-      //DistortedLibrary.logMessage("InternalRenderState: setting color mask");
-      cState.colorMaskR = mState.colorMaskR;
-      cState.colorMaskG = mState.colorMaskG;
-      cState.colorMaskB = mState.colorMaskB;
-      cState.colorMaskA = mState.colorMaskA;
-      GLES30.glColorMask(cState.colorMaskR==1,cState.colorMaskG==1,cState.colorMaskB==1,cState.colorMaskA==1);
-      }
-
-    /////////////////////////////////////////////////////
-    // 2. Enable Depth test?
-    if( mState.depthTest!=cState.depthTest )
-      {
-      cState.depthTest = mState.depthTest;
-
-      if (cState.depthTest == 0)
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun switchColorDepthOnStencilOff()
         {
-        //DistortedLibrary.logMessage("InternalRenderState: disabling depth test");
-        GLES30.glDisable(GLES30.GL_DEPTH_TEST);
+            sState.stencilTest = cState.stencilTest
+
+            if( cState.stencilTest!=0 )
+            {
+                cState.stencilTest = 0
+                //DistortedLibrary.logMessage("InternalRenderState: stencil test off");
+                GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+            }
+
+            sState.depthTest = cState.depthTest
+
+            if( cState.depthTest!=0 )
+            {
+                cState.depthTest = 0
+                //DistortedLibrary.logMessage("InternalRenderState: depth test off");
+                GLES30.glDisable(GLES30.GL_DEPTH_TEST)
+            }
+
+            sState.colorMaskR = cState.colorMaskR
+            sState.colorMaskG = cState.colorMaskG
+            sState.colorMaskB = cState.colorMaskB
+            sState.colorMaskA = cState.colorMaskA
+
+            if( cState.colorMaskR!=1 || cState.colorMaskG!=1 || cState.colorMaskB!=1 || cState.colorMaskA!=1 )
+            {
+                cState.colorMaskR = 1
+                cState.colorMaskG = 1
+                cState.colorMaskB = 1
+                cState.colorMaskA = 1
+                //DistortedLibrary.logMessage("InternalRenderState: switch on color writing");
+                GLES30.glColorMask(true, true, true, true)
+            }
+
+            sState.depthMask = cState.depthMask
+
+            if( cState.depthMask!=1)
+            {
+                cState.depthMask = 1
+                //DistortedLibrary.logMessage("InternalRenderState: switch on depth writing");
+                GLES30.glDepthMask(true)
+            }
+
+            sState.stencilMask = cState.stencilMask
+
+            if( cState.stencilMask!=0x00 )
+            {
+                cState.stencilMask = 0x00
+                //DistortedLibrary.logMessage("InternalRenderState: stencil mask off");
+                GLES30.glStencilMask(cState.stencilMask)
+            }
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun restoreColorDepthStencil()
         {
-        //DistortedLibrary.logMessage("InternalRenderState: enable depth test");
-        GLES30.glEnable(GLES30.GL_DEPTH_TEST);
+            if( sState.stencilTest!=cState.stencilTest )
+            {
+                cState.stencilTest = sState.stencilTest
+
+                if( cState.stencilTest==0 )
+                {
+                    GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+                }
+                else
+                {
+                    GLES30.glEnable(GLES30.GL_STENCIL_TEST)
+                }
+            }
+            if( sState.depthTest!=cState.depthTest )
+            {
+                cState.depthTest = sState.depthTest
+
+                if( cState.depthTest==0 )
+                {
+                    GLES30.glDisable(GLES30.GL_DEPTH_TEST)
+                }
+                else
+                {
+                    GLES30.glEnable(GLES30.GL_DEPTH_TEST)
+                }
+            }
+            if( sState.colorMaskR!=cState.colorMaskR || sState.colorMaskG!=cState.colorMaskG ||
+                sState.colorMaskB!=cState.colorMaskB || sState.colorMaskA!=cState.colorMaskA  )
+            {
+                cState.colorMaskR = sState.colorMaskR
+                cState.colorMaskG = sState.colorMaskG
+                cState.colorMaskB = sState.colorMaskB
+                cState.colorMaskA = sState.colorMaskA
+                GLES30.glColorMask(cState.colorMaskR==1, cState.colorMaskG==1, cState.colorMaskB==1, cState.colorMaskA==1)
+            }
+            if( sState.depthMask!=cState.depthMask )
+            {
+                cState.depthMask = sState.depthMask
+                GLES30.glDepthMask(cState.depthMask==1)
+            }
+            if( sState.stencilMask!=cState.stencilMask )
+            {
+                cState.stencilMask = sState.stencilMask
+                GLES30.glStencilMask(cState.stencilMask)
+            }
         }
-      }
-
-    /////////////////////////////////////////////////////
-    // 3. Change Depth Function?
-    if( mState.depthFunc!=cState.depthFunc )
-      {
-      //DistortedLibrary.logMessage("InternalRenderState: setting depth func");
-      cState.depthFunc = mState.depthFunc;
-      GLES30.glDepthFunc(cState.depthFunc);
-      }
-
-    /////////////////////////////////////////////////////
-    // 4. Write to Depth buffer?
-    if( mState.depthMask!=cState.depthMask )
-      {
-      //DistortedLibrary.logMessage("InternalRenderState: setting depth mask");
-      cState.depthMask = mState.depthMask;
-      GLES30.glDepthMask(cState.depthMask==1);
-      }
-
-    /////////////////////////////////////////////////////
-    // 5. Enable Blending?
-    if( mState.blend!=cState.blend )
-      {
-      cState.blend = mState.blend;
-
-      if (cState.blend == 0)
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun setUpStencilMark(color: Boolean)
         {
-        //DistortedLibrary.logMessage("InternalRenderState: disabling blending");
-        GLES30.glDisable(GLES30.GL_BLEND);
+            sState.stencilTest = cState.stencilTest
+
+            if( cState.stencilTest!=1 )
+            {
+                cState.stencilTest = 1
+                //DistortedLibrary.logMessage("InternalRenderState: stencil test on");
+                GLES30.glEnable(GLES30.GL_STENCIL_TEST)
+            }
+
+            sState.stencilFuncFunc = cState.stencilFuncFunc
+            sState.stencilFuncRef  = cState.stencilFuncRef
+            sState.stencilFuncMask = cState.stencilFuncMask
+
+            if( cState.stencilFuncFunc!=GLES30.GL_ALWAYS || cState.stencilFuncRef!=1 || cState.stencilFuncMask!=STENCIL_MASK )
+            {
+                cState.stencilFuncFunc = GLES30.GL_ALWAYS
+                cState.stencilFuncRef  = 1
+                cState.stencilFuncMask = STENCIL_MASK
+                //DistortedLibrary.logMessage("InternalRenderState: stencil func on");
+                GLES30.glStencilFunc(cState.stencilFuncFunc, cState.stencilFuncRef, cState.stencilFuncMask)
+            }
+
+            sState.stencilOpSfail  = cState.stencilOpSfail
+            sState.stencilOpDpfail = cState.stencilOpDpfail
+            sState.stencilOpDppass = cState.stencilOpDppass
+
+            if( cState.stencilOpSfail !=GLES30.GL_KEEP   ||
+                cState.stencilOpDpfail!=GLES30.GL_KEEP   ||
+                cState.stencilOpDppass!=GLES30.GL_REPLACE )
+            {
+                cState.stencilOpSfail  = GLES30.GL_KEEP
+                cState.stencilOpDpfail = GLES30.GL_KEEP
+                cState.stencilOpDppass = GLES30.GL_REPLACE
+                //DistortedLibrary.logMessage("InternalRenderState: stencil op on");
+                GLES30.glStencilOp(cState.stencilOpSfail, cState.stencilOpDpfail, cState.stencilOpDppass)
+            }
+
+            sState.colorMaskR = cState.colorMaskR
+            sState.colorMaskG = cState.colorMaskG
+            sState.colorMaskB = cState.colorMaskB
+            sState.colorMaskA = cState.colorMaskA
+
+            val clr = if (color) 1 else 0
+
+            if( cState.colorMaskR!=clr || cState.colorMaskG!=clr || cState.colorMaskB!=clr || cState.colorMaskA!=clr )
+            {
+                cState.colorMaskR = clr
+                cState.colorMaskG = clr
+                cState.colorMaskB = clr
+                cState.colorMaskA = clr
+                //DistortedLibrary.logMessage("InternalRenderState: switch off color writing");
+                GLES30.glColorMask(color, color, color, color)
+            }
+
+            sState.depthMask = cState.depthMask
+
+            if( cState.depthMask!=1 )
+            {
+                cState.depthMask = 1
+                //DistortedLibrary.logMessage("InternalRenderState: switch on depth writing");
+                GLES30.glDepthMask(true)
+            }
+
+            sState.stencilMask = cState.stencilMask
+
+            if( cState.stencilMask!=STENCIL_MASK )
+            {
+                cState.stencilMask = STENCIL_MASK
+                //DistortedLibrary.logMessage("InternalRenderState: stencil mask on");
+                GLES30.glStencilMask(cState.stencilMask)
+            }
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun unsetUpStencilMark()
         {
-        //DistortedLibrary.logMessage("InternalRenderState: enabling blending");
-        GLES30.glEnable(GLES30.GL_BLEND);
+            if( sState.stencilTest!=cState.stencilTest )
+            {
+                cState.stencilTest = sState.stencilTest
+
+                if( cState.stencilTest==0 )
+                {
+                    GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+                }
+                else
+                {
+                    GLES30.glEnable(GLES30.GL_STENCIL_TEST)
+                }
+            }
+            if( sState.colorMaskR!=cState.colorMaskR || sState.colorMaskG!=cState.colorMaskG ||
+                sState.colorMaskB!=cState.colorMaskB || sState.colorMaskA!=cState.colorMaskA  )
+            {
+                cState.colorMaskR = sState.colorMaskR
+                cState.colorMaskG = sState.colorMaskG
+                cState.colorMaskB = sState.colorMaskB
+                cState.colorMaskA = sState.colorMaskA
+                GLES30.glColorMask(cState.colorMaskR==1, cState.colorMaskG==1, cState.colorMaskB==1, cState.colorMaskA==1)
+            }
+            if( sState.depthMask!=cState.depthMask )
+            {
+                cState.depthMask = sState.depthMask
+                GLES30.glDepthMask(cState.depthMask==1)
+            }
+            if( sState.stencilMask!=cState.stencilMask )
+            {
+                cState.stencilMask = sState.stencilMask
+                GLES30.glStencilMask(cState.stencilMask)
+            }
         }
-      }
-
-    /////////////////////////////////////////////////////
-    // 6. Change Blend function?
-    if( mState.blendSrc!=cState.blendSrc || mState.blendDst!=cState.blendDst )
-      {
-      //DistortedLibrary.logMessage("InternalRenderState: setting blend function");
-      cState.blendSrc = mState.blendSrc;
-      cState.blendDst = mState.blendDst;
-      GLES30.glBlendFunc(cState.blendSrc,cState.blendDst);
-      }
-
-    /////////////////////////////////////////////////////
-    // 7. Enable/Disable Stencil Test?
-    if( mState.stencilTest!=cState.stencilTest )
-      {
-      cState.stencilTest = mState.stencilTest;
-
-      if (cState.stencilTest == 0)
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        /**
+         * Only for use by the library itself.
+         *
+         * @y.exclude
+         */
+        fun useStencilMark()
         {
-        //DistortedLibrary.logMessage("InternalRenderState: disabling stencil test");
-        GLES30.glDisable(GLES30.GL_STENCIL_TEST);
+            sState.stencilMask = cState.stencilMask
+
+            if( cState.stencilTest!=1 )
+            {
+                cState.stencilTest = 1
+                GLES30.glEnable(GLES30.GL_STENCIL_TEST)
+            }
+
+            sState.stencilFuncFunc = cState.stencilFuncFunc
+            sState.stencilFuncRef  = cState.stencilFuncRef
+            sState.stencilFuncMask = cState.stencilFuncMask
+
+            if( cState.stencilFuncFunc!=GLES30.GL_EQUAL || cState.stencilFuncRef!=1 || cState.stencilFuncMask!=STENCIL_MASK )
+            {
+                cState.stencilFuncFunc = GLES30.GL_EQUAL
+                cState.stencilFuncRef  = 1
+                cState.stencilFuncMask = STENCIL_MASK
+                GLES30.glStencilFunc(cState.stencilFuncFunc, cState.stencilFuncRef, cState.stencilFuncMask)
+            }
+
+            sState.stencilMask = cState.stencilMask
+
+            if( cState.stencilMask!=0x00 )
+            {
+                cState.stencilMask = 0x00
+                GLES30.glStencilMask(cState.stencilMask)
+            }
+
+            sState.depthMask = cState.depthMask
+
+            if( cState.depthMask!=0 )
+            {
+                cState.depthMask = 0
+                GLES30.glDepthMask(false)
+            }
+
+            sState.depthTest = cState.depthTest
+
+            if( cState.depthTest!=0 )
+            {
+                cState.depthTest = 0
+                GLES30.glDisable(GLES30.GL_DEPTH_TEST)
+            }
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        /**
+         * Only for use by the library itself.
+         *
+         * @y.exclude
+         */
+        fun unuseStencilMark()
         {
-        //DistortedLibrary.logMessage("InternalRenderState: enabling stencil test");
-        GLES30.glEnable(GLES30.GL_STENCIL_TEST);
+            if( sState.stencilTest!=cState.stencilTest )
+            {
+                cState.stencilTest = sState.stencilTest
+
+                if( cState.stencilTest==0 )
+                {
+                    GLES30.glDisable(GLES30.GL_STENCIL_TEST)
+                }
+                else
+                {
+                    GLES30.glEnable(GLES30.GL_STENCIL_TEST)
+                }
+            }
+            if( sState.stencilFuncFunc!=cState.stencilFuncFunc ||
+                sState.stencilFuncRef !=cState.stencilFuncRef  ||
+                sState.stencilFuncMask!=cState.stencilFuncMask  )
+            {
+                cState.stencilFuncFunc = sState.stencilFuncFunc
+                cState.stencilFuncRef  = sState.stencilFuncRef
+                cState.stencilFuncMask = sState.stencilFuncMask
+                GLES30.glStencilFunc(cState.stencilFuncFunc, cState.stencilFuncRef, cState.stencilFuncMask)
+            }
+            if( sState.stencilMask!=cState.stencilMask )
+            {
+                cState.stencilMask = sState.stencilMask
+                GLES30.glStencilMask(cState.stencilMask)
+            }
+            if( sState.depthMask!=cState.depthMask )
+            {
+                cState.depthMask = sState.depthMask
+                GLES30.glDepthMask(cState.depthMask==1)
+            }
+            if( sState.depthTest!=cState.depthTest )
+            {
+                cState.depthTest = sState.depthTest
+
+                if( cState.depthTest==0 )
+                {
+                    GLES30.glDisable(GLES30.GL_DEPTH_TEST)
+                }
+                else
+                {
+                    GLES30.glEnable(GLES30.GL_DEPTH_TEST)
+                }
+            }
         }
-      }
-
-    /////////////////////////////////////////////////////
-    // 8. Adjust Stencil function?
-    if( mState.stencilFuncFunc!=cState.stencilFuncFunc || mState.stencilFuncRef!=cState.stencilFuncRef || mState.stencilFuncMask!=cState.stencilFuncMask )
-      {
-      //DistortedLibrary.logMessage("InternalRenderState: setting stencil function");
-      cState.stencilFuncFunc = mState.stencilFuncFunc;
-      cState.stencilFuncRef  = mState.stencilFuncRef ;
-      cState.stencilFuncMask = mState.stencilFuncMask;
-      GLES30.glStencilFunc(cState.stencilFuncFunc,cState.stencilFuncRef,cState.stencilFuncMask);
-      }
-
-    /////////////////////////////////////////////////////
-    // 9. Adjust Stencil operation?
-    if( mState.stencilOpSfail!=cState.stencilOpSfail || mState.stencilOpDpfail!=cState.stencilOpDpfail || mState.stencilOpDppass!=cState.stencilOpDppass )
-      {
-      //DistortedLibrary.logMessage("InternalRenderState: setting stencil op");
-      cState.stencilOpSfail = mState.stencilOpSfail;
-      cState.stencilOpDpfail= mState.stencilOpDpfail;
-      cState.stencilOpDppass= mState.stencilOpDppass;
-      GLES30.glStencilOp(cState.stencilOpSfail,cState.stencilOpDpfail,cState.stencilOpDppass);
-      }
-
-    /////////////////////////////////////////////////////
-    // 10. Write to Stencil buffer?
-    if( mState.stencilMask!=cState.stencilMask )
-      {
-      //DistortedLibrary.logMessage("InternalRenderState: setting stencil mask");
-      cState.stencilMask = mState.stencilMask;
-      GLES30.glStencilMask(cState.stencilMask);
-      }
-
-    /////////////////////////////////////////////////////
-    // 11. Clear buffers?
-    if( mClear!=0 )
-      {
-      //DistortedLibrary.logMessage("InternalRenderState: clearing buffer");
-      GLES30.glClear(mClear);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glColorMask(boolean r, boolean g, boolean b, boolean a)
-    {
-    mState.colorMaskR = (r ? 1:0);
-    mState.colorMaskG = (g ? 1:0);
-    mState.colorMaskB = (b ? 1:0);
-    mState.colorMaskA = (a ? 1:0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glDepthMask(boolean mask)
-    {
-    mState.depthMask = (mask ? 1:0);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glStencilMask(int mask)
-    {
-    mState.stencilMask = mask;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glEnable(int test)
-    {
-         if( test==GLES30.GL_DEPTH_TEST   ) mState.depthTest   = 1;
-    else if( test==GLES30.GL_STENCIL_TEST ) mState.stencilTest = 1;
-    else if( test==GLES30.GL_BLEND        ) mState.blend       = 1;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glDisable(int test)
-    {
-         if( test==GLES30.GL_DEPTH_TEST   ) mState.depthTest   = 0;
-    else if( test==GLES30.GL_STENCIL_TEST ) mState.stencilTest = 0;
-    else if( test==GLES30.GL_BLEND        ) mState.blend       = 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glStencilFunc(int func, int ref, int mask)
-    {
-    mState.stencilFuncFunc = func;
-    mState.stencilFuncRef  = ref;
-    mState.stencilFuncMask = mask;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glStencilOp(int sfail, int dpfail, int dppass)
-    {
-    mState.stencilOpSfail = sfail;
-    mState.stencilOpDpfail= dpfail;
-    mState.stencilOpDppass= dppass;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glDepthFunc(int func)
-    {
-    mState.depthFunc = func;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glBlendFunc(int src, int dst)
-    {
-    mState.blendSrc = src;
-    mState.blendDst = dst;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void glClear(int mask)
-    {
-    mClear = mask;
     }
 }
diff --git a/src/main/java/org/distorted/library/main/InternalStackFrame.kt b/src/main/java/org/distorted/library/main/InternalStackFrame.kt
index 2efc825..b647577 100644
--- a/src/main/java/org/distorted/library/main/InternalStackFrame.kt
+++ b/src/main/java/org/distorted/library/main/InternalStackFrame.kt
@@ -17,14 +17,11 @@
 // 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.EffectType;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
+import org.distorted.library.effect.EffectType
+import org.distorted.library.main.InternalMaster.Slave
+import java.util.LinkedList
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -33,374 +30,282 @@ import java.util.LinkedList;
  * another Activity which also wants to use the library. When that happens, we create a new 'frame',
  * remember the old one. When the second Activity ends and we come back to the first, we destroy the
  * second frame and recall the first.
- * <p>
+ *
  * Not part of public API, do not document
  *
  * @y.exclude
  */
-public class InternalStackFrame
+class InternalStackFrame internal constructor(val taskId: Long)
 {
-  private static class Job
-    {
-    InternalObject object;
-    int action;
-
-    Job(InternalObject o, int a)
-      {
-      object = o;
-      action = a;
-      }
-    }
+    class Job(var internalObject: InternalObject, var action: Int)
 
-  private static final LinkedList<InternalObject> mCommonDoneList = new LinkedList<>(); //
-  private static final HashMap<Long,Job> mCommonToDoMap           = new HashMap<>();    // Common
-  private static long mCommonNextClientID                         = 0;                  // InternalObject
-  private static long mCommonNextSystemID                         = 0;                  // (postprocessing)
+    //////////////////////////////////////////////////////////////////
+    private val mDoneList = LinkedList<InternalObject>() //
+    private val mToDoMap = HashMap<Long, Job>()          //
+    private var mTaskId: Long = 0                        //
+    private var mNextClientID: Long = 0                  // InternalObject
+    private var mNextSystemID: Long = 0                  //
 
-  //////////////////////////////////////////////////////////////////
-  private final LinkedList<InternalObject> mDoneList;             //
-  private final HashMap<Long,Job> mToDoMap;                       //
-  private final long mTaskId;                                     //
-  private long mNextClientID;                                     // InternalObject
-  private long mNextSystemID;                                     //
+    //////////////////////////////////////////////////////////////////
+    var isInitialized: Boolean = false             // DistortedLibrary
 
-  //////////////////////////////////////////////////////////////////
-  private boolean mInitialized;                                   // DistortedLibrary
+    //////////////////////////////////////////////////////////////////
+    private val mMax: IntArray                          // EffectQueue
 
-  //////////////////////////////////////////////////////////////////
-  private final int[] mMax;                                       // EffectQueue
+    //////////////////////////////////////////////////////////////////
+    private var mNextEffectsID: Long = 0          // DistortedEffects;
 
-  //////////////////////////////////////////////////////////////////
-  private long mNextEffectsID;                                    // DistortedEffects;
+    //////////////////////////////////////////////////////////////////
+    private var mNextEffectID: Long = 0                     // Effect;
 
-  //////////////////////////////////////////////////////////////////
-  private long mNextEffectID;                                     // Effect;
+    //////////////////////////////////////////////////////////////////
+    private val mMapNodeID = HashMap<ArrayList<Long>, InternalNodeData>() // InternalNodeData
+    private var mNextNodeID: Long = 0
 
-  //////////////////////////////////////////////////////////////////
-  private final HashMap<ArrayList<Long>, InternalNodeData> mMapNodeID;// InternalNodeData
-  private long mNextNodeID;
+    //////////////////////////////////////////////////////////////////
+    val mSet: ArrayList<Slave> = ArrayList()          // InternalMaster
 
-  //////////////////////////////////////////////////////////////////
-  private final ArrayList<InternalMaster.Slave> mSlaves;          // InternalMaster
+    //////////////////////////////////////////////////////////////////
+    private var mNextQueueID: Long = 1                  // EffectQueue
 
-  ////////////////////////////////////////////////////////////////// EffectQueue
-  private long mNextQueueID;                                      //
-  private final HashMap<ArrayList<Long>,Long> mMapID;             // maps lists of Effect IDs (longs)
-                                                                  // to a single long - the queue ID.
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  InternalStackFrame(long taskID)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    val mMap: HashMap<ArrayList<Long>, Long> = HashMap() // maps lists of Effect IDs (longs)
+                                                         // to a single long - the queue ID.
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    init
     {
-    mDoneList     = new LinkedList<>();
-    mToDoMap      = new HashMap<>();
-    mMapNodeID    = new HashMap<>();
-    mSlaves       = new ArrayList<>();
-    mMapID        = new HashMap<>();
-    mNextEffectsID= 0;
-    mNextClientID = 0;
-    mNextSystemID = 0;
-    mNextNodeID   = 0;
-    mNextQueueID  = 1;
-    mTaskId       = taskID;
-    mMax          = new int[EffectType.LENGTH];
-
-    EffectType.reset(mMax);
+        mTaskId = taskId
+        mMax = IntArray(EffectType.LENGTH)
+        EffectType.reset(mMax)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getTaskID() = mTaskId
 
-  long getTaskId()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun onPause()
     {
-    return mTaskId;
+        onPauseGeneric(mDoneList, mToDoMap)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void onPauseCommon()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun toDo()
     {
-    onPauseGeneric(mCommonDoneList,mCommonToDoMap);
+        toDoGeneric(mDoneList, mToDoMap)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void onPause()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun generateID(type: Int, storage: Int): Long
     {
-    onPauseGeneric(mDoneList,mToDoMap);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void onPauseGeneric(LinkedList<InternalObject> list, HashMap<Long,Job> map)
-    {
-    int num = list.size();
-
-    try
-      {
-      for (int i=0; i<num; i++)
+        return if (storage==InternalObject.STORAGE_PRIVATE)
         {
-        InternalObject object = list.removeFirst();
-        Job job = new Job(object, InternalObject.JOB_CREATE);
-        map.put(object.getID(),job);
-        object.recreate();
+            if (type==InternalObject.TYPE_SYST) --mNextSystemID else ++mNextClientID
+        }
+        else
+        {
+            if (type==InternalObject.TYPE_SYST) --mCommonNextSystemID else ++mCommonNextClientID
         }
-      }
-    catch( Exception ignored )
-      {
-      // something else removed an object in the meantime; ignore
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void toDo()
-    {
-    toDoGeneric(mDoneList,mToDoMap);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void toDoCommon()
-    {
-    toDoGeneric(mCommonDoneList,mCommonToDoMap);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void toDoGeneric(LinkedList<InternalObject> list, HashMap<Long,Job> map)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun addToDoneList(obj: InternalObject, storage: Int)
     {
-    for(Long key: map.keySet())
-      {
-      Job job = map.get(key);
-      InternalObject object = job.object;
-
-      if( job.action==InternalObject.JOB_CREATE )
+        if (storage==InternalObject.STORAGE_PRIVATE)
         {
-        object.create();
-        list.add(object);
+            if (!mDoneList.contains(obj)) mDoneList.add(obj)
         }
-      else if( job.action==InternalObject.JOB_DELETE )
+        else
         {
-        object.delete();
+            if (!mCommonDoneList.contains(obj)) mCommonDoneList.add(obj)
         }
-      }
-
-    map.clear();
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void cleanCommon()
-    {
-    mCommonDoneList.clear();
-    mCommonToDoMap.clear();
-    mCommonNextClientID = 0;
-    mCommonNextSystemID = 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  long generateID(int type, int storage)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun removeFromDoneList(obj: InternalObject, storage: Int)
     {
-    if( storage==InternalObject.STORAGE_PRIVATE )
-      {
-      return type==InternalObject.TYPE_SYST ? --mNextSystemID : ++mNextClientID;
-      }
-    else
-      {
-      return type==InternalObject.TYPE_SYST ? --mCommonNextSystemID : ++mCommonNextClientID;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void addToDoneList(InternalObject obj, int storage)
-    {
-    if( storage==InternalObject.STORAGE_PRIVATE )
-      {
-      if( !mDoneList.contains(obj) )
+        if (storage==InternalObject.STORAGE_PRIVATE)
         {
-        mDoneList.add(obj);
+            mDoneList.remove(obj)
+        }
+        else
+        {
+            mCommonDoneList.remove(obj)
         }
-      }
-    else
-      {
-      if( !mCommonDoneList.contains(obj) ) mCommonDoneList.add(obj);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void removeFromDoneList(InternalObject obj, int storage)
-    {
-    if( storage==InternalObject.STORAGE_PRIVATE )
-      {
-      mDoneList.remove(obj);
-      }
-    else
-      {
-      mCommonDoneList.remove(obj);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void markFor(InternalObject obj, long id, int storage, int jobType)
-    {
-    if( storage==InternalObject.STORAGE_PRIVATE )
-      {
-      mDoneList.remove(obj);
-      mToDoMap.put(id, new Job(obj,jobType) );
-      }
-    else
-      {
-      mCommonDoneList.remove(obj);
-      mCommonToDoMap.put(id, new Job(obj,jobType) );
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean isInitialized()
-    {
-    return mInitialized;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setInitialized(boolean init)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun markFor(obj: InternalObject, id: Long, storage: Int, jobType: Int)
     {
-    mInitialized = init;
+        if (storage==InternalObject.STORAGE_PRIVATE)
+        {
+            mDoneList.remove(obj)
+            mToDoMap[id] = Job(obj,jobType)
+        }
+        else
+        {
+            mCommonDoneList.remove(obj)
+            mCommonToDoMap[id] = Job(obj,jobType)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    val nextEffectsID: Long get() = ++mNextEffectsID
+    val nextEffectID : Long get() = mNextEffectID++
 
-  long getNextEffectsID()
-    {
-    return ++mNextEffectsID;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getMax(index: Int) = mMax[index]
 
-  long getNextEffectID()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setMax(index: Int, max: Int): Boolean
     {
-    return mNextEffectID++;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        if( !isInitialized||max<mMax[index] )
+        {
+            mMax[index] = max
+            return true
+        }
 
-  int getMax(int index)
-    {
-    return mMax[index];
+        return false
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean setMax(int index, int max)
-    {
-    if( !mInitialized || max<mMax[index] )
-      {
-      mMax[index] = max;
-      return true;
-      }
-
-    return false;
-    }
+    val nextQueueID: Long
+        get()
+        {
+            mNextQueueID++
+            return mNextQueueID-1
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getMapID(key: ArrayList<Long>) = mMapNodeID[key]
 
-  public HashMap<ArrayList<Long>,Long> getMap()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun putNewDataToMap(key: ArrayList<Long>): InternalNodeData
     {
-    return mMapID;
+        val data = InternalNodeData(++mNextNodeID, key)
+        mMapNodeID[key] = data
+        return data
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public long getNextQueueID()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun removeKeyFromMap(key: ArrayList<Long>)
     {
-    mNextQueueID++;
-
-    return mNextQueueID-1;
+        mMapNodeID.remove(key)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  InternalNodeData getMapID(ArrayList<Long> key)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun debugLists(frameMarker: String)
     {
-    return mMapNodeID.get(key);
+        debugListsGeneric(mDoneList, mToDoMap, frameMarker)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  InternalNodeData putNewDataToMap(ArrayList<Long> key)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun debugMap(frameMarker: String)
     {
-    InternalNodeData data = new InternalNodeData(++mNextNodeID,key);
-    mMapNodeID.put(key,data);
-    return data;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        DistortedLibrary.logMessage("InternalStackFrame: $frameMarker")
+        var tmp: InternalNodeData?
 
-  void removeKeyFromMap(ArrayList<Long> key)
-    {
-    mMapNodeID.remove(key);
+        for (key in mMapNodeID.keys)
+        {
+            tmp = mMapNodeID[key]
+            DistortedLibrary.logMessage("InternalStackFrame: NodeID: "+tmp!!.ID+" <-- "+key)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  ArrayList<InternalMaster.Slave> getSet()
+    companion object
     {
-    return mSlaves;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        private val mCommonDoneList = LinkedList<InternalObject>() //
+        private val mCommonToDoMap = HashMap<Long, Job>()          // Common
+        private var mCommonNextClientID: Long = 0                  // InternalObject
+        private var mCommonNextSystemID: Long = 0                  // (postprocessing)
 
-  void debugLists(String frameMarker)
-    {
-    debugListsGeneric(mDoneList,mToDoMap,frameMarker);
-    }
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun onPauseCommon()
+        {
+            onPauseGeneric(mCommonDoneList, mCommonToDoMap)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun onPauseGeneric(list: LinkedList<InternalObject>, map: HashMap<Long, Job>)
+        {
+            val num = list.size
+
+            try
+            {
+                for (i in 0 until num)
+                {
+                    val intObject = list.removeFirst()
+                    val job = Job(intObject,InternalObject.JOB_CREATE)
+                    map[intObject.iD] = job
+                    intObject.recreate()
+                }
+            }
+            catch (ignored: Exception)
+            {
+                // something else removed an object in the meantime; ignore
+            }
+        }
 
-  static void debugCommonList()
-    {
-    debugListsGeneric(mCommonDoneList,mCommonToDoMap,"Common");
-    }
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun toDoCommon()
+        {
+            toDoGeneric(mCommonDoneList, mCommonToDoMap)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun toDoGeneric(list: LinkedList<InternalObject>, map: HashMap<Long, Job>)
+        {
+            for (key in map.keys)
+            {
+                val job = map[key]!!
+                val intObject = job.internalObject
+
+                if (job.action==InternalObject.JOB_CREATE)
+                {
+                    intObject.create()
+                    list.add(intObject)
+                }
+                else if ( job.action==InternalObject.JOB_DELETE )
+                {
+                    intObject.delete()
+                }
+            }
+
+            map.clear()
+        }
 
-  static void debugListsGeneric(LinkedList<InternalObject> list, HashMap<Long,Job> map,String frameMarker)
-    {
-    DistortedLibrary.logMessage("InternalStackFrame: "+frameMarker);
-    DistortedLibrary.logMessage("InternalStackFrame:  Done list:");
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun cleanCommon()
+        {
+            mCommonDoneList.clear()
+            mCommonToDoMap.clear()
+            mCommonNextClientID = 0
+            mCommonNextSystemID = 0
+        }
 
-    for(InternalObject object : list)
-      {
-      object.print("  ");
-      }
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun debugCommonList()
+        {
+            debugListsGeneric(mCommonDoneList, mCommonToDoMap, "Common")
+        }
 
-    DistortedLibrary.logMessage("InternalStackFrame: ToDo list:");
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun debugListsGeneric(list: LinkedList<InternalObject>, map: HashMap<Long, Job>, frameMarker: String)
+        {
+            DistortedLibrary.logMessage("InternalStackFrame: $frameMarker")
+            DistortedLibrary.logMessage("InternalStackFrame:  Done list:")
 
-    Job job;
+            for (`object` in list)
+            {
+                `object`.print("  ")
+            }
 
-    for(Long key: map.keySet())
-      {
-      job = map.get(key);
-      job.object.print(job.action==InternalObject.JOB_CREATE ? " create":" delete");
-      }
-    }
+            DistortedLibrary.logMessage("InternalStackFrame: ToDo list:")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            var job: Job?
 
-  void debugMap(String frameMarker)
-    {
-    DistortedLibrary.logMessage("InternalStackFrame: "+frameMarker);
-    InternalNodeData tmp;
-
-    for(ArrayList<Long> key: mMapNodeID.keySet())
-      {
-      tmp = mMapNodeID.get(key);
-      DistortedLibrary.logMessage("InternalStackFrame: NodeID: "+tmp.ID+" <-- "+key);
-      }
+            for (key in map.keys)
+            {
+                job = map[key]
+                job!!.internalObject.print(if (job.action==InternalObject.JOB_CREATE) " create" else " delete")
+            }
+        }
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/main/InternalStackFrameList.kt b/src/main/java/org/distorted/library/main/InternalStackFrameList.kt
index b38a21f..4828f1f 100644
--- a/src/main/java/org/distorted/library/main/InternalStackFrameList.kt
+++ b/src/main/java/org/distorted/library/main/InternalStackFrameList.kt
@@ -17,298 +17,225 @@
 // 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.message.EffectMessageSender;
-
-import java.util.ArrayList;
-import java.util.HashMap;
+import org.distorted.library.main.InternalMaster.Slave
+import org.distorted.library.message.EffectMessageSender.Companion.stopSending
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Implements a List of Frames (see InternalStackFrame)
- * <p>
+ *
  * Not part of public API, do not document
  *
  * @y.exclude
  */
-public class InternalStackFrameList
+object InternalStackFrameList
 {
-  private final static Object mLock = new Object();
-  private static boolean mToDo = false;
-  private static InternalStackFrame mCurrentFrame = null;
-  private static final ArrayList<InternalStackFrame> mFrameList = new ArrayList<>();
+    private val mLock = Any()
+    private var mToDo = false
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    var currentFrame: InternalStackFrame? = null
+        private set
+    private val mFrameList = ArrayList<InternalStackFrame?>()
 
-  static void onCreate(long taskId)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    @JvmStatic
+    fun onCreate(taskId: Long)
     {
-    int num = mFrameList.size();
-    InternalStackFrame frame;
-    boolean found = false;
-
-    for(int i=0; i<num; i++)
-      {
-      frame = mFrameList.get(i);
+        val num = mFrameList.size
+        var frame: InternalStackFrame
+        var found = false
 
-      if( frame.getTaskId() == taskId )
+        for (i in 0 until num)
         {
-        mCurrentFrame = frame;
-        found = true;
-        break;
+            frame = mFrameList[i]!!
+
+            if( frame.getTaskID()==taskId )
+            {
+                currentFrame = frame
+                found = true
+                break
+            }
         }
-      }
 
-    if( !found )
-      {
-      synchronized(mLock)
+        if (!found)
         {
-        mCurrentFrame = new InternalStackFrame(taskId);
-        mFrameList.add(mCurrentFrame);
+            synchronized(mLock)
+            {
+                currentFrame = InternalStackFrame(taskId)
+                mFrameList.add(currentFrame)
+            }
         }
-      }
 
-    mCurrentFrame.setInitialized(false);
+        currentFrame!!.isInitialized = false
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void onResume(long taskId)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    @JvmStatic
+    fun onResume(taskId: Long)
     {
-    int num = mFrameList.size();
-    InternalStackFrame frame;
-
-    for(int i=0; i<num; i++)
-      {
-      frame = mFrameList.get(i);
+        val num = mFrameList.size
+        var frame: InternalStackFrame
 
-      if( frame.getTaskId() == taskId )
+        for (i in 0 until num)
         {
-        mCurrentFrame = frame;
-        break;
+            frame = mFrameList[i]!!
+
+            if( frame.getTaskID()==taskId )
+            {
+                currentFrame = frame
+                break
+            }
         }
-      }
 
-    mCurrentFrame.setInitialized(false);
+        currentFrame!!.isInitialized = false
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void onPause(long taskId)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    @JvmStatic
+    fun onPause(taskId: Long)
     {
-    int num = mFrameList.size();
+        val num = mFrameList.size
 
-    for(int i=0; i<num; i++)
-      {
-      if( mFrameList.get(i).getTaskId() == taskId )
+        for (i in 0 until num)
         {
-        synchronized(mLock)
-          {
-          mCurrentFrame.onPause();
-          InternalStackFrame.onPauseCommon();
-          mToDo = true;
-          }
-
-        break;
+            if( mFrameList[i]!!.getTaskID()==taskId )
+            {
+                synchronized(mLock)
+                {
+                    currentFrame!!.onPause()
+                    InternalStackFrame.onPauseCommon()
+                    mToDo = true
+                }
+
+                break
+            }
         }
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void onDestroy(long taskId)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    @JvmStatic
+    fun onDestroy(taskId: Long)
     {
-    int num = mFrameList.size();
+        val num = mFrameList.size
 
-    for(int i=0; i<num; i++)
-      {
-      if( mFrameList.get(i).getTaskId() == taskId )
+        for (i in 0 until num)
         {
-        synchronized(mLock)
-          {
-          mFrameList.remove(i);
-          if( num==1 ) InternalStackFrame.cleanCommon();
-          }
-
-        break;
+            if( mFrameList[i]!!.getTaskID()==taskId )
+            {
+                synchronized(mLock)
+                {
+                    mFrameList.removeAt(i)
+                    if (num==1) InternalStackFrame.cleanCommon()
+                }
+
+                break
+            }
         }
-      }
-
-    setInitialized(false);
 
-    if( num<2 )
-      {
-      EffectMessageSender.stopSending();
-      }
+        isInitialized = false
+        if (num<2) stopSending()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @SuppressWarnings("unused")
-  static void debugLists()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    @Suppress("unused")
+    fun debugLists()
     {
-    int num = mFrameList.size();
-    InternalStackFrame frame;
-
-    InternalStackFrame.debugCommonList();
+        val num = mFrameList.size
+        var frame: InternalStackFrame?
 
-    for(int i=0; i<num; i++)
-      {
-      frame = mFrameList.get(i);
-      frame.debugLists("frame "+i);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        InternalStackFrame.debugCommonList()
 
-  @SuppressWarnings("unused")
-  static void debugMap()
-    {
-    int num = mFrameList.size();
-    InternalStackFrame frame;
-
-    for(int i=0; i<num; i++)
-      {
-      frame = mFrameList.get(i);
-      frame.debugMap("frame "+i);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// must be called from a thread holding OpenGL Context
-
-  static boolean toDo()
-    {
-    if( mToDo )
-      {
-      mToDo = false;
-
-      synchronized(mLock)
+        for (i in 0 until num)
         {
-        mCurrentFrame.toDo();
-        InternalStackFrame.toDoCommon();
+            frame = mFrameList[i]
+            frame!!.debugLists("frame $i")
         }
-      return true;
-      }
-
-    return false;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void markFor(InternalObject obj, long id, int storage, int job)
-    {
-    synchronized(mLock)
-      {
-      mCurrentFrame.markFor(obj,id,storage,job);
-      mToDo = true;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void removeFromDone(InternalObject obj, int storage)
-    {
-    synchronized(mLock)
-      {
-      mCurrentFrame.removeFromDoneList(obj,storage);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static InternalStackFrame getCurrentFrame()
-    {
-    return mCurrentFrame;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static long getNextEffectsID()
-    {
-    return mCurrentFrame.getNextEffectsID();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void setInitialized(boolean init)
-    {
-    mCurrentFrame.setInitialized(init);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static InternalNodeData getMapID(ArrayList<Long> key)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    @Suppress("unused")
+    fun debugMap()
     {
-    return mCurrentFrame.getMapID(key);
+        val num = mFrameList.size
+        for (i in 0 until num) mFrameList[i]!!.debugMap("frame $i")
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static InternalNodeData putNewDataToMap(ArrayList<Long> key)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // must be called from a thread holding OpenGL Context
+    fun toDo(): Boolean
     {
-    return mCurrentFrame.putNewDataToMap(key);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        if (mToDo)
+        {
+            mToDo = false
+
+            synchronized(mLock)
+            {
+                currentFrame!!.toDo()
+                InternalStackFrame.toDoCommon()
+            }
+            return true
+        }
 
-  static void removeKeyFromMap(ArrayList<Long> key)
-    {
-    mCurrentFrame.removeKeyFromMap(key);
+        return false
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static ArrayList<InternalMaster.Slave> getSet()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun markFor(obj: InternalObject, id: Long, storage: Int, job: Int)
     {
-    return mCurrentFrame.getSet();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static long getNextEffectID()
-    {
-    return mCurrentFrame.getNextEffectID();
+        synchronized(mLock)
+        {
+            currentFrame!!.markFor(obj, id, storage, job)
+            mToDo = true
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static boolean isInitialized()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun removeFromDone(obj: InternalObject, storage: Int)
     {
-    return mCurrentFrame.isInitialized();
+        synchronized(mLock)
+        {
+            currentFrame!!.removeFromDoneList(obj, storage)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    @JvmStatic val nextEffectsID: Long get() = currentFrame!!.nextEffectsID
 
-  public static int getMax(int index)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getMapID(key: ArrayList<Long>): InternalNodeData?
     {
-    return mCurrentFrame.getMax(index);
+        return currentFrame!!.getMapID(key)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static boolean setMax(int index,int max)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun putNewDataToMap(key: ArrayList<Long>): InternalNodeData
     {
-    return mCurrentFrame.setMax(index,max);
+        return currentFrame!!.putNewDataToMap(key)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static HashMap<ArrayList<Long>,Long> getMap()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun removeKeyFromMap(key: ArrayList<Long>)
     {
-    return mCurrentFrame.getMap();
+        currentFrame!!.removeKeyFromMap(key)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    val mSet: ArrayList<Slave> get() = currentFrame!!.mSet
 
-  public static long getNextQueueID()
-    {
-    return mCurrentFrame.getNextQueueID();
-    }
+    val nextEffectID: Long get() = currentFrame!!.nextEffectID
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    @JvmStatic
+    var isInitialized: Boolean
+        get() = currentFrame!!.isInitialized
+        set(init)
+        {
+            currentFrame!!.isInitialized = init
+        }
 
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun getMax(index: Int) = currentFrame!!.getMax(index)
+    fun setMax(index: Int, max: Int) = currentFrame!!.setMax(index, max)
+    val map: HashMap<ArrayList<Long>, Long> get() = currentFrame!!.mMap
+    val nextQueueID: Long get() = currentFrame!!.nextQueueID
 }
diff --git a/src/main/java/org/distorted/library/main/InternalSurface.kt b/src/main/java/org/distorted/library/main/InternalSurface.kt
index 211e0ca..efc2c33 100644
--- a/src/main/java/org/distorted/library/main/InternalSurface.kt
+++ b/src/main/java/org/distorted/library/main/InternalSurface.kt
@@ -17,66 +17,56 @@
 // 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
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // common parent class of Texture & OutputSurface; so that we can store either in Nodes.
-
-abstract class InternalSurface extends InternalObject
+abstract class InternalSurface(create: Int, numfbos: Int, numcolors: Int, type: Int, storage: Int) : InternalObject(type, storage)
 {
-  int mColorCreated;
-  int mNumColors;
-  int mNumFBOs;
-  int[] mColorH;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    @JvmField var mColorCreated: Int = create
+    @JvmField var mNumColors   : Int = numcolors
+    @JvmField var mNumFBOs     : Int = numfbos
+    @JvmField var mColorH      : IntArray? = null
 
-  InternalSurface(int create, int numfbos, int numcolors, int type, int storage)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    init
     {
-    super(type,storage);
-
-    mNumFBOs      = numfbos;
-    mNumColors    = numcolors;
-    mColorCreated = create;
-
-    allocateColor();
+        allocateColor()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void allocateColor()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun allocateColor()
     {
-    int total = mNumFBOs*mNumColors;
+        val total = mNumFBOs*mNumColors
 
-    if( total>0 )
-      {
-      mColorH = new int[total];
-      for( int i=0; i<total; i++ )  mColorH[i] = 0;
-      }
+        if( total>0 )
+        {
+            mColorH = IntArray(total)
+            for (i in 0 until total) mColorH!![i] = 0
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// debugging only
-
-  String printDetails()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // debugging only
+    override fun printDetails(): String
     {
-    return getClass().getSimpleName();
+        return javaClass.simpleName
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean setAsInput()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setAsInput(): Boolean
     {
-    if( mColorH[0]>0 )
-      {
-      GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
-      GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorH[0]);
-      return true;
-      }
+        val handle = mColorH!![0]
+
+        if( handle>0 )
+        {
+            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
+            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, handle)
+            return true
+        }
 
-    return false;
+        return false
     }
 }
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.kt b/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.kt
index 8969086..4b2bd7e 100644
--- a/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.kt
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.kt
@@ -138,7 +138,7 @@ class UniformBlockAssociation
     {
         val builder = StringBuilder()
 
-        builder.append(mUBO.id)
+        builder.append(mUBO.iD)
         builder.append(' ')
 
         for (i in 0..7)
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.kt b/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.kt
index d3dfe64..731db20 100644
--- a/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.kt
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.kt
@@ -82,7 +82,7 @@ class UniformBlockFloatUniforms
     {
         val builder = StringBuilder()
 
-        builder.append(if (mReallyUseUBO) mUBO!!.id else "NOT USED")
+        builder.append(if (mReallyUseUBO) mUBO!!.iD else "NOT USED")
         builder.append(':')
 
         for (i in 0..5)
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.kt b/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.kt
index 46d0498..68842ca 100644
--- a/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.kt
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.kt
@@ -98,7 +98,7 @@ class UniformBlockIntUniforms
     {
         val builder = StringBuilder()
 
-        builder.append(if (mReallyUseUBO) mUBO!!.id else "NOT USED")
+        builder.append(if (mReallyUseUBO) mUBO!!.iD else "NOT USED")
         builder.append(':')
 
         for (i in 0..5)
