commit 033e8feb437434b832bc0689315c416db1f10bc0
Author: LeszekKoltunski <leszek@koltunski.pl>
Date:   Mon Apr 28 16:01:31 2025 +0200

    begin to migrate the 'mesh' package.

diff --git a/src/main/java/org/distorted/library/mesh/DeferredJobs.kt b/src/main/java/org/distorted/library/mesh/DeferredJobs.kt
index 5d4e65a..60fa2af 100644
--- a/src/main/java/org/distorted/library/mesh/DeferredJobs.kt
+++ b/src/main/java/org/distorted/library/mesh/DeferredJobs.kt
@@ -18,15 +18,13 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-import org.distorted.library.effect.MatrixEffect;
-import org.distorted.library.effect.VertexEffect;
-import org.distorted.library.effectqueue.EffectQueueVertex;
-import org.distorted.library.main.DistortedLibrary;
-import org.distorted.library.type.Static4D;
-
-import java.util.ArrayList;
+import org.distorted.library.effect.MatrixEffect
+import org.distorted.library.effect.VertexEffect
+import org.distorted.library.effectqueue.EffectQueueVertex
+import org.distorted.library.main.DistortedLibrary
+import org.distorted.library.type.Static4D
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -34,402 +32,365 @@ import java.util.ArrayList;
  *
  * @y.exclude
  */
