commit 9ae0853e12327e2ec2d80c9997eb88758923aaea
Author: leszek <leszek@koltunski.pl>
Date:   Wed Apr 23 00:26:07 2025 +0200

    transition the 'effectqueue' package.

diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
index 2f5eb06..80ac635 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueue.kt
@@ -18,509 +18,460 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.effectqueue;
-
-import org.distorted.library.effect.Effect;
-import org.distorted.library.effect.EffectName;
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.main.DistortedLibrary;
-import org.distorted.library.main.InternalMaster;
-import org.distorted.library.main.InternalStackFrameList;
-import org.distorted.library.uniformblock.UniformBlockFloatUniforms;
-import org.distorted.library.uniformblock.UniformBlockIntUniforms;
-
-import java.util.ArrayList;
-import java.util.HashMap;
+package org.distorted.library.effectqueue
+
+import org.distorted.library.effect.Effect
+import org.distorted.library.effect.EffectName
+import org.distorted.library.effect.EffectType
+import org.distorted.library.main.DistortedLibrary
+import org.distorted.library.main.InternalMaster
+import org.distorted.library.main.InternalMaster.Slave
+import org.distorted.library.main.InternalStackFrameList
+import org.distorted.library.uniformblock.UniformBlockFloatUniforms
+import org.distorted.library.uniformblock.UniformBlockIntUniforms
+import kotlin.math.max
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document
- *
- * @y.exclude
- */
-public abstract class EffectQueue implements InternalMaster.Slave
-  {
-  public static final int MAIN_VARIANTS = 4; // Number of Main program variants (ATM 4: MAIN, MAIN OIT, PREPROCESS, FULL)
-
-  static final int VERT_INT_UBO_BINDING = 5;
-  static final int VERT_FLO_UBO_BINDING = 6;
-
-  private static final int CREATE = 0;
-  private static final int ATTACH = 1;
-  private static final int DETACH = 2;
-  private static final int DETALL = 3;
-
-  int mNumEffects;              // 'ToBe' will be more than mNumEffects if doWork() hasn't
-  private int mNumEffectsToBe;  // added them yet (or less if it hasn't removed some yet)
-  Effect[] mEffects;
-
-  UniformBlockFloatUniforms mUBF;
-  UniformBlockIntUniforms mUBI;
-
-  private long mID;
-  private final int mIndex;
-  private boolean mCreated;
-
-  private static class Job
+/** Not part of public API, do not document
+*
+* @y.exclude
+*/
+abstract class EffectQueue : Slave
+{
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    var numEffects: Int = 0     // 'ToBe' will be more than mNumEffects if doWork() hasn't
+    var numEffectsToBe: Int = 0 // added them yet (or less if it hasn't removed some yet)
+        private set
+    var mEffects: Array<Effect?>
+    var mUBF: UniformBlockFloatUniforms? = null
+    var mUBI: UniformBlockIntUniforms? = null
+    var iD: Long = 0
+        private set
+    private val mIndex: Int
+    private var mCreated = false
+
+    private class Job(var type: Int, var num1: Int, var num2: Int, var bool: Boolean, e: Effect?)
     {
-    int type;
-    int num1, num2;
-    boolean bool;
-    Effect effect;
-
-    Job(int t, int m1, int m2, boolean n, Effect e)
-      {
-      type  = t;
-      num1  = m1;
-      num2  = m2;
-      bool  = n;
-      effect= e;
-      }
+        var effect: Effect = e!!
     }
 
-  private final ArrayList<Job> mJobs;
+    private val mJobs: ArrayList<Job>
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  EffectQueue(int numFloatUniforms, int numIntUniforms, boolean useUBO, int index)
+    companion object
     {
-    mCreated        = false;
-    mID             = 0;
-    mNumEffects     = 0;
-    mNumEffectsToBe = 0;
-    mIndex          = index;
-
-    mJobs = new ArrayList<>();
+        const val MAIN_VARIANTS: Int = 4 // Number of Main program variants (ATM 4: MAIN, MAIN OIT, PREPROCESS, FULL)
+        const val VERT_INT_UBO_BINDING: Int = 5
+        const val VERT_FLO_UBO_BINDING: Int = 6
 
-    mJobs.add(new Job(CREATE,numFloatUniforms,numIntUniforms,useUBO,null)); // create the stuff that depends on max number
-    InternalMaster.newSlave(this);                                          // of uniforms later, on first render.
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// this is not thread safe! The 'source' might change while we're copying it.
+        private const val CREATE = 0
+        private const val ATTACH = 1
+        private const val DETACH = 2
+        private const val DETALL = 3
 
-  EffectQueue(EffectQueue source)
-    {
-    if( !source.mCreated )
-      {
-      mCreated        = false;
-      mID             = 0;
-      mNumEffects     = 0;
-      mNumEffectsToBe = 0;
-      mIndex          = source.mIndex;
-
-      mJobs = new ArrayList<>();
-
-      int numJobs = source.mJobs.size();
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        fun allocateQueues(queues: Array<EffectQueue?>, from: Array<EffectQueue?>, flags: Int)
+        {
+            queues[0] = if ((flags and DistortedLibrary.CLONE_MATRIX     ) != 0) from[0] else EffectQueueMatrix()
+            queues[1] = if ((flags and DistortedLibrary.CLONE_VERTEX     ) != 0) from[1] else EffectQueueVertex()
+            queues[2] = if ((flags and DistortedLibrary.CLONE_FRAGMENT   ) != 0) from[2] else EffectQueueFragment()
+            queues[3] = if ((flags and DistortedLibrary.CLONE_POSTPROCESS) != 0) from[3] else EffectQueuePostprocess()
+        }
 
-      for(int i=0; i<numJobs; i++)
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        fun compute(queues: Array<EffectQueue>, currTime: Long, step: Long)
         {
-        Job job = source.mJobs.get(i);
-        mJobs.add(job);
+            (queues[0] as EffectQueueMatrix     ).compute(currTime, step)
+            (queues[1] as EffectQueueVertex     ).compute(currTime, step)
+            (queues[2] as EffectQueueFragment   ).compute(currTime, step)
+            (queues[3] as EffectQueuePostprocess).compute(currTime, step)
         }
 
-      InternalMaster.newSlave(this);
-      }
-    else
-      {
-      mCreated        = true;
-      mID             = source.mID;
-      mNumEffects     = source.mNumEffects;
-      mNumEffectsToBe = source.mNumEffectsToBe;
-      mIndex          = source.mIndex;
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        fun send(queues: Array<EffectQueue>, programH: Int, distance: Float, mipmap: Float,
+                 projection: FloatArray, inflate: Float, variant: Int)
+        {
+            (queues[0] as EffectQueueMatrix  ).send(distance, mipmap, projection, variant)
+            (queues[1] as EffectQueueVertex  ).send(inflate, programH, variant)
+            (queues[2] as EffectQueueFragment).send(variant)
+        }
 
-      mJobs = new ArrayList<>();
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        // variant: 0 --> MAIN  1 --> OIT  2 --> prePOST  3 --> FULL
+        @JvmStatic
+        fun getUniforms(programH: Int, variant: Int)
+        {
+            EffectQueueFragment.uniforms(programH, variant)
+            EffectQueueVertex  .uniforms(programH, variant)
+            EffectQueueMatrix  .uniforms(programH, variant)
+        }
 
-      int max = InternalStackFrameList.getMax(mIndex);
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        @JvmStatic
+        fun setMax(index: Int, m: Int): Boolean
+        {
+            return InternalStackFrameList.setMax(index, max(m,0) )
+        }
 
-      if( max>0 )
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        @JvmStatic
+        fun getMax(index: Int): Int
         {
-        mEffects= new Effect[max];
-        mUBI  = new UniformBlockIntUniforms(source.mUBI);
-        mUBF  = new UniformBlockFloatUniforms(source.mUBF);
-
-        if( mNumEffects>=0 )
-          {
-          System.arraycopy(source.mEffects, 0, mEffects, 0, mNumEffects);
-          }
+            return InternalStackFrameList.getMax(index)
         }
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static void allocateQueues(EffectQueue[] queues, EffectQueue[] from, int flags)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    internal constructor(numFloatUniforms: Int, numIntUniforms: Int, useUBO: Boolean, index: Int)
     {
-    queues[0] = (flags & DistortedLibrary.CLONE_MATRIX     ) != 0 ? from[0] : new EffectQueueMatrix();
-    queues[1] = (flags & DistortedLibrary.CLONE_VERTEX     ) != 0 ? from[1] : new EffectQueueVertex();
-    queues[2] = (flags & DistortedLibrary.CLONE_FRAGMENT   ) != 0 ? from[2] : new EffectQueueFragment();
-    queues[3] = (flags & DistortedLibrary.CLONE_POSTPROCESS) != 0 ? from[3] : new EffectQueuePostprocess();
-    }
+        mCreated       = false
+        iD             = 0
+        numEffects     = 0
+        numEffectsToBe = 0
+        mIndex         = index
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        mJobs = ArrayList()
 
-  public static void compute(EffectQueue[] queues, long currTime, long step)
-    {
-    ((EffectQueueMatrix     )queues[0]).compute(currTime,step);
-    ((EffectQueueVertex     )queues[1]).compute(currTime,step);
-    ((EffectQueueFragment   )queues[2]).compute(currTime,step);
-    ((EffectQueuePostprocess)queues[3]).compute(currTime,step);
+        mJobs.add( Job(CREATE,numFloatUniforms,numIntUniforms,useUBO,null) ) // create the stuff that depends on max number
+        InternalMaster.newSlave(this)                                        // of uniforms later, on first render.
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static void send(EffectQueue[] queues, int programH, float distance, float mipmap,
-                          float[] projection, float inflate, int variant )
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // this is not thread safe! The 'source' might change while we're copying it.
+    internal constructor(source: EffectQueue)
     {
-    ((EffectQueueMatrix  )queues[0]).send(distance, mipmap, projection, variant);
-    ((EffectQueueVertex  )queues[1]).send(inflate, programH, variant);
-    ((EffectQueueFragment)queues[2]).send(variant);
-    }
+        if (!source.mCreated)
+        {
+            mCreated       = false
+            iD             = 0
+            numEffects     = 0
+            numEffectsToBe = 0
+            mIndex         = source.mIndex
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// variant: 0 --> MAIN  1 --> OIT  2 --> prePOST  3 --> FULL
+            mJobs = ArrayList()
 
-  public static void getUniforms(int programH, int variant)
-    {
-    EffectQueueFragment.uniforms(programH,variant);
-    EffectQueueVertex  .uniforms(programH,variant);
-    EffectQueueMatrix  .uniforms(programH,variant);
-    }
+            val numJobs = source.mJobs.size
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Every effect queue has an ID, which should be the same iff two queues hold the same effects.
-// (this is a speedup: then both queues can be applied once, which seriously speeds up stuff -
-// especially important in case of postprocessing)
+            for (i in 0..<numJobs)
+            {
+                val job = source.mJobs[i]
+                mJobs.add(job)
+            }
 
-  private void regenerateID()
-    {
-    if( mNumEffects>0 )
-      {
-      HashMap<ArrayList<Long>,Long> map = InternalStackFrameList.getMap();
-      ArrayList<Long> list = new ArrayList<>();
-      for (int i=0; i<mNumEffects; i++) list.add(mEffects[i].getID());
-      Long id = map.get(list);
-
-      if( id!=null )
-        {
-        mID = id;
+            InternalMaster.newSlave(this)
         }
-      else
+        else
         {
-        mID = InternalStackFrameList.getNextQueueID();
-        map.put(list,mID);
+            mCreated       = true
+            iD             = source.iD
+            numEffects     = source.numEffects
+            numEffectsToBe = source.numEffectsToBe
+            mIndex         = source.mIndex
+
+            mJobs = ArrayList()
+
+            val max = InternalStackFrameList.getMax(mIndex)
+
+            if (max > 0)
+            {
+                mEffects = arrayOfNulls(max)
+                mUBI = UniformBlockIntUniforms(source.mUBI!!)
+                mUBF = UniformBlockFloatUniforms(source.mUBF!!)
+                if (numEffects>=0) System.arraycopy(source.mEffects, 0, mEffects, 0, numEffects)
+            }
         }
-      }
-    else
-      {
-      mID = 0;
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public long getID()
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Every effect queue has an ID, which should be the same iff two queues hold the same effects.
+    // (this is a speedup: then both queues can be applied once, which seriously speeds up stuff -
+    // especially important in case of postprocessing)
+    private fun regenerateID()
     {
-    return mID;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static boolean setMax(int index, int m)
-    {
-    return InternalStackFrameList.setMax(index, Math.max(m,0));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static int getMax(int index)
-    {
-    return InternalStackFrameList.getMax(index);
+        if (numEffects > 0)
+        {
+            val map = InternalStackFrameList.getMap()
+            val list = ArrayList<Long>()
+            for (i in 0..<numEffects) list.add(mEffects[i]!!.id)
+            val id = map[list]
+
+            if (id != null)
+            {
+                iD = id
+            }
+            else
+            {
+                iD = InternalStackFrameList.getNextQueueID()
+                map[list] = iD
+            }
+        }
+        else
+        {
+            iD = 0
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// this assumes 0<=effect
-
-  private void removeNow(int pos)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    private fun removeNow(pos: Int)
     {
-    if( mNumEffects>pos )
-      {
-      mNumEffects--;
-      mEffects[pos].remQueue(this);
-      System.arraycopy(mEffects, pos+1, mEffects, pos, mNumEffects-pos);
-      mUBI.remove(pos, mNumEffects);
-      mEffects[mNumEffects] = null;
-      }
+        if (pos in 0..<numEffects)
+        {
+            numEffects--
+            mEffects[pos]!!.remQueue(this)
+            System.arraycopy(mEffects, pos + 1, mEffects, pos, numEffects - pos)
+            mUBI!!.remove(pos, numEffects)
+            mEffects[numEffects] = null
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void addNow(int pos, Effect effect)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    private fun addNow(pos: Int, effect: Effect)
     {
-    mEffects[pos] = effect;
-    mUBI.addOrdinal(pos, effect.getName().ordinal() );
-    effect.addQueue(this);
-
-    if( mIndex==EffectType.VERTEX.ordinal() )
-      {
-      mUBI.addAssociations(pos,effect);
-      }
+        mEffects[pos] = effect
+        mUBI!!.addOrdinal(pos, effect.name.ordinal)
+        effect.addQueue(this)
+        if (mIndex==EffectType.VERTEX.ordinal) mUBI!!.addAssociations(pos, effect)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public synchronized int removeByName(EffectName name)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    @Synchronized
+    fun removeByName(name: EffectName): Int
     {
-    int ret = 0;
+        var ret = 0
 
-    for(int i=0; i<mNumEffects; i++)
-      {
-      if( mEffects[i].getName() == name )
+        for (i in 0..<numEffects)
         {
-        mJobs.add(new Job(DETACH,0,0,true,mEffects[i]));
-        ret++;
+            if (mEffects[i]!!.name == name)
+            {
+                mJobs.add( Job(DETACH,0,0,true,mEffects[i]) )
+                ret++
+            }
         }
-      }
 
-    if( ret>0 )
-      {
-      InternalMaster.newSlave(this);
-      mNumEffectsToBe-=ret;
-      }
+        if (ret > 0)
+        {
+            InternalMaster.newSlave(this)
+            numEffectsToBe -= ret
+        }
 
-    return ret;
+        return ret
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public synchronized int removeById(long id)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    @Synchronized
+    fun removeById(id: Long): Int
     {
-    for(int i=0; i<mNumEffects; i++)
-      {
-      if( mEffects[i].getID() == id )
+        for (i in 0..<numEffects)
         {
-        mJobs.add(new Job(DETACH,0,0,true,mEffects[i]));
-        InternalMaster.newSlave(this);
-        mNumEffectsToBe--;
-        return 1;
+            if (mEffects[i]!!.id == id)
+            {
+                mJobs.add( Job(DETACH,0,0,true,mEffects[i]) )
+                InternalMaster.newSlave(this)
+                numEffectsToBe--
+                return 1
+            }
         }
-      }
 
-    return 0;
+        return 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public synchronized int removeEffect(Effect effect)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    @Synchronized
+    fun removeEffect(effect: Effect): Int
     {
-    for(int i=0; i<mNumEffects; i++)
-      {
-      if( mEffects[i]==effect )
+        for (i in 0..<numEffects)
         {
-        mJobs.add(new Job(DETACH,0,0,true,mEffects[i]));
-        InternalMaster.newSlave(this);
-        mNumEffectsToBe--;
-        return 1;
+            if (mEffects[i] === effect) {
+                mJobs.add(
+                    Job(
+                        DETACH, 0, 0, true,
+                        mEffects[i]
+                    )
+                )
+                InternalMaster.newSlave(this)
+                numEffectsToBe--
+                return 1
+            }
         }
-      }
-   
-    return 0;
-    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// we do want to notify Listeners if they called 'abortAll' themselves but don't want to notify
-// them if it is the library itself which is releasing resources.
-
-  public synchronized int removeAll(boolean notify)
-    {
-    mJobs.add(new Job(DETALL,0,0,notify,null));
-    InternalMaster.newSlave(this);
-    mNumEffectsToBe = 0;
-    return mNumEffects;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  public boolean add(Effect effect)
-    {
-    if( InternalStackFrameList.getMax(mIndex)>mNumEffectsToBe || !mCreated )
-      {
-      mJobs.add(new Job(ATTACH,-1,0,false,effect));
-      InternalMaster.newSlave(this);
-      mNumEffectsToBe++;
-      return true;
-      }
-
-    return false;
+        return 0
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean add(Effect effect, int position)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // we do want to notify Listeners if they called 'abortAll' themselves but don't want to notify
+    // them if it is the library itself which is releasing resources.
+    @Synchronized
+    fun removeAll(notify: Boolean): Int
     {
-    if( InternalStackFrameList.getMax(mIndex)>mNumEffectsToBe || !mCreated )
-      {
-      mJobs.add(new Job(ATTACH,position,0,false,effect));
-      InternalMaster.newSlave(this);
-      mNumEffectsToBe++;
-      return true;
-      }
-
-    return false;
+        mJobs.add( Job(DETALL,0,0,notify,null) )
+        InternalMaster.newSlave(this)
+        numEffectsToBe = 0
+        return numEffects
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public boolean exists(long id)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun add(effect: Effect?): Boolean
     {
-    for(int i=0; i<mNumEffects; i++)
-      {
-      if( mEffects[i].getID() == id ) return true;
-      }
+        if (InternalStackFrameList.getMax(mIndex) > numEffectsToBe || !mCreated)
+        {
+            mJobs.add( Job(ATTACH,-1,0,false,effect) )
+            InternalMaster.newSlave(this)
+            numEffectsToBe++
+            return true
+        }
 
-    return false;
+        return false
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getNumEffects()
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun add(effect: Effect?, position: Int): Boolean
     {
-    return mNumEffects;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        if (InternalStackFrameList.getMax(mIndex) > numEffectsToBe || !mCreated)
+        {
+            mJobs.add( Job(ATTACH,position,0,false,effect) )
+            InternalMaster.newSlave(this)
+            numEffectsToBe++
+            return true
+        }
 
-  public int getNumEffectsToBe()
-    {
-    return mNumEffectsToBe;
+        return false
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Effect getEffect(int position)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun exists(id: Long): Boolean
     {
-    if( position>=0 && position< mNumEffects )
-      {
-      return mEffects[position];
-      }
-    else
-      {
-      DistortedLibrary.logMessage("EffectQueue: getEffect: out of range "+position);
-      return null;
-      }
-    }
+        for (i in 0..<numEffects)
+            if (mEffects[i]!!.id==id) return true
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        return false
+    }
 
-  public void setAssociation(long effectID)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun getEffect(position: Int): Effect?
     {
-    for(int j=0; j<mNumEffects; j++)
-      {
-      if (mEffects[j].getID() == effectID)
+        if (position in 0..<numEffects)
+        {
+            return mEffects[position]
+        }
+        else
         {
-        mUBI.addAssociations(j,mEffects[j]);
+            DistortedLibrary.logMessage("EffectQueue: getEffect: out of range $position")
+            return null
         }
-      }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void markForDeletion()
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun setAssociation(effectID: Long)
     {
-    mUBI.markForDeletion();
-    mUBF.markForDeletion();
+        for (j in 0..<numEffects)
+            if (mEffects[j]!!.id == effectID)
+                mUBI!!.addAssociations(j, mEffects[j]!!)
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun markForDeletion()
+    {
+        mUBI!!.markForDeletion()
+        mUBF!!.markForDeletion()
+    }
 
-  public String retEffects()
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun retEffects(): String
     {
-    String dbg="";
+        var dbg = ""
 
-    for(int i=0; i<mNumEffects; i++)
-      {
-      dbg += (i+": "+mEffects[i].getString()+" ");
-      }
+        for (i in 0..<numEffects)
+            dbg += (i.toString() + ": " + mEffects[i]!!.string + " ")
 
-    return dbg;
+        return dbg
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void doWork()
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    override fun doWork()
     {
-    boolean changed = false;
-    int num = mJobs.size();
-    Job job;
-
-    for(int i=0; i<num; i++)
-      {
-      job = mJobs.remove(0);
+        var changed = false
+        val num = mJobs.size
+        var job: Job
 
-      switch(job.type)
+        for (i in 0..<num)
         {
-        case CREATE: int max = InternalStackFrameList.getMax(mIndex);
-                     if( max>0 )
-                       {
-                       mEffects= new Effect[max];
-                       mUBF    = new UniformBlockFloatUniforms(job.num1, max, job.bool);
-                       mUBI    = new UniformBlockIntUniforms  (job.num2, max, job.bool);
-                       }
-                     mCreated = true;
-
-                     break;
-        case ATTACH: if( InternalStackFrameList.getMax(mIndex)>mNumEffects ) // it is possible that we have first
-                       {                                                     // added effects and then lowered mMax
-                       int position = job.num1;
-
-                       if( position<0 )
-                         {
-                         addNow(mNumEffects,job.effect);
-                         mNumEffects++;
-                         changed = true;
-                         }
-                       else if( position<=mNumEffects )
-                         {
-                         System.arraycopy(mEffects, position, mEffects, position+1, mNumEffects-position);
-                         mUBI.makeHole(position, mNumEffects);
-                         addNow(position,job.effect);
-                         mNumEffects++;
-                         changed = true;
-                         }
-                       }
-                     else
-                       {
-                       DistortedLibrary.logMessage("EffectQueue: failed to add effect "+job.effect.getName());
-                       }
-                     break;
-        case DETACH: for(int j=0; j<mNumEffects; j++)
-                       {
-                       if (mEffects[j] == job.effect)
-                         {
-                         removeNow(j);
-                         changed = true;
-                         break;
-                         }
-                       }
-                     break;
-        case DETALL: for(int j=0; j<mNumEffects; j++ )
-                       {
-                       changed = true;
-                       mEffects[j] = null;
-                       }
-
-                     // TODO: notify listeners?
-                     /* if( job.bool )
-                          {
-                          // ...
-                          }
-                      */
-
-                     mNumEffects= 0;
-                     break;
+            job = mJobs.removeAt(0)
+
+            when (job.type)
+            {
+                CREATE ->
+                        {
+                            val max = InternalStackFrameList.getMax(mIndex)
+                            if (max > 0)
+                            {
+                                mEffects = arrayOfNulls(max)
+                                mUBF = UniformBlockFloatUniforms(job.num1, max, job.bool)
+                                mUBI = UniformBlockIntUniforms(job.num2, max, job.bool)
+                            }
+                            mCreated = true
+                        }
+                ATTACH ->
+                        if (InternalStackFrameList.getMax(mIndex)>numEffects)  // it is possible that we have first
+                        {                                                      // added effects and then lowered mMax
+                            val position = job.num1
+
+                            if (position < 0)
+                            {
+                                addNow(numEffects, job.effect)
+                                numEffects++
+                                changed = true
+                            }
+                            else if (position <= numEffects)
+                            {
+                                System.arraycopy(mEffects, position, mEffects, position+1, numEffects - position)
+                                mUBI!!.makeHole(position, numEffects)
+                                addNow(position, job.effect)
+                                numEffects++
+                                changed = true
+                            }
+                        }
+                        else
+                        {
+                            DistortedLibrary.logMessage("EffectQueue: failed to add effect " + job.effect.name)
+                        }
+
+                DETACH ->
+                        {
+                            var j = 0
+
+                            while (j < numEffects)
+                            {
+                                if (mEffects[j] === job.effect)
+                                {
+                                    removeNow(j)
+                                    changed = true
+                                    break
+                                }
+                                j++
+                            }
+                        }
+
+                DETALL ->
+                        {
+                            var j = 0
+                            while (j < numEffects)
+                            {
+                                changed = true
+                                mEffects[j] = null
+                                j++
+                            }
+
+                            // TODO: notify listeners?
+                            /* if( job.bool )
+                                {
+                                // ...
+                                }
+                            */
+                            numEffects = 0
+                        }
+            }
         }
-      }
 
-    if( changed && mIndex==EffectType.POSTPROCESS.ordinal() ) regenerateID();
+        if (changed && mIndex == EffectType.POSTPROCESS.ordinal) regenerateID()
     }
-  }
+
+}
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueueFragment.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueueFragment.kt
index b40c35e..44b8f05 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueueFragment.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueueFragment.kt
@@ -18,81 +18,64 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.effectqueue;
+package org.distorted.library.effectqueue
 
-import android.opengl.GLES30;
-
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.effect.FragmentEffect;
-import org.distorted.library.message.EffectMessageSender;
+import android.opengl.GLES30
+import org.distorted.library.effect.EffectType
+import org.distorted.library.effect.FragmentEffect
+import org.distorted.library.message.EffectMessageSender.Companion.newMessage
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+internal class EffectQueueFragment : EffectQueue
+{
+    companion object
+    {
+        private const val NUM_FLOAT_UNIFORMS = FragmentEffect.NUM_FLOAT_UNIFORMS
+        private const val NUM_INT_UNIFORMS = FragmentEffect.NUM_INT_UNIFORMS
+        private const val USE_UBO = false
+        private val INDEX = EffectType.FRAGMENT.ordinal
 
-class EffectQueueFragment extends EffectQueue
-  {
-  private static final int NUM_FLOAT_UNIFORMS = FragmentEffect.NUM_FLOAT_UNIFORMS;
-  private static final int NUM_INT_UNIFORMS   = FragmentEffect.NUM_INT_UNIFORMS;
-  private static final boolean USE_UBO        = false;
-  private static final int INDEX = EffectType.FRAGMENT.ordinal();
-
-  private final static int[] mNumEffectsH  = new int[MAIN_VARIANTS];
-  private final static int[] mIntUniformsH = new int[MAIN_VARIANTS];
-  private final static int[] mFloUniformsH = new int[MAIN_VARIANTS];
+        private val mNumEffectsH  = IntArray(MAIN_VARIANTS)
+        private val mIntUniformsH = IntArray(MAIN_VARIANTS)
+        private val mFloUniformsH = IntArray(MAIN_VARIANTS)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  EffectQueueFragment()
-    { 
-    super(NUM_FLOAT_UNIFORMS, NUM_INT_UNIFORMS, USE_UBO, INDEX);
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        fun uniforms(mProgramH: Int, variant: Int)
+        {
+            mNumEffectsH[variant]  = GLES30.glGetUniformLocation(mProgramH, "fNumEffects")
+            mIntUniformsH[variant] = GLES30.glGetUniformLocation(mProgramH, "fProperties")
+            mFloUniformsH[variant] = GLES30.glGetUniformLocation(mProgramH, "fUniforms")
+        }
     }
+    ///////////////////////////////////////////////////////////////////////////////////////////////
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    constructor() : super(NUM_FLOAT_UNIFORMS,NUM_INT_UNIFORMS,USE_UBO,INDEX)
+    constructor(source: EffectQueueFragment) : super(source)
 
-  EffectQueueFragment(EffectQueueFragment source)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun compute(currTime: Long, step: Long)
     {
-    super(source);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        if (numEffects > 0)
+        {
+            val array = mUBF!!.backingArray
 
-  static void uniforms(int mProgramH, int variant)
-    {
-    mNumEffectsH[variant]   = GLES30.glGetUniformLocation  ( mProgramH, "fNumEffects");
-    mIntUniformsH[variant]  = GLES30.glGetUniformLocation  ( mProgramH, "fProperties");
-    mFloUniformsH[variant]  = GLES30.glGetUniformLocation  ( mProgramH, "fUniforms");
+            for (i in 0..<numEffects)
+                if (mEffects[i]!!.compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step))
+                    newMessage(mEffects[i]!!)
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  void compute(long currTime, long step)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun send(variant: Int)
     {
-    if( mNumEffects>0 )
-      {
-      float[] array = mUBF.getBackingArray();
+        GLES30.glUniform1i(mNumEffectsH[variant],numEffects)
 
-      for(int i=0; i<mNumEffects; i++)
+        if (numEffects>0)
         {
-        if( mEffects[i].compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step) )
-          {
-          EffectMessageSender.newMessage(mEffects[i]);
-          }
+            val arrayI = mUBI!!.backingArray
+            val arrayF = mUBF!!.backingArray
+            GLES30.glUniform4iv(mIntUniformsH[variant],numEffects,arrayI,0)
+            GLES30.glUniform4fv(mFloUniformsH[variant],(NUM_FLOAT_UNIFORMS/4)*numEffects,arrayF,0)
         }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  void send(int variant)
-    {
-    GLES30.glUniform1i( mNumEffectsH[variant], mNumEffects);
-
-    if( mNumEffects>0 )
-      {
-      int[]   arrayI = mUBI.getBackingArray();
-      float[] arrayF = mUBF.getBackingArray();
-
-      GLES30.glUniform4iv( mIntUniformsH[variant],                       mNumEffects, arrayI, 0);
-      GLES30.glUniform4fv( mFloUniformsH[variant],(NUM_FLOAT_UNIFORMS/4)*mNumEffects, arrayF, 0);
-      }  
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueueMatrix.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueueMatrix.kt
index 014516b..023e861 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueueMatrix.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueueMatrix.kt
@@ -18,108 +18,106 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.effectqueue;
+package org.distorted.library.effectqueue
 
-import android.opengl.GLES30;
-
-import org.distorted.library.helpers.MatrixHelper;
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.effect.MatrixEffect;
-import org.distorted.library.message.EffectMessageSender;
+import android.opengl.GLES30
+import org.distorted.library.effect.EffectType
+import org.distorted.library.effect.MatrixEffect
+import org.distorted.library.helpers.MatrixHelper.multiply
+import org.distorted.library.message.EffectMessageSender.Companion.newMessage
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-class EffectQueueMatrix extends EffectQueue
-  {
-  private static final int NUM_FLOAT_UNIFORMS = MatrixEffect.NUM_FLOAT_UNIFORMS;
-  private static final int NUM_INT_UNIFORMS   = MatrixEffect.NUM_INT_UNIFORMS;
-  private static final boolean USE_UBO        = false;
-  private static final int INDEX = EffectType.MATRIX.ordinal();
-
-  private static final float[] mMVPMatrix       = new float[16];
-  private static final float[] mModelViewMatrixP= new float[16];
-  private static final float[] mModelViewMatrixV= new float[16];
-
-  private static final int[] mMVPMatrixH = new int[MAIN_VARIANTS];
-  private static final int[] mMVMatrixPH = new int[MAIN_VARIANTS];
-  private static final int[] mMVMatrixVH = new int[MAIN_VARIANTS];
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  EffectQueueMatrix()
-    { 
-    super(NUM_FLOAT_UNIFORMS, NUM_INT_UNIFORMS, USE_UBO, INDEX);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  EffectQueueMatrix(EffectQueueMatrix source)
+internal class EffectQueueMatrix : EffectQueue
+{
+    companion object
     {
-    super(source);
-    }
+        private const val NUM_FLOAT_UNIFORMS = MatrixEffect.NUM_FLOAT_UNIFORMS
+        private const val NUM_INT_UNIFORMS   = MatrixEffect.NUM_INT_UNIFORMS
+        private const val USE_UBO = false
+        private val INDEX = EffectType.MATRIX.ordinal
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+        private val mMVPMatrix        = FloatArray(16)
+        private val mModelViewMatrixP = FloatArray(16)
+        private val mModelViewMatrixV = FloatArray(16)
 
-  static void uniforms(int mProgramH, int variant)
-    {
-    mMVPMatrixH[variant]= GLES30.glGetUniformLocation(mProgramH, "u_MVPMatrix");
-    mMVMatrixPH[variant]= GLES30.glGetUniformLocation(mProgramH, "u_MVMatrixP");
-    mMVMatrixVH[variant]= GLES30.glGetUniformLocation(mProgramH, "u_MVMatrixV");
-    }
+        private val mMVPMatrixH = IntArray(MAIN_VARIANTS)
+        private val mMVMatrixPH = IntArray(MAIN_VARIANTS)
+        private val mMVMatrixVH = IntArray(MAIN_VARIANTS)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void compute(long currTime, long step)
-    {
-    if( mNumEffects>0 )
-      {
-      float[] array = mUBF.getBackingArray();
-
-      for(int i=0; i<mNumEffects; i++)
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        fun uniforms(mProgramH: Int, variant: Int)
         {
-        if( mEffects[i].compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step) )
-          {
-          EffectMessageSender.newMessage(mEffects[i]);
-          }
+            mMVPMatrixH[variant] = GLES30.glGetUniformLocation(mProgramH, "u_MVPMatrix")
+            mMVMatrixPH[variant] = GLES30.glGetUniformLocation(mProgramH, "u_MVMatrixP")
+            mMVMatrixVH[variant] = GLES30.glGetUniformLocation(mProgramH, "u_MVMatrixV")
         }
-      }
-    }  
+    }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    constructor() : super(NUM_FLOAT_UNIFORMS,NUM_INT_UNIFORMS,USE_UBO,INDEX)
+    constructor(source: EffectQueueMatrix) : super(source)
 
-  void send(float distance, float mipmap, float[] projection, int variant)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun compute(currTime: Long, step: Long)
     {
-    // i.e. setIdentity(); translate(0,0,-distance); scale(mipmap,mipmap,mipmap)
-    mModelViewMatrixV[ 0] = mModelViewMatrixP[ 0] = mipmap;
-    mModelViewMatrixV[ 1] = mModelViewMatrixP[ 1] = 0;
-    mModelViewMatrixV[ 2] = mModelViewMatrixP[ 2] = 0;
-    mModelViewMatrixV[ 3] = mModelViewMatrixP[ 3] = 0;
-    mModelViewMatrixV[ 4] = mModelViewMatrixP[ 4] = 0;
-    mModelViewMatrixV[ 5] = mModelViewMatrixP[ 5] = mipmap;
-    mModelViewMatrixV[ 6] = mModelViewMatrixP[ 6] = 0;
-    mModelViewMatrixV[ 7] = mModelViewMatrixP[ 7] = 0;
-    mModelViewMatrixV[ 8] = mModelViewMatrixP[ 8] = 0;
-    mModelViewMatrixV[ 9] = mModelViewMatrixP[ 9] = 0;
-    mModelViewMatrixV[10] = mModelViewMatrixP[10] = mipmap;
-    mModelViewMatrixV[11] = mModelViewMatrixP[11] = 0;
-    mModelViewMatrixV[12] = mModelViewMatrixP[12] = 0;
-    mModelViewMatrixV[13] = mModelViewMatrixP[13] = 0;
-    mModelViewMatrixV[14] = mModelViewMatrixP[14] = -distance;
-    mModelViewMatrixV[15] = mModelViewMatrixP[15] = 1;
-
-    float[] array = mUBF.getBackingArray();
-
-    // the 'Model' part of the MV matrix
-    for(int i=mNumEffects-1; i>=0; i--)
-      {
-      ((MatrixEffect)mEffects[i]).apply(mModelViewMatrixP,mModelViewMatrixV,array,i);
-      }
+        if (numEffects>0)
+        {
+            val array = mUBF!!.backingArray
 
-    // combined Model-View-Projection matrix
-    MatrixHelper.multiply(mMVPMatrix, projection, mModelViewMatrixP);
+            for (i in 0..<numEffects)
+                if (mEffects[i]!!.compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step))
+                    newMessage(mEffects[i]!!)
+        }
+    }
 
-    GLES30.glUniformMatrix4fv(mMVMatrixVH[variant], 1, false, mModelViewMatrixV, 0);
-    GLES30.glUniformMatrix4fv(mMVMatrixPH[variant], 1, false, mModelViewMatrixP, 0);
-    GLES30.glUniformMatrix4fv(mMVPMatrixH[variant], 1, false, mMVPMatrix       , 0);
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun send(distance: Float, mipmap: Float, projection: FloatArray, variant: Int)
+    {
+        // i.e. setIdentity(); translate(0,0,-distance); scale(mipmap,mipmap,mipmap)
+        mModelViewMatrixP[ 0] = mipmap
+        mModelViewMatrixV[ 0] = mModelViewMatrixP[0]
+        mModelViewMatrixP[ 1] = 0f
+        mModelViewMatrixV[ 1] = mModelViewMatrixP[1]
+        mModelViewMatrixP[ 2] = 0f
+        mModelViewMatrixV[ 2] = mModelViewMatrixP[2]
+        mModelViewMatrixP[ 3] = 0f
+        mModelViewMatrixV[ 3] = mModelViewMatrixP[3]
+        mModelViewMatrixP[ 4] = 0f
+        mModelViewMatrixV[ 4] = mModelViewMatrixP[4]
+        mModelViewMatrixP[ 5] = mipmap
+        mModelViewMatrixV[ 5] = mModelViewMatrixP[5]
+        mModelViewMatrixP[ 6] = 0f
+        mModelViewMatrixV[ 6] = mModelViewMatrixP[6]
+        mModelViewMatrixP[ 7] = 0f
+        mModelViewMatrixV[ 7] = mModelViewMatrixP[7]
+        mModelViewMatrixP[ 8] = 0f
+        mModelViewMatrixV[ 8] = mModelViewMatrixP[8]
+        mModelViewMatrixP[ 9] = 0f
+        mModelViewMatrixV[ 9] = mModelViewMatrixP[9]
+        mModelViewMatrixP[10] = mipmap
+        mModelViewMatrixV[10] = mModelViewMatrixP[10]
+        mModelViewMatrixP[11] = 0f
+        mModelViewMatrixV[11] = mModelViewMatrixP[11]
+        mModelViewMatrixP[12] = 0f
+        mModelViewMatrixV[12] = mModelViewMatrixP[12]
+        mModelViewMatrixP[13] = 0f
+        mModelViewMatrixV[13] = mModelViewMatrixP[13]
+        mModelViewMatrixP[14] = -distance
+        mModelViewMatrixV[14] = mModelViewMatrixP[14]
+        mModelViewMatrixP[15] = 1f
+        mModelViewMatrixV[15] = mModelViewMatrixP[15]
+
+        val array = mUBF!!.backingArray
+
+        // the 'Model' part of the MV matrix
+        for (i in numEffects-1 downTo 0)
+            (mEffects[i] as MatrixEffect).apply(mModelViewMatrixP, mModelViewMatrixV, array, i)
+
+        // combined Model-View-Projection matrix
+        multiply(mMVPMatrix, projection, mModelViewMatrixP)
+
+        GLES30.glUniformMatrix4fv(mMVMatrixVH[variant], 1, false, mModelViewMatrixV, 0)
+        GLES30.glUniformMatrix4fv(mMVMatrixPH[variant], 1, false, mModelViewMatrixP, 0)
+        GLES30.glUniformMatrix4fv(mMVPMatrixH[variant], 1, false, mMVPMatrix       , 0)
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
index 05c4d6e..0f45d17 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
@@ -18,219 +18,199 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.effectqueue;
-
-import android.opengl.GLES30;
-
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.effect.PostprocessEffect;
-import org.distorted.library.effect.VertexEffect;
-import org.distorted.library.main.DistortedEffects;
-import org.distorted.library.main.DistortedLibrary;
-import org.distorted.library.main.DistortedFramebuffer;
-import org.distorted.library.main.DistortedNode;
-import org.distorted.library.main.InternalOutputSurface;
-import org.distorted.library.main.InternalRenderState;
-import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.message.EffectMessageSender;
-import org.distorted.library.program.DistortedProgram;
-
-import java.io.InputStream;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document
- *
- * @y.exclude
- */
-public class EffectQueuePostprocess extends EffectQueue
-  {
-  private static final int NUM_FLOAT_UNIFORMS = PostprocessEffect.NUM_FLOAT_UNIFORMS;
-  private static final int NUM_INT_UNIFORMS   = PostprocessEffect.NUM_INT_UNIFORMS;
-  private static final boolean USE_UBO        = false;
-  private static final int INDEX = EffectType.POSTPROCESS.ordinal();
-
-  private int mHalo;
-  private boolean mUseHaloDepth;
-  private float mR, mG, mB, mA;
-
-  private static DistortedProgram mPreProgram;
-  private static int mPreColorH;
-  private static int mPreTextureH;
-  private static int mPreProgramH;
+package org.distorted.library.effectqueue
+
+import android.opengl.GLES30
+import org.distorted.library.effect.EffectType
+import org.distorted.library.effect.PostprocessEffect
+import org.distorted.library.effect.VertexEffect
+import org.distorted.library.main.DistortedFramebuffer
+import org.distorted.library.main.DistortedLibrary
+import org.distorted.library.main.DistortedNode
+import org.distorted.library.main.InternalOutputSurface
+import org.distorted.library.main.InternalRenderState
+import org.distorted.library.mesh.MeshBase
+import org.distorted.library.message.EffectMessageSender.Companion.newMessage
+import org.distorted.library.program.DistortedProgram
+import java.io.InputStream
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  EffectQueuePostprocess()
-    { 
-    super(NUM_FLOAT_UNIFORMS, NUM_INT_UNIFORMS, USE_UBO, INDEX );
+/** Not part of public API, do not document
+*
+* @y.exclude
+*/
+class EffectQueuePostprocess : EffectQueue
+{
+    private var mHalo = 0
+    private var mUseHaloDepth = false
+    private var mR = 0f
+    private var mG = 0f
+    private var mB = 0f
+    private var mA = 0f
+
+    val quality: Int
+        get() = if (numEffects>0) (mEffects[0] as PostprocessEffect).quality else 0
+
+    val renderDirectly: Boolean
+        get() = numEffects > 0 && (mEffects[0] as PostprocessEffect).renderDirectly
+
+    companion object
+    {
+        private const val NUM_FLOAT_UNIFORMS = PostprocessEffect.NUM_FLOAT_UNIFORMS
+        private const val NUM_INT_UNIFORMS = PostprocessEffect.NUM_INT_UNIFORMS
+        private const val USE_UBO = false
+        private val INDEX = EffectType.POSTPROCESS.ordinal
+
+        private var mPreProgram: DistortedProgram? = null
+        private var mPreColorH = 0
+        private var mPreTextureH = 0
+        private var mPreProgramH = 0
+
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        @JvmStatic
+        fun createPrograms(vert: InputStream, frag: InputStream, GLSL: Int)
+        {
+            val numV = VertexEffect.getNumEnabled()
+
+            val version = "#version $GLSL es\n"
+            var mainVertHeader = version + ("#define NUM_VERTEX " + (if (numV>0) DistortedLibrary.getMax(EffectType.VERTEX) else 0) + "\n")
+            val mainFragHeader = version + "\n"
+
+            mainVertHeader += "#define MAX_COMPON " + MeshBase.getMaxEffComponents() + "\n"
+            if (MeshBase.getUseCenters()) mainVertHeader += "#define COMP_CENTERS\n"
+            if (DistortedLibrary.isUBOBuggy()) mainVertHeader += "#define BUGGY_UBOS\n"
+            mainVertHeader += "#define POSTPROCESS\n"
+
+            val enabledEffectV = VertexEffect.getGLSL()
+
+            try
+            {
+                mPreProgram = DistortedProgram(vert, frag, mainVertHeader, mainFragHeader, enabledEffectV, null, GLSL, null)
+            }
+            catch (e: Exception)
+            {
+                DistortedLibrary.logMessage("EffectQueuePostprocess " + e.javaClass.simpleName + " trying to compile PRE program: " + e.message)
+                throw RuntimeException(e.message)
+            }
+
+            mPreProgramH = mPreProgram!!.programHandle
+            getUniforms(mPreProgramH, 2)
+            MeshBase.getUniforms(mPreProgramH, 2)
+            mPreColorH = GLES30.glGetUniformLocation(mPreProgramH, "u_Color")
+            mPreTextureH = GLES30.glGetUniformLocation(mPreProgramH, "u_Texture")
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    internal constructor() : super(NUM_FLOAT_UNIFORMS, NUM_INT_UNIFORMS, USE_UBO, INDEX)
 
-  EffectQueuePostprocess(EffectQueuePostprocess source)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    internal constructor(source: EffectQueuePostprocess) : super(source)
     {
-    super(source);
-
-    mHalo = source.mHalo;
-    mUseHaloDepth = source.mUseHaloDepth;
-    mR = source.mR;
-    mG = source.mG;
-    mB = source.mB;
-    mA = source.mA;
+        mHalo         = source.mHalo
+        mUseHaloDepth = source.mUseHaloDepth
+        mR            = source.mR
+        mG            = source.mG
+        mB            = source.mB
+        mA            = source.mA
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void compute(long currTime, long step)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun compute(currTime: Long, step: Long)
     {
-    mR = mG = mB = mA = 0.0f;
-    mHalo = 0;
-    int halo;
-    float[] array = mUBF.getBackingArray();
-
-    for(int i=0; i<mNumEffects; i++)
-      {
-      // first zero out the 'alpha' because BLUR effect will not overwrite this (it is a 1D effect)
-      // and if previously there was a GLOW effect here then mA would be non-zero and we don't want
-      // that (see preprocess())
-      array[NUM_FLOAT_UNIFORMS*i+5]=0.0f;
-
-      PostprocessEffect effect = (PostprocessEffect)mEffects[i];
-
-      if( effect.compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step) )
+        mA = 0.0f
+        mB = mA
+        mG = mB
+        mR = mG
+        mHalo = 0
+        var halo: Int
+        val array = mUBF!!.backingArray
+
+        for (i in 0..<numEffects)
         {
-        EffectMessageSender.newMessage(mEffects[i]);
-        }
+            // first zero out the 'alpha' because BLUR effect will not overwrite this (it is a 1D effect)
+            // and if previously there was a GLOW effect here then mA would be non-zero and we don't want
+            // that (see preprocess())
+            array[NUM_FLOAT_UNIFORMS*i+5] = 0.0f
 
-      halo = (int)array[NUM_FLOAT_UNIFORMS*i];
-      if( halo>mHalo ) mHalo = halo;
-
-      // TODO  (now only really works in case of 1 effect!)
-      mUseHaloDepth = effect.getHaloDepth();
-      }
-
-    // TODO  (now only really works in case of 1 effect!)
-    if( mNumEffects>0 )
-      {
-      mR = array[2];
-      mG = array[3];
-      mB = array[4];
-      mA = array[5];
-      }
-    }
+            val effect = mEffects[i] as PostprocessEffect
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            if (effect.compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step))
+                newMessage(mEffects[i]!!)
 
-  public static void createPrograms(InputStream vert, InputStream frag, int GLSL)
-    {
-    int numV = VertexEffect.getNumEnabled();
-
-    String version = "#version "+GLSL+" es\n";
-    String mainVertHeader= version + ("#define NUM_VERTEX " + ( numV>0 ? DistortedLibrary.getMax(EffectType.VERTEX  ) : 0 ) + "\n");
-    String mainFragHeader= version + "\n";
-
-    mainVertHeader += "#define MAX_COMPON " + MeshBase.getMaxEffComponents() + "\n";
-    if( MeshBase.getUseCenters() )      mainVertHeader += "#define COMP_CENTERS\n";
-    if( DistortedLibrary.isUBOBuggy() ) mainVertHeader += "#define BUGGY_UBOS\n";
-    mainVertHeader += "#define POSTPROCESS\n";
-
-    String enabledEffectV= VertexEffect.getGLSL();
-
-    try
-      {
-      mPreProgram = new DistortedProgram(vert, frag, mainVertHeader, mainFragHeader,
-                                         enabledEffectV, null, GLSL, null);
-      }
-    catch(Exception e)
-      {
-      DistortedLibrary.logMessage("EffectQueuePostprocess "+ e.getClass().getSimpleName()+" trying to compile PRE program: "+e.getMessage());
-      throw new RuntimeException(e.getMessage());
-      }
-
-    mPreProgramH = mPreProgram.getProgramHandle();
-    EffectQueue.getUniforms( mPreProgramH,2 );
-    MeshBase.getUniforms( mPreProgramH,2 );
-    mPreColorH  = GLES30.glGetUniformLocation( mPreProgramH, "u_Color"  );
-    mPreTextureH= GLES30.glGetUniformLocation( mPreProgramH, "u_Texture");
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO  (now only really works in case of 1 effect!)
+            halo = array[NUM_FLOAT_UNIFORMS*i].toInt()
+            if (halo>mHalo) mHalo = halo
 
-  public int getQuality()
-    {
-    return mNumEffects>0 ? ((PostprocessEffect)mEffects[0]).getQuality() : 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO  (now only really works in case of 1 effect!)
+            // TODO  (now only really works in case of 1 effect!)
+            mUseHaloDepth = effect.haloDepth
+        }
 
-  public boolean getRenderDirectly()
-    {
-    return mNumEffects>0 && ((PostprocessEffect) mEffects[0]).getRenderDirectly();
+        // TODO  (now only really works in case of 1 effect!)
+        if (numEffects > 0)
+        {
+            mR = array[2]
+            mG = array[3]
+            mB = array[4]
+            mA = array[5]
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int preprocess(InternalOutputSurface buffer, DistortedNode node, float distance, float mipmap, float[] projection)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun preprocess(buffer: InternalOutputSurface, node: DistortedNode, distance: Float,
+                   mipmap: Float, projection: FloatArray): Int
     {
-    MeshBase mesh = node.getMesh();
-    DistortedEffects effects = node.getEffects();
-
-    int width   = buffer.getWidth();
-    int height  = buffer.getHeight();
+        val mesh    = node.mesh
+        val effects = node.effects
+        val width   = buffer.width
+        val height  = buffer.height
 
-    InternalRenderState.setUpStencilMark(mA!=0.0f);
-    InternalRenderState.disableBlending();
-    if( !mUseHaloDepth ) GLES30.glDepthMask(false);
+        InternalRenderState.setUpStencilMark(mA != 0.0f)
+        InternalRenderState.disableBlending()
+        if (!mUseHaloDepth) GLES30.glDepthMask(false)
 
-    GLES30.glViewport(0, 0, width, height );
+        GLES30.glViewport(0,0,width,height)
 
-    mPreProgram.useProgram();
+        mPreProgram!!.useProgram()
 
-    mesh.bindVertexAttribs(mPreProgram);
-    mesh.send(mPreProgramH,2);
+        mesh.bindVertexAttribs(mPreProgram)
+        mesh.send(mPreProgramH, 2)
 
-    EffectQueue[] queues = effects.getQueues();
-    EffectQueueMatrix matrix = (EffectQueueMatrix)queues[0];
-    EffectQueueVertex vertex = (EffectQueueVertex)queues[1];
+        val queues = effects.queues
+        val matrix = queues[0] as EffectQueueMatrix
+        val vertex = queues[1] as EffectQueueVertex
 
-    matrix.send(distance, mipmap, projection, 2);
-    vertex.send(mHalo*0.01f,mPreProgramH,2);
+        matrix.send(distance,mipmap,projection,2)
+        vertex.send(mHalo*0.01f, mPreProgramH, 2)
 
-    if( mA!=0.0f )
-      {
-      GLES30.glUniform4f(mPreColorH, mR, mG, mB, mA);
-      GLES30.glUniform1i(mPreTextureH, 0);
-      }
+        if (mA != 0.0f)
+        {
+            GLES30.glUniform4f(mPreColorH,mR,mG,mB,mA)
+            GLES30.glUniform1i(mPreTextureH, 0)
+        }
 
-    GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mesh.getNumVertices() );
-    mPreProgram.stopUsingProgram();
+        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mesh.numVertices)
+        mPreProgram!!.stopUsingProgram()
 
-    InternalRenderState.restoreBlending();
-    InternalRenderState.unsetUpStencilMark();
-    if( !mUseHaloDepth ) GLES30.glDepthMask(true);
+        InternalRenderState.restoreBlending()
+        InternalRenderState.unsetUpStencilMark()
+        if (!mUseHaloDepth) GLES30.glDepthMask(true)
 
-    return 1;
+        return 1
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int postprocess(DistortedFramebuffer buffer)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    fun postprocess(buffer: DistortedFramebuffer?): Int
     {
-    int numRenders = 0;
-    float[] array = mUBF.getBackingArray();
+        var numRenders = 0
+        val array = mUBF!!.backingArray
 
-    GLES30.glDisable(GLES30.GL_BLEND);
+        GLES30.glDisable(GLES30.GL_BLEND)
 
-    for(int i=0; i<mNumEffects; i++)
-      {
-      numRenders += ((PostprocessEffect)mEffects[i]).postprocess(array,NUM_FLOAT_UNIFORMS*i, buffer);
-      }
+        for (i in 0..<numEffects)
+            numRenders += (mEffects[i] as PostprocessEffect).postprocess(array,NUM_FLOAT_UNIFORMS*i, buffer)
 
-    GLES30.glEnable(GLES30.GL_BLEND);
+        GLES30.glEnable(GLES30.GL_BLEND)
 
-    return numRenders;
+        return numRenders
     }
-  }
+}
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.kt
index e4e620f..7062d56 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.kt
@@ -18,99 +18,81 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-package org.distorted.library.effectqueue;
+package org.distorted.library.effectqueue
 
-import android.opengl.GLES30;
-
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.effect.VertexEffect;
-import org.distorted.library.message.EffectMessageSender;
+import android.opengl.GLES30
+import org.distorted.library.effect.EffectType
+import org.distorted.library.effect.VertexEffect
+import org.distorted.library.message.EffectMessageSender.Companion.newMessage
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used in Meshes)
- *
- * @y.exclude
- */
-public class EffectQueueVertex extends EffectQueue
-  {
-  private static final int NUM_FLOAT_UNIFORMS = VertexEffect.NUM_FLOAT_UNIFORMS;
-  private static final int NUM_INT_UNIFORMS   = VertexEffect.NUM_INT_UNIFORMS;
-  private static final boolean USE_UBO        = true;
+/** Not part of public API, do not document (public only because has to be used in Meshes)
+*
+* @y.exclude
+*/
+class EffectQueueVertex : EffectQueue
+{
+    companion object
+    {
+        private const val NUM_FLOAT_UNIFORMS = VertexEffect.NUM_FLOAT_UNIFORMS
+        private const val NUM_INT_UNIFORMS   = VertexEffect.NUM_INT_UNIFORMS
+        private const val USE_UBO = true
 
-  private static final int INDEX = EffectType.VERTEX.ordinal();
+        private val INDEX = EffectType.VERTEX.ordinal
 
-  private final static int[] mNumEffectsH  = new int[MAIN_VARIANTS];
-  private final static int[] mInflateH     = new int[MAIN_VARIANTS];
-  private final static int[] mIntBlockIndex= new int[MAIN_VARIANTS];
-  private final static int[] mFloBlockIndex= new int[MAIN_VARIANTS];
+        private val mNumEffectsH   = IntArray(MAIN_VARIANTS)
+        private val mInflateH      = IntArray(MAIN_VARIANTS)
+        private val mIntBlockIndex = IntArray(MAIN_VARIANTS)
+        private val mFloBlockIndex = IntArray(MAIN_VARIANTS)
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  public EffectQueueVertex()
-    { 
-    super(NUM_FLOAT_UNIFORMS, NUM_INT_UNIFORMS, USE_UBO, INDEX);
+        ///////////////////////////////////////////////////////////////////////////////////////////
+        fun uniforms(mProgramH: Int, variant: Int)
+        {
+            mNumEffectsH[variant]   = GLES30.glGetUniformLocation(mProgramH, "vNumEffects")
+            mInflateH[variant]      = GLES30.glGetUniformLocation(mProgramH, "u_Inflate")
+            mIntBlockIndex[variant] = GLES30.glGetUniformBlockIndex(mProgramH, "vUniformProperties")
+            mFloBlockIndex[variant] = GLES30.glGetUniformBlockIndex(mProgramH, "vUniformFloats")
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+    constructor() : super(NUM_FLOAT_UNIFORMS, NUM_INT_UNIFORMS, USE_UBO, INDEX)
+    constructor(source: EffectQueueVertex) : super(source)
 
-  public EffectQueueVertex(EffectQueueVertex source)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    /** Not part of public API, do not document (public only because has to be used in Meshes)
+    *
+    * @y.exclude
+    */
+    fun compute(currTime: Long, step: Long)
     {
-    super(source);
-    }
+        if (numEffects>0)
+        {
+            val array = mUBF!!.backingArray
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+            for (i in 0..<numEffects)
+                if (mEffects[i]!!.compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step))
+                    newMessage(mEffects[i]!!)
 
-  static void uniforms(int mProgramH, int variant)
-    {
-    mNumEffectsH[variant]   = GLES30.glGetUniformLocation  ( mProgramH, "vNumEffects");
-    mInflateH[variant]      = GLES30.glGetUniformLocation  ( mProgramH, "u_Inflate");
-    mIntBlockIndex[variant] = GLES30.glGetUniformBlockIndex( mProgramH, "vUniformProperties");
-    mFloBlockIndex[variant] = GLES30.glGetUniformBlockIndex( mProgramH, "vUniformFloats");
+            mUBF!!.invalidate()
+        }
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used in Meshes)
- *
- * @y.exclude
- */
-  public void compute(long currTime, long step)
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    /** Not part of public API, do not document (public only because has to be used in Meshes)
+    *
+    * @y.exclude
+    */
+    fun send(inflate: Float, programH: Int, variant: Int)
     {
-    if( mNumEffects>0 )
-      {
-      float[] array = mUBF.getBackingArray();
+        GLES30.glUniform1i(mNumEffectsH[variant], numEffects)
+        GLES30.glUniform1f(mInflateH[variant], inflate)
 
-      for(int i=0; i<mNumEffects; i++)
+        if (numEffects>0)
         {
-        if( mEffects[i].compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step) )
-          {
-          EffectMessageSender.newMessage(mEffects[i]);
-          }
+            GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER,VERT_INT_UBO_BINDING,mUBI!!.index)
+            GLES30.glUniformBlockBinding(programH, mIntBlockIndex[variant],VERT_INT_UBO_BINDING)
+            GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER,VERT_FLO_UBO_BINDING,mUBF!!.index)
+            GLES30.glUniformBlockBinding(programH,mFloBlockIndex[variant],VERT_FLO_UBO_BINDING)
         }
-
-      mUBF.invalidate();
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Not part of public API, do not document (public only because has to be used in Meshes)
- *
- * @y.exclude
- */
-  public void send(float inflate, int programH, int variant)
-    {
-    GLES30.glUniform1i( mNumEffectsH[variant], mNumEffects);
-    GLES30.glUniform1f( mInflateH[variant]   , inflate    );
-
-    if( mNumEffects>0 )
-      {
-      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, VERT_INT_UBO_BINDING, mUBI.getIndex());
-      GLES30.glUniformBlockBinding(programH, mIntBlockIndex[variant], VERT_INT_UBO_BINDING);
-
-      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, VERT_FLO_UBO_BINDING, mUBF.getIndex());
-      GLES30.glUniformBlockBinding(programH, mFloBlockIndex[variant], VERT_FLO_UBO_BINDING);
-      }
     }
-  }
+}