-public class DeferredJobs
-  {
-  private static final int JOB_TYPE_VERTEX       = 0;
-  private static final int JOB_TYPE_MATRIX       = 1;
-  private static final int JOB_TYPE_MERGE_TEX    = 2;
-  private static final int JOB_TYPE_MERGE_EFF    = 3;
-  private static final int JOB_TYPE_JOIN         = 4;
-  private static final int JOB_TYPE_COPY         = 5;
-  private static final int JOB_TYPE_TEXTURE      = 6;
-  private static final int JOB_TYPE_ASSOC        = 7;
-  private static final int JOB_TYPE_CENTER       = 8;
-  private static final int JOB_TYPE_ADD_EMPTY_TEX= 9;
-  private static final int JOB_TYPE_NOT_AFFECTED = 10;
-
-  private static final ArrayList<JobNode> mJobs = new ArrayList<>();
-
-  //////////////////////////////////////////////////////////////////////////
-
-  private static class Job
+object DeferredJobs
+{
+    private const val JOB_TYPE_VERTEX        = 0
+    private const val JOB_TYPE_MATRIX        = 1
+    private const val JOB_TYPE_MERGE_TEX     = 2
+    private const val JOB_TYPE_MERGE_EFF     = 3
+    private const val JOB_TYPE_JOIN          = 4
+    private const val JOB_TYPE_COPY          = 5
+    private const val JOB_TYPE_TEXTURE       = 6
+    private const val JOB_TYPE_ASSOC         = 7
+    private const val JOB_TYPE_CENTER        = 8
+    private const val JOB_TYPE_ADD_EMPTY_TEX = 9
+    private const val JOB_TYPE_NOT_AFFECTED  = 10
+
+    private val mJobs = ArrayList<JobNode>()
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun removeNode(node: JobNode)
     {
-    private final int mType;
-    private final MeshBase mTarget;
-    private final MeshBase[] mSource;
-    private final MatrixEffect mMatrixEffect;
-    private final Static4D[] mMaps;
-    private final int mComp, mAndAssoc, mEquAssoc;
-    private final float mX,mY,mZ;
-    private final int[] mComps;
-
-    private EffectQueueVertex mVertexEffects;
-
-    Job(int type, MeshBase target, MeshBase[] source, VertexEffect vEff, MatrixEffect mEff,
-        Static4D[] maps, int comp, int and, int equ, float x, float y, float z, int[] comps)
-      {
-      mType     = type;
-      mTarget   = target;
-      mSource   = source;
-      mMaps     = maps;
-      mComp     = comp;
-      mAndAssoc = and;
-      mEquAssoc = equ;
-      mX        = x;
-      mY        = y;
-      mZ        = z;
-      mComps    = comps;
-
-      if( vEff!=null )
-        {
-        mVertexEffects= new EffectQueueVertex();
-        mVertexEffects.add(vEff);
-        }
+        mJobs.remove(node)
+        var jn: JobNode?
 
-      mMatrixEffect = mEff;
-      }
+        val numPrev = node.mPrevJobs!!.size
 
-    void addEffect(VertexEffect effect)
-      {
-      mVertexEffects.add(effect);
-      }
-
-    void execute()
-      {
-      switch(mType)
+        for (i in 0 until numPrev)
         {
-        case JOB_TYPE_VERTEX       : DistortedLibrary.adjustVertices(mTarget, mVertexEffects);
-                                     break;
-        case JOB_TYPE_MATRIX       : mTarget.applyMatrix(mMatrixEffect,mAndAssoc,mEquAssoc);
-                                     break;
-        case JOB_TYPE_MERGE_TEX    : mTarget.mergeTexComponentsNow();
-                                     break;
-        case JOB_TYPE_MERGE_EFF    : mTarget.mergeEffComponentsNow();
-                                     break;
-        case JOB_TYPE_JOIN         : mTarget.joinAttribs(mSource);
-                                     break;
-        case JOB_TYPE_COPY         : mTarget.copy(mSource[0]);
-                                     break;
-        case JOB_TYPE_TEXTURE      : mTarget.textureMap(mMaps,mComp);
-                                     break;
-        case JOB_TYPE_ASSOC        : mTarget.setEffectAssociationNow(mComp,mAndAssoc,mEquAssoc);
-                                     break;
-        case JOB_TYPE_CENTER       : mTarget.setComponentCenterNow(mComp,mX,mY,mZ);
-                                     break;
-        case JOB_TYPE_ADD_EMPTY_TEX: mTarget.addEmptyTexComponentNow();
-                                     break;
-        case JOB_TYPE_NOT_AFFECTED:  mTarget.setNotAffectedComponentsNow(mComps);
+            jn = node.mPrevJobs!![i]
+            jn!!.mNextJobs!!.remove(node)
         }
-      }
 
-    void clear()
-      {
-      if( mVertexEffects!=null ) mVertexEffects.removeAll(false);
-      }
+        val numNext = node.mNextJobs!!.size
 
-    String print()
-      {
-      switch(mType)
+        for (i in 0 until numNext)
         {
-        case JOB_TYPE_VERTEX       : return "VERTEX";
-        case JOB_TYPE_MATRIX       : return "MATRIX";
-        case JOB_TYPE_MERGE_TEX    : return "MERGE_TEX";
-        case JOB_TYPE_MERGE_EFF    : return "MERGE_EFF";
-        case JOB_TYPE_JOIN         : return "JOIN";
-        case JOB_TYPE_COPY         : return "COPY";
-        case JOB_TYPE_TEXTURE      : return "TEXTURE";
-        case JOB_TYPE_ASSOC        : return "ASSOC";
-        case JOB_TYPE_CENTER       : return "CENTER";
-        case JOB_TYPE_ADD_EMPTY_TEX: return "ADD_EMPTY_TEX";
-        case JOB_TYPE_NOT_AFFECTED : return "POSTPROC COMPS";
+            jn = node.mNextJobs!![i]
+            jn!!.mPrevJobs!!.remove(node)
         }
 
-      return null;
-      }
+        node.mJob!!.mTarget.mJobNode[0] = null
     }
 
-  //////////////////////////////////////////////////////////////////////////
-
-  static class JobNode
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun vertex(target: MeshBase, effect: VertexEffect?): JobNode
     {
-    private ArrayList<JobNode> mPrevJobs;
-    private ArrayList<JobNode> mNextJobs;
-    private Job mJob;
-
-    JobNode(Job job)
-      {
-      mPrevJobs = new ArrayList<>();
-      mNextJobs = new ArrayList<>();
-      mJob      = job;
-      }
-
-    synchronized void execute()
-      {
-      if( mPrevJobs!=null )
-        {
-        JobNode node;
-        int numPrev = mPrevJobs.size();
+        val jn = target.mJobNode[0]
 
-        for(int i=0; i<numPrev; i++)
-          {
-          node = mPrevJobs.get(0);  // removeNode() rips the executed job out, thus the 0
-          node.execute();
-          }
-
-        removeNode(this);
-        mJob.execute();
+        if (jn==null)
+        {
+            val job = Job(JOB_TYPE_VERTEX, target, null, effect, null, null, 0, 0, 0, 0f, 0f, 0f, null)
+            val node = JobNode(job)
+            mJobs.add(node)
+            return node
         }
-      }
-
-    synchronized void clear()
-      {
-      mPrevJobs.clear();
-      mPrevJobs = null;
-      mNextJobs.clear();
-      mNextJobs = null;
-
-      mJob.clear();
-      mJob = null;
-      }
-
-    void print(int level)
-      {
-      int numPrev = mPrevJobs.size();
-      int numNext = mNextJobs.size();
-
-      String str = "";
-      for(int i=0; i<level; i++) str+=" ";
-
-      str += mJob.print();
-
-      str += (" next: "+numNext+" prev: "+numPrev);
-
-      DistortedLibrary.logMessage("DeferredJobs: "+str);
-
-      for(int i=0; i<numPrev; i++)
+        else
         {
-        JobNode node = mPrevJobs.get(i);
-        node.print(level+1);
+            if (jn.mJob!!.mType==JOB_TYPE_VERTEX)
+            {
+                jn.mJob!!.addEffect(effect)
+                return jn
+            }
+            else
+            {
+                val job = Job(JOB_TYPE_VERTEX, target, null, effect, null, null, 0, 0, 0, 0f, 0f, 0f, null)
+                val node = JobNode(job)
+                node.mPrevJobs!!.add(jn)
+                jn.mNextJobs!!.add(node)
+                mJobs.add(node)
+                return node
+            }
         }
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static void removeNode(JobNode node)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun matrix(target: MeshBase, effect: MatrixEffect?, andAssoc: Int, ecuAssoc: Int): JobNode
     {
-    mJobs.remove(node);
-    JobNode jn;
-
-    int numPrev = node.mPrevJobs.size();
-
-    for(int i=0; i<numPrev; i++)
-      {
-      jn = node.mPrevJobs.get(i);
-      jn.mNextJobs.remove(node);
-      }
-
-    int numNext = node.mNextJobs.size();
+        val jn = target.mJobNode[0]
+        val job = Job(JOB_TYPE_MATRIX, target, null, null, effect, null, 0, andAssoc, ecuAssoc, 0f, 0f, 0f, null)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
+    }
 
-    for(int i=0; i<numNext; i++)
-      {
-      jn = node.mNextJobs.get(i);
-      jn.mPrevJobs.remove(node);
-      }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun mergeTex(target: MeshBase): JobNode
+    {
+        val jn = target.mJobNode[0]
+        val job = Job(JOB_TYPE_MERGE_TEX, target, null, null, null, null, 0, 0, 0, 0f, 0f, 0f, null)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
+    }
 
-    node.mJob.mTarget.mJobNode[0] = null;
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun addEmptyTex(target: MeshBase): JobNode
+    {
+        val jn = target.mJobNode[0]
+        val job = Job(JOB_TYPE_ADD_EMPTY_TEX, target, null, null, null, null, 0, 0, 0, 0f, 0f, 0f, null)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun mergeEff(target: MeshBase): JobNode
+    {
+        val jn = target.mJobNode[0]
+        val job = Job(JOB_TYPE_MERGE_EFF, target, null, null, null, null, 0, 0, 0, 0f, 0f, 0f, null)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
+    }
 
-  static JobNode vertex(MeshBase target, VertexEffect effect)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun join(target: MeshBase, meshes: Array<MeshBase>): JobNode
     {
-    JobNode jn = target.mJobNode[0];
-
-    if( jn==null )
-      {
-      Job job = new Job(JOB_TYPE_VERTEX,target,null,effect,null,null,0,0,0,0,0,0,null);
-      JobNode node = new JobNode(job);
-      mJobs.add(node);
-      return node;
-      }
-    else
-      {
-      if( jn.mJob.mType==JOB_TYPE_VERTEX )
-        {
-        jn.mJob.addEffect(effect);
-        return jn;
-        }
-      else
+        var jn: JobNode?
+
+        val job = Job(JOB_TYPE_JOIN, target, meshes, null, null, null, 0, 0, 0, 0f, 0f, 0f, null)
+        val node = JobNode(job)
+
+        for (mesh in meshes)
         {
-        Job job = new Job(JOB_TYPE_VERTEX,target,null,effect,null,null,0,0,0,0,0,0,null);
-        JobNode node = new JobNode(job);
-        node.mPrevJobs.add(jn);
-        jn.mNextJobs.add(node);
-        mJobs.add(node);
-        return node;
+            jn = mesh.mJobNode[0]
+
+            if (jn!=null)
+            {
+                node.mPrevJobs!!.add(jn)
+                jn.mNextJobs!!.add(node)
+            }
         }
-      }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        mJobs.add(node)
+        return node
+    }
 
-  static JobNode matrix(MeshBase target, MatrixEffect effect, int andAssoc, int ecuAssoc)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun copy(target: MeshBase, mesh: MeshBase): JobNode
     {
-    JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_MATRIX,target,null,null,effect,null,0,andAssoc,ecuAssoc,0,0,0,null);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
+        val jn = mesh.mJobNode[0]
+        val meshes = arrayOfNulls<MeshBase>(1)
+        meshes[0] = mesh
+        val job = Job(JOB_TYPE_COPY, target, meshes, null, null, null, 0, 0, 0, 0f, 0f, 0f, null)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static JobNode mergeTex(MeshBase target)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun textureMap(target: MeshBase, maps: Array<Static4D?>?, comp: Int): JobNode
     {
-    JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_MERGE_TEX,target,null,null,null,null,0,0,0,0,0,0,null);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
+        val jn = target.mJobNode[0]
+        val job = Job(JOB_TYPE_TEXTURE, target, null, null, null, maps, comp, 0, 0, 0f, 0f, 0f, null)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static JobNode addEmptyTex(MeshBase target)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun effectAssoc(target: MeshBase, comp: Int, andAssoc: Int, equAssoc: Int): JobNode
     {
-    JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_ADD_EMPTY_TEX,target,null,null,null,null,0,0,0,0,0,0,null);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
+        val jn = target.mJobNode[0]
+        val job = Job(JOB_TYPE_ASSOC, target, null, null, null, null, comp, andAssoc, equAssoc, 0f, 0f, 0f, null)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static JobNode mergeEff(MeshBase target)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun componentCenter(target: MeshBase, comp: Int, x: Float, y: Float, z: Float): JobNode
     {
-    JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_MERGE_EFF,target,null,null,null,null,0,0,0,0,0,0,null);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
+        val jn = target.mJobNode[0]
+        val job = Job(JOB_TYPE_CENTER, target, null, null, null, null, comp, 0, 0, x, y, z, null)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setNotAffected(target: MeshBase, comps: IntArray?): JobNode
+    {
+        val jn = target.mJobNode[0]
+        val job = Job(JOB_TYPE_NOT_AFFECTED, target, null, null, null, null, 0, 0, 0, 0f, 0f, 0f, comps)
+        val node = JobNode(job)
+        node.mPrevJobs!!.add(jn)
+        jn!!.mNextJobs!!.add(node)
+        mJobs.add(node)
+        return node
+    }
 
-  static JobNode join(MeshBase target, MeshBase[] meshes)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Only for use by the library itself.
+     *
+     * @y.exclude
+     */
+    @JvmStatic
+    fun onPause()
     {
-    JobNode jn;
+        val num = mJobs.size
+        for (i in 0 until num) mJobs[i].clear()
+        mJobs.clear()
+    }
 
-    Job job = new Job(JOB_TYPE_JOIN,target,meshes,null,null,null,0,0,0,0,0,0,null);
-    JobNode node = new JobNode(job);
+    //////////////////////////////////////////////////////////////////////////
+    class Job
+        (val mType: Int, val mTarget: MeshBase, source: Array<MeshBase?>?, vEff: VertexEffect?, mEff: MatrixEffect?,
+         maps: Array<Static4D?>?, comp: Int, and: Int, equ: Int, x: Float, y: Float, z: Float, comps: IntArray?)
+    {
+        private val mSource = source!!
+        private val mMatrixEffect: MatrixEffect
+        private val mMaps = maps!!
+        private val mComp = comp
+        private val mAndAssoc = and
+        private val mEquAssoc = equ
+        private val mX = x
+        private val mY = y
+        private val mZ = z
+        private val mComps = comps
+
+        private var mVertexEffects: EffectQueueVertex? = null
+
+        init
+        {
+            if (vEff!=null)
+            {
+                mVertexEffects = EffectQueueVertex()
+                mVertexEffects!!.add(vEff)
+            }
 
-    for (MeshBase mesh : meshes)
-      {
-      jn = mesh.mJobNode[0];
+            mMatrixEffect = mEff!!
+        }
 
-      if( jn!=null )
+        fun addEffect(effect: VertexEffect?)
         {
-        node.mPrevJobs.add(jn);
-        jn.mNextJobs.add(node);
+            mVertexEffects!!.add(effect)
         }
-      }
 
-    mJobs.add(node);
-    return node;
-    }
+        fun execute()
+        {
+            when (mType)
+            {
+                JOB_TYPE_VERTEX        -> DistortedLibrary.adjustVertices(mTarget, mVertexEffects)
+                JOB_TYPE_MATRIX        -> mTarget.applyMatrix(mMatrixEffect, mAndAssoc, mEquAssoc)
+                JOB_TYPE_MERGE_TEX     -> mTarget.mergeTexComponentsNow()
+                JOB_TYPE_MERGE_EFF     -> mTarget.mergeEffComponentsNow()
+                JOB_TYPE_JOIN          -> mTarget.joinAttribs(mSource)
+                JOB_TYPE_COPY          -> mTarget.copy(mSource[0])
+                JOB_TYPE_TEXTURE       -> mTarget.textureMap(mMaps, mComp)
+                JOB_TYPE_ASSOC         -> mTarget.setEffectAssociationNow(mComp, mAndAssoc, mEquAssoc)
+                JOB_TYPE_CENTER        -> mTarget.setComponentCenterNow(mComp, mX, mY, mZ)
+                JOB_TYPE_ADD_EMPTY_TEX -> mTarget.addEmptyTexComponentNow()
+                JOB_TYPE_NOT_AFFECTED  -> mTarget.setNotAffectedComponentsNow(mComps)
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun clear()
+        {
+            mVertexEffects?.removeAll(false)
+        }
 
-  static JobNode copy(MeshBase target, MeshBase mesh)
-    {
-    JobNode jn = mesh.mJobNode[0];
-    MeshBase[] meshes = new MeshBase[1];
-    meshes[0] = mesh;
-    Job job = new Job(JOB_TYPE_COPY,target,meshes,null,null,null,0,0,0,0,0,0,null);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
+        fun print(): String?
+        {
+            when (mType)
+            {
+                JOB_TYPE_VERTEX        -> return "VERTEX"
+                JOB_TYPE_MATRIX        -> return "MATRIX"
+                JOB_TYPE_MERGE_TEX     -> return "MERGE_TEX"
+                JOB_TYPE_MERGE_EFF     -> return "MERGE_EFF"
+                JOB_TYPE_JOIN          -> return "JOIN"
+                JOB_TYPE_COPY          -> return "COPY"
+                JOB_TYPE_TEXTURE       -> return "TEXTURE"
+                JOB_TYPE_ASSOC         -> return "ASSOC"
+                JOB_TYPE_CENTER        -> return "CENTER"
+                JOB_TYPE_ADD_EMPTY_TEX -> return "ADD_EMPTY_TEX"
+                JOB_TYPE_NOT_AFFECTED  -> return "POSTPROC COMPS"
+            }
+
+            return null
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static JobNode textureMap(MeshBase target, Static4D[] maps, int comp)
+    //////////////////////////////////////////////////////////////////////////
+    class JobNode(var mJob: Job?)
     {
-    JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_TEXTURE,target,null,null,null,maps,comp,0,0,0,0,0,null);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
-    }
+        var mPrevJobs: ArrayList<JobNode?>?
+        var mNextJobs: ArrayList<JobNode?>?
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        init
+        {
+            mPrevJobs = ArrayList()
+            mNextJobs = ArrayList()
+        }
 
-  static JobNode effectAssoc(MeshBase target, int comp, int andAssoc, int equAssoc)
-    {
-    JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_ASSOC,target,null,null,null,null,comp,andAssoc,equAssoc,0,0,0,null);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
-    }
+        @Synchronized
+        fun execute()
+        {
+            if (mPrevJobs!=null)
+            {
+                var node: JobNode?
+                val numPrev = mPrevJobs!!.size
+
+                for (i in 0 until numPrev)
+                {
+                    node = mPrevJobs!![0] // removeNode() rips the executed job out, thus the 0
+                    node!!.execute()
+                }
+
+                removeNode(this)
+                mJob!!.execute()
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        @Synchronized
+        fun clear()
+        {
+            mPrevJobs!!.clear()
+            mPrevJobs = null
+            mNextJobs!!.clear()
+            mNextJobs = null
 
-  static JobNode componentCenter(MeshBase target, int comp, float x, float y, float z)
-    {
-    JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_CENTER,target,null,null,null,null,comp,0,0,x,y,z,null);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
-    }
+            mJob!!.clear()
+            mJob = null
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun print(level: Int)
+        {
+            val numPrev = mPrevJobs!!.size
+            val numNext = mNextJobs!!.size
 
-  static JobNode setNotAffected(MeshBase target, int[] comps)
-    {
-    JobNode jn = target.mJobNode[0];
-    Job job = new Job(JOB_TYPE_NOT_AFFECTED,target,null,null,null,null,0,0,0,0,0,0,comps);
-    JobNode node = new JobNode(job);
-    node.mPrevJobs.add(jn);
-    jn.mNextJobs.add(node);
-    mJobs.add(node);
-    return node;
-    }
+            var str: String? = ""
+            for (i in 0 until level) str += " "
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Only for use by the library itself.
- *
- * @y.exclude
- */
-  public static void onPause()
-    {
-    int num = mJobs.size();
+            str += mJob!!.print()
 
-    for(int i=0; i<num; i++)
-      {
-      mJobs.get(i).clear();
-      }
+            str += (" next: $numNext prev: $numPrev")
 
-    mJobs.clear();
+            DistortedLibrary.logMessage("DeferredJobs: $str")
+
+            for (i in 0 until numPrev)
+            {
+                val node = mPrevJobs!![i]
+                node!!.print(level+1)
+            }
+        }
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/mesh/MeshBandedTriangle.kt b/src/main/java/org/distorted/library/mesh/MeshBandedTriangle.kt
index 85405da..b42d724 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBandedTriangle.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshBandedTriangle.kt
@@ -18,359 +18,353 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-import org.distorted.library.main.DistortedLibrary;
+import org.distorted.library.main.DistortedLibrary
+import kotlin.math.sqrt
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Creator a triangular mesh divided into 'bands' i.e. strips parallel to one of the sides and
  * and of different elevations.
- * <p>
+ *
  * This is a building block for MeshMultigon.
  */
-public class MeshBandedTriangle extends MeshBase
-  {
-  public static final int MODE_NORMAL  = 0;
-  public static final int MODE_INVERTED= 1;
-  public static final int MODE_FLAT    = 2;
-
-  private static final int NUM_CACHE = 20;
-
-  private float mLeftX, mLeftY;
-  private float mRightX, mRightY;
-  private float mTopX, mTopY;
-
-  private float[] mNormL, mNormR;
-  private int mMode;
-
-  private float[] mBands;
-  private int mNumBands;
-
-  private int remainingVert;
-  private int numVertices;
-  private int extraBands, extraVertices;
-
-  private float[] mCurveCache;
-  private float mVecX, mVecY;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void computeNumberOfVertices()
+class MeshBandedTriangle : MeshBase
+{
+    private var mLeftX = 0f
+    private var mLeftY = 0f
+    private var mRightX = 0f
+    private var mRightY = 0f
+    private var mTopX = 0f
+    private var mTopY = 0f
+
+    private lateinit var mNormL: FloatArray
+    private lateinit var mNormR: FloatArray
+    private var mMode = 0
+
+    private lateinit var mBands: FloatArray
+    private var mNumBands = 0
+
+    private var remainingVert = 0
+    override var numVertices: Int = 0
+    private var extraBands = 0
+    private var extraVertices = 0
+
+    private lateinit var mCurveCache: FloatArray
+    private var mVecX = 0f
+    private var mVecY = 0f
+
+    companion object
     {
-    if( mMode==MODE_FLAT )
-      {
-      numVertices = 3;
-      }
-    else if( mMode==MODE_NORMAL )
-      {
-      numVertices = mNumBands*(mNumBands+2) + 4*extraVertices*extraBands;
-      }
-    else
-      {
-      numVertices = mNumBands*(mNumBands+2);
-      }
-
-    remainingVert = numVertices;
+        const val MODE_NORMAL: Int = 0
+        const val MODE_INVERTED: Int = 1
+        const val MODE_FLAT: Int = 2
+
+        private const val NUM_CACHE = 20
     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeNumberOfVertices()
+    {
+        numVertices = if (mMode==MODE_FLAT)
+        {
+            3
+        }
+        else if (mMode==MODE_NORMAL)
+        {
+            mNumBands*(mNumBands+2)+4*extraVertices*extraBands
+        }
+        else
+        {
+            mNumBands*(mNumBands+2)
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        remainingVert = numVertices
+    }
 
-  private void computeCache()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeCache()
     {
-    mCurveCache = new float[NUM_CACHE];
-    float[] tmpD = new float[mNumBands+1];
-    float[] tmpX = new float[mNumBands+1];
+        mCurveCache = FloatArray(NUM_CACHE)
+        val tmpD = FloatArray(mNumBands+1)
+        val tmpX = FloatArray(mNumBands+1)
 
-    for(int i=1; i<mNumBands; i++)
-      {
-      tmpD[i] = (mBands[2*i-1]-mBands[2*i+1]) / (mBands[2*i]-mBands[2*i-2]);
-      tmpX[i] = 1.0f - (mBands[2*i]+mBands[2*i-2])/2;
-      }
-
-    tmpD[0] = tmpD[1];
-    tmpD[mNumBands] = tmpD[mNumBands-1];
-    tmpX[0] = 0.0f;
-    tmpX[mNumBands] = 1.0f;
+        for (i in 1 until mNumBands)
+        {
+            tmpD[i] = (mBands[2*i-1]-mBands[2*i+1])/(mBands[2*i]-mBands[2*i-2])
+            tmpX[i] = 1.0f-(mBands[2*i]+mBands[2*i-2])/2
+        }
 
-    int prev = 0;
-    int next = 0;
+        tmpD[0] = tmpD[1]
+        tmpD[mNumBands] = tmpD[mNumBands-1]
+        tmpX[0] = 0.0f
+        tmpX[mNumBands] = 1.0f
 
-    for(int i=0; i<NUM_CACHE-1; i++)
-      {
-      float x = i/(NUM_CACHE-1.0f);
+        var prev = 0
+        var next = 0
 
-      if( x>=tmpX[next] )
+        for (i in 0 until NUM_CACHE-1)
         {
-        prev = next;
-        while( next<=mNumBands && x>=tmpX[next] ) next++;
+            val x = i/(NUM_CACHE-1.0f)
+
+            if (x>=tmpX[next])
+            {
+                prev = next
+                while (next<=mNumBands&&x>=tmpX[next]) next++
+            }
+
+            if (next>prev)
+            {
+                val t = (x-tmpX[prev])/(tmpX[next]-tmpX[prev])
+                mCurveCache[i] = t*(tmpD[next]-tmpD[prev])+tmpD[prev]
+            }
+            else
+            {
+                mCurveCache[i] = tmpD[next]
+            }
         }
 
-      if( next>prev )
+        mCurveCache[NUM_CACHE-1] = tmpD[mNumBands]
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun derivative(x: Float): Float
+    {
+        if (x>=1.0f)
         {
-        float t = (x-tmpX[prev]) / (tmpX[next]-tmpX[prev]);
-        mCurveCache[i] = t*(tmpD[next]-tmpD[prev]) + tmpD[prev];
+            return 0.0f
         }
-      else
+        else
         {
-        mCurveCache[i] = tmpD[next];
-        }
-      }
+            val tmp = x*(NUM_CACHE-1)
+            val i1 = tmp.toInt()
+            val i2 = i1+1
 
-    mCurveCache[NUM_CACHE-1] = tmpD[mNumBands];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float derivative(float x)
-    {
-    if( x>=1.0f )
-      {
-      return 0.0f;
-      }
-    else
-      {
-      float tmp = x*(NUM_CACHE-1);
-      int i1 = (int)tmp;
-      int i2 = i1+1;
-
-      // why 0.5? Arbitrarily; this way the cubit faces of Twisty Puzzles
-      // [the main and only user of this class] look better.
-      return 0.5f*((tmp-i1)*(mCurveCache[i2]-mCurveCache[i1]) + mCurveCache[i1]);
-      }
+            // why 0.5? Arbitrarily; this way the cubit faces of Twisty Puzzles
+            // [the main and only user of this class] look better.
+            return 0.5f*((tmp-i1)*(mCurveCache[i2]-mCurveCache[i1])+mCurveCache[i1])
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void figureOutNormalVector2D(float qx)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun figureOutNormalVector2D(qx: Float)
     {
-    mVecX = mNormL[0] + qx*(mNormR[0]-mNormL[0]);
-    mVecY = mNormL[1] + qx*(mNormR[1]-mNormL[1]);
+        mVecX = mNormL[0]+qx*(mNormR[0]-mNormL[0])
+        mVecY = mNormL[1]+qx*(mNormR[1]-mNormL[1])
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float figureOutDerivative(float qy)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun figureOutDerivative(qy: Float): Float
     {
-    switch(mMode)
-      {
-      case MODE_NORMAL  : return derivative(1-qy);
-      case MODE_INVERTED: return -derivative(qy);
-      default           : return 0;
-      }
+        return when (mMode)
+        {
+            MODE_NORMAL   ->  derivative(1-qy)
+            MODE_INVERTED -> -derivative(qy)
+            else          -> 0f
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float computeElevation(int band)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeElevation(band: Int): Float
     {
-    switch(mMode)
-      {
-      case MODE_NORMAL  : return mBands[2*band+1];
-      case MODE_INVERTED: return mBands[2*(mNumBands-band)+1];
-      default           : return mBands[2*mNumBands+1];
-      }
+        return when (mMode)
+        {
+            MODE_NORMAL   -> mBands[2*band+1]
+            MODE_INVERTED -> mBands[2*(mNumBands-band)+1]
+            else          -> mBands[2*mNumBands+1]
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int addStripVertex(int vertex, float qx, float qy, int band, float[] attribs1, float[] attribs2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun addStripVertex(vertex: Int, qx: Float, qy: Float, band: Int, attribs1: FloatArray, attribs2: FloatArray): Int
     {
-    remainingVert--;
+        remainingVert--
 
-    float bx = mLeftX + qx*(mRightX-mLeftX);
-    float by = mLeftY + qx*(mRightY-mLeftY);
+        val bx = mLeftX+qx*(mRightX-mLeftX)
+        val by = mLeftY+qx*(mRightY-mLeftY)
 
-    float q = 1-qy;
-    float x = bx + q*(mTopX-bx);
-    float y = by + q*(mTopY-by);
-    float z = computeElevation(band);
+        val q = 1-qy
+        val x = bx+q*(mTopX-bx)
+        val y = by+q*(mTopY-by)
+        val z = computeElevation(band)
 
-    figureOutNormalVector2D(band<mNumBands ? qx : 0.5f);
-    float d = figureOutDerivative(qy);
+        figureOutNormalVector2D(if (band<mNumBands) qx else 0.5f)
+        val d = figureOutDerivative(qy)
 
-    attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB  ] = x;
-    attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+1] = y;
-    attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+2] = z;
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB  ] = x
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+1] = y
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+2] = z
 
-    float vx = d*mVecX;
-    float vy = d*mVecY;
-    float vz = mVecX*mVecX + mVecY*mVecY;
-    float len = (float)Math.sqrt(vx*vx + vy*vy + vz*vz);
+        val vx = d*mVecX
+        val vy = d*mVecY
+        val vz = mVecX*mVecX+mVecY*mVecY
+        val len = sqrt((vx*vx+vy*vy+vz*vz).toDouble()).toFloat()
 
-    int index = VERT1_ATTRIBS*vertex + NOR_ATTRIB;
-    attribs1[index  ] = vx/len;
-    attribs1[index+1] = vy/len;
-    attribs1[index+2] = vz/len;
+        val index: Int = VERT1_ATTRIBS*vertex+NOR_ATTRIB
+        attribs1[index] = vx/len
+        attribs1[index+1] = vy/len
+        attribs1[index+2] = vz/len
 
-    attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = x+0.5f;
-    attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = y+0.5f;
+        attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB  ] = x+0.5f
+        attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB+1] = y+0.5f
 
-    return vertex+1;
+        return vertex+1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int createBand(int vertex, int band, float[] attribs1, float[] attribs2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun createBand(vert: Int, band: Int, attribs1: FloatArray, attribs2: FloatArray): Int
     {
-    boolean fromLeft = (band%2 == 0);
-    int extra = ((band<extraBands && mMode==MODE_NORMAL) ? extraVertices : 0);
-    int n = mNumBands-band-1;
+        var vertex = vert
+        val fromLeft = (band%2==0)
+        val extra = (if ((band<extraBands&&mMode==MODE_NORMAL)) extraVertices else 0)
+        val n = mNumBands-band-1
 
-    float b = 1.0f/(mNumBands-band);
-    float dxb = fromLeft ? b : -b;
-    float sdx = dxb/(extra+1);
+        val b = 1.0f/(mNumBands-band)
+        val dxb = if (fromLeft) b else -b
+        val sdx = dxb/(extra+1)
 
-    float qx = fromLeft ? 0.0f : 1.0f;
+        var qx = if (fromLeft) 0.0f else 1.0f
 
-    int bb = mMode==MODE_NORMAL ? band   : mNumBands-band;
-    int bt = mMode==MODE_NORMAL ? band+1 : mNumBands-(band+1);
+        val bb = if (mMode==MODE_NORMAL) band else mNumBands-band
+        val bt = if (mMode==MODE_NORMAL) band+1 else mNumBands-(band+1)
 
-    float qyb = mBands[2*bb];
-    float qyt = mBands[2*bt];
+        var qyb = mBands[2*bb]
+        var qyt = mBands[2*bt]
 
-    if( mMode!=MODE_NORMAL )
-      {
-      qyb = 1-qyb;
-      qyt = 1-qyt;
-      }
-
-    vertex = addStripVertex(vertex, qx, qyb, band, attribs1,attribs2);
+        if (mMode!=MODE_NORMAL)
+        {
+            qyb = 1-qyb
+            qyt = 1-qyt
+        }
 
-    for(int v=0; v<extra; v++)
-      {
-      vertex = addStripVertex(vertex, qx          , qyt, band+1, attribs1,attribs2);
-      vertex = addStripVertex(vertex, qx+(v+1)*sdx, qyb, band  , attribs1,attribs2);
-      }
+        vertex = addStripVertex(vertex, qx, qyb, band, attribs1, attribs2)
 
-    if( n>0 )
-      {
-      float t = 1.0f/n;
-      float dxt = fromLeft ? t : -t;
+        for (v in 0 until extra)
+        {
+            vertex = addStripVertex(vertex, qx, qyt, band+1, attribs1, attribs2)
+            vertex = addStripVertex(vertex, qx+(v+1)*sdx, qyb, band, attribs1, attribs2)
+        }
 
-      for(int v=0; v<n; v++)
+        if (n>0)
         {
-        vertex=addStripVertex(vertex, qx+ v   *dxt, qyt, band+1, attribs1, attribs2);
-        vertex=addStripVertex(vertex, qx+(v+1)*dxb, qyb, band  , attribs1, attribs2);
+            val t = 1.0f/n
+            val dxt = if (fromLeft) t else -t
+
+            for (v in 0 until n)
+            {
+                vertex = addStripVertex(vertex, qx+v*dxt, qyt, band+1, attribs1, attribs2)
+                vertex = addStripVertex(vertex, qx+(v+1)*dxb, qyb, band, attribs1, attribs2)
+            }
         }
-      }
 
-    qx = 1-qx;
+        qx = 1-qx
 
-    for(int v=0; v<=extra; v++)
-      {
-      vertex = addStripVertex(vertex, qx              , qyt, band+1, attribs1,attribs2);
-      vertex = addStripVertex(vertex, qx-dxb+(v+1)*sdx, qyb, band  , attribs1,attribs2);
-      }
+        for (v in 0..extra)
+        {
+            vertex = addStripVertex(vertex, qx, qyt, band+1, attribs1, attribs2)
+            vertex = addStripVertex(vertex, qx-dxb+(v+1)*sdx, qyb, band, attribs1, attribs2)
+        }
 
-    return vertex;
+        return vertex
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void buildGrid(float[] attribs1, float[] attribs2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildGrid(attribs1: FloatArray, attribs2: FloatArray)
     {
-    if( mMode==MODE_FLAT )
-      {
-      addStripVertex(0, 0, 1, 0, attribs1,attribs2);
-      addStripVertex(1, 0, 0, 0, attribs1,attribs2);
-      addStripVertex(2, 1, 1, 0, attribs1,attribs2);
-      }
-    else
-      {
-      int vertex=0;
-      for(int b=0; b<mNumBands; b++) vertex=createBand(vertex, b, attribs1, attribs2);
-      }
+        if (mMode==MODE_FLAT)
+        {
+            addStripVertex(0, 0f, 1f, 0, attribs1, attribs2)
+            addStripVertex(1, 0f, 0f, 0, attribs1, attribs2)
+            addStripVertex(2, 1f, 1f, 0, attribs1, attribs2)
+        }
+        else
+        {
+            var vertex = 0
+            for (b in 0 until mNumBands) vertex = createBand(vertex, b, attribs1, attribs2)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create a triangular mesh split into 'bands' - i.e. strips of triangles - which are parallel to
- * a given edge.
- *
- * @param vL         A pair of floats (x,y) which defines the 'left' vertex of the triangle.
- * @param vR         A pair of floats (x,y) which defines the 'right' vertex of the triangle.
- * @param vT         A pair of floats (x,y) which defines the 'top' vertex of the triangle.
- * @param bands      2K floats; K pairs of two floats each describing a single band.
- *                   From (1.0,Z[0]) (the 'left-right' edge, its Z elevation) to (0.0,Z[K])
- *                   (the 'top' vertex, its elevation). The polygon is split into such strips.
- *                   Must be band[2*i] > band[2*(i+1)] !
- *                   The way the bands get interpreted also depends on the 'mode' param.
- * @param normL      A pair of floats which define a 2D vector - which is the normal vector of each
- *                   mesh vertex which lies on the 'left-top' edge casted to the XY plane.
- * @param normR      Same as above but about the vertices which belong to the 'right-top' triangle
- *                   edge. This and the previous param define the vector field of normals, as seen
- *                   from above, of the whole mesh.
- * @param mode       MODE_UP, MODE_DOWN or MODE_FLAT.
- *                   This defines how we interpret the bands.
- *                   If UP, we interpret them the same as in MeshPolygon - i.e. the first band is
- *                   about the 'left-right' edge, then progressively towards the 'top'.
- *                   If DOWN, we turn the interpretation of the bands upside-down: it is the last
- *                   band which defines the strip aong the 'left-right' edge.
- *                   If FLAT, everything is flat anyway, so we disregard the bands and return a mesh
- *                   with 3 vertices.
- * @param exBands    This and the next parameter describe how to make the mesh denser at the
- *                   'left' and 'right' vertices. If e.g. exBands=3 and exVertices=2, then 3 triangles
- *                   of the outermost band (and 2 triangles of the next band, and 1 triangle of the
- *                   third band) get denser - the 3 triangles become 3+2 = 5.
- * @param exVertices See above.
- */
-  public MeshBandedTriangle(float[] vL, float[] vR, float[] vT, float[] bands, float[] normL, float[] normR, int mode, int exBands, int exVertices)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Create a triangular mesh split into 'bands' - i.e. strips of triangles - which are parallel to
+     * a given edge.
+     *
+     * @param vL         A pair of floats (x,y) which defines the 'left' vertex of the triangle.
+     * @param vR         A pair of floats (x,y) which defines the 'right' vertex of the triangle.
+     * @param vT         A pair of floats (x,y) which defines the 'top' vertex of the triangle.
+     * @param bands      2K floats; K pairs of two floats each describing a single band.
+     * From (1.0,Z[0]) (the 'left-right' edge, its Z elevation) to (0.0,Z[K])
+     * (the 'top' vertex, its elevation). The polygon is split into such strips.
+     * Must be band[2*i] > band[2*(i+1)] !
+     * The way the bands get interpreted also depends on the 'mode' param.
+     * @param normL      A pair of floats which define a 2D vector - which is the normal vector of each
+     * mesh vertex which lies on the 'left-top' edge casted to the XY plane.
+     * @param normR      Same as above but about the vertices which belong to the 'right-top' triangle
+     * edge. This and the previous param define the vector field of normals, as seen
+     * from above, of the whole mesh.
+     * @param mode       MODE_UP, MODE_DOWN or MODE_FLAT.
+     * This defines how we interpret the bands.
+     * If UP, we interpret them the same as in MeshPolygon - i.e. the first band is
+     * about the 'left-right' edge, then progressively towards the 'top'.
+     * If DOWN, we turn the interpretation of the bands upside-down: it is the last
+     * band which defines the strip aong the 'left-right' edge.
+     * If FLAT, everything is flat anyway, so we disregard the bands and return a mesh
+     * with 3 vertices.
+     * @param exBands    This and the next parameter describe how to make the mesh denser at the
+     * 'left' and 'right' vertices. If e.g. exBands=3 and exVertices=2, then 3 triangles
+     * of the outermost band (and 2 triangles of the next band, and 1 triangle of the
+     * third band) get denser - the 3 triangles become 3+2 = 5.
+     * @param exVertices See above.
+     */
+    constructor(vL: FloatArray, vR: FloatArray, vT: FloatArray, bands: FloatArray, normL: FloatArray, normR: FloatArray, mode: Int, exBands: Int, exVertices: Int) : super()
     {
-    super();
+        mLeftX = vL[0]
+        mLeftY = vL[1]
+        mRightX = vR[0]
+        mRightY = vR[1]
+        mTopX = vT[0]
+        mTopY = vT[1]
 
-    mLeftX   = vL[0];
-    mLeftY   = vL[1];
-    mRightX  = vR[0];
-    mRightY  = vR[1];
-    mTopX    = vT[0];
-    mTopY    = vT[1];
+        mMode = mode
+        mNormL = floatArrayOf(normL[0], normL[1])
+        mNormR = floatArrayOf(normR[0], normR[1])
 
-    mMode = mode;
-    mNormL= new float[] { normL[0],normL[1] };
-    mNormR= new float[] { normR[0],normR[1] };
+        mBands = bands
+        mNumBands = mBands.size/2-1
+        extraBands = exBands
+        extraVertices = exVertices
 
-    mBands        = bands;
-    mNumBands     = mBands.length/2 -1;
-    extraBands    = exBands;
-    extraVertices = exVertices;
+        computeNumberOfVertices()
+        computeCache()
 
-    computeNumberOfVertices();
-    computeCache();
+        val attribs1 = FloatArray(VERT1_ATTRIBS*numVertices)
+        val attribs2 = FloatArray(VERT2_ATTRIBS*numVertices)
 
-    float[] attribs1= new float[VERT1_ATTRIBS*numVertices];
-    float[] attribs2= new float[VERT2_ATTRIBS*numVertices];
+        buildGrid(attribs1, attribs2)
 
-    buildGrid(attribs1,attribs2);
+        if (remainingVert!=0) DistortedLibrary.logMessage("MeshBandedTriangle: remainingVert $remainingVert")
 
-    if( remainingVert!=0 )
-      DistortedLibrary.logMessage("MeshBandedTriangle: remainingVert " +remainingVert );
-
-    setAttribs(attribs1,attribs2);
+        setAttribs(attribs1, attribs2)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public MeshBandedTriangle(MeshBandedTriangle mesh, boolean deep)
-    {
-    super(mesh,deep);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshBandedTriangle copy(boolean deep)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshBandedTriangle, deep: Boolean) : super(mesh, deep)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshBandedTriangle
     {
-    return new MeshBandedTriangle(this,deep);
+        return MeshBandedTriangle(this, deep)
     }
- }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.kt b/src/main/java/org/distorted/library/mesh/MeshBase.kt
index 3e065e7..3feb6ca 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.kt
@@ -18,1458 +18,1430 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
-
-import android.opengl.GLES30;
-
-import org.distorted.library.effect.MatrixEffect;
-import org.distorted.library.effect.VertexEffect;
-import org.distorted.library.effectqueue.EffectQueue;
-import org.distorted.library.main.DistortedLibrary;
-import org.distorted.library.main.InternalBuffer;
-import org.distorted.library.program.DistortedProgram;
-import org.distorted.library.type.Static4D;
-import org.distorted.library.uniformblock.UniformBlockAssociation;
-import org.distorted.library.uniformblock.UniformBlockCenter;
-
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.DataInputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.util.ArrayList;
+package org.distorted.library.mesh
+
+import android.opengl.GLES30
+import org.distorted.library.effect.MatrixEffect
+import org.distorted.library.effect.VertexEffect
+import org.distorted.library.effectqueue.EffectQueue
+import org.distorted.library.main.DistortedLibrary
+import org.distorted.library.main.InternalBuffer
+import org.distorted.library.program.DistortedProgram
+import org.distorted.library.type.Static4D
+import org.distorted.library.uniformblock.UniformBlockAssociation
+import org.distorted.library.uniformblock.UniformBlockCenter
+import java.io.DataInputStream
+import java.io.DataOutputStream
+import java.io.IOException
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import kotlin.math.min
+import kotlin.math.sqrt
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Abstract class which represents a Mesh, ie an array of vertices (rendered as a TRIANGLE_STRIP).
- * <p>
+ *
  * If you want to render to a particular shape, extend from here, construct a float array
  * containing per-vertex attributes, and call back setAttribs().
  */
-public abstract class MeshBase
-   {
-   private static final int ASSOC_UBO_BINDING  = 3;
-   private static final int CENTER_UBO_BINDING = 4;
-
-   // sizes of attributes of an individual vertex.
-   private static final int POS_DATA_SIZE= 3; // vertex coordinates: x,y,z
-   private static final int NOR_DATA_SIZE= 3; // normal vector: x,y,z
-   private static final int TEX_DATA_SIZE= 2; // texture coordinates: s,t
-   private static final int COM_DATA_SIZE= 1; // component number, a single float
-
-   static final int POS_ATTRIB   = 0;
-   static final int NOR_ATTRIB   = POS_DATA_SIZE;
-   static final int TEX_ATTRIB   = 0;
-   static final int COM_ATTRIB   = TEX_DATA_SIZE;
-
-   static final int VERT1_ATTRIBS= POS_DATA_SIZE + NOR_DATA_SIZE;  // number of attributes of a vertex (the part changed by preapply)
-   static final int VERT2_ATTRIBS= TEX_DATA_SIZE + COM_DATA_SIZE;  // number of attributes of a vertex (the 'preapply invariant' part)
-   static final int TRAN_ATTRIBS = POS_DATA_SIZE + NOR_DATA_SIZE;  // number of attributes of a transform feedback vertex
-
-   private static final int BYTES_PER_FLOAT = 4;
-
-   private static final int OFFSET_POS = POS_ATTRIB*BYTES_PER_FLOAT;
-   private static final int OFFSET_NOR = NOR_ATTRIB*BYTES_PER_FLOAT;
-   private static final int OFFSET_TEX = TEX_ATTRIB*BYTES_PER_FLOAT;
-   private static final int OFFSET_COM = COM_ATTRIB*BYTES_PER_FLOAT;
-
-   private static final int TRAN_SIZE  = TRAN_ATTRIBS*BYTES_PER_FLOAT;
-   private static final int VERT1_SIZE = VERT1_ATTRIBS*BYTES_PER_FLOAT;
-   private static final int VERT2_SIZE = VERT2_ATTRIBS*BYTES_PER_FLOAT;
-
-   private static final int[] mCenterBlockIndex = new int[EffectQueue.MAIN_VARIANTS];
-   private static final int[] mAssocBlockIndex  = new int[EffectQueue.MAIN_VARIANTS];
-
-   private static final int TEX_COMP_SIZE = 5; // 5 four-byte entities inside the component
-
-   private static boolean mUseCenters;
-   private static int mStride;
-   private static int mMaxComponents = 100;
-
-   private boolean mShowNormals;              // when rendering this mesh, draw normal vectors?
-   private InternalBuffer mVBO1, mVBO2, mTFO; // main vertex buffer and transform feedback buffer
-   private int mNumVertices;
-   private float[] mVertAttribs1;             // packed: PosX,PosY,PosZ, NorX,NorY,NorZ
-   private float[] mVertAttribs2;             // packed: TexS,TexT, Component
-   private float mInflate;
-   private final UniformBlockAssociation mUBA;
-   private UniformBlockCenter mUBC;
-   private boolean mStrideCorrected;
-
-   DeferredJobs.JobNode[] mJobNode;
-
-   private static class TexComponent
-     {
-     private int mEndIndex;
-     private final Static4D mTextureMap;
-
-     TexComponent(int end)
-       {
-       mEndIndex  = end;
-       mTextureMap= new Static4D(0,0,1,1);
-       }
-     TexComponent(TexComponent original)
-       {
-       mEndIndex = original.mEndIndex;
-
-       float x = original.mTextureMap.get0();
-       float y = original.mTextureMap.get1();
-       float z = original.mTextureMap.get2();
-       float w = original.mTextureMap.get3();
-       mTextureMap = new Static4D(x,y,z,w);
-       }
-
-     void setMap(Static4D map)
-       {
-       mTextureMap.set(map.get0(),map.get1(),map.get2(),map.get3());
-       }
-     }
-
-   private ArrayList<TexComponent> mTexComponent;
-   private ArrayList<Integer> mEffComponent;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   MeshBase()
-     {
-     mShowNormals  = false;
-     mInflate      = 0.0f;
-     mTexComponent = new ArrayList<>();
-     mEffComponent = new ArrayList<>();
-
-     mJobNode = new DeferredJobs.JobNode[1];
-
-     mUBA = new UniformBlockAssociation();
-
-     if( mUseCenters ) mUBC = new UniformBlockCenter();
-
-     mVBO1= new InternalBuffer(GLES30.GL_ARRAY_BUFFER             , GLES30.GL_STATIC_DRAW);
-     mVBO2= new InternalBuffer(GLES30.GL_ARRAY_BUFFER             , GLES30.GL_STATIC_DRAW);
-     mTFO = new InternalBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, GLES30.GL_STATIC_READ);
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// copy constructor
-
-   MeshBase(MeshBase original, boolean deep)
-     {
-     mShowNormals= original.mShowNormals;
-     mInflate    = original.mInflate;
-     mNumVertices= original.mNumVertices;
-
-     mUBA = new UniformBlockAssociation(original.mUBA);
-
-     if( mUseCenters ) mUBC = new UniformBlockCenter(original.mUBC);
-
-     if( deep )
-       {
-       mJobNode = new DeferredJobs.JobNode[1];
-       if( original.mJobNode[0]==null ) copy(original);
-       else mJobNode[0] = DeferredJobs.copy(this,original);
-       }
-     else
-       {
-       mJobNode      = original.mJobNode;
-       mVBO1         = original.mVBO1;
-       mVertAttribs1 = original.mVertAttribs1;
-       shallowCopy(original);
-       }
-
-     mTFO = new InternalBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, GLES30.GL_STATIC_READ);
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void copy(MeshBase original)
-     {
-     shallowCopy(original);
-
-     mVBO1= new InternalBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_STATIC_DRAW);
-     mVertAttribs1= new float[mNumVertices*VERT1_ATTRIBS];
-     System.arraycopy(original.mVertAttribs1,0,mVertAttribs1,0,mNumVertices*VERT1_ATTRIBS);
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private void shallowCopy(MeshBase original)
-     {
-     int texComSize = original.mTexComponent.size();
-     mTexComponent = new ArrayList<>();
-
-     for(int i=0; i<texComSize; i++)
-       {
-       TexComponent comp = new TexComponent(original.mTexComponent.get(i));
-       mTexComponent.add(comp);
-       }
-
-     mEffComponent = new ArrayList<>();
-     mEffComponent.addAll(original.mEffComponent);
-
-     mVBO2= new InternalBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_STATIC_DRAW);
-     mVertAttribs2= new float[mNumVertices*VERT2_ATTRIBS];
-     System.arraycopy(original.mVertAttribs2,0,mVertAttribs2,0,mNumVertices*VERT2_ATTRIBS);
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void mergeTexComponentsNow()
-     {
-     int num = mTexComponent.size();
-
-     if( num>1 )
-       {
-       mTexComponent.clear();
-       mTexComponent.add(new TexComponent(mNumVertices-1));
-
-       mVBO2.invalidate();
-       }
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void addEmptyTexComponentNow()
-     {
-     mTexComponent.add(new TexComponent(mNumVertices-1));
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void mergeEffComponentsNow()
-     {
-     int num = mEffComponent.size();
-
-     if( num>1 )
-       {
-       mEffComponent.clear();
-       mEffComponent.add(mNumVertices-1);
-
-       for(int index=0; index<mNumVertices; index++)
-         {
-         mVertAttribs2[VERT2_ATTRIBS*index+COM_ATTRIB] = 0;
-         }
-
-       mVBO2.invalidate();
-       }
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   void applyMatrix(MatrixEffect effect, int andAssoc, int equAssoc)
-     {
-     float[] matrixP  = new float[16];
-     float[] matrixV  = new float[16];
-     float[] uniforms = new float[7];
-     int start, end=-1, numComp = mEffComponent.size();
-
-     matrixP[0] = matrixP[5] = matrixP[10] = matrixP[15] = 1;
-     matrixV[0] = matrixV[5] = matrixV[10] = matrixV[15] = 1;
-
-     effect.compute(uniforms,0,0,0);
-     effect.apply(matrixP, matrixV, uniforms, 0);
-
-     for(int comp=0; comp<numComp; comp++)
-       {
-       start = end+1;
-       end   = mEffComponent.get(comp);
-
-       if( mUBA.matchesAssociation(comp, andAssoc, equAssoc) )
-         {
-         applyMatrixToComponent(matrixP, matrixV, start, end);
-         }
-       }
-
-     mVBO1.invalidate();
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-   private void applyMatrixToComponent(float[] matrixP, float[] matrixV, int start, int end)
-     {
-     float x,y,z;
-
-     for(int index=start*VERT1_ATTRIBS; index<=end*VERT1_ATTRIBS; index+=VERT1_ATTRIBS )
-       {
-       x = mVertAttribs1[index+POS_ATTRIB  ];
-       y = mVertAttribs1[index+POS_ATTRIB+1];
-       z = mVertAttribs1[index+POS_ATTRIB+2];
-
-       mVertAttribs1[index+POS_ATTRIB  ] = matrixP[0]*x + matrixP[4]*y + matrixP[ 8]*z + matrixP[12];
-       mVertAttribs1[index+POS_ATTRIB+1] = matrixP[1]*x + matrixP[5]*y + matrixP[ 9]*z + matrixP[13];
-       mVertAttribs1[index+POS_ATTRIB+2] = matrixP[2]*x + matrixP[6]*y + matrixP[10]*z + matrixP[14];
-
-       x = mVertAttribs1[index+NOR_ATTRIB  ];
-       y = mVertAttribs1[index+NOR_ATTRIB+1];
-       z = mVertAttribs1[index+NOR_ATTRIB+2];
+abstract class MeshBase
+{
+    var showNormals: Boolean // when rendering this mesh, draw normal vectors?
+    private var mVBO1: InternalBuffer? = null
+    private var mVBO2: InternalBuffer? = null
+    private var mTFO: InternalBuffer // main vertex buffer and transform feedback buffer
+    var numVertices: Int = 0
+        private set
+    private var mVertAttribs1: FloatArray? = null // packed: PosX,PosY,PosZ, NorX,NorY,NorZ
+    private var mVertAttribs2: FloatArray? = null // packed: TexS,TexT, Component
+    var inflate: Float
+    private val mUBA: UniformBlockAssociation
+    private var mUBC: UniformBlockCenter? = null
+    private var mStrideCorrected = false
+
+    var mJobNode: Array<DeferredJobs.JobNode?>
+
+    private class TexComponent
+    {
+        var mEndIndex: Int
+        val mTextureMap: Static4D
+
+        constructor(end: Int)
+        {
+            mEndIndex = end
+            mTextureMap = Static4D(0f, 0f, 1f, 1f)
+        }
+
+        constructor(original: TexComponent)
+        {
+            mEndIndex = original.mEndIndex
+
+            val x = original.mTextureMap.get0()
+            val y = original.mTextureMap.get1()
+            val z = original.mTextureMap.get2()
+            val w = original.mTextureMap.get3()
+            mTextureMap = Static4D(x, y, z, w)
+        }
+
+        fun setMap(map: Static4D)
+        {
+            mTextureMap.set(map.get0(), map.get1(), map.get2(), map.get3())
+        }
+    }
 
-       mVertAttribs1[index+NOR_ATTRIB  ] = matrixV[0]*x + matrixV[4]*y + matrixV[ 8]*z;
-       mVertAttribs1[index+NOR_ATTRIB+1] = matrixV[1]*x + matrixV[5]*y + matrixV[ 9]*z;
-       mVertAttribs1[index+NOR_ATTRIB+2] = matrixV[2]*x + matrixV[6]*y + matrixV[10]*z;
+    private var mTexComponent: ArrayList<TexComponent>? = null
+    private var mEffComponent: ArrayList<Int>? = null
 
-       x = mVertAttribs1[index+NOR_ATTRIB  ];
-       y = mVertAttribs1[index+NOR_ATTRIB+1];
-       z = mVertAttribs1[index+NOR_ATTRIB+2];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    internal constructor()
+    {
+        showNormals = false
+        inflate = 0.0f
+        mTexComponent = ArrayList()
+        mEffComponent = ArrayList()
 
-       float len1 = (float)Math.sqrt(x*x + y*y + z*z);
+        mJobNode = arrayOfNulls(1)
 
-       if( len1>0.0f )
-         {
-         mVertAttribs1[index+NOR_ATTRIB  ] /= len1;
-         mVertAttribs1[index+NOR_ATTRIB+1] /= len1;
-         mVertAttribs1[index+NOR_ATTRIB+2] /= len1;
-         }
-       }
-     }
+        mUBA = UniformBlockAssociation()
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        if (useCenters) mUBC = UniformBlockCenter()
 
-   void setEffectAssociationNow(int component, int andAssociation, int equAssociation)
-     {
-     mUBA.setEffectAssociationNow(component, andAssociation, equAssociation);
-     }
+        mVBO1 = InternalBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_STATIC_DRAW)
+        mVBO2 = InternalBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_STATIC_DRAW)
+        mTFO = InternalBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, GLES30.GL_STATIC_READ)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // copy constructor
+    internal constructor(original: MeshBase, deep: Boolean)
+    {
+        showNormals = original.showNormals
+        inflate = original.inflate
+        numVertices = original.numVertices
+
+        mUBA = UniformBlockAssociation(original.mUBA)
+
+        if (useCenters) mUBC = UniformBlockCenter(original.mUBC!!)
+
+        if (deep)
+        {
+            mJobNode = arrayOfNulls(1)
+            if (original.mJobNode[0]==null) copy(original)
+            else mJobNode[0] = DeferredJobs.copy(this, original)
+        }
+        else
+        {
+            mJobNode = original.mJobNode
+            mVBO1 = original.mVBO1
+            mVertAttribs1 = original.mVertAttribs1
+            shallowCopy(original)
+        }
+
+        mTFO = InternalBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, GLES30.GL_STATIC_READ)
+    }
 
-   void setNotAffectedComponentsNow(int[] components)
-     {
-     mUBA.setNotAffectedComponentsNow(components);
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun copy(original: MeshBase)
+    {
+        shallowCopy(original)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        mVBO1 = InternalBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_STATIC_DRAW)
+        mVertAttribs1 = FloatArray(numVertices*VERT1_ATTRIBS)
+        System.arraycopy(original.mVertAttribs1, 0, mVertAttribs1, 0, numVertices*VERT1_ATTRIBS)
+    }
 
-   void setComponentCenterNow(int component, float x, float y, float z)
-     {
-     if( mUBC!=null )
-       {
-       mUBC.setEffectCenterNow(component, x, y, z);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun shallowCopy(original: MeshBase)
+    {
+        val texComSize = original.mTexComponent!!.size
+        mTexComponent = ArrayList()
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// when a derived class is done computing its mesh, it has to call this method.
+        for (i in 0 until texComSize)
+        {
+            val comp = TexComponent(original.mTexComponent!![i])
+            mTexComponent!!.add(comp)
+        }
 
-   void setAttribs(float[] vert1Attribs, float[] vert2Attribs)
-     {
-     mNumVertices = vert1Attribs.length/VERT1_ATTRIBS;
-     mVertAttribs1= vert1Attribs;
-     mVertAttribs2= vert2Attribs;
+        mEffComponent = ArrayList()
+        mEffComponent!!.addAll(original.mEffComponent!!)
 
-     mTexComponent.add(new TexComponent(mNumVertices-1));
-     mEffComponent.add(mNumVertices-1);
+        mVBO2 = InternalBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_STATIC_DRAW)
+        mVertAttribs2 = FloatArray(numVertices*VERT2_ATTRIBS)
+        System.arraycopy(original.mVertAttribs2, 0, mVertAttribs2, 0, numVertices*VERT2_ATTRIBS)
+    }
 
-     mVBO1.invalidate();
-     mVBO2.invalidate();
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun mergeTexComponentsNow()
+    {
+        val num = mTexComponent!!.size
+
+        if (num>1)
+        {
+            mTexComponent!!.clear()
+            mTexComponent!!.add(TexComponent(numVertices-1))
+            mVBO2!!.invalidate()
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun addEmptyTexComponentNow()
+    {
+        mTexComponent!!.add(TexComponent(numVertices-1))
+    }
 
-   void joinAttribs(MeshBase[] meshes)
-     {
-     MeshBase mesh;
-     TexComponent comp;
-     int numMeshes = meshes.length;
-     int numVertices,origVertices = mNumVertices;
-     int origTexComponents,numTexComponents;
-     int origEffComponents=0,numEffComponents;
-
-     if( origVertices>0 )
-       {
-       origTexComponents = mTexComponent.size();
-       mNumVertices+= ( mNumVertices%2==1 ? 2:1 );
-       mTexComponent.get(origTexComponents-1).mEndIndex = mNumVertices-1;
-       origEffComponents = mEffComponent.size();
-       mEffComponent.set(origEffComponents-1,mNumVertices-1);
-       }
-
-     for(int i=0; i<numMeshes; i++)
-       {
-       mesh = meshes[i];
-       numTexComponents = mesh.mTexComponent.size();
-       numEffComponents = mesh.mEffComponent.size();
-       numVertices = mesh.mNumVertices;
-
-       int extraVerticesBefore = mNumVertices==0 ? 0:1;
-       int extraVerticesAfter  = (i==numMeshes-1) ? 0 : (numVertices%2==1 ? 2:1);
-
-       for(int j=0; j<numTexComponents; j++)
-         {
-         comp = new TexComponent(mesh.mTexComponent.get(j));
-         comp.mEndIndex += (extraVerticesBefore+mNumVertices);
-         if( j==numTexComponents-1) comp.mEndIndex += extraVerticesAfter;
-         mTexComponent.add(comp);
-         }
-
-       for(int j=0; j<numEffComponents; j++)
-         {
-         int index = mesh.mEffComponent.get(j);
-         index += (extraVerticesBefore+mNumVertices);
-         if( j==numEffComponents-1 ) index += extraVerticesAfter;
-         mEffComponent.add(index);
-
-         if( origEffComponents<mMaxComponents )
-           {
-           mUBA.copy(origEffComponents, mesh.mUBA, j);
-           if( mUseCenters ) mUBC.copy(origEffComponents, mesh.mUBC, j);
-           origEffComponents++;
-           }
-         }
-
-       mNumVertices += (extraVerticesBefore+numVertices+extraVerticesAfter);
-       }
-
-     float[] newAttribs1 = new float[VERT1_ATTRIBS*mNumVertices];
-     float[] newAttribs2 = new float[VERT2_ATTRIBS*mNumVertices];
-     numVertices = origVertices;
-
-     if( origVertices>0 )
-       {
-       System.arraycopy(mVertAttribs1,                              0, newAttribs1,                          0, VERT1_ATTRIBS*numVertices);
-       System.arraycopy(mVertAttribs1, VERT1_ATTRIBS*(origVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS            );
-       System.arraycopy(mVertAttribs2,                              0, newAttribs2,                          0, VERT2_ATTRIBS*numVertices);
-       System.arraycopy(mVertAttribs2, VERT2_ATTRIBS*(origVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS            );
-       origVertices++;
-
-       if( numVertices%2==1 )
-         {
-         System.arraycopy(mVertAttribs1, VERT1_ATTRIBS*(origVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
-         System.arraycopy(mVertAttribs2, VERT2_ATTRIBS*(origVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS);
-         origVertices++;
-         }
-       }
-
-     for(int i=0; i<numMeshes; i++)
-       {
-       mesh = meshes[i];
-       numVertices = mesh.mNumVertices;
-
-       if( origVertices>0 )
-         {
-         System.arraycopy(mesh.mVertAttribs1, 0, newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
-         System.arraycopy(mesh.mVertAttribs2, 0, newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS);
-         origVertices++;
-         }
-       System.arraycopy(mesh.mVertAttribs1, 0, newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS*numVertices);
-       System.arraycopy(mesh.mVertAttribs2, 0, newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS*numVertices);
-       origVertices+=numVertices;
-
-       if( i<numMeshes-1 && numVertices>0 )
-         {
-         System.arraycopy(mesh.mVertAttribs1, VERT1_ATTRIBS*(numVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
-         System.arraycopy(mesh.mVertAttribs2, VERT2_ATTRIBS*(numVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS);
-         origVertices++;
-
-         if( numVertices%2==1 )
-           {
-           System.arraycopy(mesh.mVertAttribs1, VERT1_ATTRIBS*(numVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS);
-           System.arraycopy(mesh.mVertAttribs2, VERT2_ATTRIBS*(numVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS);
-           origVertices++;
-           }
-         }
-       }
-
-     if( origVertices!=mNumVertices )
-       {
-       DistortedLibrary.logMessage("MeshBase: join: origVertices: "+origVertices+" numVertices: "+mNumVertices);
-       }
-
-     int endIndex, index=0, numEffComp = mEffComponent.size();
-
-     for(int component=0; component<numEffComp; component++)
-       {
-       endIndex = mEffComponent.get(component);
-
-       for( ; index<=endIndex; index++) newAttribs2[VERT2_ATTRIBS*index+COM_ATTRIB] = component;
-       }
-
-     mVertAttribs1 = newAttribs1;
-     mVertAttribs2 = newAttribs2;
-     mVBO1.invalidate();
-     mVBO2.invalidate();
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun mergeEffComponentsNow()
+    {
+        val num = mEffComponent!!.size
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// called from MeshJoined
+        if (num>1)
+        {
+            mEffComponent!!.clear()
+            mEffComponent!!.add(numVertices-1)
 
-   void join(MeshBase[] meshes)
-     {
-     boolean immediateJoin = true;
+            for (index in 0 until numVertices)
+            {
+                mVertAttribs2!![VERT2_ATTRIBS*index+COM_ATTRIB] = 0f
+            }
 
-     for (MeshBase mesh : meshes)
-       {
-       if (mesh.mJobNode[0] != null)
-         {
-         immediateJoin = false;
-         break;
-         }
-       }
+            mVBO2!!.invalidate()
+        }
+    }
 
-     if( immediateJoin ) joinAttribs(meshes);
-     else                mJobNode[0] = DeferredJobs.join(this,meshes);
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun applyMatrix(effect: MatrixEffect, andAssoc: Int, equAssoc: Int)
+    {
+        val matrixP = FloatArray(16)
+        val matrixV = FloatArray(16)
+        val uniforms = FloatArray(7)
+        var start: Int
+        var end = -1
+        val numComp = mEffComponent!!.size
+
+        matrixP[15] = 1f
+        matrixP[10] = matrixP[15]
+        matrixP[5] = matrixP[10]
+        matrixP[0] = matrixP[5]
+        matrixV[15] = 1f
+        matrixV[10] = matrixV[15]
+        matrixV[5] = matrixV[10]
+        matrixV[0] = matrixV[5]
+
+        effect.compute(uniforms, 0, 0, 0)
+        effect.apply(matrixP, matrixV, uniforms, 0)
+
+        for (comp in 0 until numComp)
+        {
+            start = end+1
+            end = mEffComponent!![comp]
+
+            if (mUBA.matchesAssociation(comp, andAssoc, equAssoc))
+            {
+                applyMatrixToComponent(matrixP, matrixV, start, end)
+            }
+        }
+
+        mVBO1!!.invalidate()
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun applyMatrixToComponent(matrixP: FloatArray, matrixV: FloatArray, start: Int, end: Int)
+    {
+        var x: Float
+        var y: Float
+        var z: Float
+
+        var index = start*VERT1_ATTRIBS
+        while (index<=end*VERT1_ATTRIBS)
+        {
+            x = mVertAttribs1!![index+POS_ATTRIB  ]
+            y = mVertAttribs1!![index+POS_ATTRIB+1]
+            z = mVertAttribs1!![index+POS_ATTRIB+2]
+
+            mVertAttribs1!![index+POS_ATTRIB  ] = matrixP[0]*x+matrixP[4]*y+matrixP[8]*z+matrixP[12]
+            mVertAttribs1!![index+POS_ATTRIB+1] = matrixP[1]*x+matrixP[5]*y+matrixP[9]*z+matrixP[13]
+            mVertAttribs1!![index+POS_ATTRIB+2] = matrixP[2]*x+matrixP[6]*y+matrixP[10]*z+matrixP[14]
+
+            x = mVertAttribs1!![index+NOR_ATTRIB  ]
+            y = mVertAttribs1!![index+NOR_ATTRIB+1]
+            z = mVertAttribs1!![index+NOR_ATTRIB+2]
+
+            mVertAttribs1!![index+NOR_ATTRIB  ] = matrixV[0]*x+matrixV[4]*y+matrixV[8]*z
+            mVertAttribs1!![index+NOR_ATTRIB+1] = matrixV[1]*x+matrixV[5]*y+matrixV[9]*z
+            mVertAttribs1!![index+NOR_ATTRIB+2] = matrixV[2]*x+matrixV[6]*y+matrixV[10]*z
+
+            x = mVertAttribs1!![index+NOR_ATTRIB]
+            y = mVertAttribs1!![index+NOR_ATTRIB+1]
+            z = mVertAttribs1!![index+NOR_ATTRIB+2]
+
+            val len1 = sqrt((x*x+y*y+z*z).toDouble()).toFloat()
+
+            if (len1>0.0f)
+            {
+                mVertAttribs1!![index+NOR_ATTRIB  ] /= len1
+                mVertAttribs1!![index+NOR_ATTRIB+1] /= len1
+                mVertAttribs1!![index+NOR_ATTRIB+2] /= len1
+            }
+            index += VERT1_ATTRIBS
+        }
+    }
 
-   void textureMap(Static4D[] maps, int startComponent)
-     {
-     int num_comp = mTexComponent.size();
-     if( startComponent>num_comp ) return;
-
-     int num_maps = maps.length;
-     int min = Math.min(num_comp-startComponent, num_maps);
-     int vertex = startComponent>0 ? mTexComponent.get(startComponent-1).mEndIndex+1 : 0;
-     Static4D newMap, oldMap;
-     TexComponent comp;
-     float newW, newH, ratW, ratH, movX, movY;
-
-     for(int i=0; i<min; i++)
-       {
-       newMap = maps[i];
-       comp = mTexComponent.get(i+startComponent);
-
-       if( newMap!=null )
-         {
-         newW = newMap.get2();
-         newH = newMap.get3();
-
-         if( newW!=0.0f && newH!=0.0f )
-           {
-           oldMap = comp.mTextureMap;
-           ratW = newW/oldMap.get2();
-           ratH = newH/oldMap.get3();
-           movX = newMap.get0() - ratW*oldMap.get0();
-           movY = newMap.get1() - ratH*oldMap.get1();
-
-           for( int index=vertex*VERT2_ATTRIBS+TEX_ATTRIB ; vertex<=comp.mEndIndex; vertex++, index+=VERT2_ATTRIBS)
-             {
-             mVertAttribs2[index  ] = ratW*mVertAttribs2[index  ] + movX;
-             mVertAttribs2[index+1] = ratH*mVertAttribs2[index+1] + movY;
-             }
-           comp.setMap(newMap);
-           }
-         }
-
-       vertex= comp.mEndIndex+1;
-       }
-
-     mVBO2.invalidate();
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setEffectAssociationNow(component: Int, andAssociation: Int, equAssociation: Int)
+    {
+        mUBA.setEffectAssociationNow(component, andAssociation, equAssociation)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Are we going to need per-component centers (needed for correct postprocessing of concave meshes)
- * Switching this on allocates 16*MAX_EFFECT_COMPONENTS bytes for uniforms in the vertex shader.
- */
-   public static void setUseCenters()
-     {
-     mUseCenters = true;
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setNotAffectedComponentsNow(components: IntArray?)
+    {
+        mUBA.setNotAffectedComponentsNow(components)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Are we using per-component centers?
- */
-   public static boolean getUseCenters()
-     {
-     return mUseCenters;
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun setComponentCenterNow(component: Int, x: Float, y: Float, z: Float)
+    {
+        if (mUBC!=null) mUBC!!.setEffectCenterNow(component, x, y, z)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // when a derived class is done computing its mesh, it has to call this method.
+    fun setAttribs(vert1Attribs: FloatArray, vert2Attribs: FloatArray?)
+    {
+        numVertices = vert1Attribs.size/VERT1_ATTRIBS
+        mVertAttribs1 = vert1Attribs
+        mVertAttribs2 = vert2Attribs
 
-   public static int getMaxEffComponents()
-     {
-     return mMaxComponents;
-     }
+        mTexComponent!!.add(TexComponent(numVertices-1))
+        mEffComponent!!.add(numVertices-1)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * How many mesh components are we going to need? Call before compilation of the shaders.
- */
-   public static void setMaxEffComponents(int max)
-     {
-     mMaxComponents = max;
-     }
+        mVBO1!!.invalidate()
+        mVBO2!!.invalidate()
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun joinAttribs(meshes: Array<MeshBase>)
+    {
+        var mesh: MeshBase
+        var comp: TexComponent
+        val numMeshes = meshes.size
+        var numVertices: Int
+        var origVertices = this.numVertices
+        val origTexComponents: Int
+        var numTexComponents: Int
+        var origEffComponents = 0
+        var numEffComponents: Int
+
+        if (origVertices>0)
+        {
+            origTexComponents = mTexComponent!!.size
+            this.numVertices += (if (this.numVertices%2==1) 2 else 1)
+            mTexComponent!![origTexComponents-1].mEndIndex = this.numVertices-1
+            origEffComponents = mEffComponent!!.size
+            mEffComponent!![origEffComponents-1] = this.numVertices-1
+        }
+
+        for (i in 0 until numMeshes)
+        {
+            mesh = meshes[i]
+            numTexComponents = mesh.mTexComponent!!.size
+            numEffComponents = mesh.mEffComponent!!.size
+            numVertices = mesh.numVertices
+
+            val extraVerticesBefore = if (this.numVertices==0) 0 else 1
+            val extraVerticesAfter = if ((i==numMeshes-1)) 0 else (if (numVertices%2==1) 2 else 1)
+
+            for (j in 0 until numTexComponents)
+            {
+                comp = TexComponent(mesh.mTexComponent!![j])
+                comp.mEndIndex += (extraVerticesBefore+this.numVertices)
+                if (j==numTexComponents-1) comp.mEndIndex += extraVerticesAfter
+                mTexComponent!!.add(comp)
+            }
+
+            for (j in 0 until numEffComponents)
+            {
+                var index = mesh.mEffComponent!![j]
+                index += (extraVerticesBefore+this.numVertices)
+                if (j==numEffComponents-1) index += extraVerticesAfter
+                mEffComponent!!.add(index)
+
+                if (origEffComponents<maxEffComponents)
+                {
+                    mUBA.copy(origEffComponents, mesh.mUBA, j)
+                    if (useCenters) mUBC!!.copy(origEffComponents, mesh.mUBC!!, j)
+                    origEffComponents++
+                }
+            }
+
+            this.numVertices += (extraVerticesBefore+numVertices+extraVerticesAfter)
+        }
+
+        val newAttribs1 = FloatArray(VERT1_ATTRIBS*this.numVertices)
+        val newAttribs2 = FloatArray(VERT2_ATTRIBS*this.numVertices)
+        numVertices = origVertices
+
+        if (origVertices>0)
+        {
+            System.arraycopy(mVertAttribs1, 0, newAttribs1, 0, VERT1_ATTRIBS*numVertices)
+            System.arraycopy(mVertAttribs1, VERT1_ATTRIBS*(origVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS)
+            System.arraycopy(mVertAttribs2, 0, newAttribs2, 0, VERT2_ATTRIBS*numVertices)
+            System.arraycopy(mVertAttribs2, VERT2_ATTRIBS*(origVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS)
+            origVertices++
+
+            if (numVertices%2==1)
+            {
+                System.arraycopy(mVertAttribs1, VERT1_ATTRIBS*(origVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS)
+                System.arraycopy(mVertAttribs2, VERT2_ATTRIBS*(origVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS)
+                origVertices++
+            }
+        }
+
+        for (i in 0 until numMeshes)
+        {
+            mesh = meshes[i]
+            numVertices = mesh.numVertices
+
+            if (origVertices>0)
+            {
+                System.arraycopy(mesh.mVertAttribs1, 0, newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS)
+                System.arraycopy(mesh.mVertAttribs2, 0, newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS)
+                origVertices++
+            }
+            System.arraycopy(mesh.mVertAttribs1, 0, newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS*numVertices)
+            System.arraycopy(mesh.mVertAttribs2, 0, newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS*numVertices)
+            origVertices += numVertices
+
+            if (i<numMeshes-1&&numVertices>0)
+            {
+                System.arraycopy(mesh.mVertAttribs1, VERT1_ATTRIBS*(numVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS)
+                System.arraycopy(mesh.mVertAttribs2, VERT2_ATTRIBS*(numVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS)
+                origVertices++
+
+                if (numVertices%2==1)
+                {
+                    System.arraycopy(mesh.mVertAttribs1, VERT1_ATTRIBS*(numVertices-1), newAttribs1, VERT1_ATTRIBS*origVertices, VERT1_ATTRIBS)
+                    System.arraycopy(mesh.mVertAttribs2, VERT2_ATTRIBS*(numVertices-1), newAttribs2, VERT2_ATTRIBS*origVertices, VERT2_ATTRIBS)
+                    origVertices++
+                }
+            }
+        }
+
+        if (origVertices!=this.numVertices)
+        {
+            DistortedLibrary.logMessage("MeshBase: join: origVertices: "+origVertices+" numVertices: "+this.numVertices)
+        }
+
+        var endIndex: Int
+        var index = 0
+        val numEffComp = mEffComponent!!.size
+
+        for (component in 0 until numEffComp)
+        {
+            endIndex = mEffComponent!![component]
+
+            while (index<=endIndex)
+            {
+                newAttribs2[VERT2_ATTRIBS*index+COM_ATTRIB] = component.toFloat()
+                index++
+            }
+        }
+
+        mVertAttribs1 = newAttribs1
+        mVertAttribs2 = newAttribs2
+        mVBO1!!.invalidate()
+        mVBO2!!.invalidate()
+    }
 
-   public static void getUniforms(int programH, int variant)
-     {
-     mCenterBlockIndex[variant]= GLES30.glGetUniformBlockIndex(programH, "componentCenter");
-     mAssocBlockIndex[variant] = GLES30.glGetUniformBlockIndex(programH, "componentAssociation");
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // called from MeshJoined
+    fun join(meshes: Array<MeshBase>)
+    {
+        var immediateJoin = true
+
+        for (mesh in meshes)
+        {
+            if (mesh.mJobNode[0]!=null)
+            {
+                immediateJoin = false
+                break
+            }
+        }
+
+        if (immediateJoin) joinAttribs(meshes)
+        else mJobNode[0] = DeferredJobs.join(this, meshes)
+    }
 
-     if( mStride==0 )
-       {
-       String[] uniformNames = { "vComAssoc" };
-       int[] indices = new int[1];
-       int[] params  = new int[1];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    fun textureMap(maps: Array<Static4D?>, startComponent: Int)
+    {
+        val num_comp = mTexComponent!!.size
+        if (startComponent>num_comp) return
+
+        val num_maps = maps.size
+        val min = min((num_comp-startComponent).toDouble(), num_maps.toDouble()).toInt()
+        var vertex = if (startComponent>0) mTexComponent!![startComponent-1].mEndIndex+1 else 0
+        var newMap: Static4D?
+        var oldMap: Static4D
+        var comp: TexComponent
+        var newW: Float
+        var newH: Float
+        var ratW: Float
+        var ratH: Float
+        var movX: Float
+        var movY: Float
+
+        for (i in 0 until min)
+        {
+            newMap = maps[i]
+            comp = mTexComponent!![i+startComponent]
+
+            if (newMap!=null)
+            {
+                newW = newMap.get2()
+                newH = newMap.get3()
+
+                if (newW!=0.0f&&newH!=0.0f)
+                {
+                    oldMap = comp.mTextureMap
+                    ratW = newW/oldMap.get2()
+                    ratH = newH/oldMap.get3()
+                    movX = newMap.get0()-ratW*oldMap.get0()
+                    movY = newMap.get1()-ratH*oldMap.get1()
+
+                    var index = vertex*VERT2_ATTRIBS+TEX_ATTRIB
+                    while (vertex<=comp.mEndIndex)
+                    {
+                        mVertAttribs2!![index] = ratW*mVertAttribs2!![index]+movX
+                        mVertAttribs2!![index+1] = ratH*mVertAttribs2!![index+1]+movY
+                        vertex++
+                        index += VERT2_ATTRIBS
+                    }
+                    comp.setMap(newMap)
+                }
+            }
 
-       GLES30.glGetUniformIndices(programH, uniformNames, indices, 0);
-       GLES30.glGetActiveUniformsiv( programH, 1, indices, 0, GLES30.GL_UNIFORM_ARRAY_STRIDE, params,0 );
+            vertex = comp.mEndIndex+1
+        }
 
-       mStride = params[0]/4;
-       }
-     }
+        mVBO2!!.invalidate()
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void copyTransformToVertex()
-     {
-     ByteBuffer buffer = (ByteBuffer)GLES30.glMapBufferRange( GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0,
-                                                              TRAN_SIZE*mNumVertices, GLES30.GL_MAP_READ_BIT);
-     if( buffer!=null )
-       {
-       FloatBuffer feedback = buffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
-       feedback.get(mVertAttribs1,0,VERT1_ATTRIBS*mNumVertices);
-       mVBO1.updateFloat(mVertAttribs1);
-       }
-     else
-       {
-       int error = GLES30.glGetError();
-       DistortedLibrary.logMessage("MeshBase: failed to map tf buffer, error="+error);
-       }
-
-     GLES30.glUnmapBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER);
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun copyTransformToVertex()
+    {
+        val buffer = GLES30.glMapBufferRange(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0,
+                                             TRAN_SIZE*numVertices, GLES30.GL_MAP_READ_BIT) as ByteBuffer
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void debugJobs()
-     {
-     if( mJobNode[0]!=null )
-       {
-       mJobNode[0].print(0);
-       }
-     else
-       {
-       DistortedLibrary.logMessage("MeshBase: JobNode null");
-       }
-     }
+        val feedback = buffer.order(ByteOrder.nativeOrder()).asFloatBuffer()
+        feedback[mVertAttribs1, 0, VERT1_ATTRIBS*numVertices]
+        mVBO1!!.updateFloat(mVertAttribs1)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void printTexComponent(int comp)
-     {
-     if( comp>=0 && comp<getNumTexComponents() )
-       {
-       int beg = comp>0 ? mTexComponent.get(comp-1).mEndIndex+1 : 0;
-       int end = mTexComponent.get(comp).mEndIndex;
-       StringBuilder sb = new StringBuilder();
-
-       for( int vert=beg; vert<=end; vert++)
-         {
-         sb.append('(');
-         sb.append(mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB  ]);
-         sb.append(',');
-         sb.append(mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB+1]);
-         sb.append(',');
-         sb.append(mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB+2]);
-         sb.append(") ");
-         }
-
-       DistortedLibrary.logMessage("MeshBase: "+sb);
-       }
-     }
+        GLES30.glUnmapBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void printVertex(int v)
-     {
-     String pos = "\n pos: (" + mVertAttribs1[VERT1_ATTRIBS*v +POS_ATTRIB  ] + ","
-                              + mVertAttribs1[VERT1_ATTRIBS*v +POS_ATTRIB+1] + ","
-                              + mVertAttribs1[VERT1_ATTRIBS*v +POS_ATTRIB+2];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun debugJobs()
+    {
+        if (mJobNode[0]!=null)
+        {
+            mJobNode[0]!!.print(0)
+        }
+        else
+        {
+            DistortedLibrary.logMessage("MeshBase: JobNode null")
+        }
+    }
 
-     String nor = "\n nor: (" + mVertAttribs1[VERT1_ATTRIBS*v +NOR_ATTRIB  ] + ","
-                              + mVertAttribs1[VERT1_ATTRIBS*v +NOR_ATTRIB+1] + ","
-                              + mVertAttribs1[VERT1_ATTRIBS*v +NOR_ATTRIB+2];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun printTexComponent(comp: Int)
+    {
+        if (comp>=0&&comp<numTexComponents)
+        {
+            val beg = if (comp>0) mTexComponent!![comp-1].mEndIndex+1 else 0
+            val end = mTexComponent!![comp].mEndIndex
+            val sb = StringBuilder()
+
+            for (vert in beg..end)
+            {
+                sb.append('(')
+                sb.append(mVertAttribs1!![VERT1_ATTRIBS*vert+POS_ATTRIB])
+                sb.append(',')
+                sb.append(mVertAttribs1!![VERT1_ATTRIBS*vert+POS_ATTRIB+1])
+                sb.append(',')
+                sb.append(mVertAttribs1!![VERT1_ATTRIBS*vert+POS_ATTRIB+2])
+                sb.append(") ")
+            }
+
+            DistortedLibrary.logMessage("MeshBase: $sb")
+        }
+    }
 
-     String tex = "\n tex: (" + mVertAttribs2[VERT2_ATTRIBS*v +TEX_ATTRIB  ] + ","
-                              + mVertAttribs2[VERT2_ATTRIBS*v +TEX_ATTRIB+1];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun printVertex(v: Int)
+    {
+        val pos = ("""
+ pos: (${mVertAttribs1!![VERT1_ATTRIBS*v+POS_ATTRIB]},${mVertAttribs1!![VERT1_ATTRIBS*v+POS_ATTRIB+1]},${mVertAttribs1!![VERT1_ATTRIBS*v+POS_ATTRIB+2]}""")
 
-     DistortedLibrary.logMessage("MeshBase: first vertex:"+pos+nor+tex);
-     }
+        val nor = ("""
+ nor: (${mVertAttribs1!![VERT1_ATTRIBS*v+NOR_ATTRIB]},${mVertAttribs1!![VERT1_ATTRIBS*v+NOR_ATTRIB+1]},${mVertAttribs1!![VERT1_ATTRIBS*v+NOR_ATTRIB+2]}""")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void printPos()
-     {
-     StringBuilder sb = new StringBuilder();
-
-     for(int i=0; i<mNumVertices; i++)
-       {
-       sb.append('(');
-       sb.append(mVertAttribs1[VERT1_ATTRIBS*i+POS_ATTRIB  ]);
-       sb.append(',');
-       sb.append(mVertAttribs1[VERT1_ATTRIBS*i+POS_ATTRIB+1]);
-       sb.append(',');
-       sb.append(mVertAttribs1[VERT1_ATTRIBS*i+POS_ATTRIB+2]);
-       sb.append(")\n");
-       }
-     DistortedLibrary.logMessage("MeshBase: vertices: \n"+sb);
-     }
+        val tex = ("""
+ tex: (${mVertAttribs2!![VERT2_ATTRIBS*v+TEX_ATTRIB]},${mVertAttribs2!![VERT2_ATTRIBS*v+TEX_ATTRIB+1]}""")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   /**
-    * Not part of public API, do not document (public only because has to be used from the main package)
-    *
-    * @y.exclude
-    */
-   public void printNor()
-     {
-     StringBuilder sb = new StringBuilder();
-
-     for(int i=0; i<mNumVertices; i++)
-       {
-       sb.append('(');
-       sb.append(mVertAttribs1[VERT1_ATTRIBS*i+NOR_ATTRIB  ]);
-       sb.append(',');
-       sb.append(mVertAttribs1[VERT1_ATTRIBS*i+NOR_ATTRIB+1]);
-       sb.append(',');
-       sb.append(mVertAttribs1[VERT1_ATTRIBS*i+NOR_ATTRIB+2]);
-       sb.append(")\n");
-       }
-     DistortedLibrary.logMessage("MeshBase: normals:\n"+sb);
-     }
+        DistortedLibrary.logMessage("MeshBase: first vertex:$pos$nor$tex")
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void printCom()
-     {
-     StringBuilder sb = new StringBuilder();
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun printPos()
+    {
+        val sb = StringBuilder()
+
+        for (i in 0 until numVertices)
+        {
+            sb.append('(')
+            sb.append(mVertAttribs1!![VERT1_ATTRIBS*i+POS_ATTRIB])
+            sb.append(',')
+            sb.append(mVertAttribs1!![VERT1_ATTRIBS*i+POS_ATTRIB+1])
+            sb.append(',')
+            sb.append(mVertAttribs1!![VERT1_ATTRIBS*i+POS_ATTRIB+2])
+            sb.append(")\n")
+        }
+        DistortedLibrary.logMessage("MeshBase: vertices: \n$sb")
+    }
 
-     for(int i=0; i<mNumVertices; i++)
-       {
-       sb.append(mVertAttribs2[VERT2_ATTRIBS*i+COM_ATTRIB]);
-       sb.append(' ');
-       }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun printNor()
+    {
+        val sb = StringBuilder()
+
+        for (i in 0 until numVertices)
+        {
+            sb.append('(')
+            sb.append(mVertAttribs1!![VERT1_ATTRIBS*i+NOR_ATTRIB])
+            sb.append(',')
+            sb.append(mVertAttribs1!![VERT1_ATTRIBS*i+NOR_ATTRIB+1])
+            sb.append(',')
+            sb.append(mVertAttribs1!![VERT1_ATTRIBS*i+NOR_ATTRIB+2])
+            sb.append(")\n")
+        }
+        DistortedLibrary.logMessage("MeshBase: normals:\n$sb")
+    }
 
-     DistortedLibrary.logMessage("MeshBase: "+sb);
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun printCom()
+    {
+        val sb = StringBuilder()
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void printTex()
-     {
-     int vert=0;
-     int num = getNumTexComponents();
-     StringBuilder sb = new StringBuilder();
-     sb.append("tex components: ").append(num);
-
-     for(int i=0; i<num; i++)
-       {
-       int end = mTexComponent.get(i).mEndIndex;
-       sb.append("\n");
-
-       for( ; vert<=end; vert++)
-         {
-         sb.append(' ');
-         sb.append('(');
-         sb.append(mVertAttribs2[VERT2_ATTRIBS*vert+TEX_ATTRIB]);
-         sb.append(',');
-         sb.append(mVertAttribs2[VERT2_ATTRIBS*vert+TEX_ATTRIB+1]);
-         sb.append(')');
-         }
-       }
-
-     DistortedLibrary.logMessage("MeshBase: "+sb);
-     }
+        for (i in 0 until numVertices)
+        {
+            sb.append(mVertAttribs2!![VERT2_ATTRIBS*i+COM_ATTRIB])
+            sb.append(' ')
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public int getTFO()
-     {
-     return mTFO.createImmediatelyFloat(mNumVertices*TRAN_SIZE, null);
-     }
+        DistortedLibrary.logMessage("MeshBase: $sb")
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public int getNumVertices()
-     {
-     return mNumVertices;
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun printTex()
+    {
+        var vert = 0
+        val num = numTexComponents
+        val sb = StringBuilder()
+        sb.append("tex components: ").append(num)
+
+        for (i in 0 until num)
+        {
+            val end = mTexComponent!![i].mEndIndex
+            sb.append("\n")
+
+            while (vert<=end)
+            {
+                sb.append(' ')
+                sb.append('(')
+                sb.append(mVertAttribs2!![VERT2_ATTRIBS*vert+TEX_ATTRIB])
+                sb.append(',')
+                sb.append(mVertAttribs2!![VERT2_ATTRIBS*vert+TEX_ATTRIB+1])
+                sb.append(')')
+                vert++
+            }
+        }
+
+        DistortedLibrary.logMessage("MeshBase: $sb")
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void send(int programH, int variant)
-     {
-     if( !mStrideCorrected )
-       {
-       mStrideCorrected = true;
-       mUBA.correctStride(mStride);
-       }
-
-     int indexA = mUBA.getIndex();
-     GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, ASSOC_UBO_BINDING, indexA);
-     GLES30.glUniformBlockBinding(programH, mAssocBlockIndex[variant], ASSOC_UBO_BINDING);
-
-     if( mUseCenters )
-       {
-       int indexC = mUBC.getIndex();
-       GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, CENTER_UBO_BINDING, indexC);
-       GLES30.glUniformBlockBinding(programH, mCenterBlockIndex[variant], CENTER_UBO_BINDING);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    val tFO: Int
+        /**
+         * Not part of public API, do not document (public only because has to be used from the main package)
+         *
+         * @y.exclude
+         */
+        get()
+        = mTFO.createImmediatelyFloat(numVertices*TRAN_SIZE, null)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun send(programH: Int, variant: Int)
+    {
+        if (!mStrideCorrected)
+        {
+            mStrideCorrected = true
+            mUBA.correctStride(mStride)
+        }
+
+        val indexA = mUBA.index
+        GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, ASSOC_UBO_BINDING, indexA)
+        GLES30.glUniformBlockBinding(programH, mAssocBlockIndex[variant], ASSOC_UBO_BINDING)
+
+        if (useCenters)
+        {
+            val indexC = mUBC!!.index
+            GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, CENTER_UBO_BINDING, indexC)
+            GLES30.glUniformBlockBinding(programH, mCenterBlockIndex[variant], CENTER_UBO_BINDING)
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void bindVertexAttribs(DistortedProgram program)
-     {
-     if( mJobNode[0]!=null )
-       {
-       mJobNode[0].execute();  // this will set itself to null
-       }
-
-     int index1 = mVBO1.createImmediatelyFloat(mNumVertices*VERT1_SIZE, mVertAttribs1);
-     int index2 = mVBO2.createImmediatelyFloat(mNumVertices*VERT2_SIZE, mVertAttribs2);
-     int[] attr = program.mAttribute;
-
-     switch(program.mAttributeLayout )
-       {
-       case DistortedProgram.ATTR_LAYOUT_PNTC:
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1 );
-               GLES30.glVertexAttribPointer(attr[0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS);
-               GLES30.glVertexAttribPointer(attr[1], NOR_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_NOR);
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2 );
-               GLES30.glVertexAttribPointer(attr[2], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX);
-               GLES30.glVertexAttribPointer(attr[3], COM_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_COM);
-               break;
-       case DistortedProgram.ATTR_LAYOUT_PNT:
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1 );
-               GLES30.glVertexAttribPointer(attr[0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS);
-               GLES30.glVertexAttribPointer(attr[1], NOR_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_NOR);
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2 );
-               GLES30.glVertexAttribPointer(attr[2], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX);
-               break;
-       case DistortedProgram.ATTR_LAYOUT_PNC:
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1 );
-               GLES30.glVertexAttribPointer(attr[0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS);
-               GLES30.glVertexAttribPointer(attr[1], NOR_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_NOR);
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2 );
-               GLES30.glVertexAttribPointer(attr[2], COM_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_COM);
-               break;
-       case DistortedProgram.ATTR_LAYOUT_PTC:
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1 );
-               GLES30.glVertexAttribPointer(attr[0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS);
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2 );
-               GLES30.glVertexAttribPointer(attr[1], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX);
-               GLES30.glVertexAttribPointer(attr[2], COM_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_COM);
-               break;
-       case DistortedProgram.ATTR_LAYOUT_PT:
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1 );
-               GLES30.glVertexAttribPointer(attr[0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS);
-               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2 );
-               GLES30.glVertexAttribPointer(attr[1], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX);
-               break;
-       case DistortedProgram.ATTR_LAYOUT_UNK:
-               DistortedLibrary.logMessage("Unknown attribute layout: "+program.mAttributeLayout);
-       }
-
-     GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun bindVertexAttribs(program: DistortedProgram)
+    {
+        if (mJobNode[0]!=null) mJobNode[0]!!.execute() // this will set itself to null
+
+        val index1 = mVBO1!!.createImmediatelyFloat(numVertices*VERT1_SIZE, mVertAttribs1)
+        val index2 = mVBO2!!.createImmediatelyFloat(numVertices*VERT2_SIZE, mVertAttribs2)
+        val attr = program.mAttribute
+
+        when (program.mAttributeLayout)
+        {
+            DistortedProgram.ATTR_LAYOUT_PNTC ->
+            {
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1)
+                GLES30.glVertexAttribPointer(attr!![0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS)
+                GLES30.glVertexAttribPointer(attr[1], NOR_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_NOR)
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2)
+                GLES30.glVertexAttribPointer(attr[2], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX)
+                GLES30.glVertexAttribPointer(attr[3], COM_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_COM)
+            }
+
+            DistortedProgram.ATTR_LAYOUT_PNT ->
+            {
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1)
+                GLES30.glVertexAttribPointer(attr!![0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS)
+                GLES30.glVertexAttribPointer(attr[1], NOR_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_NOR)
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2)
+                GLES30.glVertexAttribPointer(attr[2], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX)
+            }
+
+            DistortedProgram.ATTR_LAYOUT_PNC ->
+            {
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1)
+                GLES30.glVertexAttribPointer(attr!![0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS)
+                GLES30.glVertexAttribPointer(attr[1], NOR_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_NOR)
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2)
+                GLES30.glVertexAttribPointer(attr[2], COM_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_COM)
+            }
+
+            DistortedProgram.ATTR_LAYOUT_PTC ->
+            {
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1)
+                GLES30.glVertexAttribPointer(attr!![0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS)
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2)
+                GLES30.glVertexAttribPointer(attr[1], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX)
+                GLES30.glVertexAttribPointer(attr[2], COM_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_COM)
+            }
+
+            DistortedProgram.ATTR_LAYOUT_PT ->
+            {
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index1)
+                GLES30.glVertexAttribPointer(attr!![0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS)
+                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index2)
+                GLES30.glVertexAttribPointer(attr[1], TEX_DATA_SIZE, GLES30.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX)
+            }
+
+            DistortedProgram.ATTR_LAYOUT_UNK -> DistortedLibrary.logMessage("Unknown attribute layout: "+program.mAttributeLayout)
+        }
+
+        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void bindTransformAttribs(DistortedProgram program)
-     {
-     int index = mTFO.createImmediatelyFloat(mNumVertices*TRAN_SIZE, null);
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Not part of public API, do not document (public only because has to be used from the main package)
+     *
+     * @y.exclude
+     */
+    fun bindTransformAttribs(program: DistortedProgram)
+    {
+        val index = mTFO.createImmediatelyFloat(numVertices*TRAN_SIZE, null)
 
-     GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index );
-     GLES30.glVertexAttribPointer(program.mAttribute[0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, 0);
-     GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
-     }
+        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, index)
+        GLES30.glVertexAttribPointer(program.mAttribute!![0], POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, 0)
+        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public void setInflate(float inflate)
-     {
-     mInflate = inflate;
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used from the main package)
- *
- * @y.exclude
- */
-   public float getInflate()
-     {
-     return mInflate;
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Return the number of bytes read.
-
-   int read(DataInputStream stream)
-     {
-     byte[] buffer = new byte[BYTES_PER_FLOAT*4];
-     int version, numEff, numTex;
-
-     //////////////////////////////////////////////////////////////////////////////////////////
-     // read version, number of vertices, number of tex components, number of effect components
-     //////////////////////////////////////////////////////////////////////////////////////////
-
-     try
-       {
-       stream.read(buffer);
-       ByteBuffer byteBuf = ByteBuffer.wrap(buffer);
-
-       version = byteBuf.getInt(0);
-
-       if( version==1 || version==2 )
-         {
-         mNumVertices= byteBuf.getInt(4);
-         numTex      = byteBuf.getInt(8);
-         numEff      = byteBuf.getInt(12);
-         }
-       else
-         {
-         DistortedLibrary.logMessage("MeshBase: Error: unknown mesh file version "+String.format("0x%08X", version));
-         return 0;
-         }
-       }
-     catch(Exception e)
-       {
-       DistortedLibrary.logMessage("MeshBase: Exception1 trying to read file: "+ e);
-       return 0;
-       }
-
-     //////////////////////////////////////////////////////////////////////////////////////////
-     // read contents of all texture and effect components
-     //////////////////////////////////////////////////////////////////////////////////////////
-
-     if( mNumVertices>0 && numEff>0 && numTex>0 )
-       {
-       int size = numEff+TEX_COMP_SIZE*numTex;
-       float[] tmp = new float[size];
-       mVertAttribs1 = new float[VERT1_ATTRIBS*mNumVertices];
-       mVertAttribs2 = new float[VERT2_ATTRIBS*mNumVertices];
-
-       // version 1 had extra 3 floats (the 'inflate' vector) in its vert1 array
-       int vert1InFile = version==2 ? VERT1_ATTRIBS : VERT1_ATTRIBS+3;
-       // ... and there was no center.
-       int centerSize  = version==2 ? 3*numEff : 0;
-
-       buffer = new byte[BYTES_PER_FLOAT*(size + centerSize + mNumVertices*(vert1InFile+VERT2_ATTRIBS))];
-
-       try
-         {
-         stream.read(buffer);
-         }
-       catch(Exception e)
-         {
-         DistortedLibrary.logMessage("MeshBase: Exception2 trying to read file: "+ e);
-         return 0;
-         }
-
-       ByteBuffer byteBuf = ByteBuffer.wrap(buffer);
-       FloatBuffer floatBuf = byteBuf.asFloatBuffer();
-
-       floatBuf.get(tmp,0,size);
-
-       TexComponent tex;
-       int index, texComp;
-       float x, y, z, w;
-
-       for(texComp=0; texComp<numTex; texComp++)
-         {
-         index = (int)tmp[TEX_COMP_SIZE*texComp];
-
-         x= tmp[TEX_COMP_SIZE*texComp+1];
-         y= tmp[TEX_COMP_SIZE*texComp+2];
-         z= tmp[TEX_COMP_SIZE*texComp+3];
-         w= tmp[TEX_COMP_SIZE*texComp+4];
-
-         tex = new TexComponent(index);
-         tex.setMap(new Static4D(x,y,z,w));
-
-         mTexComponent.add(tex);
-         }
-
-       for(int effComp=0; effComp<numEff; effComp++)
-         {
-         index = (int)tmp[TEX_COMP_SIZE*texComp + effComp ];
-         mEffComponent.add(index);
-         }
-
-       //////////////////////////////////////////////////////////////////////////////////////////
-       // read vert1 array, vert2 array and (if version>1) the component centers.
-       //////////////////////////////////////////////////////////////////////////////////////////
-
-       switch(version)
-         {
-         case 1 : index = floatBuf.position();
-
-                  for(int vert=0; vert<mNumVertices; vert++)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Return the number of bytes read.
+    fun read(stream: DataInputStream): Int
+    {
+        var buffer = ByteArray(BYTES_PER_FLOAT*4)
+        val version: Int
+        val numEff: Int
+        val numTex: Int
+
+        //////////////////////////////////////////////////////////////////////////////////////////
+        // read version, number of vertices, number of tex components, number of effect components
+        //////////////////////////////////////////////////////////////////////////////////////////
+        try
+        {
+            stream.read(buffer)
+            val byteBuf = ByteBuffer.wrap(buffer)
+
+            version = byteBuf.getInt(0)
+
+            if (version==1||version==2)
+            {
+                numVertices = byteBuf.getInt(4)
+                numTex = byteBuf.getInt(8)
+                numEff = byteBuf.getInt(12)
+            }
+            else
+            {
+                DistortedLibrary.logMessage("MeshBase: Error: unknown mesh file version "+String.format("0x%08X", version))
+                return 0
+            }
+        }
+        catch (e: Exception)
+        {
+            DistortedLibrary.logMessage("MeshBase: Exception1 trying to read file: $e")
+            return 0
+        }
+
+        //////////////////////////////////////////////////////////////////////////////////////////
+        // read contents of all texture and effect components
+        //////////////////////////////////////////////////////////////////////////////////////////
+        if (numVertices>0&&numEff>0&&numTex>0)
+        {
+            val size = numEff+TEX_COMP_SIZE*numTex
+            val tmp = FloatArray(size)
+            mVertAttribs1 = FloatArray(VERT1_ATTRIBS*numVertices)
+            mVertAttribs2 = FloatArray(VERT2_ATTRIBS*numVertices)
+
+            // version 1 had extra 3 floats (the 'inflate' vector) in its vert1 array
+            val vert1InFile = if (version==2) VERT1_ATTRIBS else VERT1_ATTRIBS+3
+            // ... and there was no center.
+            val centerSize = if (version==2) 3*numEff else 0
+
+            buffer = ByteArray(BYTES_PER_FLOAT*(size+centerSize+numVertices*(vert1InFile+VERT2_ATTRIBS)))
+
+            try
+            {
+                stream.read(buffer)
+            }
+            catch (e: Exception)
+            {
+                DistortedLibrary.logMessage("MeshBase: Exception2 trying to read file: $e")
+                return 0
+            }
+
+            val byteBuf = ByteBuffer.wrap(buffer)
+            val floatBuf = byteBuf.asFloatBuffer()
+
+            floatBuf[tmp, 0, size]
+
+            var tex: TexComponent
+            var index: Int
+            var x: Float
+            var y: Float
+            var z: Float
+            var w: Float
+
+            var texComp = 0
+            while (texComp<numTex)
+            {
+                index = tmp[TEX_COMP_SIZE*texComp].toInt()
+
+                x = tmp[TEX_COMP_SIZE*texComp+1]
+                y = tmp[TEX_COMP_SIZE*texComp+2]
+                z = tmp[TEX_COMP_SIZE*texComp+3]
+                w = tmp[TEX_COMP_SIZE*texComp+4]
+
+                tex = TexComponent(index)
+                tex.setMap(Static4D(x, y, z, w))
+
+                mTexComponent!!.add(tex)
+                texComp++
+            }
+
+            for (effComp in 0 until numEff)
+            {
+                index = tmp[TEX_COMP_SIZE*texComp+effComp].toInt()
+                mEffComponent!!.add(index)
+            }
+
+            //////////////////////////////////////////////////////////////////////////////////////////
+            // read vert1 array, vert2 array and (if version>1) the component centers.
+            //////////////////////////////////////////////////////////////////////////////////////////
+            when (version)
+            {
+                1 ->
+                {
+                    index = floatBuf.position()
+
+                    var vert = 0
+                    while (vert<numVertices)
                     {
-                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB  ] = floatBuf.get(index+POS_ATTRIB  );
-                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB+1] = floatBuf.get(index+POS_ATTRIB+1);
-                    mVertAttribs1[VERT1_ATTRIBS*vert+POS_ATTRIB+2] = floatBuf.get(index+POS_ATTRIB+2);
-                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB  ] = floatBuf.get(index+NOR_ATTRIB  );
-                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB+1] = floatBuf.get(index+NOR_ATTRIB+1);
-                    mVertAttribs1[VERT1_ATTRIBS*vert+NOR_ATTRIB+2] = floatBuf.get(index+NOR_ATTRIB+2);
-                    index+=vert1InFile;
+                        mVertAttribs1!![VERT1_ATTRIBS*vert+POS_ATTRIB] = floatBuf[index+POS_ATTRIB]
+                        mVertAttribs1!![VERT1_ATTRIBS*vert+POS_ATTRIB+1] = floatBuf[index+POS_ATTRIB+1]
+                        mVertAttribs1!![VERT1_ATTRIBS*vert+POS_ATTRIB+2] = floatBuf[index+POS_ATTRIB+2]
+                        mVertAttribs1!![VERT1_ATTRIBS*vert+NOR_ATTRIB] = floatBuf[index+NOR_ATTRIB]
+                        mVertAttribs1!![VERT1_ATTRIBS*vert+NOR_ATTRIB+1] = floatBuf[index+NOR_ATTRIB+1]
+                        mVertAttribs1!![VERT1_ATTRIBS*vert+NOR_ATTRIB+2] = floatBuf[index+NOR_ATTRIB+2]
+                        index += vert1InFile
+                        vert++
                     }
-                  floatBuf.position(index);
-                  break;
-         case 2 : float[] centers = new float[3*numEff];
-                  floatBuf.get(centers,0,3*numEff);
+                    floatBuf.position(index)
+                }
 
-                  if( mUseCenters )
+                2 ->
+                {
+                    val centers = FloatArray(3*numEff)
+                    floatBuf[centers, 0, 3*numEff]
+
+                    if (useCenters)
                     {
-                    for(int eff=0; eff<numEff; eff++)
-                      {
-                      mUBC.setEffectCenterNow(eff, centers[3*eff], centers[3*eff+1], centers[3*eff+2]);
-                      }
+                        var eff = 0
+                        while (eff<numEff)
+                        {
+                            mUBC!!.setEffectCenterNow(eff, centers[3*eff], centers[3*eff+1], centers[3*eff+2])
+                            eff++
+                        }
                     }
 
-                  floatBuf.get(mVertAttribs1, 0, VERT1_ATTRIBS*mNumVertices);
-                  break;
-         default: DistortedLibrary.logMessage("MeshBase: Error: unknown mesh file version "+String.format("0x%08X", version));
-                  return 0;
-         }
+                    floatBuf[mVertAttribs1, 0, VERT1_ATTRIBS*numVertices]
+                }
 
-       floatBuf.get(mVertAttribs2, 0, VERT2_ATTRIBS*mNumVertices);
-       }
+                else ->
+                {
+                    DistortedLibrary.logMessage("MeshBase: Error: unknown mesh file version "+String.format("0x%08X", version))
+                    return 0
+                }
+            }
 
-     return BYTES_PER_FLOAT*( 4 + numEff+TEX_COMP_SIZE*numTex + VERT1_ATTRIBS*mNumVertices + VERT2_ATTRIBS*mNumVertices );
-     }
+            floatBuf[mVertAttribs2, 0, VERT2_ATTRIBS*numVertices]
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @y.exclude
- */
-   public int getLastVertexEff(int effComponent)
-     {
-     if( effComponent>=0 && effComponent<mTexComponent.size() )
-       {
-       return mEffComponent.get(effComponent);
-       }
-
-     return 0;
-     }
+        return BYTES_PER_FLOAT*(4+numEff+TEX_COMP_SIZE*numTex+VERT1_ATTRIBS*numVertices+VERT2_ATTRIBS*numVertices)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * @y.exclude
- */
-   public int getLastVertexTex(int texComponent)
-     {
-     if( texComponent>=0 && texComponent<mTexComponent.size() )
-       {
-       return mTexComponent.get(texComponent).mEndIndex;
-       }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * @y.exclude
+     */
+    fun getLastVertexEff(effComponent: Int): Int
+    {
+        if (effComponent>=0&&effComponent<mTexComponent!!.size)
+        {
+            return mEffComponent!![effComponent]
+        }
 
-     return 0;
-     }
+        return 0
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Write the Mesh to a File.
- */
-   public void write(DataOutputStream stream)
-     {
-     TexComponent tex;
-
-     int numTex = mTexComponent.size();
-     int numEff = mEffComponent.size();
-
-     try
-       {
-       stream.writeInt(2);  // version
-       stream.writeInt(mNumVertices);
-       stream.writeInt(numTex);
-       stream.writeInt(numEff);
-
-       for(int i=0; i<numTex; i++)
-         {
-         tex = mTexComponent.get(i);
-
-         stream.writeFloat((float)tex.mEndIndex);
-         stream.writeFloat(tex.mTextureMap.get0());
-         stream.writeFloat(tex.mTextureMap.get1());
-         stream.writeFloat(tex.mTextureMap.get2());
-         stream.writeFloat(tex.mTextureMap.get3());
-         }
-
-       for(int i=0; i<numEff; i++)
-         {
-         stream.writeFloat((float)mEffComponent.get(i));
-         }
-
-       float[] centers = mUseCenters ? mUBC.getBackingArray() : new float[4*numEff];
-
-       for(int i=0; i<numEff; i++)
-         {
-         stream.writeFloat(centers[4*i  ]);
-         stream.writeFloat(centers[4*i+1]);
-         stream.writeFloat(centers[4*i+2]);
-         }
-
-       ByteBuffer vertBuf1 = ByteBuffer.allocate(VERT1_SIZE*mNumVertices);
-       vertBuf1.asFloatBuffer().put(mVertAttribs1);
-       ByteBuffer vertBuf2 = ByteBuffer.allocate(VERT2_SIZE*mNumVertices);
-       vertBuf2.asFloatBuffer().put(mVertAttribs2);
-
-       stream.write(vertBuf1.array());
-       stream.write(vertBuf2.array());
-       }
-     catch(IOException ex)
-       {
-       DistortedLibrary.logMessage("MeshBase: IOException trying to write a mesh: "+ ex);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * @y.exclude
+     */
+    fun getLastVertexTex(texComponent: Int): Int
+    {
+        if (texComponent>=0&&texComponent<mTexComponent!!.size)
+        {
+            return mTexComponent!![texComponent].mEndIndex
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this Mesh, do we want to render the Normal vectors as well?
- * <p>
- * Will work only on OpenGL ES >= 3.0 devices.
- *
- * @param show Controls if we render the Normal vectors or not.
- */
-   public void setShowNormals(boolean show)
-     {
-     mShowNormals = show;
-     }
+        return 0
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * When rendering this mesh, should we also draw the normal vectors?
- *
- * @return <i>true</i> if we do render normal vectors
- */
-   public boolean getShowNormals()
-     {
-     return mShowNormals;
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Write the Mesh to a File.
+     */
+    fun write(stream: DataOutputStream)
+    {
+        var tex: TexComponent
+
+        val numTex = mTexComponent!!.size
+        val numEff = mEffComponent!!.size
+
+        try
+        {
+            stream.writeInt(2) // version
+            stream.writeInt(numVertices)
+            stream.writeInt(numTex)
+            stream.writeInt(numEff)
+
+            for (i in 0 until numTex)
+            {
+                tex = mTexComponent!![i]
+
+                stream.writeFloat(tex.mEndIndex.toFloat())
+                stream.writeFloat(tex.mTextureMap.get0())
+                stream.writeFloat(tex.mTextureMap.get1())
+                stream.writeFloat(tex.mTextureMap.get2())
+                stream.writeFloat(tex.mTextureMap.get3())
+            }
+
+            for (i in 0 until numEff)
+            {
+                stream.writeFloat(mEffComponent!![i].toFloat())
+            }
+
+            val centers = if (useCenters) mUBC!!.backingArray else FloatArray(4*numEff)
+
+            for (i in 0 until numEff)
+            {
+                stream.writeFloat(centers[4*i])
+                stream.writeFloat(centers[4*i+1])
+                stream.writeFloat(centers[4*i+2])
+            }
+
+            val vertBuf1 = ByteBuffer.allocate(VERT1_SIZE*numVertices)
+            vertBuf1.asFloatBuffer().put(mVertAttribs1)
+            val vertBuf2 = ByteBuffer.allocate(VERT2_SIZE*numVertices)
+            vertBuf2.asFloatBuffer().put(mVertAttribs2)
+
+            stream.write(vertBuf1.array())
+            stream.write(vertBuf2.array())
+        }
+        catch (ex: IOException)
+        {
+            DistortedLibrary.logMessage("MeshBase: IOException trying to write a mesh: $ex")
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Merge all texture components of this Mesh into a single one.
- */
-   public void mergeTexComponents()
-     {
-     if( mJobNode[0]==null )
-       {
-       mergeTexComponentsNow();
-       }
-     else
-       {
-       mJobNode[0] = DeferredJobs.mergeTex(this);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Merge all effect components of this Mesh into a single one.
- */
-   public void mergeEffComponents()
-     {
-     if( mJobNode[0]==null )
-       {
-       mergeEffComponentsNow();
-       }
-     else
-       {
-       mJobNode[0] = DeferredJobs.mergeEff(this);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Release all internal resources.
- */
-   public void markForDeletion()
-     {
-     mVertAttribs1 = null;
-     mVertAttribs2 = null;
-
-     mVBO1.markForDeletion();
-     mVBO2.markForDeletion();
-     mTFO.markForDeletion();
-     mUBA.markForDeletion();
-
-     if( mUBC!=null )
-       {
-       mUBC.markForDeletion();
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Merge all texture components of this Mesh into a single one.
+     */
+    fun mergeTexComponents()
+    {
+        if (mJobNode[0]==null)
+        {
+            mergeTexComponentsNow()
+        }
+        else
+        {
+            mJobNode[0] = DeferredJobs.mergeTex(this)
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Apply a Matrix Effect to the components which match the (addAssoc,equAssoc) association.
- * <p>
- * This is a static, permanent modification of the vertices contained in this Mesh. If the effect
- * contains any Dynamics, they will be evaluated at 0.
- *
- * @param effect List of Matrix Effects to apply to the Mesh.
- * @param andAssoc 'Logical AND' association which defines which components will be affected.
- * @param equAssoc 'equality' association which defines which components will be affected.
- */
-   public void apply(MatrixEffect effect, int andAssoc, int equAssoc)
-     {
-     if( mJobNode[0]==null )
-       {
-       applyMatrix(effect,andAssoc,equAssoc);
-       }
-     else
-       {
-       mJobNode[0] = DeferredJobs.matrix(this,effect,andAssoc,equAssoc);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Merge all effect components of this Mesh into a single one.
+     */
+    fun mergeEffComponents()
+    {
+        if (mJobNode[0]==null)
+        {
+            mergeEffComponentsNow()
+        }
+        else
+        {
+            mJobNode[0] = DeferredJobs.mergeEff(this)
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Apply a Vertex Effect to the vertex mesh.
- * <p>
- * This is a static, permanent modification of the vertices contained in this Mesh. If the effects
- * contain any Dynamics, the Dynamics will be evaluated at 0.
- * We would call this several times building up a list of Effects to do. This list of effects gets
- * lazily executed only when the Mesh is used for rendering for the first time.
- *
- * @param effect Vertex Effect to apply to the Mesh.
- */
-   public void apply(VertexEffect effect)
-     {
-     mJobNode[0] = DeferredJobs.vertex(this,effect);
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Release all internal resources.
+     */
+    fun markForDeletion()
+    {
+        mVertAttribs1 = null
+        mVertAttribs2 = null
+
+        mVBO1!!.markForDeletion()
+        mVBO2!!.markForDeletion()
+        mTFO.markForDeletion()
+        mUBA.markForDeletion()
+
+        if (mUBC!=null)
+        {
+            mUBC!!.markForDeletion()
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets texture maps for (some of) the components of this mesh.
- * <p>
- * Format: ( x of lower-left corner, y of lower-left corner, width, height ).
- * For example maps[0] = new Static4D( 0.0, 0.5, 0.5, 0.5 ) sets the 0th component texture map to the
- * upper-left quadrant of the texture.
- * <p>
- * Probably the most common user case would be sending as many maps as there are components in this
- * Mesh. One can also send less, or more (the extraneous ones will be ignored) and set some of them
- * to null (those will be ignored as well). So if there are 5 components, and we want to set the map
- * of the 2nd and 4rd one, call this with
- * maps = new Static4D[4]
- * maps[0] = null
- * maps[1] = the map for the 2nd component
- * maps[2] = null
- * maps[3] = the map for the 4th component
- * A map's width and height have to be non-zero (but can be negative!)
- *
- * @param maps            List of texture maps to apply to the texture's components.
- * @param startComponent  the component the first of the maps refers to.
- */
-   public void setTextureMap(Static4D[] maps, int startComponent)
-     {
-     if( mJobNode[0]==null )
-       {
-       textureMap(maps,startComponent);
-       }
-     else
-       {
-       mJobNode[0] = DeferredJobs.textureMap(this,maps,startComponent);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Apply a Matrix Effect to the components which match the (addAssoc,equAssoc) association.
+     *
+     *
+     * This is a static, permanent modification of the vertices contained in this Mesh. If the effect
+     * contains any Dynamics, they will be evaluated at 0.
+     *
+     * @param effect List of Matrix Effects to apply to the Mesh.
+     * @param andAssoc 'Logical AND' association which defines which components will be affected.
+     * @param equAssoc 'equality' association which defines which components will be affected.
+     */
+    fun apply(effect: MatrixEffect, andAssoc: Int, equAssoc: Int)
+    {
+        if (mJobNode[0]==null)
+        {
+            applyMatrix(effect, andAssoc, equAssoc)
+        }
+        else
+        {
+            mJobNode[0] = DeferredJobs.matrix(this, effect, andAssoc, equAssoc)
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the texture map of one of the components.
- *
- * @param component The component number whose texture map we want to retrieve.
- */
-   public Static4D getTextureMap(int component)
-     {
-     return (component>=0 && component<mTexComponent.size()) ? mTexComponent.get(component).mTextureMap : null;
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Apply a Vertex Effect to the vertex mesh.
+     *
+     *
+     * This is a static, permanent modification of the vertices contained in this Mesh. If the effects
+     * contain any Dynamics, the Dynamics will be evaluated at 0.
+     * We would call this several times building up a list of Effects to do. This list of effects gets
+     * lazily executed only when the Mesh is used for rendering for the first time.
+     *
+     * @param effect Vertex Effect to apply to the Mesh.
+     */
+    fun apply(effect: VertexEffect?)
+    {
+        mJobNode[0] = DeferredJobs.vertex(this, effect)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Set Effect association.
- * This creates an association between a Component of this Mesh and a Vertex Effect.
- * One can set two types of associations - an 'logical and' and a 'equal' associations and the Effect
- * will only be active on vertices of Components such that
- * (effect andAssoc) & (component andAssoc) != 0 || (effect equAssoc) == (mesh equAssoc)
- * (see main_vertex_shader)
- * The point: this way we can configure the system so that each Vertex Effect acts only on a certain
- * subset of a Mesh, thus potentially significantly reducing the number of render calls.
- * <p>
- * Only the bottom 31 bits of the 'andAssociation' are possible, the top one is taken by Postprocessing
- * Association [i.e. marking which components are disabled when we postprocess]
- */
-   public void setEffectAssociation(int component, int andAssociation, int equAssociation)
-     {
-     if( component>=0 && component<mMaxComponents )
-       {
-       if( mJobNode[0]==null )
-         {
-         setEffectAssociationNow(component, andAssociation, equAssociation);
-         }
-       else
-         {
-         mJobNode[0] = DeferredJobs.effectAssoc(this,component,andAssociation,equAssociation);
-         }
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Sets texture maps for (some of) the components of this mesh.
+     *
+     *
+     * Format: ( x of lower-left corner, y of lower-left corner, width, height ).
+     * For example maps[0] = new Static4D( 0.0, 0.5, 0.5, 0.5 ) sets the 0th component texture map to the
+     * upper-left quadrant of the texture.
+     *
+     *
+     * Probably the most common user case would be sending as many maps as there are components in this
+     * Mesh. One can also send less, or more (the extraneous ones will be ignored) and set some of them
+     * to null (those will be ignored as well). So if there are 5 components, and we want to set the map
+     * of the 2nd and 4rd one, call this with
+     * maps = new Static4D[4]
+     * maps[0] = null
+     * maps[1] = the map for the 2nd component
+     * maps[2] = null
+     * maps[3] = the map for the 4th component
+     * A map's width and height have to be non-zero (but can be negative!)
+     *
+     * @param maps            List of texture maps to apply to the texture's components.
+     * @param startComponent  the component the first of the maps refers to.
+     */
+    fun setTextureMap(maps: Array<Static4D?>, startComponent: Int)
+    {
+        if (mJobNode[0]==null)
+        {
+            textureMap(maps, startComponent)
+        }
+        else
+        {
+            mJobNode[0] = DeferredJobs.textureMap(this, maps, startComponent)
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Set certain components to be not affected by any subsequent postprocessing.
- * Calling this the second time resets the list. Calling this with null makes all components
- * affected again.
- *
- * @param components list of components which will not be not affected by any Postprocessing.
- */
-  public void setComponentsNotAffectedByPostprocessing(int[] components)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Return the texture map of one of the components.
+     *
+     * @param component The component number whose texture map we want to retrieve.
+     */
+    fun getTextureMap(component: Int): Static4D?
     {
-    if( mJobNode[0]==null )
-      {
-      setNotAffectedComponentsNow(components);
-      }
-    else
-      {
-      mJobNode[0] = DeferredJobs.setNotAffected(this,components);
-      }
+        return if ((component>=0&&component<mTexComponent!!.size)) mTexComponent!![component].mTextureMap else null
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Set center of a Component.
- * A 'center' of a (effect) component is a 3D point in space. The array of centers gets sent to
- * the vertex shader as a Uniform Buffer Object; in the vertex shader we can then use it to compute
- * the 'Inflation' of the whole Mesh per-component. 'Inflation' is needed by postprocess effects to
- * add the 'halo' around an object.
- * This is all 'per-component' so that a user has a chance to make the halo look right in case of
- * non-convex meshes: then we need to ensure that each component of such a mesh is a convex
- * sub-mesh with its center being more-or-less the center of gravity of the sub-mesh.
- */
-   public void setComponentCenter(int component, float centerX, float centerY, float centerZ)
-     {
-     if( component>=0 && component<mMaxComponents )
-       {
-       if( mJobNode[0]==null )
-         {
-         setComponentCenterNow(component, centerX, centerY, centerZ);
-         }
-       else
-         {
-         mJobNode[0] = DeferredJobs.componentCenter(this,component,centerX, centerY, centerZ);
-         }
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Set Effect association.
+     * This creates an association between a Component of this Mesh and a Vertex Effect.
+     * One can set two types of associations - an 'logical and' and a 'equal' associations and the Effect
+     * will only be active on vertices of Components such that
+     * (effect andAssoc) & (component andAssoc) != 0 || (effect equAssoc) == (mesh equAssoc)
+     * (see main_vertex_shader)
+     * The point: this way we can configure the system so that each Vertex Effect acts only on a certain
+     * subset of a Mesh, thus potentially significantly reducing the number of render calls.
+     *
+     *
+     * Only the bottom 31 bits of the 'andAssociation' are possible, the top one is taken by Postprocessing
+     * Association [i.e. marking which components are disabled when we postprocess]
+     */
+    fun setEffectAssociation(component: Int, andAssociation: Int, equAssociation: Int)
+    {
+        if (component>=0&&component<maxEffComponents)
+        {
+            if (mJobNode[0]==null)
+            {
+                setEffectAssociationNow(component, andAssociation, equAssociation)
+            }
+            else
+            {
+                mJobNode[0] = DeferredJobs.effectAssoc(this, component, andAssociation, equAssociation)
+            }
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Set the centers of a all components to the same value in one go.
- */
-   public void setAllComponentCenters(float centerX, float centerY, float centerZ)
-     {
-     if( mJobNode[0]==null )
-       {
-       setComponentCenterNow(-1, centerX, centerY, centerZ);
-       }
-     else
-       {
-       mJobNode[0] = DeferredJobs.componentCenter(this,-1,centerX, centerY, centerZ);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Set certain components to be not affected by any subsequent postprocessing.
+     * Calling this the second time resets the list. Calling this with null makes all components
+     * affected again.
+     *
+     * @param components list of components which will not be not affected by any Postprocessing.
+     */
+    fun setComponentsNotAffectedByPostprocessing(components: IntArray?)
+    {
+        if (mJobNode[0]==null)
+        {
+            setNotAffectedComponentsNow(components)
+        }
+        else
+        {
+            mJobNode[0] = DeferredJobs.setNotAffected(this, components)
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Adds an empty (no vertices) texture component to the end of the text component list.
- * Sometimes we want to do this to have several Meshes with equal number of tex components each.
- */
-   public void addEmptyTexComponent()
-     {
-      if( mJobNode[0]==null )
-       {
-       addEmptyTexComponentNow();
-       }
-     else
-       {
-       mJobNode[0] = DeferredJobs.addEmptyTex(this);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Set center of a Component.
+     * A 'center' of a (effect) component is a 3D point in space. The array of centers gets sent to
+     * the vertex shader as a Uniform Buffer Object; in the vertex shader we can then use it to compute
+     * the 'Inflation' of the whole Mesh per-component. 'Inflation' is needed by postprocess effects to
+     * add the 'halo' around an object.
+     * This is all 'per-component' so that a user has a chance to make the halo look right in case of
+     * non-convex meshes: then we need to ensure that each component of such a mesh is a convex
+     * sub-mesh with its center being more-or-less the center of gravity of the sub-mesh.
+     */
+    fun setComponentCenter(component: Int, centerX: Float, centerY: Float, centerZ: Float)
+    {
+        if (component>=0&&component<maxEffComponents)
+        {
+            if (mJobNode[0]==null)
+            {
+                setComponentCenterNow(component, centerX, centerY, centerZ)
+            }
+            else
+            {
+                mJobNode[0] = DeferredJobs.componentCenter(this, component, centerX, centerY, centerZ)
+            }
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the number of texture components, i.e. individual subsets of the whole set of vertices
- * which can be independently textured.
- *
- * @return The number of Texture Components of this Mesh.
- */
-   public int getNumTexComponents()
-     {
-     return mTexComponent.size();
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Set the centers of a all components to the same value in one go.
+     */
+    fun setAllComponentCenters(centerX: Float, centerY: Float, centerZ: Float)
+    {
+        if (mJobNode[0]==null)
+        {
+            setComponentCenterNow(-1, centerX, centerY, centerZ)
+        }
+        else
+        {
+            mJobNode[0] = DeferredJobs.componentCenter(this, -1, centerX, centerY, centerZ)
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return the number of 'effect' components, i.e. individual subsets of the whole set of vertices
- * to which a VertexEffect can be addressed, independently of other vertices.
- *
- * @return The number of Effect Components of this Mesh.
- */
-   public int getNumEffComponents()
-     {
-     return mEffComponent.size();
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Adds an empty (no vertices) texture component to the end of the text component list.
+     * Sometimes we want to do this to have several Meshes with equal number of tex components each.
+     */
+    fun addEmptyTexComponent()
+    {
+        if (mJobNode[0]==null)
+        {
+            addEmptyTexComponentNow()
+        }
+        else
+        {
+            mJobNode[0] = DeferredJobs.addEmptyTex(this)
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             and normals (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-   public abstract MeshBase copy(boolean deep);
-   }
\ No newline at end of file
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    val numTexComponents: Int
+        /**
+         * Return the number of texture components, i.e. individual subsets of the whole set of vertices
+         * which can be independently textured.
+         *
+         * @return The number of Texture Components of this Mesh.
+         */
+        get()
+        = mTexComponent!!.size
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    val numEffComponents: Int
+        /**
+         * Return the number of 'effect' components, i.e. individual subsets of the whole set of vertices
+         * to which a VertexEffect can be addressed, independently of other vertices.
+         *
+         * @return The number of Effect Components of this Mesh.
+         */
+        get()
+        = mEffComponent!!.size
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * and normals (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    abstract fun copy(deep: Boolean): MeshBase
+
+    companion object
+    {
+        private const val ASSOC_UBO_BINDING = 3
+        private const val CENTER_UBO_BINDING = 4
+
+        // sizes of attributes of an individual vertex.
+        private const val POS_DATA_SIZE = 3 // vertex coordinates: x,y,z
+        private const val NOR_DATA_SIZE = 3 // normal vector: x,y,z
+        private const val TEX_DATA_SIZE = 2 // texture coordinates: s,t
+        private const val COM_DATA_SIZE = 1 // component number, a single float
+
+        const val POS_ATTRIB: Int = 0
+        const val NOR_ATTRIB: Int = POS_DATA_SIZE
+        const val TEX_ATTRIB: Int = 0
+        const val COM_ATTRIB: Int = TEX_DATA_SIZE
+
+        const val VERT1_ATTRIBS: Int = POS_DATA_SIZE+NOR_DATA_SIZE // number of attributes of a vertex (the part changed by preapply)
+        const val VERT2_ATTRIBS: Int = TEX_DATA_SIZE+COM_DATA_SIZE // number of attributes of a vertex (the 'preapply invariant' part)
+        const val TRAN_ATTRIBS: Int = POS_DATA_SIZE+NOR_DATA_SIZE // number of attributes of a transform feedback vertex
+
+        private const val BYTES_PER_FLOAT = 4
+
+        private const val OFFSET_POS = POS_ATTRIB*BYTES_PER_FLOAT
+        private const val OFFSET_NOR = NOR_ATTRIB*BYTES_PER_FLOAT
+        private const val OFFSET_TEX = TEX_ATTRIB*BYTES_PER_FLOAT
+        private const val OFFSET_COM = COM_ATTRIB*BYTES_PER_FLOAT
+
+        private const val TRAN_SIZE = TRAN_ATTRIBS*BYTES_PER_FLOAT
+        private const val VERT1_SIZE = VERT1_ATTRIBS*BYTES_PER_FLOAT
+        private const val VERT2_SIZE = VERT2_ATTRIBS*BYTES_PER_FLOAT
+
+        private val mCenterBlockIndex = IntArray(EffectQueue.MAIN_VARIANTS)
+        private val mAssocBlockIndex = IntArray(EffectQueue.MAIN_VARIANTS)
+
+        private const val TEX_COMP_SIZE = 5 // 5 four-byte entities inside the component
+
+        /**
+         * Are we using per-component centers?
+         */
+        var useCenters: Boolean = false
+            private set
+        private var mStride = 0
+
+        /**
+         * How many mesh components are we going to need? Call before compilation of the shaders.
+         */
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        var maxEffComponents: Int = 100
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        /**
+         * Are we going to need per-component centers (needed for correct postprocessing of concave meshes)
+         * Switching this on allocates 16*MAX_EFFECT_COMPONENTS bytes for uniforms in the vertex shader.
+         */
+        fun setUseCenters()
+        {
+            useCenters = true
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        @JvmStatic
+        fun getUniforms(programH: Int, variant: Int)
+        {
+            mCenterBlockIndex[variant] = GLES30.glGetUniformBlockIndex(programH, "componentCenter")
+            mAssocBlockIndex[variant] = GLES30.glGetUniformBlockIndex(programH, "componentAssociation")
+
+            if (mStride==0)
+            {
+                val uniformNames = arrayOf("vComAssoc")
+                val indices = IntArray(1)
+                val params = IntArray(1)
+
+                GLES30.glGetUniformIndices(programH, uniformNames, indices, 0)
+                GLES30.glGetActiveUniformsiv(programH, 1, indices, 0, GLES30.GL_UNIFORM_ARRAY_STRIDE, params, 0)
+
+                mStride = params[0]/4
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/mesh/MeshCubes.kt b/src/main/java/org/distorted/library/mesh/MeshCubes.kt
index 7f26711..69f3a97 100644
--- a/src/main/java/org/distorted/library/mesh/MeshCubes.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshCubes.kt
@@ -18,911 +18,938 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-import org.distorted.library.main.DistortedLibrary;
-import org.distorted.library.type.Static4D;
-
-import java.util.ArrayList;
+import org.distorted.library.main.DistortedLibrary
+import org.distorted.library.type.Static4D
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Create a 3D grid composed of Cubes.
- * <p>
+ *
  * Any subset of a MxNx1 cuboid is possible. (repeated arbitrary number of times into Z-dir)
  */
-public class MeshCubes extends MeshBase
-   {
-   private static final float R = 0.0f;
-
-   private static final int FRONT = 0;
-   private static final int BACK  = 1;
-   private static final int LEFT  = 2;
-   private static final int RIGHT = 3;
-   private static final int TOP   = 4;
-   private static final int BOTTOM= 5;
-
-   private static final int NORTH = 0;
-   private static final int WEST  = 1;
-   private static final int EAST  = 2;
-   private static final int SOUTH = 3;
-
-   private static final float[] mNormalX = new float[4];
-   private static final float[] mNormalY = new float[4];
-   private static final float[] mNormalZ = new float[4];
-
-   private static class Edge
-     {
-     final int side; 
-     final int row;
-     final int col;
-     
-     Edge(int s, int r, int c)
-       {
-       side= s; 
-       row = r;
-       col = c;
-       }
-     }
-
-   private float[] mTexMappingX,mTexMappingY, mTexMappingW, mTexMappingH;
-
-   private int mCols, mRows, mSlices;
-   private int[][] mCubes;
-   private byte[][] mInflateX, mInflateY;
-   private ArrayList<Edge> mEdges = new ArrayList<>();
-
-   private int currVert;
-   private int numVertices;
-   private int mSideBends;
-   private int mEdgeNum;
-   private int mSideWalls;
+class MeshCubes : MeshBase
+{
+    private class Edge
+        (val side: Int, val row: Int, val col: Int)
+
+    private var mTexMappingX: FloatArray? = null
+    private var mTexMappingY: FloatArray? = null
+    private var mTexMappingW: FloatArray? = null
+    private var mTexMappingH: FloatArray? = null
+
+    private var mCols = 0
+    private var mRows = 0
+    private var mSlices = 0
+    private var mCubes: Array<IntArray>?     = null
+    private var mInflateX: Array<ByteArray>? = null
+    private var mInflateY: Array<ByteArray>? = null
+    private var mEdges: ArrayList<Edge>? = ArrayList()
+
+    private var currVert = 0
+    override var numVertices: Int = 0
+    private var mSideBends = 0
+    private var mEdgeNum = 0
+    private var mSideWalls = 0
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // a Block is split into two triangles along the NE-SW line iff it is in the top-right
+    // or bottom-left quadrant of the grid.
+    private fun isNE(row: Int, col: Int): Boolean
+    {
+        return ((2*row<mRows) xor (2*col<mCols))
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// a Block is split into two triangles along the NE-SW line iff it is in the top-right
-// or bottom-left quadrant of the grid.
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // fill per-side texture mappings. Default: all 6 sides map to the whole texture.
+    // Each Static4D describes the way its side will map. Format:
+    //
+    // 1st float: X-coord of the texture point that our top-left corner of the side maps to
+    // 2nd float: Y-coord of the texture point that our top-left corner of the side maps to
+    // 3rd float: X-coord of the texture point that our bot-right corner of the side maps to
+    // 4th float: Y-coord of the texture point that our bot-right corner of the side maps to
+    private fun fillTexMappings(front: Static4D, back: Static4D, left: Static4D, right: Static4D, top: Static4D, bottom: Static4D)
+    {
+        if (mTexMappingX==null) mTexMappingX = FloatArray(6)
+        if (mTexMappingY==null) mTexMappingY = FloatArray(6)
+        if (mTexMappingW==null) mTexMappingW = FloatArray(6)
+        if (mTexMappingH==null) mTexMappingH = FloatArray(6)
+
+        mTexMappingX!![FRONT] = front.get0()
+        mTexMappingY!![FRONT] = front.get1()
+        mTexMappingW!![FRONT] = front.get2()-front.get0()
+        mTexMappingH!![FRONT] = front.get3()-front.get1()
+
+        mTexMappingX!![BACK] = back.get0()
+        mTexMappingY!![BACK] = back.get1()
+        mTexMappingW!![BACK] = back.get2()-back.get0()
+        mTexMappingH!![BACK] = back.get3()-back.get1()
+
+        mTexMappingX!![LEFT] = left.get0()
+        mTexMappingY!![LEFT] = left.get1()
+        mTexMappingW!![LEFT] = left.get2()-left.get0()
+        mTexMappingH!![LEFT] = left.get3()-left.get1()
+
+        mTexMappingX!![RIGHT] = right.get0()
+        mTexMappingY!![RIGHT] = right.get1()
+        mTexMappingW!![RIGHT] = right.get2()-right.get0()
+        mTexMappingH!![RIGHT] = right.get3()-right.get1()
+
+        mTexMappingX!![TOP] = top.get0()
+        mTexMappingY!![TOP] = top.get1()
+        mTexMappingW!![TOP] = top.get2()-top.get0()
+        mTexMappingH!![TOP] = top.get3()-top.get1()
+
+        mTexMappingX!![BOTTOM] = bottom.get0()
+        mTexMappingY!![BOTTOM] = bottom.get1()
+        mTexMappingW!![BOTTOM] = bottom.get2()-bottom.get0()
+        mTexMappingH!![BOTTOM] = bottom.get3()-bottom.get1()
+    }
 
-   private boolean isNE(int row,int col)
-     {
-     return ( (2*row<mRows)^(2*col<mCols) );
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // return the number of vertices our grid will contain
+    private fun computeDataLength(): Int
+    {
+        var frontWalls = 0
+        var frontSegments = 0
+        var triangleShifts = 0
+        var windingShifts = 0
+        val shiftCol = (mCols-1)/2
+
+        var lastBlockIsNE = false
+        var thisBlockIsNE: Boolean // the block we are currently looking at is split into
+        // two triangles along the NE-SW line (rather than NW-SE)
+        for (row in 0 until mRows)
+        {
+            if (mCols>=2&&(mCubes!![row][shiftCol]%2==1)&&(mCubes!![row][shiftCol+1]%2==1)) triangleShifts++
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// fill per-side texture mappings. Default: all 6 sides map to the whole texture.
-// Each Static4D describes the way its side will map. Format:
-//
-// 1st float: X-coord of the texture point that our top-left corner of the side maps to
-// 2nd float: Y-coord of the texture point that our top-left corner of the side maps to
-// 3rd float: X-coord of the texture point that our bot-right corner of the side maps to
-// 4th float: Y-coord of the texture point that our bot-right corner of the side maps to
-
-   private void fillTexMappings(Static4D front, Static4D back, Static4D left, Static4D right, Static4D top, Static4D bottom)
-     {
-     if( mTexMappingX==null ) mTexMappingX = new float[6];
-     if( mTexMappingY==null ) mTexMappingY = new float[6];
-     if( mTexMappingW==null ) mTexMappingW = new float[6];
-     if( mTexMappingH==null ) mTexMappingH = new float[6];
-
-     mTexMappingX[FRONT]  = front.get0();
-     mTexMappingY[FRONT]  = front.get1();
-     mTexMappingW[FRONT]  = front.get2() - front.get0();
-     mTexMappingH[FRONT]  = front.get3() - front.get1();
-
-     mTexMappingX[BACK]   = back.get0();
-     mTexMappingY[BACK]   = back.get1();
-     mTexMappingW[BACK]   = back.get2() - back.get0();
-     mTexMappingH[BACK]   = back.get3() - back.get1();
-
-     mTexMappingX[LEFT]   = left.get0();
-     mTexMappingY[LEFT]   = left.get1();
-     mTexMappingW[LEFT]   = left.get2() - left.get0();
-     mTexMappingH[LEFT]   = left.get3() - left.get1();
-
-     mTexMappingX[RIGHT]  = right.get0();
-     mTexMappingY[RIGHT]  = right.get1();
-     mTexMappingW[RIGHT]  = right.get2() - right.get0();
-     mTexMappingH[RIGHT]  = right.get3() - right.get1();
-
-     mTexMappingX[TOP]    = top.get0();
-     mTexMappingY[TOP]    = top.get1();
-     mTexMappingW[TOP]    = top.get2() - top.get0();
-     mTexMappingH[TOP]    = top.get3() - top.get1();
-
-     mTexMappingX[BOTTOM] = bottom.get0();
-     mTexMappingY[BOTTOM] = bottom.get1();
-     mTexMappingW[BOTTOM] = bottom.get2() - bottom.get0();
-     mTexMappingH[BOTTOM] = bottom.get3() - bottom.get1();
-     }
+            for (col in 0 until mCols)
+            {
+                if (mCubes!![row][col]%2==1)  // land
+                {
+                    thisBlockIsNE = isNE(row, col)
+                    if (thisBlockIsNE xor lastBlockIsNE) windingShifts++
+                    lastBlockIsNE = thisBlockIsNE
+                    frontWalls++
+                    if (col==mCols-1||mCubes!![row][col+1]%2==0) frontSegments++
+                }
+            }
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// return the number of vertices our grid will contain
+        val frontVert = 2*(frontWalls+2*frontSegments-1)+2*triangleShifts+windingShifts
+        val sideVertOneSlice = 2*(mSideWalls+mSideBends+mEdgeNum-1)
+        val sideVert = 2*(mSlices-1)+mSlices*sideVertOneSlice
+        val firstWinding = if ((mSlices>0&&(frontVert+1)%2==1)) 1 else 0
+        val dataL = if (mSlices==0) frontVert else (frontVert+1)+firstWinding+(1+sideVert+1)+(1+frontVert)
+
+        return if (dataL<0) 0 else dataL
+    }
 
-   private int computeDataLength()
-      {
-      int frontWalls=0, frontSegments=0, triangleShifts=0, windingShifts=0;
-      int shiftCol = (mCols-1)/2;
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun prepareDataStructures(cols: Int, desc: String, slices: Int)
+    {
+        mRows = 0
+        mCols = 0
+        mSlices = slices
+        numVertices = 0
 
-      boolean lastBlockIsNE=false;
-      boolean thisBlockIsNE;        // the block we are currently looking at is split into
-                                    // two triangles along the NE-SW line (rather than NW-SE)
-      for(int row=0; row<mRows; row++)
+        if (cols>0&&desc.contains("1"))
         {
-        if( mCols>=2 && (mCubes[row][shiftCol]%2 == 1) && (mCubes[row][shiftCol+1]%2 == 1) ) triangleShifts++;
+            mCols = cols
+            mRows = desc.length/cols
+
+            mCubes = Array(mRows) { IntArray(mCols) }
+            mInflateX = Array(mRows+1) { ByteArray(mCols+1) }
+            mInflateY = Array(mRows+1) { ByteArray(mCols+1) }
 
-        for(int col=0; col<mCols; col++)
-          {
-          if( mCubes[row][col]%2 == 1 )  // land
+            for (col in 0 until mCols) for (row in 0 until mRows) mCubes!![row][col] = (if (desc[row*mCols+col]=='1') 1 else 0)
+
+            for (col in 0 until mCols+1) for (row in 0 until mRows+1)
             {
-            thisBlockIsNE = isNE(row,col);
-            if( thisBlockIsNE^lastBlockIsNE ) windingShifts++;
-            lastBlockIsNE = thisBlockIsNE;
-            frontWalls++;
-            if( col==mCols-1 || mCubes[row][col+1]%2 == 0 ) frontSegments++;
+                fillInflate(row, col)
             }
-          }
+
+            markRegions()
+            numVertices = computeDataLength()
+            currVert = 0
         }
+    }
 
-      int frontVert       = 2*( frontWalls + 2*frontSegments - 1) +2*triangleShifts + windingShifts;
-      int sideVertOneSlice= 2*( mSideWalls + mSideBends + mEdgeNum -1);
-      int sideVert        = 2*(mSlices-1) + mSlices*sideVertOneSlice;
-      int firstWinding    = (mSlices>0 && (frontVert+1)%2==1 ) ? 1:0;
-      int dataL           = mSlices==0 ? frontVert : (frontVert+1) +firstWinding+ (1+sideVert+1) + (1+frontVert);
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // full grid
+    private fun prepareDataStructures(cols: Int, rows: Int, slices: Int)
+    {
+        mRows = rows
+        mCols = cols
+        mSlices = slices
+        numVertices = 0
 
-      return dataL<0 ? 0:dataL;
-      }
+        if (cols>0&&rows>0)
+        {
+            mCubes = Array(mRows) { IntArray(mCols) }
+            mInflateX = Array(mRows+1) { ByteArray(mCols+1) }
+            mInflateY = Array(mRows+1) { ByteArray(mCols+1) }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            for (col in 0 until mCols) for (row in 0 until mRows) mCubes!![row][col] = 1
 
-   private void prepareDataStructures(int cols, String desc, int slices)
-     {
-     mRows       =0;
-     mCols       =0;
-     mSlices     =slices;
-     numVertices =0;
-
-     if( cols>0 && desc.contains("1") )
-       {
-       mCols = cols;
-       mRows = desc.length()/cols;
-
-       mCubes    = new int[mRows][mCols];
-       mInflateX = new byte[mRows+1][mCols+1];
-       mInflateY = new byte[mRows+1][mCols+1];
-
-       for(int col=0; col<mCols; col++)
-         for(int row=0; row<mRows; row++)
-           mCubes[row][col] = (desc.charAt(row * mCols + col) == '1' ? 1 : 0);
-
-       for(int col=0; col<mCols+1; col++)
-         for(int row=0; row<mRows+1; row++)
-           {
-           fillInflate(row,col);
-           }
-
-       markRegions();
-       numVertices = computeDataLength();
-       currVert = 0;
-       }
-     }
+            for (col in 0 until mCols+1) for (row in 0 until mRows+1)
+            {
+                fillInflate(row, col)
+            }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// full grid
-
-   private void prepareDataStructures(int cols, int rows, int slices)
-     {
-     mRows        =rows;
-     mCols        =cols;
-     mSlices      =slices;
-     numVertices  =0;
-
-     if( cols>0 && rows>0 )
-       {
-       mCubes    = new int[mRows][mCols];
-       mInflateX = new byte[mRows+1][mCols+1];
-       mInflateY = new byte[mRows+1][mCols+1];
-
-       for(int col=0; col<mCols; col++)
-         for(int row=0; row<mRows; row++)
-           mCubes[row][col] = 1;
-
-       for(int col=0; col<mCols+1; col++)
-         for(int row=0; row<mRows+1; row++)
-           {
-           fillInflate(row,col);
-           }
-
-       markRegions();
-       numVertices = computeDataLength();
-       currVert = 0;
-       }
-     }
+            markRegions()
+            numVertices = computeDataLength()
+            currVert = 0
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Mark all the 'regions' of our grid  - i.e. separate pieces of 'land' (connected blocks that will 
-// be rendered) and 'water' (connected holes in between) with integers. Each connected block of land
-// gets a unique odd integer, each connected block of water a unique even integer.
-//
-// Water on the edges of the grid is also considered connected to itself!   
-//   
-// This function also creates a list of 'Edges'. Each Edge is a data structure from which later on we
-// will start building the side walls of each connected block of land (and sides of holes of water
-// inside). Each Edge needs to point from Land to Water (thus the '(SOUTH,row-1,col)' below) - otherwise
-// later on setting up normal vectors wouldn't work.
-   
-  private void markRegions()
-     {
-     int row, col, numWater=1, numLand=0;
-     
-     for(row=0; row<mRows; row++) if( mCubes[    row][      0]==0 ) markRegion((short)2,    row,       0);
-     for(row=0; row<mRows; row++) if( mCubes[    row][mCols-1]==0 ) markRegion((short)2,    row, mCols-1);
-     for(col=0; col<mCols; col++) if( mCubes[0      ][    col]==0 ) markRegion((short)2,      0,     col);
-     for(col=0; col<mCols; col++) if( mCubes[mRows-1][    col]==0 ) markRegion((short)2,mRows-1,     col);
-           
-     for(row=0; row<mRows; row++)
-        for(col=0; col<mCols; col++)
-           {
-           if( mCubes[row][col] == 0 ) { numWater++; markRegion( (short)(2*numWater ),row,col); mEdges.add(new Edge(SOUTH,row-1,col)); }
-           if( mCubes[row][col] == 1 ) { numLand ++; markRegion( (short)(2*numLand+1),row,col); mEdges.add(new Edge(NORTH,row  ,col)); }
-           }
-     
-     // now we potentially need to kick out some Edges . Otherwise the following does not work:
-     //
-     // 0 1 0
-     // 1 0 1
-     // 0 1 0
-     
-     mEdgeNum= mEdges.size();
-     int initCol, initRow, initSide, lastSide;
-     Edge e1,e2;
-     
-     for(int edge=0; edge<mEdgeNum; edge++)
-       {
-       e1 = mEdges.get(edge);
-       initRow= e1.row;
-       initCol= e1.col;
-       initSide=e1.side;
-
-       do
-         {
-         mSideWalls++;
-
-         if( e1.side==NORTH || e1.side==SOUTH )
-           {
-           for(int j=edge+1;j<mEdgeNum;j++)
-             {
-             e2 = mEdges.get(j);
-
-             if( e2.side==e1.side && e2.row==e1.row && e2.col==e1.col )
-               {
-               mEdges.remove(j);
-               mEdgeNum--;
-               j--;
-               }
-             }
-           }
-
-         lastSide = e1.side;
-         e1 = getNextEdge(e1);
-         if( e1.side!=lastSide ) mSideBends++;
-         }
-       while( e1.col!=initCol || e1.row!=initRow || e1.side!=initSide );
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Mark all the 'regions' of our grid  - i.e. separate pieces of 'land' (connected blocks that will 
+    // be rendered) and 'water' (connected holes in between) with integers. Each connected block of land
+    // gets a unique odd integer, each connected block of water a unique even integer.
+    //
+    // Water on the edges of the grid is also considered connected to itself!   
+    //   
+    // This function also creates a list of 'Edges'. Each Edge is a data structure from which later on we
+    // will start building the side walls of each connected block of land (and sides of holes of water
+    // inside). Each Edge needs to point from Land to Water (thus the '(SOUTH,row-1,col)' below) - otherwise
+    // later on setting up normal vectors wouldn't work.
+    private fun markRegions()
+    {
+        var col: Int
+        var numWater = 1
+        var numLand = 0
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// when calling, make sure that newVal != val
-   
-  private void markRegion(short newVal, int row, int col)
-     {
-     int val = mCubes[row][col];
-     mCubes[row][col] = newVal;
-     
-     if( row>0       && mCubes[row-1][col  ]==val ) markRegion(newVal, row-1, col  );
-     if( row<mRows-1 && mCubes[row+1][col  ]==val ) markRegion(newVal, row+1, col  );
-     if( col>0       && mCubes[row  ][col-1]==val ) markRegion(newVal, row  , col-1);
-     if( col<mCols-1 && mCubes[row  ][col+1]==val ) markRegion(newVal, row  , col+1);
-     }
-   
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  private void createNormals(boolean front, int row, int col)
-     {
-     int td,lr; 
-      
-     int nw = (col>0       && row>0      ) ? (mCubes[row-1][col-1]%2) : 0;
-     int w  = (col>0                     ) ? (mCubes[row  ][col-1]%2) : 0;
-     int n  = (               row>0      ) ? (mCubes[row-1][col  ]%2) : 0;
-     int c  =                                (mCubes[row  ][col  ]%2);
-     int sw = (col>0       && row<mRows-1) ? (mCubes[row+1][col-1]%2) : 0;
-     int s  = (               row<mRows-1) ? (mCubes[row+1][col  ]%2) : 0;
-     int ne = (col<mCols-1 && row>0      ) ? (mCubes[row-1][col+1]%2) : 0;
-     int e  = (col<mCols-1               ) ? (mCubes[row  ][col+1]%2) : 0;
-     int se = (col<mCols-1 && row<mRows-1) ? (mCubes[row+1][col+1]%2) : 0;
-
-     if(front)
-       {
-       mNormalZ[0] = 1.0f;
-       mNormalZ[1] = 1.0f;
-       mNormalZ[2] = 1.0f;
-       mNormalZ[3] = 1.0f;
-       }
-     else
-       {
-       mNormalZ[0] =-1.0f;
-       mNormalZ[1] =-1.0f;
-       mNormalZ[2] =-1.0f;
-       mNormalZ[3] =-1.0f;
-       }
-
-     td = nw+n-w-c;
-     lr = c+n-w-nw;
-     if( td<0 ) td=-1;
-     if( td>0 ) td= 1;
-     if( lr<0 ) lr=-1;
-     if( lr>0 ) lr= 1;
-     mNormalX[0] = lr*R;
-     mNormalY[0] = td*R;
-     
-     td = w+c-sw-s;
-     lr = c+s-w-sw;
-     if( td<0 ) td=-1;
-     if( td>0 ) td= 1;
-     if( lr<0 ) lr=-1;
-     if( lr>0 ) lr= 1;
-     mNormalX[1] = lr*R;
-     mNormalY[1] = td*R;
-     
-     td = n+ne-c-e;
-     lr = e+ne-c-n;
-     if( td<0 ) td=-1;
-     if( td>0 ) td= 1;
-     if( lr<0 ) lr=-1;
-     if( lr>0 ) lr= 1;
-     mNormalX[2] = lr*R;
-     mNormalY[2] = td*R;
-     
-     td = c+e-s-se;
-     lr = e+se-c-s;
-     if( td<0 ) td=-1;
-     if( td>0 ) td= 1;
-     if( lr<0 ) lr=-1;
-     if( lr>0 ) lr= 1;
-     mNormalX[3] = lr*R;
-     mNormalY[3] = td*R;
-     }
+        var row = 0
+        while (row<mRows)
+        {
+            if (mCubes!![row][0]==0) markRegion(2.toShort(), row, 0)
+            row++
+        }
+        row = 0
+        while (row<mRows)
+        {
+            if (mCubes!![row][mCols-1]==0) markRegion(2.toShort(), row, mCols-1)
+            row++
+        }
+        col = 0
+        while (col<mCols)
+        {
+            if (mCubes!![0][col]==0) markRegion(2.toShort(), 0, col)
+            col++
+        }
+        col = 0
+        while (col<mCols)
+        {
+            if (mCubes!![mRows-1][col]==0) markRegion(2.toShort(), mRows-1, col)
+            col++
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        row = 0
+        while (row<mRows)
+        {
+            col = 0
+            while (col<mCols)
+            {
+                if (mCubes!![row][col]==0)
+                {
+                    numWater++
+                    markRegion((2*numWater).toShort(), row, col)
+                    mEdges!!.add(Edge(SOUTH, row-1, col))
+                }
+                if (mCubes!![row][col]==1)
+                {
+                    numLand++
+                    markRegion((2*numLand+1).toShort(), row, col)
+                    mEdges!!.add(Edge(NORTH, row, col))
+                }
+                col++
+            }
+            row++
+        }
 
-  private void buildFrontBackGrid(boolean front, float[] attribs1, float[] attribs2)
-     {
-     int last, current;
-     boolean seenLand=false;
-     boolean lastBlockIsNE = false;
-     boolean currentBlockIsNE;
-     float vectZ = (front ? 0.5f : -0.5f);
-
-     for(int row=0; row<mRows; row++)
-       {
-       last =0;
-         
-       for(int col=0; col<mCols; col++)
-         {
-         current = mCubes[row][col];
-
-         if( current%2 == 1 )
-           {
-           currentBlockIsNE = isNE(row,col);
-
-           if( !seenLand && !front && ((currVert%2==1)^currentBlockIsNE) )
-             {
-             repeatLast(attribs1,attribs2);
-             }
-
-           createNormals(front,row,col);
-
-           if( currentBlockIsNE )
-             {
-             if( (last!=current) || !lastBlockIsNE )
-               {
-               if( seenLand  && (last != current) ) repeatLast(attribs1,attribs2);
-               addFrontVertex( 0, vectZ, col, row, attribs1,attribs2);
-               if( seenLand  && (last != current) ) repeatLast(attribs1,attribs2);
-               if( !lastBlockIsNE || (!front && !seenLand) ) repeatLast(attribs1,attribs2);
-               addFrontVertex( 1, vectZ, col, row+1, attribs1,attribs2);
-               }
-             addFrontVertex( 2, vectZ, col+1, row  , attribs1,attribs2);
-             addFrontVertex( 3, vectZ, col+1, row+1, attribs1,attribs2);
-             }
-           else
-             {
-             if( (last!=current) || lastBlockIsNE )
-               {
-               if( seenLand  && (last != current) ) repeatLast(attribs1,attribs2);
-               addFrontVertex( 1, vectZ, col, row+1, attribs1,attribs2);
-               if( seenLand  && (last != current) ) repeatLast(attribs1,attribs2);
-               if( lastBlockIsNE || (!front && !seenLand) ) repeatLast(attribs1,attribs2);
-               addFrontVertex( 0, vectZ, col, row, attribs1,attribs2);
-               }
-             addFrontVertex( 3, vectZ, col+1, row+1, attribs1,attribs2);
-             addFrontVertex( 2, vectZ, col+1, row  , attribs1,attribs2);
-             }
-
-           seenLand = true;
-           lastBlockIsNE = currentBlockIsNE;
-           }
-            
-         last = current;
-         }
-       }
-     }
+        // now we potentially need to kick out some Edges . Otherwise the following does not work:
+        //
+        // 0 1 0
+        // 1 0 1
+        // 0 1 0
+        mEdgeNum = mEdges!!.size
+        var initCol: Int
+        var initRow: Int
+        var initSide: Int
+        var lastSide: Int
+        var e1: Edge
+        var e2: Edge
+
+        for (edge in 0 until mEdgeNum)
+        {
+            e1 = mEdges!![edge]
+            initRow = e1.row
+            initCol = e1.col
+            initSide = e1.side
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            do
+            {
+                mSideWalls++
+
+                if (e1.side==NORTH||e1.side==SOUTH)
+                {
+                    var j = edge+1
+                    while (j<mEdgeNum)
+                    {
+                        e2 = mEdges!![j]
+
+                        if (e2.side==e1.side&&e2.row==e1.row&&e2.col==e1.col)
+                        {
+                            mEdges!!.removeAt(j)
+                            mEdgeNum--
+                            j--
+                        }
+                        j++
+                    }
+                }
+
+                lastSide = e1.side
+                e1 = getNextEdge(e1)
+                if (e1.side!=lastSide) mSideBends++
+            }
+            while (e1.col!=initCol||e1.row!=initRow||e1.side!=initSide)
+        }
+    }
 
-  private void buildSideGrid(float[] attribs1,float[] attribs2)
-     {
-     for(int i=0; i<mEdgeNum; i++)
-       {
-       buildIthSide(mEdges.get(i), attribs1, attribs2);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // when calling, make sure that newVal != val
+    private fun markRegion(newVal: Short, row: Int, col: Int)
+    {
+        val `val` = mCubes!![row][col]
+        mCubes!![row][col] = newVal.toInt()
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        if (row>0&&mCubes!![row-1][col]==`val`) markRegion(newVal, row-1, col)
+        if (row<mRows-1&&mCubes!![row+1][col]==`val`) markRegion(newVal, row+1, col)
+        if (col>0&&mCubes!![row][col-1]==`val`) markRegion(newVal, row, col-1)
+        if (col<mCols-1&&mCubes!![row][col+1]==`val`) markRegion(newVal, row, col+1)
+    }
 
-  private void buildIthSide(Edge curr, float[] attribs1, float[] attribs2)
-     {
-     Edge prev, next;
-     int col, row, side;
-
-     if( curr.side==NORTH ) // water outside
-       {
-       prev = new Edge(WEST,curr.row,curr.col);
-       }
-     else                   // land outside; we need to move forward one link because we are
-       {                    // going in opposite direction and we need to start from a bend.
-       prev = curr;
-       curr = new Edge(EAST,curr.row+1,curr.col-1);
-       }
-
-     for(int slice=0; slice<mSlices; slice++)
-       {
-       col = curr.col;
-       row = curr.row;
-       side= curr.side;
-       next = getNextEdge(curr);
-     
-       addSideVertex(curr,true,slice+1,prev.side,attribs1,attribs2);
-
-       do
-         {
-         if( prev.side!=curr.side )
-           {
-           addSideVertex(curr,true,slice+1,prev.side,attribs1,attribs2);
-           addSideVertex(curr,true,slice  ,prev.side,attribs1,attribs2);
-           }
-       
-         addSideVertex(curr,false,slice+1,next.side,attribs1,attribs2);
-         addSideVertex(curr,false,slice  ,next.side,attribs1,attribs2);
-       
-         prev = curr;
-         curr = next;
-         next = getNextEdge(curr);
-         }
-       while( curr.col!=col || curr.row!=row || curr.side!=side );
-     
-       repeatLast(attribs1,attribs2);
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun createNormals(front: Boolean, row: Int, col: Int)
+    {
+        var td: Int
+        var lr: Int
+
+        val nw = if ((col>0&&row>0)) (mCubes!![row-1][col-1]%2) else 0
+        val w = if ((col>0)) (mCubes!![row][col-1]%2) else 0
+        val n = if ((row>0)) (mCubes!![row-1][col]%2) else 0
+        val c = (mCubes!![row][col]%2)
+        val sw = if ((col>0&&row<mRows-1)) (mCubes!![row+1][col-1]%2) else 0
+        val s = if ((row<mRows-1)) (mCubes!![row+1][col]%2) else 0
+        val ne = if ((col<mCols-1&&row>0)) (mCubes!![row-1][col+1]%2) else 0
+        val e = if ((col<mCols-1)) (mCubes!![row][col+1]%2) else 0
+        val se = if ((col<mCols-1&&row<mRows-1)) (mCubes!![row+1][col+1]%2) else 0
+
+        if (front)
+        {
+            mNormalZ[0] = 1.0f
+            mNormalZ[1] = 1.0f
+            mNormalZ[2] = 1.0f
+            mNormalZ[3] = 1.0f
+        }
+        else
+        {
+            mNormalZ[0] = -1.0f
+            mNormalZ[1] = -1.0f
+            mNormalZ[2] = -1.0f
+            mNormalZ[3] = -1.0f
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        td = nw+n-w-c
+        lr = c+n-w-nw
+        if (td<0) td = -1
+        if (td>0) td = 1
+        if (lr<0) lr = -1
+        if (lr>0) lr = 1
+        mNormalX[0] = lr*R
+        mNormalY[0] = td*R
+
+        td = w+c-sw-s
+        lr = c+s-w-sw
+        if (td<0) td = -1
+        if (td>0) td = 1
+        if (lr<0) lr = -1
+        if (lr>0) lr = 1
+        mNormalX[1] = lr*R
+        mNormalY[1] = td*R
+
+        td = n+ne-c-e
+        lr = e+ne-c-n
+        if (td<0) td = -1
+        if (td>0) td = 1
+        if (lr<0) lr = -1
+        if (lr>0) lr = 1
+        mNormalX[2] = lr*R
+        mNormalY[2] = td*R
+
+        td = c+e-s-se
+        lr = e+se-c-s
+        if (td<0) td = -1
+        if (td>0) td = 1
+        if (lr<0) lr = -1
+        if (lr>0) lr = 1
+        mNormalX[3] = lr*R
+        mNormalY[3] = td*R
+    }
 
-  private Edge getNextEdge(Edge curr)
-     {
-     int col = curr.col;
-     int row = curr.row;
-
-     switch(curr.side) 
-       {
-       case NORTH: if( col==mCols-1 ) 
-                     return new Edge(EAST,row,col);
-                   if( row>0 && mCubes[row-1][col+1]==mCubes[row][col] )
-                     return new Edge(WEST,row-1,col+1);
-                   if( mCubes[row][col+1]==mCubes[row][col] )
-                     return new Edge(NORTH,row,col+1);
-                   else  
-                     return new Edge(EAST,row,col);
-                   
-       case SOUTH: if( col==0 ) 
-                     return new Edge(WEST,row,col);
-                   if( (row<mRows-1) && mCubes[row+1][col-1]==mCubes[row][col] )
-                     return new Edge(EAST,row+1,col-1); 
-                   if( mCubes[row][col-1]==mCubes[row][col] )
-                     return new Edge(SOUTH,row,col-1);
-                   else
-                     return new Edge(WEST,row,col); 
-                     
-       case EAST : if( row==mRows-1 ) 
-                     return new Edge(SOUTH,row,col);
-                   if( (col<mCols-1) && mCubes[row+1][col+1]==mCubes[row][col] )
-                     return new Edge(NORTH,row+1,col+1);
-                   if( mCubes[row+1][col]==mCubes[row][col] )
-                     return new Edge(EAST,row+1,col);
-                   else 
-                     return new Edge(SOUTH,row,col);
-                   
-       default   : if( row==0 )
-                     return new Edge(NORTH,row,col);
-                   if( col>0 && mCubes[row-1][col-1]==mCubes[row][col] )
-                     return new Edge(SOUTH,row-1,col-1);
-                   if( mCubes[row-1][col]==mCubes[row][col] )
-                     return new Edge(WEST,row-1,col);
-                   else
-                     return new Edge(NORTH,row,col);     
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildFrontBackGrid(front: Boolean, attribs1: FloatArray, attribs2: FloatArray)
+    {
+        var last: Int
+        var current: Int
+        var seenLand = false
+        var lastBlockIsNE = false
+        var currentBlockIsNE: Boolean
+        val vectZ = (if (front) 0.5f else -0.5f)
+
+        for (row in 0 until mRows)
+        {
+            last = 0
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            for (col in 0 until mCols)
+            {
+                current = mCubes!![row][col]
+
+                if (current%2==1)
+                {
+                    currentBlockIsNE = isNE(row, col)
+
+                    if (!seenLand&&!front&&((currVert%2==1) xor currentBlockIsNE))
+                    {
+                        repeatLast(attribs1, attribs2)
+                    }
+
+                    createNormals(front, row, col)
+
+                    if (currentBlockIsNE)
+                    {
+                        if ((last!=current)||!lastBlockIsNE)
+                        {
+                            if (seenLand&&(last!=current)) repeatLast(attribs1, attribs2)
+                            addFrontVertex(0, vectZ, col, row, attribs1, attribs2)
+                            if (seenLand&&(last!=current)) repeatLast(attribs1, attribs2)
+                            if (!lastBlockIsNE||(!front&&!seenLand)) repeatLast(attribs1, attribs2)
+                            addFrontVertex(1, vectZ, col, row+1, attribs1, attribs2)
+                        }
+                        addFrontVertex(2, vectZ, col+1, row, attribs1, attribs2)
+                        addFrontVertex(3, vectZ, col+1, row+1, attribs1, attribs2)
+                    }
+                    else
+                    {
+                        if ((last!=current)||lastBlockIsNE)
+                        {
+                            if (seenLand&&(last!=current)) repeatLast(attribs1, attribs2)
+                            addFrontVertex(1, vectZ, col, row+1, attribs1, attribs2)
+                            if (seenLand&&(last!=current)) repeatLast(attribs1, attribs2)
+                            if (lastBlockIsNE||(!front&&!seenLand)) repeatLast(attribs1, attribs2)
+                            addFrontVertex(0, vectZ, col, row, attribs1, attribs2)
+                        }
+                        addFrontVertex(3, vectZ, col+1, row+1, attribs1, attribs2)
+                        addFrontVertex(2, vectZ, col+1, row, attribs1, attribs2)
+                    }
+
+                    seenLand = true
+                    lastBlockIsNE = currentBlockIsNE
+                }
+
+                last = current
+            }
+        }
+    }
 
-  private void fillInflate(int row, int col)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildSideGrid(attribs1: FloatArray, attribs2: FloatArray)
     {
-    int diff;
-
-         if( col==0     ) mInflateX[row][col] = -1;
-    else if( col==mCols ) mInflateX[row][col] = +1;
-    else
-      {
-      if( row==0 )
+        for (i in 0 until mEdgeNum)
         {
-        diff = mCubes[0][col-1]-mCubes[0][col];
+            buildIthSide(mEdges!![i], attribs1, attribs2)
         }
-      else if( row==mRows )
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildIthSide(cur: Edge, attribs1: FloatArray, attribs2: FloatArray)
+    {
+        var curr = cur
+        var prev: Edge
+        var next: Edge
+        var col: Int
+        var row: Int
+        var side: Int
+
+        if (curr.side==NORTH)  // water outside
         {
-        diff = mCubes[mRows-1][col-1]-mCubes[mRows-1][col];
+            prev = Edge(WEST, curr.row, curr.col)
+        }
+        else  // land outside; we need to move forward one link because we are
+        {                    // going in opposite direction and we need to start from a bend.
+            prev = curr
+            curr = Edge(EAST, curr.row+1, curr.col-1)
         }
-      else
+
+        for (slice in 0 until mSlices)
         {
-        diff = (mCubes[row  ][col-1]-mCubes[row  ][col]) +
-               (mCubes[row-1][col-1]-mCubes[row-1][col]) ;
+            col = curr.col
+            row = curr.row
+            side = curr.side
+            next = getNextEdge(curr)
+
+            addSideVertex(curr, true, slice+1, prev.side, attribs1, attribs2)
+
+            do
+            {
+                if (prev.side!=curr.side)
+                {
+                    addSideVertex(curr, true, slice+1, prev.side, attribs1, attribs2)
+                    addSideVertex(curr, true, slice, prev.side, attribs1, attribs2)
+                }
+
+                addSideVertex(curr, false, slice+1, next.side, attribs1, attribs2)
+                addSideVertex(curr, false, slice, next.side, attribs1, attribs2)
+
+                prev = curr
+                curr = next
+                next = getNextEdge(curr)
+            }
+            while (curr.col!=col||curr.row!=row||curr.side!=side)
 
-        if( diff==-2 ) diff=-1;
-        if( diff== 2 ) diff= 1;
+            repeatLast(attribs1, attribs2)
         }
+    }
 
-      mInflateX[row][col] = (byte)diff;
-      }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun getNextEdge(curr: Edge): Edge
+    {
+        val col = curr.col
+        val row = curr.row
 
-         if( row==0     ) mInflateY[row][col] = +1;
-    else if( row==mRows ) mInflateY[row][col] = -1;
-    else
-      {
-      if( col==0 )
+        when (curr.side)
         {
-        diff = mCubes[row][0]-mCubes[row-1][0];
+            NORTH ->
+            {
+                if (col==mCols-1) return Edge(EAST, row, col)
+                if (row>0&&mCubes!![row-1][col+1]==mCubes!![row][col]) return Edge(WEST, row-1, col+1)
+                return if (mCubes!![row][col+1]==mCubes!![row][col]) Edge(NORTH, row, col+1)
+                else Edge(EAST, row, col)
+            }
+
+            SOUTH ->
+            {
+                if (col==0) return Edge(WEST, row, col)
+                if ((row<mRows-1)&&mCubes!![row+1][col-1]==mCubes!![row][col]) return Edge(EAST, row+1, col-1)
+                return if (mCubes!![row][col-1]==mCubes!![row][col]) Edge(SOUTH, row, col-1)
+                else Edge(WEST, row, col)
+            }
+
+            EAST ->
+            {
+                if (row==mRows-1) return Edge(SOUTH, row, col)
+                if ((col<mCols-1)&&mCubes!![row+1][col+1]==mCubes!![row][col]) return Edge(NORTH, row+1, col+1)
+                return if (mCubes!![row+1][col]==mCubes!![row][col]) Edge(EAST, row+1, col)
+                else Edge(SOUTH, row, col)
+            }
+
+            else ->
+            {
+                if (row==0) return Edge(NORTH, row, col)
+                if (col>0&&mCubes!![row-1][col-1]==mCubes!![row][col]) return Edge(SOUTH, row-1, col-1)
+                return if (mCubes!![row-1][col]==mCubes!![row][col]) Edge(WEST, row-1, col)
+                else Edge(NORTH, row, col)
+            }
         }
-      else if( col==mCols )
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun fillInflate(row: Int, col: Int)
+    {
+        var diff: Int
+
+        if (col==0) mInflateX!![row][col] = -1
+        else if (col==mCols) mInflateX!![row][col] = 1
+        else
         {
-        diff = mCubes[row][mCols-1]-mCubes[row-1][mCols-1];
+            if (row==0)
+            {
+                diff = mCubes!![0][col-1]-mCubes!![0][col]
+            }
+            else if (row==mRows)
+            {
+                diff = mCubes!![mRows-1][col-1]-mCubes!![mRows-1][col]
+            }
+            else
+            {
+                diff = (mCubes!![row][col-1]-mCubes!![row][col])+
+                        (mCubes!![row-1][col-1]-mCubes!![row-1][col])
+
+                if (diff==-2) diff = -1
+                if (diff==2) diff = 1
+            }
+
+            mInflateX!![row][col] = diff.toByte()
         }
-      else
+
+        if (row==0) mInflateY!![row][col] = 1
+        else if (row==mRows) mInflateY!![row][col] = -1
+        else
         {
-        diff = (mCubes[row  ][col-1]+mCubes[row  ][col]) -
-               (mCubes[row-1][col-1]+mCubes[row-1][col]) ;
+            if (col==0)
+            {
+                diff = mCubes!![row][0]-mCubes!![row-1][0]
+            }
+            else if (col==mCols)
+            {
+                diff = mCubes!![row][mCols-1]-mCubes!![row-1][mCols-1]
+            }
+            else
+            {
+                diff = (mCubes!![row][col-1]+mCubes!![row][col])-
+                        (mCubes!![row-1][col-1]+mCubes!![row-1][col])
+
+                if (diff==-2) diff = -1
+                if (diff==2) diff = 1
+            }
+
+            mInflateY!![row][col] = diff.toByte()
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun addFrontVertex(index: Int, vectZ: Float, col: Int, row: Int, attribs1: FloatArray, attribs2: FloatArray)
+    {
+        val x = col.toFloat()/mCols
+        val y = row.toFloat()/mRows
 
-        if( diff==-2 ) diff=-1;
-        if( diff== 2 ) diff= 1;
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB] = x-0.5f
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+1] = 0.5f-y
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+2] = vectZ
+
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB] = mNormalX[index]
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+1] = mNormalY[index]
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+2] = mNormalZ[index]
+
+        if (vectZ>0)
+        {
+            attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB] = mTexMappingX!![FRONT]+x*mTexMappingW!![FRONT]
+            attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB+1] = mTexMappingY!![FRONT]+(1.0f-y)*mTexMappingH!![FRONT]
+        }
+        else
+        {
+            attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB] = mTexMappingX!![BACK]+x*mTexMappingW!![BACK]
+            attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB+1] = mTexMappingY!![BACK]+(1.0f-y)*mTexMappingH!![BACK]
         }
 
-      mInflateY[row][col] = (byte)diff;
-      }
+        currVert++
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun addSideVertex(curr: Edge, back: Boolean, slice: Int, side: Int, attribs1: FloatArray, attribs2: FloatArray)
+    {
+        val x: Float
+        val y: Float
+        val z: Float
+        val row: Int
+        val col: Int
 
-  private void addFrontVertex(int index, float vectZ, int col, int row, float[] attribs1, float[] attribs2)
-     {
-     float x = (float)col/mCols;
-     float y = (float)row/mRows;
-
-     attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB  ] = x-0.5f;
-     attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+1] = 0.5f-y;
-     attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+2] = vectZ;
-
-     attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB  ] = mNormalX[index];
-     attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+1] = mNormalY[index];
-     attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+2] = mNormalZ[index];
-
-     if( vectZ>0 )
-       {
-       attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[FRONT] +       x  * mTexMappingW[FRONT];
-       attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[FRONT] + (1.0f-y) * mTexMappingH[FRONT];
-       }
-     else
-       {
-       attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[BACK]  +       x  * mTexMappingW[BACK];
-       attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[BACK]  + (1.0f-y) * mTexMappingH[BACK];
-       }
-
-     currVert++;
-     }
+        when (curr.side)
+        {
+            NORTH ->
+            {
+                row = curr.row
+                col = (if (back) (curr.col) else (curr.col+1))
+                x = col.toFloat()/mCols
+                y = 0.5f-row.toFloat()/mRows
+                z = 0.5f-slice.toFloat()/mSlices
+
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB] = x-0.5f
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+1] = y
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+2] = z
+
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB] = if (side==NORTH) 0.0f else (if (side==WEST) -R else R)
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+1] = 1.0f
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+2] = (if (slice==0) R else (if (slice==mSlices) -R else 0f))
+
+                attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB] = mTexMappingX!![TOP]+x*mTexMappingW!![TOP]
+                attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB+1] = mTexMappingY!![TOP]+(0.5f-z)*mTexMappingH!![TOP]
+            }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            SOUTH ->
+            {
+                row = curr.row+1
+                col = (if (back) (curr.col+1) else (curr.col))
+                x = col.toFloat()/mCols
+                y = 0.5f-row.toFloat()/mRows
+                z = 0.5f-slice.toFloat()/mSlices
+
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB] = x-0.5f
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+1] = y
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+2] = z
+
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB] = if (side==SOUTH) 0.0f else (if (side==EAST) -R else R)
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+1] = -1.0f
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+2] = (if (slice==0) R else (if (slice==mSlices) -R else 0f))
+
+                attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB] = mTexMappingX!![BOTTOM]+x*mTexMappingW!![BOTTOM]
+                attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB+1] = mTexMappingY!![BOTTOM]+(0.5f-z)*mTexMappingH!![BOTTOM]
+            }
 
-  private void addSideVertex(Edge curr, boolean back, int slice, int side, float[] attribs1, float[] attribs2)
-     {
-     float x, y, z;
-     int row, col;
-
-     switch(curr.side)
-       {
-       case NORTH: row = curr.row;
-                   col = (back ? (curr.col  ):(curr.col+1));
-                   x = (float)col/mCols;
-                   y = 0.5f - (float)row/mRows;
-                   z = 0.5f - (float)slice/mSlices;
-
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB  ] = x - 0.5f;
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+1] = y;
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+2] = z;
-
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB  ] = side==NORTH ? 0.0f : (side==WEST?-R:R);
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+1] = 1.0f;
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+2] = (slice==0 ? R : (slice==mSlices ? -R:0) );
-
-                   attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[TOP] +       x  * mTexMappingW[TOP];
-                   attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[TOP] + (0.5f-z) * mTexMappingH[TOP];
-
-                   break;
-       case SOUTH: row = curr.row+1;
-                   col = (back ? (curr.col+1):(curr.col));
-                   x = (float)col/mCols;
-                   y = 0.5f - (float)row/mRows;
-                   z = 0.5f - (float)slice/mSlices;
-
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB  ] = x - 0.5f;
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+1] = y;
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+2] = z;
-
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB  ] = side==SOUTH ? 0.0f: (side==EAST?-R:R);
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+1] =-1.0f;
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+2] = (slice==0 ? R : (slice==mSlices ? -R:0) );
-
-                   attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[BOTTOM] +       x  * mTexMappingW[BOTTOM];
-                   attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[BOTTOM] + (0.5f-z) * mTexMappingH[BOTTOM];
-
-                   break;
-       case WEST : row = (back  ? (curr.row+1):(curr.row));
-                   col = curr.col;
-                   x = (float)col/mCols -0.5f;
-                   y = (float)row/mRows;
-                   z = 0.5f - (float)slice/mSlices;
-
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB  ] = x;
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+1] = 0.5f - y;
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+2] = z;
-
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB  ] =-1.0f;
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+1] = side==WEST ? 0.0f : (side==NORTH?-R:R);
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+2] = (slice==0 ? R : (slice==mSlices ? -R:0) );
-
-                   attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[LEFT] + (0.5f-z) * mTexMappingW[LEFT];
-                   attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[LEFT] + (1.0f-y) * mTexMappingH[LEFT];
-
-                   break;
-       case EAST : row = (back  ? (curr.row):(curr.row+1));
-                   col = (curr.col+1);
-                   x = (float)col/mCols -0.5f;
-                   y = (float)row/mRows;
-                   z = 0.5f - (float)slice/mSlices;
-
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB  ] = x;
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+1] = 0.5f - y;
-                   attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+2] = z;
-
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB  ] = 1.0f;
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+1] = side==EAST ? 0.0f : (side==SOUTH?-R:R);
-                   attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+2] = (slice==0 ? R : (slice==mSlices ? -R:0) );
-
-                   attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = mTexMappingX[RIGHT] + (0.5f-z) * mTexMappingW[RIGHT];
-                   attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = mTexMappingY[RIGHT] + (1.0f-y) * mTexMappingH[RIGHT];
-
-                   break;
-       }
-
-     currVert++;
-     }
+            WEST ->
+            {
+                row = (if (back) (curr.row+1) else (curr.row))
+                col = curr.col
+                x = col.toFloat()/mCols-0.5f
+                y = row.toFloat()/mRows
+                z = 0.5f-slice.toFloat()/mSlices
+
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB] = x
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+1] = 0.5f-y
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+2] = z
+
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB] = -1.0f
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+1] = if (side==WEST) 0.0f else (if (side==NORTH) -R else R)
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+2] = (if (slice==0) R else (if (slice==mSlices) -R else 0f))
+
+                attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB] = mTexMappingX!![LEFT]+(0.5f-z)*mTexMappingW!![LEFT]
+                attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB+1] = mTexMappingY!![LEFT]+(1.0f-y)*mTexMappingH!![LEFT]
+            }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            EAST ->
+            {
+                row = (if (back) (curr.row) else (curr.row+1))
+                col = (curr.col+1)
+                x = col.toFloat()/mCols-0.5f
+                y = row.toFloat()/mRows
+                z = 0.5f-slice.toFloat()/mSlices
+
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB] = x
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+1] = 0.5f-y
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+2] = z
+
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB] = 1.0f
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+1] = if (side==EAST) 0.0f else (if (side==SOUTH) -R else R)
+                attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+2] = (if (slice==0) R else (if (slice==mSlices) -R else 0f))
+
+                attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB] = mTexMappingX!![RIGHT]+(0.5f-z)*mTexMappingW!![RIGHT]
+                attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB+1] = mTexMappingY!![RIGHT]+(1.0f-y)*mTexMappingH!![RIGHT]
+            }
+        }
 
-   private void repeatLast(float[] attribs1, float[] attribs2)
-     {
-     attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(currVert-1) + POS_ATTRIB  ];
-     attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(currVert-1) + POS_ATTRIB+1];
-     attribs1[VERT1_ATTRIBS*currVert + POS_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(currVert-1) + POS_ATTRIB+2];
+        currVert++
+    }
 
-     attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(currVert-1) + NOR_ATTRIB  ];
-     attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(currVert-1) + NOR_ATTRIB+1];
-     attribs1[VERT1_ATTRIBS*currVert + NOR_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(currVert-1) + NOR_ATTRIB+2];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun repeatLast(attribs1: FloatArray, attribs2: FloatArray)
+    {
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB] = attribs1[MeshBase.Companion.VERT1_ATTRIBS*(currVert-1)+MeshBase.Companion.POS_ATTRIB]
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+1] = attribs1[MeshBase.Companion.VERT1_ATTRIBS*(currVert-1)+MeshBase.Companion.POS_ATTRIB+1]
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.POS_ATTRIB+2] = attribs1[MeshBase.Companion.VERT1_ATTRIBS*(currVert-1)+MeshBase.Companion.POS_ATTRIB+2]
 
-     attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(currVert-1) + TEX_ATTRIB  ];
-     attribs2[VERT2_ATTRIBS*currVert + TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(currVert-1) + TEX_ATTRIB+1];
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB] = attribs1[MeshBase.Companion.VERT1_ATTRIBS*(currVert-1)+MeshBase.Companion.NOR_ATTRIB]
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+1] = attribs1[MeshBase.Companion.VERT1_ATTRIBS*(currVert-1)+MeshBase.Companion.NOR_ATTRIB+1]
+        attribs1[MeshBase.Companion.VERT1_ATTRIBS*currVert+MeshBase.Companion.NOR_ATTRIB+2] = attribs1[MeshBase.Companion.VERT1_ATTRIBS*(currVert-1)+MeshBase.Companion.NOR_ATTRIB+2]
 
-     currVert++;
-     }
+        attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB] = attribs2[MeshBase.Companion.VERT2_ATTRIBS*(currVert-1)+MeshBase.Companion.TEX_ATTRIB]
+        attribs2[MeshBase.Companion.VERT2_ATTRIBS*currVert+MeshBase.Companion.TEX_ATTRIB+1] = attribs2[MeshBase.Companion.VERT2_ATTRIBS*(currVert-1)+MeshBase.Companion.TEX_ATTRIB+1]
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        currVert++
+    }
 
-  private void build()
-     {
-     float[] attribs1= new float[VERT1_ATTRIBS*numVertices];
-     float[] attribs2= new float[VERT2_ATTRIBS*numVertices];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun build()
+    {
+        val attribs1 = FloatArray(MeshBase.Companion.VERT1_ATTRIBS*numVertices)
+        val attribs2 = FloatArray(MeshBase.Companion.VERT2_ATTRIBS*numVertices)
 
-     buildFrontBackGrid(true,attribs1,attribs2);
+        buildFrontBackGrid(true, attribs1, attribs2)
 
-     if( mSlices>0 )
-       {
-       repeatLast(attribs1,attribs2);
-       if( currVert%2==1 ) repeatLast(attribs1,attribs2);
-       buildSideGrid(attribs1,attribs2);
-       buildFrontBackGrid(false,attribs1,attribs2);
-       }
+        if (mSlices>0)
+        {
+            repeatLast(attribs1, attribs2)
+            if (currVert%2==1) repeatLast(attribs1, attribs2)
+            buildSideGrid(attribs1, attribs2)
+            buildFrontBackGrid(false, attribs1, attribs2)
+        }
 
-     mEdges.clear();
-     mEdges = null;
-     mCubes = null;
-     mInflateX = null;
-     mInflateY = null;
+        mEdges!!.clear()
+        mEdges = null
+        mCubes = null
+        mInflateX = null
+        mInflateY = null
 
-     if( currVert!=numVertices )
-       DistortedLibrary.logMessage("MeshCubes: currVert " +currVert+" numVertices="+numVertices );
+        if (currVert!=numVertices) DistortedLibrary.logMessage("MeshCubes: currVert $currVert numVertices=$numVertices")
 
-     setAttribs(attribs1,attribs2);
-     }
+        setAttribs(attribs1, attribs2)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Creates the underlying mesh of vertices, normals, texture coords.
- *    
- * @param cols   Integer helping to parse the next parameter.
- * @param desc   String describing the subset of a MxNx1 cuboid that we want to create.
- *               Its MxN characters - all 0 or 1 - decide of appropriate field is taken or not.
- *               <p></p>
- *               <p>
- *               <pre>
- *               For example, (cols=2, desc="111010", slices=1) describes the following shape:
- *
- *               XX
- *               X
- *               X
- *
- *               whereas (cols=2,desc="110001", slices=1) describes
- *
- *               XX
- *
- *                X
- *               </pre>
- *               </p>
- * @param slices Number of slices, i.e. 'depth' of the Mesh.
- */
- public MeshCubes(int cols, String desc, int slices)
-   {
-   super();
-   Static4D map = new Static4D(0.0f,0.0f,1.0f,1.0f);
-   fillTexMappings(map,map,map,map,map,map);
-   prepareDataStructures(cols,desc,slices);
-   build();
-   }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates the underlying mesh of vertices, normals, texture coords.
+     *
+     * @param cols   Integer helping to parse the next parameter.
+     * @param desc   String describing the subset of a MxNx1 cuboid that we want to create.
+     * Its MxN characters - all 0 or 1 - decide of appropriate field is taken or not.
+     *
+     *
+     *
+     *
+     * <pre>
+     * For example, (cols=2, desc="111010", slices=1) describes the following shape:
+     *
+     * XX
+     * X
+     * X
+     *
+     * whereas (cols=2,desc="110001", slices=1) describes
+     *
+     * XX
+     *
+     * X
+    </pre> *
+     *
+     * @param slices Number of slices, i.e. 'depth' of the Mesh.
+     */
+    constructor(cols: Int, desc: String, slices: Int) : super()
+    {
+        val map = Static4D(0.0f, 0.0f, 1.0f, 1.0f)
+        fillTexMappings(map, map, map, map, map, map)
+        prepareDataStructures(cols, desc, slices)
+        build()
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Creates the underlying mesh of vertices, normals, texture coords with custom texture mappings.
- *
- * The mappings decide what part of the texture appears on a given side. Example:
- * front = (0.0,0.5,0.5,1.0) means that the front side of the grid will be textured with the
- * bottom-left quadrant of the texture.
- *
- * Default is: each of the 6 sides maps to the whole texture, i.e. (0.0,0.0,1.0,1.0)
- *
- * @param cols   Integer helping to parse the next parameter.
- * @param desc   String describing the subset of a MxNx1 cuboid that we want to create.
- *               Its MxN characters - all 0 or 1 - decide of appropriate field is taken or not.
- *               <p></p>
- *               <p>
- *               <pre>
- *               For example, (cols=2, desc="111010", slices=1) describes the following shape:
- *
- *               XX
- *               X
- *               X
- *
- *               whereas (cols=2,desc="110001", slices=1) describes
- *
- *               XX
- *
- *                X
- *               </pre>
- *               </p>
- * @param slices Number of slices, i.e. 'depth' of the Mesh.
- * @param front  Texture mapping the front side maps to.
- * @param back   Texture mapping the back side maps to.
- * @param left   Texture mapping the left side maps to.
- * @param right  Texture mapping the right side maps to.
- * @param top    Texture mapping the top side maps to.
- * @param bottom Texture mapping the bottom side maps to.
- */
- public MeshCubes(int cols, String desc, int slices, Static4D front, Static4D back, Static4D left, Static4D right, Static4D top, Static4D bottom)
-   {
-   super();
-   fillTexMappings(front,back,left,right,top,bottom);
-   prepareDataStructures(cols,desc,slices);
-   build();
-   }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates the underlying mesh of vertices, normals, texture coords with custom texture mappings.
+     *
+     * The mappings decide what part of the texture appears on a given side. Example:
+     * front = (0.0,0.5,0.5,1.0) means that the front side of the grid will be textured with the
+     * bottom-left quadrant of the texture.
+     *
+     * Default is: each of the 6 sides maps to the whole texture, i.e. (0.0,0.0,1.0,1.0)
+     *
+     * @param cols   Integer helping to parse the next parameter.
+     * @param desc   String describing the subset of a MxNx1 cuboid that we want to create.
+     * Its MxN characters - all 0 or 1 - decide of appropriate field is taken or not.
+     *
+     *
+     *
+     *
+     * <pre>
+     * For example, (cols=2, desc="111010", slices=1) describes the following shape:
+     *
+     * XX
+     * X
+     * X
+     *
+     * whereas (cols=2,desc="110001", slices=1) describes
+     *
+     * XX
+     *
+     * X
+    </pre> *
+     *
+     * @param slices Number of slices, i.e. 'depth' of the Mesh.
+     * @param front  Texture mapping the front side maps to.
+     * @param back   Texture mapping the back side maps to.
+     * @param left   Texture mapping the left side maps to.
+     * @param right  Texture mapping the right side maps to.
+     * @param top    Texture mapping the top side maps to.
+     * @param bottom Texture mapping the bottom side maps to.
+     */
+    constructor(cols: Int, desc: String, slices: Int, front: Static4D, back: Static4D, left: Static4D, right: Static4D, top: Static4D, bottom: Static4D) : super()
+    {
+        fillTexMappings(front, back, left, right, top, bottom)
+        prepareDataStructures(cols, desc, slices)
+        build()
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Creates a full, hole-less underlying mesh of vertices, normals, texture coords and colors.
- *
- * @param cols   Number of columns, i.e. 'width' of the Mesh.
- * @param rows   Number of rows, i.e. 'height' of the Mesh.
- * @param slices Number of slices, i.e. 'depth' of the Mesh.
- */
- public MeshCubes(int cols, int rows, int slices)
-   {
-   super();
-   Static4D map = new Static4D(0.0f,0.0f,1.0f,1.0f);
-   fillTexMappings(map,map,map,map,map,map);
-   prepareDataStructures(cols,rows,slices);
-   build();
-   }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates a full, hole-less underlying mesh of vertices, normals, texture coords and colors.
+     *
+     * @param cols   Number of columns, i.e. 'width' of the Mesh.
+     * @param rows   Number of rows, i.e. 'height' of the Mesh.
+     * @param slices Number of slices, i.e. 'depth' of the Mesh.
+     */
+    constructor(cols: Int, rows: Int, slices: Int) : super()
+    {
+        val map = Static4D(0.0f, 0.0f, 1.0f, 1.0f)
+        fillTexMappings(map, map, map, map, map, map)
+        prepareDataStructures(cols, rows, slices)
+        build()
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Creates a full, hole-less underlying mesh of vertices, normals, texture coords and colors with
- * custom texture mappings.
- *
- * The mappings decide what part of the texture appears on a given side. Example:
- * front = (0.0,0.5,0.5,1.0) means that the front side of the grid will be textured with the
- * bottom-left quadrant of the texture.
- *
- * Default is: each of the 6 sides maps to the whole texture, i.e. (0.0,0.0,1.0,1.0)
- *
- * @param cols   Number of columns, i.e. 'width' of the Mesh.
- * @param rows   Number of rows, i.e. 'height' of the Mesh.
- * @param slices Number of slices, i.e. 'depth' of the Mesh.
- * @param front  Texture mapping the front side maps to.
- * @param back   Texture mapping the back side maps to.
- * @param left   Texture mapping the left side maps to.
- * @param right  Texture mapping the right side maps to.
- * @param top    Texture mapping the top side maps to.
- * @param bottom Texture mapping the bottom side maps to.
- */
- public MeshCubes(int cols, int rows, int slices, Static4D front, Static4D back, Static4D left, Static4D right, Static4D top, Static4D bottom)
-   {
-   super();
-   fillTexMappings(front,back,left,right,top,bottom);
-   prepareDataStructures(cols,rows,slices);
-   build();
-   }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates a full, hole-less underlying mesh of vertices, normals, texture coords and colors with
+     * custom texture mappings.
+     *
+     * The mappings decide what part of the texture appears on a given side. Example:
+     * front = (0.0,0.5,0.5,1.0) means that the front side of the grid will be textured with the
+     * bottom-left quadrant of the texture.
+     *
+     * Default is: each of the 6 sides maps to the whole texture, i.e. (0.0,0.0,1.0,1.0)
+     *
+     * @param cols   Number of columns, i.e. 'width' of the Mesh.
+     * @param rows   Number of rows, i.e. 'height' of the Mesh.
+     * @param slices Number of slices, i.e. 'depth' of the Mesh.
+     * @param front  Texture mapping the front side maps to.
+     * @param back   Texture mapping the back side maps to.
+     * @param left   Texture mapping the left side maps to.
+     * @param right  Texture mapping the right side maps to.
+     * @param top    Texture mapping the top side maps to.
+     * @param bottom Texture mapping the bottom side maps to.
+     */
+    constructor(cols: Int, rows: Int, slices: Int, front: Static4D, back: Static4D, left: Static4D, right: Static4D, top: Static4D, bottom: Static4D) : super()
+    {
+        fillTexMappings(front, back, left, right, top, bottom)
+        prepareDataStructures(cols, rows, slices)
+        build()
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
- public MeshCubes(MeshCubes mesh, boolean deep)
-   {
-   super(mesh,deep);
-   }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshCubes, deep: Boolean) : super(mesh, deep)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied).
+     */
+    override fun copy(deep: Boolean): MeshCubes
+    {
+        return MeshCubes(this, deep)
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied).
- */
- public MeshCubes copy(boolean deep)
-   {
-   return new MeshCubes(this,deep);
-   }
- }
+    companion object
+    {
+        private const val R = 0.0f
+
+        private const val FRONT = 0
+        private const val BACK = 1
+        private const val LEFT = 2
+        private const val RIGHT = 3
+        private const val TOP = 4
+        private const val BOTTOM = 5
+
+        private const val NORTH = 0
+        private const val WEST = 1
+        private const val EAST = 2
+        private const val SOUTH = 3
+
+        private val mNormalX = FloatArray(4)
+        private val mNormalY = FloatArray(4)
+        private val mNormalZ = FloatArray(4)
+    }
+}
diff --git a/src/main/java/org/distorted/library/mesh/MeshFile.kt b/src/main/java/org/distorted/library/mesh/MeshFile.kt
index fe9b719..9b02476 100644
--- a/src/main/java/org/distorted/library/mesh/MeshFile.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshFile.kt
@@ -18,61 +18,48 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-import java.io.DataInputStream;
+import java.io.DataInputStream
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Read a file in our mesh format, 'dmesh' and create a Mesh from that.
- * <p>
+ *
  * Such a file must have been created with help of MeshBase.write().
  */
-public class MeshFile extends MeshBase
+class MeshFile : MeshBase
 {
-   private int mBytes;
+    private var numBytes: Int = 0  // number of bytes read from the mesh file.
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Read mesh description from a file.
- * <p>
- * File format: as written by MeshBase.write().
- */
-  public MeshFile(DataInputStream stream)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Read mesh description from a file.
+     *
+     *
+     * File format: as written by MeshBase.write().
+     */
+    constructor(stream: DataInputStream?) : super()
     {
-    super();
-
-    mBytes = read(stream);
+        numBytes = read(stream!!)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public MeshFile(MeshFile mesh, boolean deep)
-   {
-   super(mesh,deep);
-   }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshFile copy(boolean deep)
-   {
-   return new MeshFile(this,deep);
-   }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshFile, deep: Boolean) : super(mesh, deep)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return number of bytes read from the mesh file.
- */
-  public int getNumBytes()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshFile
     {
-    return mBytes;
+        return MeshFile(this, deep)
     }
 }
diff --git a/src/main/java/org/distorted/library/mesh/MeshJoined.kt b/src/main/java/org/distorted/library/mesh/MeshJoined.kt
index 06fed17..84f70ab 100644
--- a/src/main/java/org/distorted/library/mesh/MeshJoined.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshJoined.kt
@@ -18,45 +18,40 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Create a Mesh which is a union of several simpler Meshes.
  */
-public class MeshJoined extends MeshBase
+class MeshJoined : MeshBase
 {
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Join a list of (probably already changed by Vertex Effects) Meshes into one.
- * <p>
- * You need to try and keep the origin (0,0,0) in the center of gravity of the whole thing.
- */
-  public MeshJoined(MeshBase[] meshes)
+    /**
+     * Join a list of (probably already changed by Vertex Effects) Meshes into one.
+     *
+     * You need to try and keep the origin (0,0,0) in the center of gravity of the whole thing.
+     */
+    constructor(meshes: Array<MeshBase>) : super()
     {
-    super();
-    join(meshes);
+        join(meshes)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public MeshJoined(MeshJoined mesh, boolean deep)
-   {
-   super(mesh,deep);
-   }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshJoined, deep: Boolean) : super(mesh, deep)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshJoined copy(boolean deep)
-   {
-   return new MeshJoined(this,deep);
-   }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshJoined
+    {
+        return MeshJoined(this, deep)
+    }
 }
diff --git a/src/main/java/org/distorted/library/mesh/MeshMultigon.kt b/src/main/java/org/distorted/library/mesh/MeshMultigon.kt
index 90af5f5..ab2bbd5 100644
--- a/src/main/java/org/distorted/library/mesh/MeshMultigon.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshMultigon.kt
@@ -18,750 +18,723 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-import java.util.ArrayList;
+import kotlin.math.atan2
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
 /**
  * Create a 'multigon' mesh - a union of several polygons.
  *
- * <p>
  * Specify several lists of vertices. Each list defines one polygon. (@see MeshPolygon).
  * If two such polygons share an edge (or several edges) then the edge in both of them is
  * marked 'up'.
  */
-public class MeshMultigon extends MeshBase
-  {
-  private static final float MAX_ERROR = 0.000001f;
-  private float[][][] mOuterAndHoleVertices;
-  private float[][][] mEdgeVectors;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+class MeshMultigon : MeshBase
+{
+    private lateinit var mOuterAndHoleVertices: Array<Array<FloatArray>>
+    private lateinit var mEdgeVectors: Array<Array<FloatArray?>?>
 
-  private static boolean isSame(float dx, float dy)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun produceNormalVector(outerAndHole: Array<FloatArray>, xs: Float, ys: Float): FloatArray
     {
-    return (dx*dx + dy*dy < MAX_ERROR);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        val len = outerAndHole.size
 
-  private static int isUp(float[][][] vertices, int polygon, int edge)
-    {
-    float[][] p= vertices[polygon];
-    int lenP = p.length;
-    int len  = vertices.length;
-    int next = (edge==lenP-1 ? 0 : edge+1);
-
-    float v1x = p[edge][0];
-    float v1y = p[edge][1];
-    float v2x = p[next][0];
-    float v2y = p[next][1];
-
-    for(int i=0; i<len; i++)
-      if( i!=polygon )
+        var vCurr = 0
+        while (vCurr<len)
         {
-        int num = vertices[i].length;
-
-        for(int j=0; j<num; j++)
-          {
-          int n = (j==num-1 ? 0 : j+1);
-          float[][] v = vertices[i];
-          float x1 = v[j][0];
-          float y1 = v[j][1];
-          float x2 = v[n][0];
-          float y2 = v[n][1];
-
-          if( isSame(v2x-x1,v2y-y1) && isSame(v1x-x2,v1y-y2) ) return i;
-          }
+            val vs = outerAndHole[vCurr]
+            if (isSame(vs[0]-xs, vs[1]-ys)) break
+            vCurr++
         }
 
-    return -1;
-    }
+        if (vCurr==len) println("MeshMultigon: ERROR in produceNormalVector!!")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        val vPrev = if (vCurr==0) len-1 else vCurr-1
+        val vNext = if (vCurr>=len-1) 0 else vCurr+1
+        val vp = outerAndHole[vPrev]
+        val vn = outerAndHole[vNext]
 
-  private static float[] detectFirstOuterVertex(float[][][] vertices)
-    {
-    float X = -Float.MAX_VALUE;
-    int I=0,J=0, len = vertices.length;
-
-    for(int i=0; i<len; i++ )
-      {
-      float[][] v = vertices[i];
-      int num = v.length;
-
-      for(int j=0; j<num; j++)
-        if( v[j][0]>X )
-          {
-          X = v[j][0];
-          I = i;
-          J = j;
-          }
-      }
-
-    float[] v = vertices[I][J];
-    return new float[] {v[0],v[1]};
+        return floatArrayOf(vp[1]-vn[1], vn[0]-vp[0])
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static double computeAngle(float x1,float y1, float x2, float y2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun produceNormalVector(outerAndHole: Array<FloatArray>, xs: Float, ys: Float, xe: Float, ye: Float): FloatArray
     {
-    double diff = Math.atan2(y2,x2)-Math.atan2(y1,x1);
-    return diff<0 ? diff+(2*Math.PI) : diff;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static float[] detectNextOuterVertex(float[][][] vertices, float[] curr, float[] vect, int[][] edgesUp)
-    {
-    double maxAngle = 0;
-    float x=0, y=0;
-    int numC = vertices.length;
-
-    for( int c=0; c<numC; c++ )
-      {
-      float[][] vert = vertices[c];
-      int numV = vert.length;
+        val len = outerAndHole.size
 
-      for( int v=0; v<numV; v++)
+        var vCurr = 0
+        while (vCurr<len)
         {
-        float xc = vert[v][0];
-        float yc = vert[v][1];
+            val vs = outerAndHole[vCurr]
 
-        if( edgesUp[c][v]<0 && isSame(xc-curr[0],yc-curr[1]) )
-          {
-          int n = (v==numV-1 ? 0 : v+1);
-          float xn = vert[n][0];
-          float yn = vert[n][1];
-
-          double angle = computeAngle(vect[0], vect[1], xn-xc, yn-yc);
-
-          if (angle > maxAngle)
+            if (isSame(vs[0]-xs, vs[1]-ys))
             {
-            maxAngle = angle;
-            x = xn;
-            y = yn;
+                val vNext = if (vCurr==len-1) 0 else vCurr+1
+                val ve = outerAndHole[vNext]
+                if (isSame(ve[0]-xe, ve[1]-ye)) break
             }
-
-          break;
-          }
+            vCurr++
         }
-      }
 
-    return new float[] {x,y};
-    }
+        val vPrev = if (vCurr==0) len-1 else vCurr-1
+        val vNext = if (vCurr>=len-1) 0 else vCurr+1
+        val vp = outerAndHole[vPrev]
+        val vn = outerAndHole[vNext]
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        return floatArrayOf(vp[1]-vn[1], vn[0]-vp[0])
+    }
 
-  private static int getNextIndex(float[][][] vertices, int[][] unclaimedEdges, int currIndex, int numHoleVerts)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeEdgeVectors(vertices: Array<Array<FloatArray>>, edgesUp: Array<IntArray?>)
     {
-    int[] currEdge = unclaimedEdges[currIndex];
-    int currP = currEdge[0];
-    int currE = currEdge[1];
-    float[][] polygon = vertices[currP];
-    int numV = polygon.length;
-    int nextE= currE<numV-1 ? currE+1 : 0;
-
-    float x = polygon[nextE][0];
-    float y = polygon[nextE][1];
-
-    for(int e=0; e<numHoleVerts; e++)
-      {
-      int[] edge = unclaimedEdges[e];
+        val numPoly = vertices.size
+        mEdgeVectors = arrayOfNulls(numPoly)
 
-      if( edge[2]==1 )
+        for (c in 0 until numPoly)
         {
-        int po = edge[0];
-        int ed = edge[1];
+            val polygon = vertices[c]
+            val numV = polygon.size
+            mEdgeVectors[c] = arrayOfNulls(numV)
 
-        float cx = vertices[po][ed][0];
-        float cy = vertices[po][ed][1];
-
-        if( isSame(cx-x,cy-y) )
-          {
-          edge[2] = 0;
-          return e;
-          }
+            for (v in 0 until numV)
+            {
+                val edgeUp = edgesUp[c]!![v]
+                val xs = polygon[v][0]
+                val ys = polygon[v][1]
+                val n = if (v==numV-1) 0 else v+1
+                val xe = polygon[n][0]
+                val ye = polygon[n][1]
+
+                if (edgeUp<0)  // external edge, normal vector
+                {
+                    val verts = mOuterAndHoleVertices[-edgeUp-1]
+                    mEdgeVectors[c]?.set(v, produceNormalVector(verts, xs, ys, xe, ye))
+                }
+                else  // internal edge - but the vertex that begins it might still be outer!
+                {
+                    val numComp = retIndexBelongs(xs, ys)
+
+                    if (numComp<0) mEdgeVectors[c]?.set(v, null)
+                    else
+                    {
+                        val verts = mOuterAndHoleVertices[numComp]
+                        mEdgeVectors[c]?.set(v, produceNormalVector(verts, xs, ys))
+                    }
+                }
+            }
         }
-      }
-
-    return -1;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static int generateHole(float[][][] vertices, int[][] unclaimedEdges, float[][] output, int[][] edgesUp, int holeNumber, int numHoleVerts)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // ret:
+    // -1  : vertex (x,y) is 'inner' (does not belong to outer vertices or any hole)
+    // N>=0: vertex (x,y) is outer [ N==0 --> outer, 1--> 1sr hole, 2 --> 2nd hole, etc ]
+    private fun retIndexBelongs(x: Float, y: Float): Int
     {
-    int firstIndex=-1;
+        val numOuterComponents = mOuterAndHoleVertices.size
 
-    for(int e=0; e<numHoleVerts; e++)
-      if( unclaimedEdges[e][2]==1 )
+        for (c in 0 until numOuterComponents)
         {
-        firstIndex = e;
-        break;
+            val comp = mOuterAndHoleVertices[c]
+
+            for (v in comp) if (isSame(x-v[0], y-v[1])) return c
         }
 
-    int currIndex = firstIndex;
-    int nextIndex=-1;
-    int numAdded = 0;
-
-    while( nextIndex!=firstIndex )
-      {
-      nextIndex = getNextIndex(vertices,unclaimedEdges,currIndex,numHoleVerts);
-
-      int p = unclaimedEdges[nextIndex][0];
-      int v = unclaimedEdges[nextIndex][1];
-      edgesUp[p][v] = -holeNumber-2;
-      currIndex = nextIndex;
-      int[] e = unclaimedEdges[nextIndex];
-      float[] ve = vertices[e[0]][e[1]];
-      output[numAdded][0] = ve[0];
-      output[numAdded][1] = ve[1];
-      numAdded++;
-      }
-
-    return numAdded;
+        return -1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static boolean doesNotBelongToOuter(float x, float y, float[][] outer)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun retOuterVector(component: Int, vert: Int): FloatArray?
     {
-    for(float[] v : outer)
-      if(isSame(x-v[0], y-v[1])) return false;
-
-    return true;
+        return mEdgeVectors[component]?.get(vert)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static float[][][] computeHoles(float[][][] vertices, int[][] edgesUp, float[][] outer, int numHoleVerts)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeVertsUp(vertices: Array<Array<FloatArray>>): Array<BooleanArray?>
     {
-    int[][] unclaimedEdges = new int[numHoleVerts][];
-    int index = 0;
-    int numPoly = vertices.length;
-
-    for(int p=0; p<numPoly; p++)
-      {
-      float[][] ve = vertices[p];
-      int numEdges = ve.length;
-
-      for(int e=0; e<numEdges; e++)
-        if( edgesUp[p][e]<0 )
-          {
-          float x1 = ve[e][0];
-          float y1 = ve[e][1];
-
-          int n = e<numEdges-1 ? e+1 : 0;
-          float x2 = ve[n][0];
-          float y2 = ve[n][1];
-
-          // yes, we need to check both ends of the edge - otherwise the
-          // following 3x3 situation (x - wall, o - hole ) does not work:
-          // x x x
-          // x o x
-          // o x x
-
-          if( doesNotBelongToOuter(x1,y1,outer) || doesNotBelongToOuter(x2,y2,outer) )
+        val numPoly = vertices.size
+        val up = arrayOfNulls<BooleanArray>(numPoly)
+
+        for (i in 0 until numPoly)
+        {
+            val v = vertices[i]
+            val len = v.size
+            up[i] = BooleanArray(len)
+
+            for (j in 0 until len)
             {
-            unclaimedEdges[index]=new int[] {p, e, 1};
-            index++;
+                val vect = retOuterVector(i, j)
+                up[i]!![j] = (vect==null)
             }
-          }
-      }
-
-    int remaining = numHoleVerts;
-    ArrayList<float[][]> holes = new ArrayList<>();
-    float[][] output = new float[numHoleVerts][2];
-    int numHoles = 0;
-
-    while(remaining>0)
-      {
-      int num = generateHole(vertices,unclaimedEdges,output,edgesUp,numHoles,numHoleVerts);
-      remaining -= num;
-      numHoles++;
-
-      float[][] hole = new float[num][2];
-      for(int h=0; h<num; h++)
-        {
-        hole[h][0] = output[h][0];
-        hole[h][1] = output[h][1];
         }
 
-      holes.add(hole);
-      }
-
-    float[][][] ret = new float[numHoles][][];
-    for(int h=0; h<numHoles; h++) ret[h] = holes.remove(0);
-
-    return ret;
+        return up
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static int countEdgesDown(int[][] edgesUp)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeCenter(vertices: Array<FloatArray>): FloatArray
     {
-    int numEdgesDown = 0;
+        val num = vertices.size
+        val ret = FloatArray(2)
 
-    for(int[] edgeUp : edgesUp)
-      for(int edge : edgeUp)
-        if( edge<0 ) numEdgesDown++;
+        for (vertex in vertices)
+        {
+            ret[0] += vertex[0]
+            ret[1] += vertex[1]
+        }
 
-    return numEdgesDown;
-    }
+        ret[0] /= num.toFloat()
+        ret[1] /= num.toFloat()
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        return ret
+    }
 
-  private float[] produceNormalVector(float[][] outerAndHole, float xs, float ys)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeCenters(vertices: Array<Array<FloatArray>>): Array<FloatArray?>
     {
-    int vCurr,len = outerAndHole.length;
-
-    for(vCurr=0; vCurr<len; vCurr++)
-      {
-      float[] vs = outerAndHole[vCurr];
-      if( isSame(vs[0]-xs,vs[1]-ys) ) break;
-      }
-
-    if( vCurr==len ) System.out.println("MeshMultigon: ERROR in produceNormalVector!!");
-
-    int vPrev = vCurr==0 ? len-1 : vCurr-1;
-    int vNext = vCurr>=len-1 ? 0 : vCurr+1;
-    float[] vp = outerAndHole[vPrev];
-    float[] vn = outerAndHole[vNext];
-
-    return new float[] { vp[1]-vn[1], vn[0]-vp[0] };
+        val num = vertices.size
+        val ret = arrayOfNulls<FloatArray>(num)
+        for (i in 0 until num) ret[i] = computeCenter(vertices[i])
+        return ret
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float[] produceNormalVector(float[][] outerAndHole, float xs, float ys, float xe, float ye)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeMode(vL: FloatArray, vR: FloatArray, vT: FloatArray, normL: FloatArray, normR: FloatArray,
+                            edgesUp: Array<IntArray?>, vertsUp: Array<BooleanArray?>, vertices: Array<Array<FloatArray>>, centers: Array<FloatArray?>, component: Int, curr: Int): Int
     {
-    int vCurr,len = outerAndHole.length;
-
-    for(vCurr=0; vCurr<len; vCurr++)
-      {
-      float[] vs = outerAndHole[vCurr];
-
-      if( isSame(vs[0]-xs,vs[1]-ys) )
+        val v = vertices[component]
+        val edges = edgesUp[component]
+        val numPoly = v.size
+        val next = if (curr==numPoly-1) 0 else curr+1
+        val prev = if (curr==0) numPoly-1 else curr-1
+        val eupc = edges!![curr]
+
+        if (eupc<0)
         {
-        int vNext = vCurr==len-1 ? 0 : vCurr+1;
-        float[] ve = outerAndHole[vNext];
-        if( isSame(ve[0]-xe,ve[1]-ye) ) break;
-        }
-      }
+            vL[0] = v[curr][0]
+            vL[1] = v[curr][1]
+            vR[0] = v[next][0]
+            vR[1] = v[next][1]
+            vT[0] = centers[component]!![0]
+            vT[1] = centers[component]!![1]
 
-    int vPrev = vCurr==0 ? len-1 : vCurr-1;
-    int vNext = vCurr>=len-1 ? 0 : vCurr+1;
-    float[] vp = outerAndHole[vPrev];
-    float[] vn = outerAndHole[vNext];
+            val outerL = retOuterVector(component, curr)
+            val outerR = retOuterVector(component, next)
 
-    return new float[] { vp[1]-vn[1], vn[0]-vp[0] };
-    }
+            if (outerL!=null&&outerR!=null)
+            {
+                normL[0] = -outerL[0]
+                normL[1] = -outerL[1]
+                normR[0] = -outerR[0]
+                normR[1] = -outerR[1]
+            }
+            else
+            {
+                val eupp = edges[prev]
+                val eupn = edges[next]
+
+                if (eupp<0)
+                {
+                    normL[0] = vL[0]-vT[0]
+                    normL[1] = vL[1]-vT[1]
+                }
+                else
+                {
+                    normL[0] = v[curr][0]-v[prev][0]
+                    normL[1] = v[curr][1]-v[prev][1]
+                }
+
+                if (eupn<0)
+                {
+                    normR[0] = vR[0]-vT[0]
+                    normR[1] = vR[1]-vT[1]
+                }
+                else
+                {
+                    val nnxt = if (next==numPoly-1) 0 else next+1
+                    normR[0] = v[next][0]-v[nnxt][0]
+                    normR[1] = v[next][1]-v[nnxt][1]
+                }
+            }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            return MeshBandedTriangle.Companion.MODE_NORMAL
+        }
+        else
+        {
+            vL[0] = centers[eupc]!![0]
+            vL[1] = centers[eupc]!![1]
+            vR[0] = centers[component]!![0]
+            vR[1] = centers[component]!![1]
+            vT[0] = v[curr][0]
+            vT[1] = v[curr][1]
 
-  private void computeEdgeVectors(float[][][] vertices, int[][] edgesUp)
-    {
-    int numPoly = vertices.length;
-    mEdgeVectors = new float[numPoly][][];
+            val vup = vertsUp[component]!![curr]
 
-    for(int c=0; c<numPoly; c++)
-      {
-      float[][] polygon = vertices[c];
-      int numV = polygon.length;
-      mEdgeVectors[c] = new float[numV][];
+            if (vup)
+            {
+                normL[0] = 0f
+                normL[1] = 1f
+                normR[0] = 0f
+                normR[1] = 1f
 
-      for(int v=0; v<numV; v++)
-        {
-        int edgeUp = edgesUp[c][v];
-        float xs = polygon[v][0];
-        float ys = polygon[v][1];
-        int n = v==numV-1 ? 0 : v+1;
-        float xe = polygon[n][0];
-        float ye = polygon[n][1];
-
-        if( edgeUp<0 ) // external edge, normal vector
-          {
-          float[][] verts = mOuterAndHoleVertices[-edgeUp-1];
-          mEdgeVectors[c][v] = produceNormalVector(verts,xs,ys,xe,ye);
-          }
-        else // internal edge - but the vertex that begins it might still be outer!
-          {
-          int numComp = retIndexBelongs(xs,ys);
-
-          if( numComp<0 ) mEdgeVectors[c][v] = null;
-          else
+                return MeshBandedTriangle.Companion.MODE_FLAT
+            }
+            else
             {
-            float[][] verts = mOuterAndHoleVertices[numComp];
-            mEdgeVectors[c][v] = produceNormalVector(verts,xs,ys);
+                val outerT = retOuterVector(component, curr)
+
+                if (outerT!=null)
+                {
+                    normL[0] = outerT[0]
+                    normL[1] = outerT[1]
+                    normR[0] = outerT[0]
+                    normR[1] = outerT[1]
+                }
+                else
+                {
+                    val dx = v[next][0]-v[curr][0]
+                    val dy = v[next][1]-v[curr][1]
+                    normL[0] = dx
+                    normL[1] = dy
+                    normR[0] = dx
+                    normR[1] = dy
+                }
+
+                return MeshBandedTriangle.Companion.MODE_INVERTED
             }
-          }
         }
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// ret:
-// -1  : vertex (x,y) is 'inner' (does not belong to outer vertices or any hole)
-// N>=0: vertex (x,y) is outer [ N==0 --> outer, 1--> 1sr hole, 2 --> 2nd hole, etc ]
-
-  private int retIndexBelongs(float x, float y)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun printVertices(vertices: Array<Array<FloatArray>>)
     {
-    int numOuterComponents = mOuterAndHoleVertices.length;
+        val sb = StringBuilder()
+        var loop = 0
 
-    for(int c=0; c<numOuterComponents; c++)
-      {
-      float[][] comp = mOuterAndHoleVertices[c];
+        for (vert in vertices)
+        {
+            sb.append("Loop ")
+            sb.append(loop)
+            sb.append(" : { ")
 
-      for(float[] v : comp)
-        if( isSame(x-v[0],y-v[1]) ) return c;
-      }
+            loop++
+            var num = 0
 
-    return -1;
-    }
+            for (v in vert)
+            {
+                if (num>=0) sb.append(", ")
+                num++
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+                sb.append(v[0])
+                sb.append(", ")
+                sb.append(v[1])
+            }
 
-  private float[] retOuterVector(int component, int vert)
-    {
-    return mEdgeVectors[component][vert];
-    }
+            sb.append(" }\n")
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        println("vertices: \n$sb")
+    }
 
-  private boolean[][] computeVertsUp(float[][][] vertices)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun printBand(band: FloatArray)
     {
-    int numPoly = vertices.length;
-    boolean[][] up = new boolean[numPoly][];
-
-    for(int i=0; i<numPoly; i++)
-      {
-      float[][] v = vertices[i];
-      int len = v.length;
-      up[i] = new boolean[len];
+        val sb = StringBuilder()
 
-      for(int j=0; j<len; j++)
+        for (v in band)
         {
-        float[] vect = retOuterVector(i,j);
-        up[i][j] = (vect==null);
+            sb.append(v)
+            sb.append("f, ")
         }
-      }
 
-    return up;
+        println("band: \n$sb")
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float[] computeCenter(float[][] vertices)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Specify several lists of vertices. Each list defines one polygon. (@see MeshPolygon).
+     * If two such polygons share an edge (or several edges) then the edge in both of them is
+     * marked 'up'.
+     *
+     * @param vertices   an array float[][]s, each specifying vertices of a single polygon.
+     * @param band       see MeshPolygon. Shared among all polygons.
+     * @param exBands    see MeshPolygon. Shared among all polygons.
+     * @param exVertices see MeshPolygon. Shared among all polygons.
+     */
+    constructor(vertices: Array<Array<FloatArray>>, band: FloatArray, exBands: Int, exVertices: Int) : super()
     {
-    int num = vertices.length;
-    float[] ret = new float[2];
+        var numTriangles = 0
+        for (vertex in vertices) numTriangles += vertex.size
+        val triangles = arrayOfNulls<MeshBandedTriangle>(numTriangles)
 
-    for(float[] vertex : vertices)
-      {
-      ret[0] += vertex[0];
-      ret[1] += vertex[1];
-      }
+        val edgesUp = computeEdgesUp(vertices)
+        mOuterAndHoleVertices = computeOuterAndHoleVertices(vertices, edgesUp)
 
-    ret[0] /= num;
-    ret[1] /= num;
+        computeEdgeVectors(vertices, edgesUp)
 
-    return ret;
-    }
+        val vertsUp = computeVertsUp(vertices)
+        val centers = computeCenters(vertices)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        val vL = FloatArray(2)
+        val vR = FloatArray(2)
+        val vT = FloatArray(2)
+        val normL = FloatArray(2)
+        val normR = FloatArray(2)
 
-  private float[][] computeCenters(float[][][] vertices)
-    {
-    int num = vertices.length;
-    float[][] ret = new float[num][];
-    for(int i=0; i<num; i++) ret[i] = computeCenter(vertices[i]);
-    return ret;
+        var index = 0
+        val len = vertices.size
+
+        for (i in 0 until len)
+        {
+            val num = vertices[i].size
+
+            for (j in 0 until num)
+            {
+                val mode = computeMode(vL, vR, vT, normL, normR, edgesUp, vertsUp, vertices, centers, i, j)
+                triangles[index++] = MeshBandedTriangle(vL, vR, vT, band, normL, normR, mode, exBands, exVertices)
+            }
+        }
+
+        join(triangles)
+        mergeEffComponents()
+        mergeTexComponents()
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshMultigon, deep: Boolean) : super(mesh, deep)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshMultigon
+    {
+        return MeshMultigon(this, deep)
+    }
 
-  private int computeMode(float[] vL, float[] vR, float[] vT, float[] normL, float[] normR,
-                          int[][] edgesUp, boolean[][] vertsUp, float[][][] vertices, float[][] centers, int component, int curr)
+    companion object
     {
-    float[][] v = vertices[component];
-    int[] edges = edgesUp[component];
-    int numPoly = v.length;
-    int next= curr==numPoly-1 ? 0 : curr+1;
-    int prev= curr==0 ? numPoly-1 : curr-1;
-    int eupc = edges[curr];
-
-    if( eupc<0 )
-      {
-      vL[0] = v[curr][0];
-      vL[1] = v[curr][1];
-      vR[0] = v[next][0];
-      vR[1] = v[next][1];
-      vT[0] = centers[component][0];
-      vT[1] = centers[component][1];
-
-      float[] outerL = retOuterVector(component,curr);
-      float[] outerR = retOuterVector(component,next);
-
-      if( outerL!=null && outerR!=null )
+        private const val MAX_ERROR = 0.000001f
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun isSame(dx: Float, dy: Float): Boolean
         {
-        normL[0] = -outerL[0];
-        normL[1] = -outerL[1];
-        normR[0] = -outerR[0];
-        normR[1] = -outerR[1];
+            return (dx*dx+dy*dy<MAX_ERROR)
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun isUp(vertices: Array<Array<FloatArray>>, polygon: Int, edge: Int): Int
         {
-        int eupp = edges[prev];
-        int eupn = edges[next];
-
-        if( eupp<0 )
-          {
-          normL[0]= vL[0]-vT[0];
-          normL[1]= vL[1]-vT[1];
-          }
-        else
-          {
-          normL[0]= v[curr][0] - v[prev][0];
-          normL[1]= v[curr][1] - v[prev][1];
-          }
-
-        if( eupn<0 )
-          {
-          normR[0]= vR[0]-vT[0];
-          normR[1]= vR[1]-vT[1];
-          }
-        else
-          {
-          int nnxt= next==numPoly-1 ? 0 : next+1;
-          normR[0]= v[next][0] - v[nnxt][0];
-          normR[1]= v[next][1] - v[nnxt][1];
-          }
-        }
+            val p = vertices[polygon]
+            val lenP = p.size
+            val len = vertices.size
+            val next = (if (edge==lenP-1) 0 else edge+1)
 
-      return MeshBandedTriangle.MODE_NORMAL;
-      }
-    else
-      {
-      vL[0] = centers[eupc][0];
-      vL[1] = centers[eupc][1];
-      vR[0] = centers[component][0];
-      vR[1] = centers[component][1];
-      vT[0] = v[curr][0];
-      vT[1] = v[curr][1];
+            val v1x = p[edge][0]
+            val v1y = p[edge][1]
+            val v2x = p[next][0]
+            val v2y = p[next][1]
 
-      boolean vup = vertsUp[component][curr];
+            for (i in 0 until len) if (i!=polygon)
+            {
+                val num = vertices[i].size
+
+                for (j in 0 until num)
+                {
+                    val n = (if (j==num-1) 0 else j+1)
+                    val v = vertices[i]
+                    val x1 = v[j][0]
+                    val y1 = v[j][1]
+                    val x2 = v[n][0]
+                    val y2 = v[n][1]
+
+                    if (isSame(v2x-x1, v2y-y1)&&isSame(v1x-x2, v1y-y2)) return i
+                }
+            }
 
-      if( vup )
+            return -1
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun detectFirstOuterVertex(vertices: Array<Array<FloatArray>>): FloatArray
         {
-        normL[0]=0;
-        normL[1]=1;
-        normR[0]=0;
-        normR[1]=1;
+            var X = -Float.MAX_VALUE
+            var I = 0
+            var J = 0
+            val len = vertices.size
 
-        return MeshBandedTriangle.MODE_FLAT;
+            for (i in 0 until len)
+            {
+                val v = vertices[i]
+                val num = v.size
+
+                for (j in 0 until num) if (v[j][0]>X)
+                {
+                    X = v[j][0]
+                    I = i
+                    J = j
+                }
+            }
+
+            val v = vertices[I][J]
+            return floatArrayOf(v[0], v[1])
         }
-      else
+
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun computeAngle(x1: Float, y1: Float, x2: Float, y2: Float): Double
         {
-        float[] outerT = retOuterVector(component,curr);
-
-        if( outerT!=null )
-          {
-          normL[0]= outerT[0];
-          normL[1]= outerT[1];
-          normR[0]= outerT[0];
-          normR[1]= outerT[1];
-          }
-        else
-          {
-          float dx = v[next][0]-v[curr][0];
-          float dy = v[next][1]-v[curr][1];
-          normL[0]= dx;
-          normL[1]= dy;
-          normR[0]= dx;
-          normR[1]= dy;
-          }
-
-        return MeshBandedTriangle.MODE_INVERTED;
+            val diff = atan2(y2.toDouble(), x2.toDouble())-atan2(y1.toDouble(), x1.toDouble())
+            return if (diff<0) diff+(2*Math.PI) else diff
         }
-      }
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun detectNextOuterVertex(vertices: Array<Array<FloatArray>>, curr: FloatArray, vect: FloatArray, edgesUp: Array<IntArray?>): FloatArray
+        {
+            var maxAngle = 0.0
+            var x = 0f
+            var y = 0f
+            val numC = vertices.size
 
-  public static int[][] computeEdgesUp(float[][][] vertices)
-    {
-    int numPoly = vertices.length;
-    int[][] up = new int[numPoly][];
+            for (c in 0 until numC)
+            {
+                val vert = vertices[c]
+                val numV = vert.size
+
+                for (v in 0 until numV)
+                {
+                    val xc = vert[v][0]
+                    val yc = vert[v][1]
+
+                    if (edgesUp[c]!![v]<0&&isSame(xc-curr[0], yc-curr[1]))
+                    {
+                        val n = (if (v==numV-1) 0 else v+1)
+                        val xn = vert[n][0]
+                        val yn = vert[n][1]
+
+                        val angle = computeAngle(vect[0], vect[1], xn-xc, yn-yc)
+
+                        if (angle>maxAngle)
+                        {
+                            maxAngle = angle
+                            x = xn
+                            y = yn
+                        }
+
+                        break
+                    }
+                }
+            }
 
-    for(int p=0; p<numPoly; p++)
-      {
-      int numEdges = vertices[p].length;
-      up[p] = new int[numEdges];
-      for(int e=0; e<numEdges; e++) up[p][e] = isUp(vertices,p,e);
-      }
+            return floatArrayOf(x, y)
+        }
 
-    return up;
-    }
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun getNextIndex(vertices: Array<Array<FloatArray>>, unclaimedEdges: Array<IntArray?>, currIndex: Int, numHoleVerts: Int): Int
+        {
+            val currEdge = unclaimedEdges[currIndex]
+            val currP = currEdge!![0]
+            val currE = currEdge[1]
+            val polygon = vertices[currP]
+            val numV = polygon.size
+            val nextE = if (currE<numV-1) currE+1 else 0
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            val x = polygon[nextE][0]
+            val y = polygon[nextE][1]
 
-  public static float[][][] computeOuterAndHoleVertices(float[][][] vertices, int[][] edgesUp)
-    {
-    ArrayList<float[]> tmp = new ArrayList<>();
-
-    float[] vect = new float[] {1,0};
-    float[] first= detectFirstOuterVertex(vertices);
-    float[] next = first;
-
-    do
-      {
-      float[] prev = next;
-      next = detectNextOuterVertex(vertices,next,vect,edgesUp);
-      vect[0] = prev[0]-next[0];
-      vect[1] = prev[1]-next[1];
-      tmp.add(next);
-      }
-    while( !isSame(next[0]-first[0],next[1]-first[1]) );
-
-    int numOuterVerts= tmp.size();
-    float[][] outerVertices = new float[numOuterVerts][];
-    for(int i=0; i<numOuterVerts; i++) outerVertices[i] = tmp.remove(0);
-
-    int numEdgesDown = countEdgesDown(edgesUp);
-    int numHoleVerts= numEdgesDown-numOuterVerts;
-    float[][][] holeVertices=null;
-    if( numHoleVerts>0 ) holeVertices = computeHoles(vertices,edgesUp,outerVertices,numHoleVerts);
-    int numHoles = holeVertices==null ? 0 : holeVertices.length;
-    float[][][] ret = new float[1+numHoles][][];
-    ret[0] = outerVertices;
-    for(int i=0; i<numHoles; i++) ret[i+1] = holeVertices[i];
-
-    return ret;
-    }
+            for (e in 0 until numHoleVerts)
+            {
+                val edge = unclaimedEdges[e]
+
+                if (edge!![2]==1)
+                {
+                    val po = edge[0]
+                    val ed = edge[1]
+
+                    val cx = vertices[po][ed][0]
+                    val cy = vertices[po][ed][1]
+
+                    if (isSame(cx-x, cy-y))
+                    {
+                        edge[2] = 0
+                        return e
+                    }
+                }
+            }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            return -1
+        }
 
-  private void printVertices(float[][][] vertices)
-    {
-    StringBuilder sb = new StringBuilder();
-    int loop=0;
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun generateHole(vertices: Array<Array<FloatArray>>, unclaimedEdges: Array<IntArray?>, output: Array<FloatArray>, edgesUp: Array<IntArray?>, holeNumber: Int, numHoleVerts: Int): Int
+        {
+            var firstIndex = -1
 
-    for(float[][] vert : vertices)
-      {
-      sb.append("Loop ");
-      sb.append(loop);
-      sb.append(" : { ");
+            for (e in 0 until numHoleVerts) if (unclaimedEdges[e]!![2]==1)
+            {
+                firstIndex = e
+                break
+            }
 
-      loop++;
-      int num=0;
+            var currIndex = firstIndex
+            var nextIndex = -1
+            var numAdded = 0
 
-      for( float[] v : vert)
-        {
-        if( num>=0 ) sb.append(", ");
-        num++;
+            while (nextIndex!=firstIndex)
+            {
+                nextIndex = getNextIndex(vertices, unclaimedEdges, currIndex, numHoleVerts)
+
+                val p = unclaimedEdges[nextIndex]!![0]
+                val v = unclaimedEdges[nextIndex]!![1]
+                edgesUp[p]!![v] = -holeNumber-2
+                currIndex = nextIndex
+                val e = unclaimedEdges[nextIndex]
+                val ve = vertices[e!![0]][e[1]]
+                output[numAdded][0] = ve[0]
+                output[numAdded][1] = ve[1]
+                numAdded++
+            }
 
-        sb.append(v[0]);
-        sb.append(", ");
-        sb.append(v[1]);
+            return numAdded
         }
 
-      sb.append(" }\n");
-      }
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun doesNotBelongToOuter(x: Float, y: Float, outer: Array<FloatArray>): Boolean
+        {
+            for (v in outer) if (isSame(x-v[0], y-v[1])) return false
 
-    System.out.println("vertices: \n"+sb);
-    }
+            return true
+        }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun computeHoles(vertices: Array<Array<FloatArray>>, edgesUp: Array<IntArray?>, outer: Array<FloatArray>, numHoleVerts: Int): Array<Array<FloatArray?>>
+        {
+            val unclaimedEdges = arrayOfNulls<IntArray>(numHoleVerts)
+            var index = 0
+            val numPoly = vertices.size
 
-  private void printBand(float[] band)
-    {
-    StringBuilder sb = new StringBuilder();
+            for (p in 0 until numPoly)
+            {
+                val ve = vertices[p]
+                val numEdges = ve.size
+
+                for (e in 0 until numEdges) if (edgesUp[p]!![e]<0)
+                {
+                    val x1 = ve[e][0]
+                    val y1 = ve[e][1]
+
+                    val n = if (e<numEdges-1) e+1 else 0
+                    val x2 = ve[n][0]
+                    val y2 = ve[n][1]
+
+                    // yes, we need to check both ends of the edge - otherwise the
+                    // following 3x3 situation (x - wall, o - hole ) does not work:
+                    // x x x
+                    // x o x
+                    // o x x
+                    if (doesNotBelongToOuter(x1, y1, outer)||doesNotBelongToOuter(x2, y2, outer))
+                    {
+                        unclaimedEdges[index] = intArrayOf(p, e, 1)
+                        index++
+                    }
+                }
+            }
 
-    for(float v : band)
-      {
-      sb.append(v);
-      sb.append("f, ");
-      }
+            var remaining = numHoleVerts
+            val holes = ArrayList<Array<FloatArray?>>()
+            val output = Array(numHoleVerts) { FloatArray(2) }
+            var numHoles = 0
 
-    System.out.println("band: \n"+sb);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Specify several lists of vertices. Each list defines one polygon. (@see MeshPolygon).
- * If two such polygons share an edge (or several edges) then the edge in both of them is
- * marked 'up'.
- *
- * @param vertices   an array float[][]s, each specifying vertices of a single polygon.
- * @param band       see MeshPolygon. Shared among all polygons.
- * @param exBands    see MeshPolygon. Shared among all polygons.
- * @param exVertices see MeshPolygon. Shared among all polygons.
- */
-  public MeshMultigon(float[][][] vertices, float[] band, int exBands, int exVertices)
-    {
-    super();
+            while (remaining>0)
+            {
+                val num = generateHole(vertices, unclaimedEdges, output, edgesUp, numHoles, numHoleVerts)
+                remaining -= num
+                numHoles++
+
+                val hole = Array<FloatArray?>(num) { FloatArray(2) }
+                for (h in 0 until num)
+                {
+                    hole[h]!![0] = output[h][0]
+                    hole[h]!![1] = output[h][1]
+                }
+
+                holes.add(hole)
+            }
 
-    int numTriangles=0;
-    for(float[][] vertex : vertices) numTriangles+=vertex.length;
-    MeshBandedTriangle[] triangles = new MeshBandedTriangle[numTriangles];
+            val ret: Array<Array<FloatArray?>> = arrayOfNulls(numHoles)
+            for (h in 0 until numHoles) ret[h] = holes.removeAt(0)
 
-    int[][] edgesUp = computeEdgesUp(vertices);
-    mOuterAndHoleVertices = computeOuterAndHoleVertices(vertices,edgesUp);
+            return ret
+        }
 
-    computeEdgeVectors(vertices,edgesUp);
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        private fun countEdgesDown(edgesUp: Array<IntArray>): Int
+        {
+            var numEdgesDown = 0
 
-    boolean[][] vertsUp = computeVertsUp(vertices);
-    float[][] centers = computeCenters(vertices);
+            for (edgeUp in edgesUp) for (edge in edgeUp) if (edge<0) numEdgesDown++
 
-    float[] vL = new float[2];
-    float[] vR = new float[2];
-    float[] vT = new float[2];
-    float[] normL = new float[2];
-    float[] normR = new float[2];
+            return numEdgesDown
+        }
 
-    int index=0,len = vertices.length;
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        // PUBLIC API
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun computeEdgesUp(vertices: Array<Array<FloatArray>>): Array<IntArray?>
+        {
+            val numPoly = vertices.size
+            val up = arrayOfNulls<IntArray>(numPoly)
 
-    for(int i=0; i<len; i++)
-      {
-      int num=vertices[i].length;
+            for (p in 0 until numPoly)
+            {
+                val numEdges = vertices[p].size
+                up[p] = IntArray(numEdges)
+                for (e in 0 until numEdges) up[p]!![e] = isUp(vertices, p, e)
+            }
 
-      for(int j=0; j<num; j++)
-        {
-        int mode=computeMode(vL, vR, vT, normL, normR, edgesUp, vertsUp,vertices,centers, i,j);
-        triangles[index++] = new MeshBandedTriangle(vL, vR, vT, band, normL, normR, mode, exBands, exVertices);
+            return up
         }
-      }
 
-    join(triangles);
-    mergeEffComponents();
-    mergeTexComponents();
-    }
+        ///////////////////////////////////////////////////////////////////////////////////////////////////
+        fun computeOuterAndHoleVertices(vertices: Array<Array<FloatArray>>, edgesUp: Array<IntArray?>): Array<Array<FloatArray>>
+        {
+            val tmp = ArrayList<FloatArray>()
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public MeshMultigon(MeshMultigon mesh, boolean deep)
-    {
-    super(mesh,deep);
-    }
+            val vect = floatArrayOf(1f, 0f)
+            val first = detectFirstOuterVertex(vertices)
+            var next = first
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshMultigon copy(boolean deep)
-    {
-    return new MeshMultigon(this,deep);
+            do
+            {
+                val prev = next
+                next = detectNextOuterVertex(vertices, next, vect, edgesUp)
+                vect[0] = prev[0]-next[0]
+                vect[1] = prev[1]-next[1]
+                tmp.add(next)
+            }
+            while (!isSame(next[0]-first[0], next[1]-first[1]))
+
+            val numOuterVerts = tmp.size
+            val outerVertices = arrayOfNulls<FloatArray>(numOuterVerts)
+            for (i in 0 until numOuterVerts) outerVertices[i] = tmp.removeAt(0)
+
+            val numEdgesDown = countEdgesDown(edgesUp)
+            val numHoleVerts = numEdgesDown-numOuterVerts
+            var holeVertices: Array<Array<FloatArray?>>? = null
+            if (numHoleVerts>0) holeVertices = computeHoles(vertices, edgesUp, outerVertices, numHoleVerts)
+            val numHoles = holeVertices?.size ?: 0
+            val ret: Array<Array<FloatArray?>> = arrayOfNulls(1+numHoles)
+            ret[0] = outerVertices
+            for (i in 0 until numHoles) ret[i+1] = holeVertices!![i]
+
+            return ret
+        }
     }
- }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/mesh/MeshPolygon.kt b/src/main/java/org/distorted/library/mesh/MeshPolygon.kt
index 1eabdf0..0255193 100644
--- a/src/main/java/org/distorted/library/mesh/MeshPolygon.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshPolygon.kt
@@ -18,359 +18,336 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-import org.distorted.library.main.DistortedLibrary;
+import org.distorted.library.main.DistortedLibrary
+import kotlin.math.sqrt
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Create a polygon of any shape and varying elevations from the edges towards the center.
- * <p>
+ *
  * Specify a list of vertices. Any two adjacent vertices + the center (0,0,0) form a triangle. The
  * polygon is going to be split into such triangles, and each triangle is split into adjustable number
  * of 'bands' form the outer edge towards the center. Edges of each band can can at any elevation.
  */
-public class MeshPolygon extends MeshBase
-  {
-  private static final int NUM_CACHE = 20;
-
-  private float[][] mPolygonVertices;
-  private int mNumPolygonVertices;
-  private float[] mPolygonBands;
-  private int mNumPolygonBands;
-
-  private int remainingVert;
-  private int numVertices;
-  private int extraBand, extraVertices;
+class MeshPolygon : MeshBase
+{
+    private var mPolygonVertices: Array<FloatArray>
+    private var mNumPolygonVertices = 0
+    private var mPolygonBands: FloatArray
+    private var mNumPolygonBands = 0
 
-  private float[] mCurveCache;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// polygonVertices>=3 , polygonBands>=2
-
-  private void computeNumberOfVertices()
-     {
-     if( mNumPolygonBands==2 && extraBand>0 )
-       {
-       numVertices = 1 + 2*mNumPolygonVertices*(1+extraBand+2*extraVertices);
-       }
-     else
-       {
-       numVertices = (mNumPolygonVertices*mNumPolygonBands+2)*(mNumPolygonBands-1) - 1;
-       numVertices+= 2*mNumPolygonVertices*(2*extraBand*extraVertices);
-       }
-
-     remainingVert = numVertices;
-     }
+    private var remainingVert = 0
+    override var numVertices: Int = 0
+    private var extraBand = 0
+    private var extraVertices = 0
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    private var mCurveCache: FloatArray
 
-  private void computeCache()
+    companion object
     {
-    mCurveCache = new float[NUM_CACHE];
-    float[] tmpD = new float[mNumPolygonBands+1];
-    float[] tmpX = new float[mNumPolygonBands+1];
-
-    for(int i=1; i<mNumPolygonBands; i++)
-      {
-      tmpD[i] = (mPolygonBands[2*i-1]-mPolygonBands[2*i+1]) / (mPolygonBands[2*i]-mPolygonBands[2*i-2]);
-      tmpX[i] = 1.0f - (mPolygonBands[2*i]+mPolygonBands[2*i-2])/2;
-      }
-
-    tmpD[0] = tmpD[1];
-    tmpD[mNumPolygonBands] = tmpD[mNumPolygonBands-1];
-    tmpX[0] = 0.0f;
-    tmpX[mNumPolygonBands] = 1.0f;
+        private const val NUM_CACHE = 20
+    }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // polygonVertices>=3 , polygonBands>=2
+    private fun computeNumberOfVertices()
+    {
+        if (mNumPolygonBands==2&&extraBand>0)
+        {
+            numVertices = 1+2*mNumPolygonVertices*(1+extraBand+2*extraVertices)
+        }
+        else
+        {
+            numVertices = (mNumPolygonVertices*mNumPolygonBands+2)*(mNumPolygonBands-1)-1
+            numVertices += 2*mNumPolygonVertices*(2*extraBand*extraVertices)
+        }
 
-    int prev = 0;
-    int next = 0;
+        remainingVert = numVertices
+    }
 
-    for(int i=0; i<NUM_CACHE-1; i++)
-      {
-      float x = i/(NUM_CACHE-1.0f);
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeCache()
+    {
+        mCurveCache = FloatArray(NUM_CACHE)
+        val tmpD = FloatArray(mNumPolygonBands+1)
+        val tmpX = FloatArray(mNumPolygonBands+1)
 
-      if( x>=tmpX[next] )
+        for (i in 1 until mNumPolygonBands)
         {
-        prev = next;
-        while( next<=mNumPolygonBands && x>=tmpX[next] ) next++;
+            tmpD[i] = (mPolygonBands[2*i-1]-mPolygonBands[2*i+1])/(mPolygonBands[2*i]-mPolygonBands[2*i-2])
+            tmpX[i] = 1.0f-(mPolygonBands[2*i]+mPolygonBands[2*i-2])/2
         }
 
-      if( next>prev )
-        {
-        float t = (x-tmpX[prev]) / (tmpX[next]-tmpX[prev]);
-        mCurveCache[i] = t*(tmpD[next]-tmpD[prev]) + tmpD[prev];
-        }
-      else
+        tmpD[0] = tmpD[1]
+        tmpD[mNumPolygonBands] = tmpD[mNumPolygonBands-1]
+        tmpX[0] = 0.0f
+        tmpX[mNumPolygonBands] = 1.0f
+
+        var prev = 0
+        var next = 0
+
+        for (i in 0 until NUM_CACHE-1)
         {
-        mCurveCache[i] = tmpD[next];
+            val x = i/(NUM_CACHE-1.0f)
+
+            if (x>=tmpX[next])
+            {
+                prev = next
+                while (next<=mNumPolygonBands&&x>=tmpX[next]) next++
+            }
+
+            if (next>prev)
+            {
+                val t = (x-tmpX[prev])/(tmpX[next]-tmpX[prev])
+                mCurveCache[i] = t*(tmpD[next]-tmpD[prev])+tmpD[prev]
+            }
+            else
+            {
+                mCurveCache[i] = tmpD[next]
+            }
         }
-      }
 
-    mCurveCache[NUM_CACHE-1] = tmpD[mNumPolygonBands];
+        mCurveCache[NUM_CACHE-1] = tmpD[mNumPolygonBands]
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float getSpecialQuot(int index)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun getSpecialQuot(index: Int): Float
     {
-    int num = 1 + extraBand + 2*extraVertices;
-    int change1 = extraVertices+1;
-    int change2 = num-change1;
-    float quot = 1.0f/(extraBand+1);
-
-    if( index<change1 )      return index*quot/change1;
-    else if( index>change2 ) return 1-quot + (index-change2)*quot/change1;
-    else                     return (index-change1+1)*quot;
+        val num = 1+extraBand+2*extraVertices
+        val change1 = extraVertices+1
+        val change2 = num-change1
+        val quot = 1.0f/(extraBand+1)
+
+        return if (index<change1) index*quot/change1
+        else if (index>change2) 1-quot+(index-change2)*quot/change1
+        else (index-change1+1)*quot
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float getQuot(int index, int band, boolean isExtra)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun getQuot(index: Int, band: Int, isExtra: Boolean): Float
     {
-    int num = mNumPolygonBands-1-band;
+        val num = mNumPolygonBands-1-band
 
-    if( num>0 )
-      {
-      if( isExtra )
+        if (num>0)
         {
-        int extra = extraBand-band+extraVertices;
-
-        if( index < extra )
-          {
-          float quot = ((float)extraBand-band)/(extra*num);
-          return index*quot;
-          }
-        else if( index > num+2*extraVertices-extra )
-          {
-          float quot = ((float)extraBand-band)/(extra*num);
-          return (1.0f-((float)extraBand-band)/num) + (index-num-2*extraVertices+extra)*quot;
-          }
-        else
-          {
-          return ((float)(index-extraVertices))/num;
-          }
+            if (isExtra)
+            {
+                val extra = extraBand-band+extraVertices
+
+                if (index<extra)
+                {
+                    val quot = (extraBand.toFloat()-band)/(extra*num)
+                    return index*quot
+                }
+                else if (index>num+2*extraVertices-extra)
+                {
+                    val quot = (extraBand.toFloat()-band)/(extra*num)
+                    return (1.0f-(extraBand.toFloat()-band)/num)+(index-num-2*extraVertices+extra)*quot
+                }
+                else
+                {
+                    return ((index-extraVertices).toFloat())/num
+                }
+            }
+
+            return index.toFloat()/num
         }
 
-      return (float)index/num;
-      }
-
-    return 1.0f;
+        return 1.0f
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private float derivative(float x)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun derivative(x: Float): Float
     {
-    if( x>=1.0f )
-      {
-      return 0.0f;
-      }
-    else
-      {
-      float tmp = x*(NUM_CACHE-1);
-      int i1 = (int)tmp;
-      int i2 = i1+1;
-
-      // why 0.5? Arbitrarily; this way the cubit faces of Twisty Puzzles
-      // [the main and only user of this class] look better.
-      return 0.5f*((tmp-i1)*(mCurveCache[i2]-mCurveCache[i1]) + mCurveCache[i1]);
-      }
-    }
+        if (x>=1.0f)
+        {
+            return 0.0f
+        }
+        else
+        {
+            val tmp = x*(NUM_CACHE-1)
+            val i1 = tmp.toInt()
+            val i2 = i1+1
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            // why 0.5? Arbitrarily; this way the cubit faces of Twisty Puzzles
+            // [the main and only user of this class] look better.
+            return 0.5f*((tmp-i1)*(mCurveCache[i2]-mCurveCache[i1])+mCurveCache[i1])
+        }
+    }
 
-  private int addVertex(int vertex, int polyBand, int polyVertex, int polyEndVer, float quot,
-                        float[] attribs1, float[] attribs2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun addVertex(vertex: Int, polyBand: Int, polyVertex: Int, polyEndVer: Int, quot: Float,
+                          attribs1: FloatArray, attribs2: FloatArray): Int
     {
-    remainingVert--;
+        remainingVert--
 
-    float Xfirst= mPolygonVertices[polyVertex][0];
-    float Yfirst= mPolygonVertices[polyVertex][1];
-    float Xlast = mPolygonVertices[polyEndVer][0];
-    float Ylast = mPolygonVertices[polyEndVer][1];
+        val Xfirst = mPolygonVertices[polyVertex][0]
+        val Yfirst = mPolygonVertices[polyVertex][1]
+        val Xlast = mPolygonVertices[polyEndVer][0]
+        val Ylast = mPolygonVertices[polyEndVer][1]
 
-    float xEdge = Xfirst + quot*(Xlast-Xfirst);
-    float yEdge = Yfirst + quot*(Ylast-Yfirst);
+        val xEdge = Xfirst+quot*(Xlast-Xfirst)
+        val yEdge = Yfirst+quot*(Ylast-Yfirst)
 
-    float q = mPolygonBands[2*polyBand];
-    float x = q*xEdge;
-    float y = q*yEdge;
-    float z = mPolygonBands[2*polyBand+1];
+        val q = mPolygonBands[2*polyBand]
+        val x = q*xEdge
+        val y = q*yEdge
+        val z = mPolygonBands[2*polyBand+1]
 
-    attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB  ] = x;
-    attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+1] = y;
-    attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+2] = z;
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB  ] = x
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+1] = y
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+2] = z
 
-    float d = derivative(1-mPolygonBands[2*polyBand]);
+        val d = derivative(1-mPolygonBands[2*polyBand])
 
-    float vx = d*xEdge;
-    float vy = d*yEdge;
-    float vz = xEdge*xEdge + yEdge*yEdge;
-    float len = (float)Math.sqrt(vx*vx + vy*vy + vz*vz);
+        val vx = d*xEdge
+        val vy = d*yEdge
+        val vz = xEdge*xEdge+yEdge*yEdge
+        val len = sqrt((vx*vx+vy*vy+vz*vz).toDouble()).toFloat()
 
-    int index = VERT1_ATTRIBS*vertex + NOR_ATTRIB;
-    attribs1[index  ] = vx/len;
-    attribs1[index+1] = vy/len;
-    attribs1[index+2] = vz/len;
+        val index: Int = VERT1_ATTRIBS*vertex+NOR_ATTRIB
+        attribs1[index] = vx/len
+        attribs1[index+1] = vy/len
+        attribs1[index+2] = vz/len
 
-    attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = x+0.5f;
-    attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = y+0.5f;
+        attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB  ] = x+0.5f
+        attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB+1] = y+0.5f
 
-    return vertex+1;
+        return vertex+1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int createBandStrip(int vertex, int polyBand, int polyVertex, float[] attribs1, float[] attribs2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun createBandStrip(vert: Int, polyBand: Int, polyVertex: Int, attribs1: FloatArray, attribs2: FloatArray): Int
     {
-    if( polyVertex==0 )
-      {
-      vertex = addVertex(vertex,polyBand,0,1,0,attribs1,attribs2);
-      if( polyBand>0 ) vertex = addVertex(vertex,polyBand,0,1,0,attribs1,attribs2);
-      }
-
-    boolean specialCase = mNumPolygonBands==2 && polyBand==0 && extraBand>0;
-    boolean isExtra = polyBand<extraBand;
-    int numPairs = specialCase ? extraBand+1 : mNumPolygonBands-1-polyBand;
-    if( isExtra ) numPairs += 2*extraVertices;
-
-    int polyEndVer = polyVertex==mNumPolygonVertices-1 ? 0 : polyVertex+1;
-    float quot1, quot2;
-
-    for(int index=0; index<numPairs; index++)
-      {
-      if( specialCase )
+        var vertex = vert
+        if (polyVertex==0)
         {
-        quot1 = 1.0f;
-        quot2 = getSpecialQuot(index+1);
+            vertex = addVertex(vertex, polyBand, 0, 1, 0f, attribs1, attribs2)
+            if (polyBand>0) vertex = addVertex(vertex, polyBand, 0, 1, 0f, attribs1, attribs2)
         }
-      else
+
+        val specialCase = mNumPolygonBands==2&&polyBand==0&&extraBand>0
+        val isExtra = polyBand<extraBand
+        var numPairs = if (specialCase) extraBand+1 else mNumPolygonBands-1-polyBand
+        if (isExtra) numPairs += 2*extraVertices
+
+        val polyEndVer = if (polyVertex==mNumPolygonVertices-1) 0 else polyVertex+1
+        var quot1: Float
+        var quot2: Float
+
+        for (index in 0 until numPairs)
         {
-        quot1 = getQuot(index  ,polyBand+1, isExtra);
-        quot2 = getQuot(index+1,polyBand  , isExtra);
+            if (specialCase)
+            {
+                quot1 = 1.0f
+                quot2 = getSpecialQuot(index+1)
+            }
+            else
+            {
+                quot1 = getQuot(index, polyBand+1, isExtra)
+                quot2 = getQuot(index+1, polyBand, isExtra)
+            }
+
+            vertex = addVertex(vertex, polyBand+1, polyVertex, polyEndVer, quot1, attribs1, attribs2)
+            vertex = addVertex(vertex, polyBand  , polyVertex, polyEndVer, quot2, attribs1, attribs2)
         }
 
-      vertex = addVertex(vertex,polyBand+1,polyVertex,polyEndVer,quot1,attribs1,attribs2);
-      vertex = addVertex(vertex,polyBand  ,polyVertex,polyEndVer,quot2,attribs1,attribs2);
-      }
-
-    return vertex;
+        return vertex
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void buildGrid(float[] attribs1, float[] attribs2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildGrid(attribs1: FloatArray, attribs2: FloatArray)
     {
-    int vertex=0;
+        var vertex = 0
 
-    for(int polyBand=0; polyBand<mNumPolygonBands-1; polyBand++)
-      for(int polyVertex=0; polyVertex<mNumPolygonVertices; polyVertex++)
-        vertex = createBandStrip(vertex,polyBand,polyVertex,attribs1,attribs2);
+        for (polyBand in 0 until mNumPolygonBands-1) for (polyVertex in 0 until mNumPolygonVertices) vertex = createBandStrip(vertex, polyBand, polyVertex, attribs1, attribs2)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create a polygon of any shape and varying elevations from the edges towards the center.
- * Optionally make it more dense at the vertices.
- *
- * @param verticesXY [N][2] floats - polygon vertices. N pairs (x,y).
- *                   Vertices HAVE TO be specified in a COUNTERCLOCKWISE order (starting from any).
- * @param bands      2K floats; K pairs of two floats each describing a single band.
- *                   From (1.0,Z[0]) (outer edge, its Z elevation) to (0.0,Z[K]) (the center,
- *                   its elevation). The polygon is split into such concentric bands.
- *                   Must be band[2*i] > band[2*(i+1)] !
- * @param exBand     This and the next parameter describe how to make the mesh denser at the
- *                   polyVertices. If e.g. exIndex=3 and exVertices=2, then 3 triangles of the
- *                   outermost band (and 2 triangles of the next band, and 1 triangle of the third
- *                   band) get denser - the 3 triangles become 3+2 = 5.
- * @param exVertices See above.
- * @param centerX    the X coordinate of the 'center' of the Polygon, i.e. point of the mesh
- *                   all bands go to.
- * @param centerY    Y coordinate of the center.
- */
-  public MeshPolygon(float[][] verticesXY, float[] bands, int exBand, int exVertices, float centerX, float centerY)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Create a polygon of any shape and varying elevations from the edges towards the center.
+     * Optionally make it more dense at the vertices.
+     *
+     * @param verticesXY [N][2] floats - polygon vertices. N pairs (x,y).
+     * Vertices HAVE TO be specified in a COUNTERCLOCKWISE order (starting from any).
+     * @param bands      2K floats; K pairs of two floats each describing a single band.
+     * From (1.0,Z[0]) (outer edge, its Z elevation) to (0.0,Z[K]) (the center,
+     * its elevation). The polygon is split into such concentric bands.
+     * Must be band[2*i] > band[2*(i+1)] !
+     * @param exBand     This and the next parameter describe how to make the mesh denser at the
+     * polyVertices. If e.g. exIndex=3 and exVertices=2, then 3 triangles of the
+     * outermost band (and 2 triangles of the next band, and 1 triangle of the third
+     * band) get denser - the 3 triangles become 3+2 = 5.
+     * @param exVertices See above.
+     * @param centerX    the X coordinate of the 'center' of the Polygon, i.e. point of the mesh
+     * all bands go to.
+     * @param centerY    Y coordinate of the center.
+     */
+    @JvmOverloads
+    constructor(verticesXY: Array<FloatArray>, bands: FloatArray, exBand: Int = 0, exVertices: Int = 0, centerX: Float = 0.0f, centerY: Float = 0.0f) : super()
     {
-    super();
-
-    mPolygonVertices   = verticesXY;
-    mPolygonBands      = bands;
-    mNumPolygonVertices= mPolygonVertices.length;
-    mNumPolygonBands   = mPolygonBands.length /2;
-    extraBand          = exBand;
-    extraVertices      = exVertices;
-
-    if( centerX!=0.0f || centerY!=0.0f )
-      {
-      for(int v=0; v<mNumPolygonVertices; v++)
+        mPolygonVertices = verticesXY
+        mPolygonBands = bands
+        mNumPolygonVertices = mPolygonVertices.size
+        mNumPolygonBands = mPolygonBands.size/2
+        extraBand = exBand
+        extraVertices = exVertices
+
+        if (centerX!=0.0f||centerY!=0.0f)
         {
-        mPolygonVertices[v][0] -= centerX;
-        mPolygonVertices[v][1] -= centerY;
+            for (v in 0 until mNumPolygonVertices)
+            {
+                mPolygonVertices[v][0] -= centerX
+                mPolygonVertices[v][1] -= centerY
+            }
         }
-      }
 
-    computeNumberOfVertices();
-    computeCache();
+        computeNumberOfVertices()
+        computeCache()
 
-    float[] attribs1= new float[VERT1_ATTRIBS*numVertices];
-    float[] attribs2= new float[VERT2_ATTRIBS*numVertices];
+        val attribs1 = FloatArray(VERT1_ATTRIBS*numVertices)
+        val attribs2 = FloatArray(VERT2_ATTRIBS*numVertices)
 
-    buildGrid(attribs1,attribs2);
+        buildGrid(attribs1, attribs2)
 
-    if( remainingVert!=0 )
-      DistortedLibrary.logMessage("MeshPolygon: remainingVert " +remainingVert );
+        if (remainingVert!=0) DistortedLibrary.logMessage("MeshPolygon: remainingVert $remainingVert")
 
-    if( centerX!=0.0f || centerY!=0.0f )
-      {
-      for(int v=0; v<numVertices; v++)
+        if (centerX!=0.0f||centerY!=0.0f)
         {
-        attribs1[VERT1_ATTRIBS*v + POS_ATTRIB  ] += centerX;
-        attribs1[VERT1_ATTRIBS*v + POS_ATTRIB+1] += centerY;
-        attribs2[VERT2_ATTRIBS*v + TEX_ATTRIB  ] += centerX;
-        attribs2[VERT2_ATTRIBS*v + TEX_ATTRIB+1] += centerY;
+            for (v in 0 until numVertices)
+            {
+                attribs1[VERT1_ATTRIBS*v+POS_ATTRIB  ] += centerX
+                attribs1[VERT1_ATTRIBS*v+POS_ATTRIB+1] += centerY
+                attribs2[VERT2_ATTRIBS*v+TEX_ATTRIB  ] += centerX
+                attribs2[VERT2_ATTRIBS*v+TEX_ATTRIB+1] += centerY
+            }
         }
-      }
-
-    setAttribs(attribs1,attribs2);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public MeshPolygon(float[][] verticesXY, float[] bands, int exIndex, int exVertices)
-    {
-    this(verticesXY,bands,exIndex,exVertices,0.0f,0.0f);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create a polygon of any shape and varying elevations from the edges towards the center.
- * Equivalent of the previous with exIndex=0 or exVertices=0.
- */
-  public MeshPolygon(float[][] verticesXY, float[] bands)
-    {
-    this(verticesXY,bands,0,0,0.0f,0.0f);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public MeshPolygon(MeshPolygon mesh, boolean deep)
-    {
-    super(mesh,deep);
+        setAttribs(attribs1, attribs2)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshPolygon copy(boolean deep)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    constructor(verticesXY: Array<FloatArray>, bands: FloatArray, exIndex: Int, exVertices: Int) : this(verticesXY, bands, exIndex, exVertices, 0.0f, 0.0f)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshPolygon, deep: Boolean) : super(mesh, deep)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshPolygon
     {
-    return new MeshPolygon(this,deep);
+        return MeshPolygon(this, deep)
     }
- }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/mesh/MeshQuad.kt b/src/main/java/org/distorted/library/mesh/MeshQuad.kt
index 61f6d9c..fa9684b 100644
--- a/src/main/java/org/distorted/library/mesh/MeshQuad.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshQuad.kt
@@ -18,73 +18,68 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+
 /**
  * Create a quad - two triangles with vertices at (+-0.5,+-0.5).
- * <p>
+ *
  * Mainly to have a simple example showing how to create a Mesh; otherwise a MeshQuad can be perfectly
  * emulated by a MeshRectangles(1,1) or a MeshCubes(1,1,0).
  */
-public class MeshQuad extends MeshBase
-  {
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addVertex( float x, float y, float[] attribs1, float[] attribs2, int index)
+class MeshQuad : MeshBase
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun addVertex(x: Float, y: Float, attribs1: FloatArray, attribs2: FloatArray, index: Int)
     {
-    attribs1[VERT1_ATTRIBS*index + POS_ATTRIB  ] = x-0.5f;
-    attribs1[VERT1_ATTRIBS*index + POS_ATTRIB+1] = 0.5f-y;
-    attribs1[VERT1_ATTRIBS*index + POS_ATTRIB+2] = 0.0f;
+        attribs1[VERT1_ATTRIBS*index+POS_ATTRIB  ] = x-0.5f
+        attribs1[VERT1_ATTRIBS*index+POS_ATTRIB+1] = 0.5f-y
+        attribs1[VERT1_ATTRIBS*index+POS_ATTRIB+2] = 0.0f
 
-    attribs1[VERT1_ATTRIBS*index + NOR_ATTRIB  ] = 0.0f;
-    attribs1[VERT1_ATTRIBS*index + NOR_ATTRIB+1] = 0.0f;
-    attribs1[VERT1_ATTRIBS*index + NOR_ATTRIB+2] = 1.0f;
+        attribs1[VERT1_ATTRIBS*index+NOR_ATTRIB  ] = 0.0f
+        attribs1[VERT1_ATTRIBS*index+NOR_ATTRIB+1] = 0.0f
+        attribs1[VERT1_ATTRIBS*index+NOR_ATTRIB+2] = 1.0f
 
-    attribs2[VERT2_ATTRIBS*index + TEX_ATTRIB  ] = x;
-    attribs2[VERT2_ATTRIBS*index + TEX_ATTRIB+1] = 1.0f-y;
+        attribs2[VERT2_ATTRIBS*index+TEX_ATTRIB  ] = x
+        attribs2[VERT2_ATTRIBS*index+TEX_ATTRIB+1] = 1.0f-y
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  /**
-   * Creates the underlying grid of 4 vertices, normals, inflates and texture coords.
-   */
-  public MeshQuad()
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates the underlying grid of 4 vertices, normals, inflates and texture coords.
+     */
+    constructor() : super()
     {
-    super();
+        val attribs1 = FloatArray(VERT1_ATTRIBS*4)
+        val attribs2 = FloatArray(VERT2_ATTRIBS*4)
 
-    float[] attribs1= new float[VERT1_ATTRIBS*4];
-    float[] attribs2= new float[VERT2_ATTRIBS*4];
+        addVertex(0.0f, 0.0f, attribs1, attribs2, 0)
+        addVertex(1.0f, 0.0f, attribs1, attribs2, 1)
+        addVertex(0.0f, 1.0f, attribs1, attribs2, 2)
+        addVertex(1.0f, 1.0f, attribs1, attribs2, 3)
 
-    addVertex(0.0f,0.0f, attribs1, attribs2, 0);
-    addVertex(1.0f,0.0f, attribs1, attribs2, 1);
-    addVertex(0.0f,1.0f, attribs1, attribs2, 2);
-    addVertex(1.0f,1.0f, attribs1, attribs2, 3);
-
-    setAttribs(attribs1,attribs2);
+        setAttribs(attribs1, attribs2)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public MeshQuad(MeshQuad mesh, boolean deep)
-    {
-    super(mesh,deep);
-    }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshQuad, deep: Boolean) : super(mesh, deep)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshQuad copy(boolean deep)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshQuad
     {
-    return new MeshQuad(this,deep);
+        return MeshQuad(this, deep)
     }
-  }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/mesh/MeshSphere.kt b/src/main/java/org/distorted/library/mesh/MeshSphere.kt
index 65578aa..b8aa0c6 100644
--- a/src/main/java/org/distorted/library/mesh/MeshSphere.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshSphere.kt
@@ -18,273 +18,275 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-import org.distorted.library.main.DistortedLibrary;
+import org.distorted.library.main.DistortedLibrary
+import kotlin.math.cos
+import kotlin.math.sin
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
 /**
  * Create a Mesh which approximates a sphere.
- * <p>
+ *
  * Do so by starting off with a 16-faced solid which is basically a regular dodecahedron with each
  * of its 8 faces vertically split into 2 triangles, and which each step divide each of its triangular
  * faces into smaller and smaller subtriangles and inflate their vertices to lay on the surface or the
  * sphere.
  */
-public class MeshSphere extends MeshBase
-  {
-  private static final int NUMFACES = 16;
-  private static final double P = Math.PI;
-
-  // An array of 16 entries, each describing a single face of a solid in an (admittedly) weird
-  // fashion. Each face is a triangle, with 2 vertices on the same latitude.
-  // Single row is (longitude of V1, longitude of V2, (common) latitude of V1 and V2, latitude of V3)
-  // longitude of V3 is simply midpoint of V1 and V2 so we don't have to specify it here.
-
-  private static final double[][] FACES =      {
-
-      { 0.00*P, 0.25*P, 0.0, 0.5*P },
-      { 0.25*P, 0.50*P, 0.0, 0.5*P },
-      { 0.50*P, 0.75*P, 0.0, 0.5*P },
-      { 0.75*P, 1.00*P, 0.0, 0.5*P },
-      { 1.00*P, 1.25*P, 0.0, 0.5*P },
-      { 1.25*P, 1.50*P, 0.0, 0.5*P },
-      { 1.50*P, 1.75*P, 0.0, 0.5*P },
-      { 1.75*P, 0.00*P, 0.0, 0.5*P },
-
-      { 0.00*P, 0.25*P, 0.0,-0.5*P },
-      { 0.25*P, 0.50*P, 0.0,-0.5*P },
-      { 0.50*P, 0.75*P, 0.0,-0.5*P },
-      { 0.75*P, 1.00*P, 0.0,-0.5*P },
-      { 1.00*P, 1.25*P, 0.0,-0.5*P },
-      { 1.25*P, 1.50*P, 0.0,-0.5*P },
-      { 1.50*P, 1.75*P, 0.0,-0.5*P },
-      { 1.75*P, 0.00*P, 0.0,-0.5*P },
-                                               };
-  private int currentVert;
-  private int numVertices;
+class MeshSphere : MeshBase
+{
+    private var currentVert = 0
+    override var numVertices: Int = 0
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Each of the 16 faces of the solid requires (level*level + 4*level) vertices for the face
-// itself and a join to the next face (which requires 2 vertices). We don't need the join in case
-// of the last, 16th face, thus the -2.
-// (level*level +4*level) because there are level*level little triangles, each requiring new vertex,
-// plus 2 extra vertices to start off a row and 2 to move to the next row (or the next face in case
-// of the last row) and there are 'level' rows.
-
-  private void computeNumberOfVertices(int level)
+    companion object
     {
-    numVertices = NUMFACES*level*(level+4) -2;
-    currentVert = 0;
+        private const val NUMFACES = 16
+        private const val P = Math.PI
+
+        // An array of 16 entries, each describing a single face of a solid in an (admittedly) weird
+        // fashion. Each face is a triangle, with 2 vertices on the same latitude.
+        // Single row is (longitude of V1, longitude of V2, (common) latitude of V1 and V2, latitude of V3)
+        // longitude of V3 is simply midpoint of V1 and V2 so we don't have to specify it here.
+        private val FACES = arrayOf(
+            doubleArrayOf(0.00*P, 0.25*P, 0.0, 0.5*P),
+            doubleArrayOf(0.25*P, 0.50*P, 0.0, 0.5*P),
+            doubleArrayOf(0.50*P, 0.75*P, 0.0, 0.5*P),
+            doubleArrayOf(0.75*P, 1.00*P, 0.0, 0.5*P),
+            doubleArrayOf(1.00*P, 1.25*P, 0.0, 0.5*P),
+            doubleArrayOf(1.25*P, 1.50*P, 0.0, 0.5*P),
+            doubleArrayOf(1.50*P, 1.75*P, 0.0, 0.5*P),
+            doubleArrayOf(1.75*P, 0.00*P, 0.0, 0.5*P),
+
+            doubleArrayOf(0.00*P, 0.25*P, 0.0, -0.5*P),
+            doubleArrayOf(0.25*P, 0.50*P, 0.0, -0.5*P),
+            doubleArrayOf(0.50*P, 0.75*P, 0.0, -0.5*P),
+            doubleArrayOf(0.75*P, 1.00*P, 0.0, -0.5*P),
+            doubleArrayOf(1.00*P, 1.25*P, 0.0, -0.5*P),
+            doubleArrayOf(1.25*P, 1.50*P, 0.0, -0.5*P),
+            doubleArrayOf(1.50*P, 1.75*P, 0.0, -0.5*P),
+            doubleArrayOf(1.75*P, 0.00*P, 0.0, -0.5*P),
+        )
+    }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Each of the 16 faces of the solid requires (level*level + 4*level) vertices for the face
+    // itself and a join to the next face (which requires 2 vertices). We don't need the join in case
+    // of the last, 16th face, thus the -2.
+    // (level*level +4*level) because there are level*level little triangles, each requiring new vertex,
+    // plus 2 extra vertices to start off a row and 2 to move to the next row (or the next face in case
+    // of the last row) and there are 'level' rows.
+    private fun computeNumberOfVertices(level: Int)
+    {
+        numVertices = NUMFACES*level*(level+4)-2
+        currentVert = 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void repeatVertex(float[] attribs1, float[] attribs2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun repeatVertex(attribs1: FloatArray, attribs2: FloatArray)
     {
-    if( currentVert>0 )
-      {
-      attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(currentVert-1) + POS_ATTRIB  ];
-      attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(currentVert-1) + POS_ATTRIB+1];
-      attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(currentVert-1) + POS_ATTRIB+2];
+        if (currentVert>0)
+        {
+            attribs1[VERT1_ATTRIBS*currentVert+POS_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(currentVert-1)+POS_ATTRIB  ]
+            attribs1[VERT1_ATTRIBS*currentVert+POS_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(currentVert-1)+POS_ATTRIB+1]
+            attribs1[VERT1_ATTRIBS*currentVert+POS_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(currentVert-1)+POS_ATTRIB+2]
 
-      attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(currentVert-1) + NOR_ATTRIB  ];
-      attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(currentVert-1) + NOR_ATTRIB+1];
-      attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(currentVert-1) + NOR_ATTRIB+2];
+            attribs1[VERT1_ATTRIBS*currentVert+NOR_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(currentVert-1)+NOR_ATTRIB  ]
+            attribs1[VERT1_ATTRIBS*currentVert+NOR_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(currentVert-1)+NOR_ATTRIB+1]
+            attribs1[VERT1_ATTRIBS*currentVert+NOR_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(currentVert-1)+NOR_ATTRIB+2]
 
-      attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(currentVert-1) + TEX_ATTRIB  ];
-      attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(currentVert-1) + TEX_ATTRIB+1];
+            attribs2[VERT2_ATTRIBS*currentVert+TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(currentVert-1)+TEX_ATTRIB  ]
+            attribs2[VERT2_ATTRIBS*currentVert+TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(currentVert-1)+TEX_ATTRIB+1]
 
-      currentVert++;
-      }
+            currentVert++
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Supposed to return the latitude of the point between two points on the sphere with latitudes
-// lat1 and lat2, so if for example quot=0.2, then it will return the latitude of something 20%
-// along the way from lat1 to lat2.
-//
-// This is approximation only - in general it is of course not true that the midpoint of two points
-// on a unit sphere with spherical coords (A1,B1) and (A2,B2) is ( (A1+A2)/2, (B1+B2)/2 ) - take
-// (0,0) and (PI, epsilon) as a counterexample.
-//
-// Here however, the latitudes we are interested at are the latitudes of the vertices of a regular
-// icosahedron - i.e. +=A and +=PI/2, whose longitudes are close, and we don't really care if the
-// split into smaller triangles is exact.
-//
-// quot better be between 0.0 and 1.0.
-// this is 'directed' i.e. from lat1 to lat2.
-
-  private double midLatitude(double lat1, double lat2, double quot)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Supposed to return the latitude of the point between two points on the sphere with latitudes
+    // lat1 and lat2, so if for example quot=0.2, then it will return the latitude of something 20%
+    // along the way from lat1 to lat2.
+    //
+    // This is approximation only - in general it is of course not true that the midpoint of two points
+    // on a unit sphere with spherical coords (A1,B1) and (A2,B2) is ( (A1+A2)/2, (B1+B2)/2 ) - take
+    // (0,0) and (PI, epsilon) as a counterexample.
+    //
+    // Here however, the latitudes we are interested at are the latitudes of the vertices of a regular
+    // icosahedron - i.e. +=A and +=PI/2, whose longitudes are close, and we don't really care if the
+    // split into smaller triangles is exact.
+    //
+    // quot better be between 0.0 and 1.0.
+    // this is 'directed' i.e. from lat1 to lat2.
+    private fun midLatitude(lat1: Double, lat2: Double, quot: Double): Double
     {
-    return lat1*(1.0-quot)+lat2*quot;
+        return lat1*(1.0-quot)+lat2*quot
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Same in case of longitude. This is for our needs exact, because we are ever only calling this with
-// two longitudes of two vertices with the same latitude. Additional problem: things can wrap around
-// the circle.
-// this is 'undirected' i.e. we don't assume from lon1 to lon2 - just along the smaller arc joining
-// lon1 to lon2.
-
-  private double midLongitude(double lon1, double lon2, double quot)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Same in case of longitude. This is for our needs exact, because we are ever only calling this with
+    // two longitudes of two vertices with the same latitude. Additional problem: things can wrap around
+    // the circle.
+    // this is 'undirected' i.e. we don't assume from lon1 to lon2 - just along the smaller arc joining
+    // lon1 to lon2.
+    private fun midLongitude(lon1: Double, lon2: Double, quot: Double): Double
     {
-    double min, max;
+        var min: Double
+        val max: Double
 
-    if( lon1<lon2 ) { min=lon1; max=lon2; }
-    else            { min=lon2; max=lon1; }
+        if (lon1<lon2)
+        {
+            min = lon1
+            max = lon2
+        }
+        else
+        {
+            min = lon2
+            max = lon1
+        }
 
-    double diff = max-min;
-    if( diff>P ) { diff=2*P-diff; min=max; }
+        var diff = max-min
+        if (diff>P)
+        {
+            diff = 2*P-diff
+            min = max
+        }
 
-    double ret = min+quot*diff;
-    if( ret>=2*P ) ret-=2*P;
+        var ret = min+quot*diff
+        if (ret>=2*P) ret -= 2*P
 
-    return ret;
+        return ret
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// linear map (column,row, level):
-//
-// (      0,       0, level) -> (lonV1,latV12)
-// (      0, level-1, level) -> (lonV3,latV3 )
-// (level-1,       0, level) -> (lonV2,latV12)
-
-  private void addVertex(float[] attribs1, float[] attribs2, int column, int row, int level,
-                         double lonV1, double lonV2, double latV12, double latV3)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // linear map (column,row, level):
+    //
+    // (      0,       0, level) -> (lonV1,latV12)
+    // (      0, level-1, level) -> (lonV3,latV3 )
+    // (level-1,       0, level) -> (lonV2,latV12)
+    private fun addVertex(attribs1: FloatArray, attribs2: FloatArray, column: Int, row: Int, level: Int,
+                          lonV1: Double, lonV2: Double, latV12: Double, latV3: Double)
     {
-    double quotX = (double)column/level;
-    double quotY = (double)row   /level;
-    double quotZ;
-
-    if( latV12*latV3 < 0.0 )  // equatorial triangle
-      {
-      quotZ = quotX + 0.5*quotY;
-      }
-    else                      // polar triangle
-      {
-      quotZ = (quotY==1.0 ? 0.5 : quotX / (1.0-quotY));
-      }
-
-    double longitude = midLongitude(lonV1, lonV2, quotZ );
-    double latitude  = midLatitude(latV12, latV3, quotY );
-
-    double sinLON = Math.sin(longitude);
-    double cosLON = Math.cos(longitude);
-    double sinLAT = Math.sin(latitude);
-    double cosLAT = Math.cos(latitude);
-
-    float x = (float)(cosLAT*sinLON / 2.0f);
-    float y = (float)(sinLAT        / 2.0f);
-    float z = (float)(cosLAT*cosLON / 2.0f);
-
-    double texX = 0.5 + longitude/(2*P);
-    if( texX>=1.0 ) texX-=1.0;
-
-    double texY = 0.5 + latitude/P;
-
-    attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB  ] = x;  //
-    attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB+1] = y;  //
-    attribs1[VERT1_ATTRIBS*currentVert + POS_ATTRIB+2] = z;  //
-                                                             //  In case of this Mesh so it happens that
-    attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB  ] = 2*x;//  the vertex coords, normal vector, and
-    attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB+1] = 2*y;//  inflate vector have identical (x,y,z).
-    attribs1[VERT1_ATTRIBS*currentVert + NOR_ATTRIB+2] = 2*z;//
-
-    attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB  ] = (float)texX;
-    attribs2[VERT2_ATTRIBS*currentVert + TEX_ATTRIB+1] = (float)texY;
-
-    currentVert++;
-
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // Problem: on the 'change of date' line in the back of the sphere, some triangles see texX
-    // coords suddenly jump from 1-epsilon to 0, which looks like a seam with a narrow copy of
-    // the whole texture there. Solution: remap texX to 1.0.
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-
-    if( currentVert>=3 && texX==0.0 )
-      {
-      double tex1 = attribs2[VERT2_ATTRIBS*(currentVert-2) + TEX_ATTRIB];
-      double tex2 = attribs2[VERT2_ATTRIBS*(currentVert-3) + TEX_ATTRIB];
-
-      // if the triangle is not degenerate and last vertex was on the western hemisphere
-      if( tex1!=tex2 && tex1>0.5 )
+        val quotX = column.toDouble()/level
+        val quotY = row.toDouble()/level
+
+        val quotZ = if (latV12*latV3<0.0)  // equatorial triangle
         {
-        attribs2[VERT2_ATTRIBS*(currentVert-1) + TEX_ATTRIB] = 1.0f;
+            quotX+0.5*quotY
         }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void buildFace(float[] attribs1, float[] attribs2, int level, int face, double lonV1, double lonV2, double latV12, double latV3)
-    {
-    for(int row=0; row<level; row++)
-      {
-      for (int column=0; column<level-row; column++)
+        else  // polar triangle
         {
-        addVertex(attribs1, attribs2, column, row  , level, lonV1, lonV2, latV12, latV3);
-        if (column==0 && !(face==0 && row==0 ) ) repeatVertex(attribs1, attribs2);
-        addVertex(attribs1, attribs2, column, row+1, level, lonV1, lonV2, latV12, latV3);
+            (if (quotY==1.0) 0.5 else quotX/(1.0-quotY))
         }
 
-      addVertex(attribs1, attribs2, level-row, row , level, lonV1, lonV2, latV12, latV3);
-      if( row!=level-1 || face!=NUMFACES-1 ) repeatVertex(attribs1, attribs2);
-      }
-    }
+        val longitude = midLongitude(lonV1, lonV2, quotZ)
+        val latitude = midLatitude(latV12, latV3, quotY)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  /**
-   * Creates the underlying grid of vertices with the usual attribs which approximates a sphere.
-   * <p>
-   * When level=1, it outputs the 12 vertices of a regular icosahedron.
-   * When level=N, it divides each of the 20 icosaherdon's triangular faces into N^2 smaller triangles
-   * (by dividing each side into N equal segments) and 'inflates' the internal vertices so that they
-   * touch the sphere.
-   *
-   * @param level Specifies the approximation level. Valid values: level &ge; 1
-   */
-  public MeshSphere(int level)
-    {
-    super();
+        val sinLON = sin(longitude)
+        val cosLON = cos(longitude)
+        val sinLAT = sin(latitude)
+        val cosLAT = cos(latitude)
+
+        val x = (cosLAT*sinLON/2.0f).toFloat()
+        val y = (sinLAT/2.0f).toFloat()
+        val z = (cosLAT*cosLON/2.0f).toFloat()
 
-    computeNumberOfVertices(level);
-    float[] attribs1= new float[VERT1_ATTRIBS*numVertices];
-    float[] attribs2= new float[VERT2_ATTRIBS*numVertices];
+        var texX = 0.5+longitude/(2*P)
+        if (texX>=1.0) texX -= 1.0
 
-    for(int face=0; face<NUMFACES; face++ )
-      {
-      buildFace(attribs1, attribs2, level, face, FACES[face][0], FACES[face][1], FACES[face][2], FACES[face][3]);
-      }
+        val texY = 0.5+latitude/P
 
-    if( currentVert!=numVertices )
-      DistortedLibrary.logMessage("MeshSphere: currentVert= " +currentVert+" numVertices="+numVertices );
+        attribs1[VERT1_ATTRIBS*currentVert+POS_ATTRIB  ] = x   //
+        attribs1[VERT1_ATTRIBS*currentVert+POS_ATTRIB+1] = y   //
+        attribs1[VERT1_ATTRIBS*currentVert+POS_ATTRIB+2] = z   //
+                                                               //  In case of this Mesh so it happens that
+        attribs1[VERT1_ATTRIBS*currentVert+NOR_ATTRIB  ] = 2*x //  the vertex coords, normal vector, and
+        attribs1[VERT1_ATTRIBS*currentVert+NOR_ATTRIB+1] = 2*y //  inflate vector have identical (x,y,z).
+        attribs1[VERT1_ATTRIBS*currentVert+NOR_ATTRIB+2] = 2*z //
 
-    setAttribs(attribs1, attribs2);
+        attribs2[VERT2_ATTRIBS*currentVert+TEX_ATTRIB  ] = texX.toFloat()
+        attribs2[VERT2_ATTRIBS*currentVert+TEX_ATTRIB+1] = texY.toFloat()
+
+        currentVert++
+
+        ////////////////////////////////////////////////////////////////////////////////////////////////
+        // Problem: on the 'change of date' line in the back of the sphere, some triangles see texX
+        // coords suddenly jump from 1-epsilon to 0, which looks like a seam with a narrow copy of
+        // the whole texture there. Solution: remap texX to 1.0.
+        ////////////////////////////////////////////////////////////////////////////////////////////////
+        if (currentVert>=3&&texX==0.0)
+        {
+            val tex1 = attribs2[VERT2_ATTRIBS*(currentVert-2)+TEX_ATTRIB].toDouble()
+            val tex2 = attribs2[VERT2_ATTRIBS*(currentVert-3)+TEX_ATTRIB].toDouble()
+
+            // if the triangle is not degenerate and last vertex was on the western hemisphere
+            if (tex1!=tex2&&tex1>0.5)
+            {
+                attribs2[VERT2_ATTRIBS*(currentVert-1)+TEX_ATTRIB] = 1.0f
+            }
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy cconstructor.
- */
-  public MeshSphere(MeshSphere mesh, boolean deep)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildFace(attribs1: FloatArray, attribs2: FloatArray, level: Int, face: Int, lonV1: Double, lonV2: Double, latV12: Double, latV3: Double)
     {
-    super(mesh,deep);
+        for (row in 0 until level)
+        {
+            for (column in 0 until level-row)
+            {
+                addVertex(attribs1, attribs2, column, row, level, lonV1, lonV2, latV12, latV3)
+                if (column==0&&!(face==0&&row==0)) repeatVertex(attribs1, attribs2)
+                addVertex(attribs1, attribs2, column, row+1, level, lonV1, lonV2, latV12, latV3)
+            }
+
+            addVertex(attribs1, attribs2, level-row, row, level, lonV1, lonV2, latV12, latV3)
+            if (row!=level-1||face!=NUMFACES-1) repeatVertex(attribs1, attribs2)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshSphere copy(boolean deep)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates the underlying grid of vertices with the usual attribs which approximates a sphere.
+     *
+     *
+     * When level=1, it outputs the 12 vertices of a regular icosahedron.
+     * When level=N, it divides each of the 20 icosaherdon's triangular faces into N^2 smaller triangles
+     * (by dividing each side into N equal segments) and 'inflates' the internal vertices so that they
+     * touch the sphere.
+     *
+     * @param level Specifies the approximation level. Valid values: level  1
+     */
+    constructor(level: Int) : super()
+    {
+        computeNumberOfVertices(level)
+        val attribs1 = FloatArray(VERT1_ATTRIBS*numVertices)
+        val attribs2 = FloatArray(VERT2_ATTRIBS*numVertices)
+
+        for (face in 0 until NUMFACES)
+        {
+            buildFace(attribs1, attribs2, level, face, FACES[face][0], FACES[face][1], FACES[face][2], FACES[face][3])
+        }
+
+        if (currentVert!=numVertices) DistortedLibrary.logMessage("MeshSphere: currentVert= $currentVert numVertices=$numVertices")
+
+        setAttribs(attribs1, attribs2)
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy cconstructor.
+     */
+    constructor(mesh: MeshSphere, deep: Boolean) : super(mesh, deep)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshSphere
     {
-    return new MeshSphere(this,deep);
+        return MeshSphere(this, deep)
     }
-  }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/mesh/MeshSquare.kt b/src/main/java/org/distorted/library/mesh/MeshSquare.kt
index 41566ca..1e5d58e 100644
--- a/src/main/java/org/distorted/library/mesh/MeshSquare.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshSquare.kt
@@ -18,235 +18,229 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-import org.distorted.library.main.DistortedLibrary;
+import org.distorted.library.main.DistortedLibrary
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
 /**
  * Create a flat, rectangular grid.
- * <p>
+ *
  * Perfect if you just want to display a flat Texture. If you are not planning to apply any VERTEX
  * effects to it, use MeshRectangles(1,1), i.e. a Quad. Otherwise, create more vertices for more realistic effects!
  */
-public class MeshSquare extends MeshBase
-  {
-  private int mCols, mRows;
-  private int remainingVert;
-  private int numVertices;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Create a flat, full grid.
-
-  private void computeNumberOfVertices(int cols, int rows)
-     {
-     mRows=rows;
-     mCols=cols;
-
-     if( cols==1 && rows==1 )
-       {
-       numVertices = 4;
-       }
-     else
-       {
-       numVertices = 2*( mRows*mCols +2*mRows - 1) +2*(mCols>=2 ? mRows:0) +
-                     (mCols>=2 && mRows>=2 ? 2*mRows-2 : 1);
-       }
-
-     remainingVert = numVertices;
-     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int addVertex(int vertex, float x, float y, float[] attribs1, float[] attribs2)
-     {
-     remainingVert--;
-
-     attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB  ] =    x;
-     attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+1] =   -y;
-     attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+2] = 0.0f;
+class MeshSquare : MeshBase
+{
+    private var mCols = 0
+    private var mRows = 0
+    private var remainingVert = 0
+    override var numVertices: Int = 0
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // Create a flat, full grid.
+    private fun computeNumberOfVertices(cols: Int, rows: Int)
+    {
+        mRows = rows
+        mCols = cols
+
+        numVertices = if (cols==1&&rows==1)
+        {
+            4
+        }
+        else
+        {
+            2*(mRows*mCols+2*mRows-1)+2*(if (mCols>=2) mRows else 0)+(if (mCols>=2&&mRows>=2) 2*mRows-2 else 1)
+        }
+
+        remainingVert = numVertices
+    }
 
-     attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB  ] = 0.0f;
-     attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB+1] = 0.0f;
-     attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB+2] = 1.0f;
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun addVertex(vertex: Int, x: Float, y: Float, attribs1: FloatArray, attribs2: FloatArray): Int
+    {
+        remainingVert--
 
-     attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = x+0.5f;
-     attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = 0.5f-y;
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB  ] = x
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+1] = -y
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+2] = 0.0f
 
-     return vertex+1;
-     }
+        attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB  ] = 0.0f
+        attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB+1] = 0.0f
+        attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB+2] = 1.0f
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB  ] = x+0.5f
+        attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB+1] = 0.5f-y
 
-  private int repeatLast(int vertex, float[] attribs1, float[] attribs2)
-     {
-     if( vertex>0 )
-       {
-       remainingVert--;
+        return vertex+1
+    }
 
-       attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(vertex-1) + POS_ATTRIB  ];
-       attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(vertex-1) + POS_ATTRIB+1];
-       attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(vertex-1) + POS_ATTRIB+2];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun repeatLast(vert: Int, attribs1: FloatArray, attribs2: FloatArray): Int
+    {
+        var vertex = vert
 
-       attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(vertex-1) + NOR_ATTRIB  ];
-       attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(vertex-1) + NOR_ATTRIB+1];
-       attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(vertex-1) + NOR_ATTRIB+2];
+        if (vertex>0)
+        {
+            remainingVert--
 
-       attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(vertex-1) + TEX_ATTRIB  ];
-       attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(vertex-1) + TEX_ATTRIB+1];
+            attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(vertex-1)+POS_ATTRIB]
+            attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(vertex-1)+POS_ATTRIB+1]
+            attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(vertex-1)+POS_ATTRIB+2]
 
-       vertex++;
-       }
+            attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB  ] = attribs1[VERT1_ATTRIBS*(vertex-1)+NOR_ATTRIB]
+            attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB+1] = attribs1[VERT1_ATTRIBS*(vertex-1)+NOR_ATTRIB+1]
+            attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB+2] = attribs1[VERT1_ATTRIBS*(vertex-1)+NOR_ATTRIB+2]
 
-     return vertex;
-     }
+            attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB  ] = attribs2[VERT2_ATTRIBS*(vertex-1)+TEX_ATTRIB  ]
+            attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB+1] = attribs2[VERT2_ATTRIBS*(vertex-1)+TEX_ATTRIB+1]
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            vertex++
+        }
 
-  private void buildGrid(float[] attribs1, float[] attribs2)
-     {
-     boolean currBlockIsNE, lastBlockIsNE = false;
-     int vertex = 0;
-     float x,y;
-     final float dx = 1.0f/mCols;
-     final float dy = 1.0f/mRows;
-
-     y =-0.5f;
-
-     for(int row=0; row<mRows; row++)
-       {
-       x =-0.5f;
-
-       for(int col=0; col<mCols; col++)
-         {
-         currBlockIsNE = (2*row<=mRows-1)^(2*col<=mCols-1);
-
-         if( col==0 || (lastBlockIsNE^currBlockIsNE) )
-           {
-           if( row!=0 && col==0 ) vertex = repeatLast(vertex,attribs1,attribs2);
-           vertex= addVertex( vertex, x, y+(currBlockIsNE?0:dy), attribs1,attribs2);
-           if( row!=0 && col==0 ) vertex = repeatLast(vertex,attribs1,attribs2);
-           if( lastBlockIsNE^currBlockIsNE)  vertex = repeatLast(vertex,attribs1,attribs2);
-           vertex= addVertex( vertex, x, y+(currBlockIsNE?dy:0), attribs1,attribs2);
-           }
-         vertex= addVertex( vertex, x+dx, y+(currBlockIsNE?0:dy), attribs1,attribs2);
-         vertex= addVertex( vertex, x+dx, y+(currBlockIsNE?dy:0), attribs1,attribs2);
-
-         lastBlockIsNE = currBlockIsNE;
-         x+=dx;
-         }
-
-       y+=dy;
-       }
-     }
+        return vertex
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildGrid(attribs1: FloatArray, attribs2: FloatArray)
+    {
+        var currBlockIsNE: Boolean
+        var lastBlockIsNE = false
+        var vertex = 0
+        var x: Float
+        val dx = 1.0f/mCols
+        val dy = 1.0f/mRows
+
+        var y = -0.5f
+
+        for (row in 0 until mRows)
+        {
+            x = -0.5f
+
+            for (col in 0 until mCols)
+            {
+                currBlockIsNE = (2*row<=mRows-1) xor (2*col<=mCols-1)
+
+                if (col==0||(lastBlockIsNE xor currBlockIsNE))
+                {
+                    if (row!=0&&col==0) vertex = repeatLast(vertex, attribs1, attribs2)
+                    vertex = addVertex(vertex, x, y+(if (currBlockIsNE) 0f else dy), attribs1, attribs2)
+                    if (row!=0&&col==0) vertex = repeatLast(vertex, attribs1, attribs2)
+                    if (lastBlockIsNE xor currBlockIsNE) vertex = repeatLast(vertex, attribs1, attribs2)
+                    vertex = addVertex(vertex, x, y+(if (currBlockIsNE) dy else 0f), attribs1, attribs2)
+                }
+                vertex = addVertex(vertex, x+dx, y+(if (currBlockIsNE) 0f else dy), attribs1, attribs2)
+                vertex = addVertex(vertex, x+dx, y+(if (currBlockIsNE) dy else 0f), attribs1, attribs2)
+
+                lastBlockIsNE = currBlockIsNE
+                x += dx
+            }
+
+            y += dy
+        }
+    }
 
-  private void buildGrid(float[] attribs1, float[] attribs2, float[] xLoc, float[] yLoc)
-     {
-     boolean currBlockIsNE,lastBlockIsNE = false;
-     int vertex = 0;
-     float dx,dy,x,y= yLoc[0];
-
-     for(int row=0; row<mRows; row++)
-       {
-       x = xLoc[0];
-       dy= yLoc[row+1];
-
-       for(int col=0; col<mCols; col++)
-         {
-         dx = xLoc[col+1];
-         currBlockIsNE = (2*row<=mRows-1)^(2*col<=mCols-1);
-
-         if( col==0 || (lastBlockIsNE^currBlockIsNE) )
-           {
-           if( row!=0 && col==0 ) vertex = repeatLast(vertex,attribs1,attribs2);
-           vertex= addVertex( vertex, x, y+(currBlockIsNE?0:dy), attribs1,attribs2);
-           if( row!=0 && col==0 ) vertex = repeatLast(vertex,attribs1,attribs2);
-           if( lastBlockIsNE^currBlockIsNE)  vertex = repeatLast(vertex,attribs1,attribs2);
-           vertex= addVertex( vertex, x, y+(currBlockIsNE?dy:0), attribs1,attribs2);
-           }
-         vertex= addVertex( vertex, x+dx, y+(currBlockIsNE?0:dy), attribs1,attribs2);
-         vertex= addVertex( vertex, x+dx, y+(currBlockIsNE?dy:0), attribs1,attribs2);
-
-         lastBlockIsNE = currBlockIsNE;
-         x+=dx;
-         }
-
-       y+=dy;
-       }
-     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildGrid(attribs1: FloatArray, attribs2: FloatArray, xLoc: FloatArray, yLoc: FloatArray)
+    {
+        var currBlockIsNE: Boolean
+        var lastBlockIsNE = false
+        var vertex = 0
+        var dx: Float
+        var dy: Float
+        var x: Float
+        var y = yLoc[0]
+
+        for (row in 0 until mRows)
+        {
+            x = xLoc[0]
+            dy = yLoc[row+1]
+
+            for (col in 0 until mCols)
+            {
+                dx = xLoc[col+1]
+                currBlockIsNE = (2*row<=mRows-1) xor (2*col<=mCols-1)
+
+                if (col==0||(lastBlockIsNE xor currBlockIsNE))
+                {
+                    if (row!=0&&col==0) vertex = repeatLast(vertex, attribs1, attribs2)
+                    vertex = addVertex(vertex, x, y+(if (currBlockIsNE) 0f else dy), attribs1, attribs2)
+                    if (row!=0&&col==0) vertex = repeatLast(vertex, attribs1, attribs2)
+                    if (lastBlockIsNE xor currBlockIsNE) vertex = repeatLast(vertex, attribs1, attribs2)
+                    vertex = addVertex(vertex, x, y+(if (currBlockIsNE) dy else 0f), attribs1, attribs2)
+                }
+                vertex = addVertex(vertex, x+dx, y+(if (currBlockIsNE) 0f else dy), attribs1, attribs2)
+                vertex = addVertex(vertex, x+dx, y+(if (currBlockIsNE) dy else 0f), attribs1, attribs2)
+
+                lastBlockIsNE = currBlockIsNE
+                x += dx
+            }
+
+            y += dy
+        }
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Creates a rectangular grid of vertices, normals and texture coords.
- *
- * @param cols Number of columns in the grid.
- * @param rows Number of rows in the grid.
- */
-  public MeshSquare(int cols, int rows)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates a rectangular grid of vertices, normals and texture coords.
+     *
+     * @param cols Number of columns in the grid.
+     * @param rows Number of rows in the grid.
+     */
+    constructor(cols: Int, rows: Int) : super()
     {
-    super();
-    computeNumberOfVertices(cols,rows);
+        computeNumberOfVertices(cols, rows)
 
-    float[] attribs1= new float[VERT1_ATTRIBS*numVertices];
-    float[] attribs2= new float[VERT2_ATTRIBS*numVertices];
+        val attribs1 = FloatArray(VERT1_ATTRIBS*numVertices)
+        val attribs2 = FloatArray(VERT2_ATTRIBS*numVertices)
 
-    buildGrid(attribs1,attribs2);
+        buildGrid(attribs1, attribs2)
 
-    if( remainingVert!=0 )
-      DistortedLibrary.logMessage("MeshSquare: remainingVert " +remainingVert );
+        if (remainingVert!=0) DistortedLibrary.logMessage("MeshSquare: remainingVert $remainingVert")
 
-    setAttribs(attribs1,attribs2);
+        setAttribs(attribs1, attribs2)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Creates a rectangular grid of vertices, normals and texture coords.
- *
- * @param xLoc list of x-coordinates of vertices. First value: distance of the left edge from 0.
- *             Next values: distance of the next column from the previous. Must be NonNull!
- * @param yLoc list of y-coordinates of vertices. First value: distance of the bottom edge from 0.
- *             Next values: distance of the next row from the previous. Must be NonNull!
- */
-  public MeshSquare(float[] xLoc, float[] yLoc)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates a rectangular grid of vertices, normals and texture coords.
+     *
+     * @param xLoc list of x-coordinates of vertices. First value: distance of the left edge from 0.
+     * Next values: distance of the next column from the previous. Must be NonNull!
+     * @param yLoc list of y-coordinates of vertices. First value: distance of the bottom edge from 0.
+     * Next values: distance of the next row from the previous. Must be NonNull!
+     */
+    constructor(xLoc: FloatArray, yLoc: FloatArray) : super()
     {
-    super();
-    computeNumberOfVertices(xLoc.length-1,yLoc.length-1);
+        computeNumberOfVertices(xLoc.size-1, yLoc.size-1)
 
-    float[] attribs1= new float[VERT1_ATTRIBS*numVertices];
-    float[] attribs2= new float[VERT2_ATTRIBS*numVertices];
+        val attribs1 = FloatArray(VERT1_ATTRIBS*numVertices)
+        val attribs2 = FloatArray(VERT2_ATTRIBS*numVertices)
 
-    buildGrid(attribs1,attribs2,xLoc,yLoc);
+        buildGrid(attribs1, attribs2, xLoc, yLoc)
 
-    if( remainingVert!=0 )
-      DistortedLibrary.logMessage("MeshSquare: remainingVert " +remainingVert );
+        if (remainingVert!=0) DistortedLibrary.logMessage("MeshSquare: remainingVert $remainingVert")
 
-    setAttribs(attribs1,attribs2);
+        setAttribs(attribs1, attribs2)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public MeshSquare(MeshSquare mesh, boolean deep)
-    {
-    super(mesh,deep);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshSquare copy(boolean deep)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshSquare, deep: Boolean) : super(mesh, deep)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshSquare
     {
-    return new MeshSquare(this,deep);
+        return MeshSquare(this, deep)
     }
- }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/mesh/MeshTriangle.kt b/src/main/java/org/distorted/library/mesh/MeshTriangle.kt
index 386ef3c..0db6ce7 100644
--- a/src/main/java/org/distorted/library/mesh/MeshTriangle.kt
+++ b/src/main/java/org/distorted/library/mesh/MeshTriangle.kt
@@ -18,129 +18,121 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.mesh;
+package org.distorted.library.mesh
 
-import org.distorted.library.main.DistortedLibrary;
+import org.distorted.library.main.DistortedLibrary
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
 /**
  * Create a Mesh which approximates a triangle with vertices at (-0.5,-0.5),(+0.5,-0.5),(0.0,0.5)
  */
-public class MeshTriangle extends MeshBase
-  {
-  private int numVertices;
-  private int remainingVert;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private int addVertex(int vertex, float x, float y, float[] attribs1, float[] attribs2)
-     {
-     remainingVert--;
+class MeshTriangle : MeshBase
+{
+    override var numVertices: Int = 0
+    private var remainingVert = 0
 
-     attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB  ] = x-0.5f;
-     attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+1] = y-0.5f;
-     attribs1[VERT1_ATTRIBS*vertex + POS_ATTRIB+2] = 0.0f;
-
-     attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB  ] = 0.0f;
-     attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB+1] = 0.0f;
-     attribs1[VERT1_ATTRIBS*vertex + NOR_ATTRIB+2] = 1.0f;
-
-     attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB  ] = x;
-     attribs2[VERT2_ATTRIBS*vertex + TEX_ATTRIB+1] = y;
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun addVertex(vertex: Int, x: Float, y: Float, attribs1: FloatArray, attribs2: FloatArray): Int
+    {
+        remainingVert--
 
-     return vertex+1;
-     }
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB  ] = x-0.5f
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+1] = y-0.5f
+        attribs1[VERT1_ATTRIBS*vertex+POS_ATTRIB+2] = 0.0f
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB  ] = 0.0f
+        attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB+1] = 0.0f
+        attribs1[VERT1_ATTRIBS*vertex+NOR_ATTRIB+2] = 1.0f
 
-  private void computeNumberOfVertices(int level)
-     {
-     numVertices = level*(level+2);
-     remainingVert = numVertices;
-     }
+        attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB  ] = x
+        attribs2[VERT2_ATTRIBS*vertex+TEX_ATTRIB+1] = y
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        return vertex+1
+    }
 
-  private int buildRow(int vertex,float sx, float sy, int length, float dx, float dy, float[] attribs1, float[] attribs2)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun computeNumberOfVertices(level: Int)
     {
-    for(int i=0; i<length; i++)
-      {
-      vertex = addVertex(vertex, sx   , sy   , attribs1, attribs2);
-      vertex = addVertex(vertex, sx+dx, sy+dy, attribs1, attribs2);
-      sx += 2*dx;
-      }
-
-    vertex = addVertex(vertex, sx, sy, attribs1, attribs2);
-
-    return vertex;
+        numVertices = level*(level+2)
+        remainingVert = numVertices
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void buildGrid(float[] attribs1, float[] attribs2, int level)
-     {
-     float sx = 0.0f;
-     float sy = 0.0f;
-     float dx = 0.5f/level;
-     float dy = 1.0f/level;
-     int vertex = 0;
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildRow(vert: Int, sx: Float, sy: Float, length: Int, dx: Float, dy: Float, attribs1: FloatArray, attribs2: FloatArray): Int
+    {
+        var vertex = vert
+        var sxLocal = sx
 
-     for(int row=level; row>=1; row--)
-       {
-       vertex = buildRow(vertex,sx,sy,row,dx,dy,attribs1,attribs2);
+        for (i in 0 until length)
+        {
+            vertex = addVertex(vertex, sxLocal, sy, attribs1, attribs2)
+            vertex = addVertex(vertex, sxLocal+dx, sy+dy, attribs1, attribs2)
+            sxLocal += 2*dx
+        }
 
-       sx += 2*dx*(row-0.5f);
-       sy += dy;
-       dx *= -1;
-       }
-     }
+        vertex = addVertex(vertex, sxLocal, sy, attribs1, attribs2)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  /**
-   * Creates the underlying grid of vertices with the usual attribs which approximates a triangle
-   * with vertices at (-0.5,-0.5),(+0.5,-0.5),(0.0,0.5)
-   *
-   * @param level Specifies the level of slicing. Valid values: level &ge; 1
-   */
-  public MeshTriangle(int level)
-     {
-     super();
+        return vertex
+    }
 
-     computeNumberOfVertices(level);
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    private fun buildGrid(attribs1: FloatArray, attribs2: FloatArray, level: Int)
+    {
+        var sx = 0.0f
+        var sy = 0.0f
+        var dx = 0.5f/level
+        val dy = 1.0f/level
+        var vertex = 0
+
+        for (row in level downTo 1)
+        {
+            vertex = buildRow(vertex, sx, sy, row, dx, dy, attribs1, attribs2)
+
+            sx += 2*dx*(row-0.5f)
+            sy += dy
+            dx *= -1f
+        }
+    }
 
-     float[] attribs1= new float[VERT1_ATTRIBS*numVertices];
-     float[] attribs2= new float[VERT2_ATTRIBS*numVertices];
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC API
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Creates the underlying grid of vertices with the usual attribs which approximates a triangle
+     * with vertices at (-0.5,-0.5),(+0.5,-0.5),(0.0,0.5)
+     *
+     * @param level Specifies the level of slicing. Valid values: level  1
+     */
+    constructor(level: Int) : super()
+    {
+        computeNumberOfVertices(level)
 
-     buildGrid(attribs1, attribs2, level);
+        val attribs1 = FloatArray(VERT1_ATTRIBS*numVertices)
+        val attribs2 = FloatArray(VERT2_ATTRIBS*numVertices)
 
-     if( remainingVert!=0 )
-       DistortedLibrary.logMessage("MeshTriangle: remainingVert " +remainingVert );
+        buildGrid(attribs1, attribs2, level)
 
-     setAttribs(attribs1, attribs2);
-     }
+        if (remainingVert!=0) DistortedLibrary.logMessage("MeshTriangle: remainingVert $remainingVert")
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy constructor.
- */
-  public MeshTriangle(MeshTriangle mesh, boolean deep)
-    {
-    super(mesh,deep);
+        setAttribs(attribs1, attribs2)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Copy the Mesh.
- *
- * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
- *             normals and inflates (the rest, in particular the mVertAttribs2 containing texture
- *             coordinates and effect associations, is always deep copied)
- */
-  public MeshTriangle copy(boolean deep)
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy constructor.
+     */
+    constructor(mesh: MeshTriangle, deep: Boolean) : super(mesh, deep)
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * Copy the Mesh.
+     *
+     * @param deep If to be a deep or shallow copy of mVertAttribs1, i.e. the array holding vertices,
+     * normals and inflates (the rest, in particular the mVertAttribs2 containing texture
+     * coordinates and effect associations, is always deep copied)
+     */
+    override fun copy(deep: Boolean): MeshTriangle
     {
-    return new MeshTriangle(this,deep);
+        return MeshTriangle(this, deep)
     }
-  }
+}
