commit 8c57d77be7282846b76567274f954fb6138d621d
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Fri Mar 31 17:24:38 2023 +0200

    Decouple (to a large degree) the OpenGL Library from Android.

diff --git a/src/main/java/org/distorted/library/effect/Effect.java b/src/main/java/org/distorted/library/effect/Effect.java
index 4a14b85..60aefa2 100644
--- a/src/main/java/org/distorted/library/effect/Effect.java
+++ b/src/main/java/org/distorted/library/effect/Effect.java
@@ -21,6 +21,7 @@
 package org.distorted.library.effect;
 
 import org.distorted.library.effectqueue.EffectQueue;
+import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.InternalStackFrameList;
 import org.distorted.library.message.EffectListener;
 
@@ -206,7 +207,7 @@ public abstract class Effect
           }
         catch(NoSuchMethodException ex)
           {
-          android.util.Log.e("Effect", "exception getting method: "+ex.getMessage());
+          DistortedLibrary.logMessage("Effect: exception getting method: "+ex.getMessage());
           method = null;
           }
 
@@ -216,7 +217,7 @@ public abstract class Effect
           }
         catch(Exception ex)
           {
-          android.util.Log.e("Effect", "exception invoking method: "+ex.getMessage());
+          DistortedLibrary.logMessage("Effect: exception invoking method: "+ex.getMessage());
           }
         }
       }
diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffect.java b/src/main/java/org/distorted/library/effect/PostprocessEffect.java
index 45d8e0d..2f7095d 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffect.java
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffect.java
@@ -22,6 +22,7 @@ package org.distorted.library.effect;
 
 import org.distorted.library.effectqueue.EffectQueue;
 import org.distorted.library.main.DistortedFramebuffer;
+import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.InternalMaster;
 import org.distorted.library.program.DistortedProgram;
 
@@ -135,7 +136,7 @@ public abstract class PostprocessEffect extends Effect implements InternalMaster
         }
       catch(Exception e)
         {
-        android.util.Log.e("Effects", "exception trying to compile "+source.mName+" program: "+e.getMessage());
+        DistortedLibrary.logMessage("PostproocessEffect: exception trying to compile "+source.mName+" program: "+e.getMessage());
         throw new RuntimeException(e.getMessage());
         }
       }
@@ -259,7 +260,7 @@ public abstract class PostprocessEffect extends Effect implements InternalMaster
           }
         catch(NoSuchMethodException ex)
           {
-          android.util.Log.e("postprocess", cls.getSimpleName()+": exception getting method: "+ex.getMessage());
+          DistortedLibrary.logMessage("PostprocessEffect: "+cls.getSimpleName()+": exception getting method: "+ex.getMessage());
           method = null;
           }
 
@@ -269,7 +270,7 @@ public abstract class PostprocessEffect extends Effect implements InternalMaster
           }
         catch(Exception ex)
           {
-          android.util.Log.e("postprocess", "exception invoking method: "+ex.getMessage());
+          DistortedLibrary.logMessage("PostprocessEffect: exception invoking method: "+ex.getMessage());
           }
         }
       }
diff --git a/src/main/java/org/distorted/library/effect/VertexEffect.java b/src/main/java/org/distorted/library/effect/VertexEffect.java
index c9792d5..e9bdecf 100644
--- a/src/main/java/org/distorted/library/effect/VertexEffect.java
+++ b/src/main/java/org/distorted/library/effect/VertexEffect.java
@@ -21,6 +21,7 @@
 package org.distorted.library.effect;
 
 import org.distorted.library.effectqueue.EffectQueue;
+import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.type.Static1D;
 import org.distorted.library.type.Static3D;
 import org.distorted.library.type.Static4D;
@@ -97,7 +98,7 @@ public abstract class VertexEffect extends Effect
           }
         catch(NoSuchMethodException ex)
           {
-          android.util.Log.e("Effect", "exception getting method: "+ex.getMessage());
+          DistortedLibrary.logMessage("VertexEffect: exception getting method: "+ex.getMessage());
           method = null;
           }
 
@@ -113,7 +114,7 @@ public abstract class VertexEffect extends Effect
           }
         catch(Exception ex)
           {
-          android.util.Log.e("Effect", "exception invoking method: "+ex.getMessage());
+          DistortedLibrary.logMessage("VertexEffect: exception invoking method: "+ex.getMessage());
           }
         }
       }
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueue.java b/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
index f39997f..6a1c027 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
@@ -405,7 +405,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
       }
     else
       {
-      android.util.Log.e("queue", "getEffect: out of range "+position);
+      DistortedLibrary.logMessage("EffectQueue: getEffect: out of range "+position);
       return null;
       }
     }
@@ -490,7 +490,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
                        }
                      else
                        {
-                       android.util.Log.e("queue", "failed to add effect "+job.effect.getName());
+                       DistortedLibrary.logMessage("EffectQueue: failed to add effect "+job.effect.getName());
                        }
                      break;
         case DETACH: for(int j=0; j<mNumEffects; j++)
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
index 78118dc..5a3f4b7 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
@@ -20,11 +20,8 @@
 
 package org.distorted.library.effectqueue;
 
-import android.content.res.Resources;
 import android.opengl.GLES30;
-import android.util.Log;
 
-import org.distorted.library.R;
 import org.distorted.library.effect.EffectType;
 import org.distorted.library.effect.PostprocessEffect;
 import org.distorted.library.effect.VertexEffect;
@@ -74,6 +71,13 @@ public class EffectQueuePostprocess extends EffectQueue
   EffectQueuePostprocess(EffectQueuePostprocess source)
     {
     super(source);
+
+    mHalo = source.mHalo;
+    mUseHaloDepth = source.mUseHaloDepth;
+    mR = source.mR;
+    mG = source.mG;
+    mB = source.mB;
+    mA = source.mA;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -118,11 +122,8 @@ public class EffectQueuePostprocess extends EffectQueue
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  public static void createPrograms(Resources resources, int GLSL)
+  public static void createPrograms(InputStream vert, InputStream frag, int GLSL)
     {
-    final InputStream mainVertStream = resources.openRawResource(R.raw.main_vertex_shader);
-    final InputStream mainFragStream = resources.openRawResource(R.raw.preprocess_fragment_shader);
-
     int numV = VertexEffect.getNumEnabled();
 
     String version = "#version "+GLSL+" es\n";
@@ -136,12 +137,12 @@ public class EffectQueuePostprocess extends EffectQueue
 
     try
       {
-      mPreProgram = new DistortedProgram(mainVertStream, mainFragStream, mainVertHeader, mainFragHeader,
+      mPreProgram = new DistortedProgram(vert, frag, mainVertHeader, mainFragHeader,
                                          enabledEffectV, null, GLSL, null);
       }
     catch(Exception e)
       {
-      Log.e("POSTPROCESS", e.getClass().getSimpleName()+" trying to compile PRE program: "+e.getMessage());
+      DistortedLibrary.logMessage("EffectQueuePostprocess "+ e.getClass().getSimpleName()+" trying to compile PRE program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
diff --git a/src/main/java/org/distorted/library/main/DistortedFramebuffer.java b/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
index a5a5cc0..e60a3d5 100644
--- a/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
@@ -21,7 +21,6 @@
 package org.distorted.library.main;
 
 import android.opengl.GLES30;
-import android.util.Log;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -154,7 +153,7 @@ public class DistortedFramebuffer extends InternalOutputSurface
 
     if(status != GLES30.GL_FRAMEBUFFER_COMPLETE)
       {
-      Log.e("DistortedFramebuffer", "FRAMEBUFFER INCOMPLETE, "+message+" error="+status);
+      DistortedLibrary.logMessage("DistortedFramebuffer: FRAMEBUFFER INCOMPLETE, "+message+" error="+status);
 
       GLES30.glDeleteTextures(1, mColorH, 0);
       GLES30.glDeleteTextures(1, mDepthStencilH, 0);
diff --git a/src/main/java/org/distorted/library/main/DistortedLibrary.java b/src/main/java/org/distorted/library/main/DistortedLibrary.java
index 6e0276e..4910900 100644
--- a/src/main/java/org/distorted/library/main/DistortedLibrary.java
+++ b/src/main/java/org/distorted/library/main/DistortedLibrary.java
@@ -20,13 +20,16 @@
 
 package org.distorted.library.main;
 
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.pm.ConfigurationInfo;
-import android.content.res.Resources;
 import android.opengl.GLES30;
 import android.opengl.GLES31;
-import android.util.Log;
+
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.distorted.library.R;
 import org.distorted.library.effect.Effect;
@@ -44,14 +47,6 @@ import org.distorted.library.program.DistortedProgram;
 import org.distorted.library.program.VertexCompilationException;
 import org.distorted.library.type.Dynamic;
 
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * A singleton class used to control various global dialog_settings.
@@ -219,14 +214,19 @@ public class DistortedLibrary
    * compile its OIT problem only on the first attempt to use the OIT
    * Those will mainly be hardware-related: shaders do not compile on particular hardware, the required
    * OpenGL ES 3.0 is not supported, etc.
+   *
+   * Additionally, the User must be able to provide version of the OpenGL supported by the underlying
+   * hardware and InputStreams to given local files.
    */
-  public interface ExceptionListener
+  public interface LibraryUser
     {
     void distortedException(Exception ex);
+    int openGlVersion();
+    InputStream localFile(int fileID);
+    void logMessage(String message);
     }
 
-  private static ExceptionListener mListener;
-  private static Resources mResources;
+  private static LibraryUser mUser;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // private: hide this from Javadoc
@@ -241,8 +241,8 @@ public class DistortedLibrary
   private static void createMainProgram()
     {
     // MAIN PROGRAM ////////////////////////////////////
-    final InputStream mainVertStream = mResources.openRawResource(R.raw.main_vertex_shader);
-    final InputStream mainFragStream = mResources.openRawResource(R.raw.main_fragment_shader);
+    final InputStream mainVertStream = mUser.localFile(R.raw.main_vertex_shader);
+    final InputStream mainFragStream = mUser.localFile(R.raw.main_fragment_shader);
 
     int numF = FragmentEffect.getNumEnabled();
     int numV = VertexEffect.getNumEnabled();
@@ -267,7 +267,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile MAIN program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile MAIN program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -278,8 +278,8 @@ public class DistortedLibrary
     mTransformFeedbackH= GLES30.glGetUniformLocation( mMainProgramH, "u_TransformFeedback");
 
     // BLIT PROGRAM ////////////////////////////////////
-    final InputStream blitVertStream = mResources.openRawResource(R.raw.blit_vertex_shader);
-    final InputStream blitFragStream = mResources.openRawResource(R.raw.blit_fragment_shader);
+    final InputStream blitVertStream = mUser.localFile(R.raw.blit_vertex_shader);
+    final InputStream blitFragStream = mUser.localFile(R.raw.blit_fragment_shader);
 
     try
       {
@@ -287,7 +287,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile BLIT program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile BLIT program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -296,8 +296,8 @@ public class DistortedLibrary
     mBlitDepthH    = GLES30.glGetUniformLocation( blitProgramH, "u_Depth");
 
     // BLIT DEPTH PROGRAM ////////////////////////////////////
-    final InputStream blitDepthVertStream = mResources.openRawResource(R.raw.blit_depth_vertex_shader);
-    final InputStream blitDepthFragStream = mResources.openRawResource(R.raw.blit_depth_fragment_shader);
+    final InputStream blitDepthVertStream = mUser.localFile(R.raw.blit_depth_vertex_shader);
+    final InputStream blitDepthFragStream = mUser.localFile(R.raw.blit_depth_fragment_shader);
 
     try
       {
@@ -305,7 +305,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile BLIT DEPTH program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile BLIT DEPTH program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -317,11 +317,11 @@ public class DistortedLibrary
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static void createNormalProgram(Resources resources)
+  private static void createNormalProgram()
     {
     // NORMAL PROGRAM //////////////////////////////////////
-    final InputStream normalVertexStream   = resources.openRawResource(R.raw.normal_vertex_shader);
-    final InputStream normalFragmentStream = resources.openRawResource(R.raw.normal_fragment_shader);
+    final InputStream normalVertexStream   = mUser.localFile(R.raw.normal_vertex_shader);
+    final InputStream normalFragmentStream = mUser.localFile(R.raw.normal_fragment_shader);
 
     try
       {
@@ -329,7 +329,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile NORMAL program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile NORMAL program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -339,10 +339,10 @@ public class DistortedLibrary
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static void createFullProgram(Resources resources)
+  private static void createFullProgram()
     {
-    final InputStream fullVertStream = resources.openRawResource(R.raw.main_vertex_shader);
-    final InputStream fullFragStream = resources.openRawResource(R.raw.main_fragment_shader);
+    final InputStream fullVertStream = mUser.localFile(R.raw.main_vertex_shader);
+    final InputStream fullFragStream = mUser.localFile(R.raw.main_fragment_shader);
 
     int numV = VertexEffect.getAllEnabled();
 
@@ -367,7 +367,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile FULL program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile FULL program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -378,11 +378,11 @@ public class DistortedLibrary
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static void createOITProgram(Resources resources)
+  private static void createOITProgram()
     {
     // MAIN OIT PROGRAM ////////////////////////////////
-    final InputStream mainVertStream = resources.openRawResource(R.raw.main_vertex_shader);
-    final InputStream mainFragStream = resources.openRawResource(R.raw.main_fragment_shader);
+    final InputStream mainVertStream = mUser.localFile(R.raw.main_vertex_shader);
+    final InputStream mainFragStream = mUser.localFile(R.raw.main_fragment_shader);
 
     int numF = FragmentEffect.getNumEnabled();
     int numV = VertexEffect.getNumEnabled();
@@ -404,7 +404,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile MAIN OIT program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile MAIN OIT program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -416,8 +416,8 @@ public class DistortedLibrary
     mMainOITNumRecordsH = GLES30.glGetUniformLocation( mMainOITProgramH, "u_numRecords");
 
     // OIT CLEAR PROGRAM ////////////////////////////////////
-    final InputStream oitClearVertStream = resources.openRawResource(R.raw.oit_vertex_shader);
-    final InputStream oitClearFragStream = resources.openRawResource(R.raw.oit_clear_fragment_shader);
+    final InputStream oitClearVertStream = mUser.localFile(R.raw.oit_vertex_shader);
+    final InputStream oitClearFragStream = mUser.localFile(R.raw.oit_clear_fragment_shader);
 
     try
       {
@@ -425,7 +425,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile OIT CLEAR program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile OIT CLEAR program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -435,8 +435,8 @@ public class DistortedLibrary
     mOITClearSizeH         = GLES30.glGetUniformLocation( oitClearProgramH, "u_Size");
 
     // OIT BUILD PROGRAM ////////////////////////////////////
-    final InputStream oitBuildVertStream = resources.openRawResource(R.raw.oit_vertex_shader);
-    final InputStream oitBuildFragStream = resources.openRawResource(R.raw.oit_build_fragment_shader);
+    final InputStream oitBuildVertStream = mUser.localFile(R.raw.oit_vertex_shader);
+    final InputStream oitBuildFragStream = mUser.localFile(R.raw.oit_build_fragment_shader);
 
     try
       {
@@ -444,7 +444,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile OIT BUILD program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile OIT BUILD program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -457,8 +457,8 @@ public class DistortedLibrary
     mOITBuildNumRecordsH   = GLES30.glGetUniformLocation( oitBuildProgramH, "u_numRecords");
 
     // OIT COLLAPSE PROGRAM ///////////////////////////
-    final InputStream oitCollapseVertStream = resources.openRawResource(R.raw.oit_vertex_shader);
-    final InputStream oitCollapseFragStream = resources.openRawResource(R.raw.oit_collapse_fragment_shader);
+    final InputStream oitCollapseVertStream = mUser.localFile(R.raw.oit_vertex_shader);
+    final InputStream oitCollapseFragStream = mUser.localFile(R.raw.oit_collapse_fragment_shader);
 
     try
       {
@@ -466,7 +466,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile OIT COLLAPSE program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile OIT COLLAPSE program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -477,8 +477,8 @@ public class DistortedLibrary
     mOITCollapseSizeH         = GLES30.glGetUniformLocation( oitCollapseProgramH, "u_Size");
 
     // OIT RENDER PROGRAM ///////////////////////////
-    final InputStream oitRenderVertStream = resources.openRawResource(R.raw.oit_vertex_shader);
-    final InputStream oitRenderFragStream = resources.openRawResource(R.raw.oit_render_fragment_shader);
+    final InputStream oitRenderVertStream = mUser.localFile(R.raw.oit_vertex_shader);
+    final InputStream oitRenderFragStream = mUser.localFile(R.raw.oit_render_fragment_shader);
 
     try
       {
@@ -486,7 +486,7 @@ public class DistortedLibrary
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile OIT RENDER program: "+e.getMessage());
+      mUser.logMessage(e.getClass().getSimpleName()+" trying to compile OIT RENDER program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -504,11 +504,11 @@ public class DistortedLibrary
       {
       try
         {
-        createNormalProgram(mResources);
+        createNormalProgram();
         }
       catch(Exception ex)
         {
-        mListener.distortedException(ex);
+        mUser.distortedException(ex);
         return;
         }
       }
@@ -546,11 +546,11 @@ public class DistortedLibrary
       {
       try
         {
-        createFullProgram(mResources);
+        createFullProgram();
         }
       catch(Exception ex)
         {
-        mListener.distortedException(ex);
+        mUser.distortedException(ex);
         return;
         }
       }
@@ -684,7 +684,7 @@ public class DistortedLibrary
       }
     else
       {
-      Log.e("effects", "print: failed to map atomic buffer");
+      mUser.logMessage("printPreviousBuffer: failed to map atomic buffer");
       }
 
     GLES30.glUnmapBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER);
@@ -705,7 +705,7 @@ public class DistortedLibrary
       }
     else
       {
-      Log.e("effects", "zero: failed to map atomic buffer");
+      mUser.logMessage("zeroBuffer: failed to map atomic buffer");
       }
 
     GLES30.glUnmapBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER);
@@ -761,11 +761,11 @@ public class DistortedLibrary
 
         try
           {
-          createOITProgram(mResources);
+          createOITProgram();
           }
         catch(Exception ex)
           {
-          mListener.distortedException(ex);
+          mUser.distortedException(ex);
           return;
           }
         }
@@ -933,7 +933,7 @@ public class DistortedLibrary
 
             if( drvVersion<22 )
               {
-              Log.e("DISTORTED", "You are running this on a ARM Mali driver r"+driverVersion+".\n" +
+              mUser.logMessage("You are running this on a ARM Mali driver r"+driverVersion+".\n" +
                     "This is a buggy driver, please update to r22. Inserting workaround which uses a lot of memory.");
 
               mFBOQueueSize = queueSize;
@@ -943,15 +943,15 @@ public class DistortedLibrary
         }
       catch(Exception ex)
         {
-        android.util.Log.e("library", "exception trying to pattern match version: "+ex.toString());
+        mUser.logMessage("DistortedLibrary: exception trying to pattern match version: "+ex.toString());
         }
       }
     else if( mVendor.contains("Imagination") )
       {
       if( mRenderer.contains("GE8") )
         {
-        Log.e("DISTORTED", "You are running this on a PowerVR GE8XXX.\nDue to a buggy compiler OIT rendering will not work");
-        Log.e("DISTORTED", "GLSL Version "+GLES30.glGetString(GLES31.GL_SHADING_LANGUAGE_VERSION));
+        mUser.logMessage("You are running this on a PowerVR GE8XXX.\nDue to a buggy compiler OIT rendering will not work");
+        mUser.logMessage("GLSL Version "+GLES30.glGetString(GLES31.GL_SHADING_LANGUAGE_VERSION));
         }
       }
     else if( mVendor.contains("Qualcomm"))
@@ -960,17 +960,29 @@ public class DistortedLibrary
 
       if( mRenderer.contains("308") && (driverVersion==331 || driverVersion==415) )
         {
-        Log.e("DISTORTED", "You are running this on an Adreno 308 driver 331 or 415.\nStrange shit might happen.");
+        mUser.logMessage("You are running this on an Adreno 308 driver 331 or 415.\nStrange shit might happen.");
         mBuggyUBOs = true;
         }
       if( mGLSL<=300 && driverVersion<415 ) // This is only on old enough drivers, 84,95,100,104,140 and known bad, 415 is known good.
         {
-        Log.e("DISTORTED", "Slow compilation of Transform Feedback programs!");
+        mUser.logMessage("Slow compilation of Transform Feedback programs!");
         mFastCompilationTF = false;
         }
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Not an external API
+ *
+ * @y.exclude
+ */
+
+  public static void logMessage(String message)
+    {
+    mUser.logMessage(message);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Return OpenGL ES version supported by the hardware we are running on.
@@ -989,12 +1001,11 @@ public class DistortedLibrary
  * <p>
  * Needs to be called from a thread holding the OpenGL context.
  *
- * @param context  Context of the App using the library - used to open up Resources and read Shader code.
- * @param listener The library will send all (asynchronous!) exceptions there.
+ * @param user The Code which will be using this library, which must implement the LibraryUser interface.
  */
-  public static void onSurfaceCreated(final Context context, final ExceptionListener listener)
+  public static void onSurfaceCreated(final LibraryUser user)
     {
-    onSurfaceCreated(context,listener,4);
+    onSurfaceCreated(user,4);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1004,26 +1015,21 @@ public class DistortedLibrary
  * <p>
  * Needs to be called from a thread holding the OpenGL context.
  *   
- * @param context   Context of the App using the library - used to open up Resources and read Shader code.
- * @param listener  The library will send all (asynchronous!) exceptions there.
+ * @param user The Code which will be using this library, which must implement the LibraryUser interface.
  * @param queueSize the size of the FBO queue, a workaround for the bug on Mali drivers. Use a small integer - 1,...,4
  */
-  public static void onSurfaceCreated(final Context context, final ExceptionListener listener, int queueSize)
+  public static void onSurfaceCreated(final LibraryUser user, int queueSize)
     {
-    final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-    final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
-
-    int glESversion = configurationInfo.reqGlEsVersion;
-    int major = glESversion >> 16;
-    int minor = glESversion & 0xff;
-
-    mListener = listener;
+    mUser = user;
+    int version = mUser.openGlVersion();
+    int major = version/100;
+    int minor = ((version/10)%10);
 
     if( major< 3 )
       {
       mGLSL = 100*major + 10*minor;
       VertexCompilationException ex = new VertexCompilationException("at least OpenGL ES 3.0 required, this device supports only "+major+"."+minor);
-      mListener.distortedException(ex);
+      mUser.distortedException(ex);
       }
     else
       {
@@ -1038,11 +1044,11 @@ public class DistortedLibrary
     GLES30.glGetIntegerv(GLES30.GL_MAX_FRAGMENT_UNIFORM_VECTORS, tmp, 0);
     mMaxNumberOfFraUniforms = tmp[0];
 
-    android.util.Log.e("DISTORTED", "Using OpenGL ES "+major+"."+minor);
+    mUser.logMessage("Using OpenGL ES "+major+"."+minor);
 /*
-    android.util.Log.e("DISTORTED", "max texture size: "+mMaxTextureSize);
-    android.util.Log.e("DISTORTED", "max num vert: "+mMaxNumberOfVerUniforms);
-    android.util.Log.e("DISTORTED", "max num frag: "+mMaxNumberOfFraUniforms);
+    mUser.logMessage("max texture size: "+mMaxTextureSize);
+    mUser.logMessage("max num vert: "+mMaxNumberOfVerUniforms);
+    mUser.logMessage("max num frag: "+mMaxNumberOfFraUniforms);
 */
     mGLSL_VERSION = "#version "+mGLSL+" es\n";
 
@@ -1052,24 +1058,24 @@ public class DistortedLibrary
     detectBuggyDriversAndSetQueueSize(queueSize);
     EffectMessageSender.startSending();
 
-    mResources = context.getResources();
-
     try
       {
       createMainProgram();
       }
     catch(Exception ex)
       {
-      mListener.distortedException(ex);
+      mUser.distortedException(ex);
       }
 
     try
       {
-      EffectQueuePostprocess.createPrograms(mResources, mGLSL);
+      final InputStream vertStream = mUser.localFile(R.raw.main_vertex_shader);
+      final InputStream fragStream = mUser.localFile(R.raw.preprocess_fragment_shader);
+      EffectQueuePostprocess.createPrograms(vertStream, fragStream, mGLSL);
       }
     catch(Exception ex)
       {
-      mListener.distortedException(ex);
+      mUser.distortedException(ex);
       }
 
     try
@@ -1078,7 +1084,7 @@ public class DistortedLibrary
       }
     catch(Exception ex)
       {
-      mListener.distortedException(ex);
+      mUser.distortedException(ex);
       }
     }
 
diff --git a/src/main/java/org/distorted/library/main/DistortedNode.java b/src/main/java/org/distorted/library/main/DistortedNode.java
index 1c7d84a..baf3a7e 100644
--- a/src/main/java/org/distorted/library/main/DistortedNode.java
+++ b/src/main/java/org/distorted/library/main/DistortedNode.java
@@ -786,7 +786,7 @@ public class DistortedNode implements InternalChildrenList.Parent
   public void debug(int depth)
     {
     String dbg = mEffects.debug(depth);
-    android.util.Log.e("D", dbg);
+    DistortedLibrary.logMessage(dbg);
 
     int numChildren = mChildren.getNumChildren();
 
diff --git a/src/main/java/org/distorted/library/main/DistortedScreen.java b/src/main/java/org/distorted/library/main/DistortedScreen.java
index a113781..bbc2deb 100644
--- a/src/main/java/org/distorted/library/main/DistortedScreen.java
+++ b/src/main/java/org/distorted/library/main/DistortedScreen.java
@@ -209,7 +209,7 @@ public class DistortedScreen extends DistortedFramebuffer
 
     if( err!=GLES30.GL_NO_ERROR )
       {
-      android.util.Log.e("D", "DISTORTED: OpenGL error "+err);
+      DistortedLibrary.logMessage("DistortedScreen: OpenGL error "+err);
       }
 */
     return numrender+1;
diff --git a/src/main/java/org/distorted/library/main/DistortedTexture.java b/src/main/java/org/distorted/library/main/DistortedTexture.java
index f04f8a3..98ff9d7 100644
--- a/src/main/java/org/distorted/library/main/DistortedTexture.java
+++ b/src/main/java/org/distorted/library/main/DistortedTexture.java
@@ -147,7 +147,7 @@ public class DistortedTexture extends InternalSurface
 
     if( width>max || height>max )
       {
-      android.util.Log.e("texture","error, trying to upload too large texture of size "+width+" x "+height+". Max is "+max);
+      DistortedLibrary.logMessage("DistortedTexture: error, trying to upload too large texture of size "+width+" x "+height+". Max is "+max);
       return false;
       }
     else
@@ -178,7 +178,7 @@ public class DistortedTexture extends InternalSurface
 
     if( width>max || height>max )
       {
-      android.util.Log.e("texture","error, trying to upload too large texture of size "+width+" x "+height+". Max is "+max);
+      DistortedLibrary.logMessage("DistortedTexture: error, trying to upload too large texture of size "+width+" x "+height+". Max is "+max);
       return false;
       }
     else
diff --git a/src/main/java/org/distorted/library/main/InternalObject.java b/src/main/java/org/distorted/library/main/InternalObject.java
index b399347..a90dac6 100644
--- a/src/main/java/org/distorted/library/main/InternalObject.java
+++ b/src/main/java/org/distorted/library/main/InternalObject.java
@@ -73,7 +73,7 @@ abstract class InternalObject
       default       : str+=" ERROR? ";
       }
 
-    android.util.Log.e("Object", str+printDetails()+msg);
+    DistortedLibrary.logMessage("InternalObject: "+str+printDetails()+msg);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/main/InternalRenderState.java b/src/main/java/org/distorted/library/main/InternalRenderState.java
index 6e38c3d..ecaab47 100644
--- a/src/main/java/org/distorted/library/main/InternalRenderState.java
+++ b/src/main/java/org/distorted/library/main/InternalRenderState.java
@@ -273,7 +273,7 @@ public class InternalRenderState
     if( cState.stencilTest!=0 )
       {
       cState.stencilTest = 0;
-      //android.util.Log.d("State", "stencil test off");
+      //DistortedLibrary.logMessage("InternalRenderState: stencil test off");
       GLES30.glDisable(GLES30.GL_STENCIL_TEST);
       }
 
@@ -282,7 +282,7 @@ public class InternalRenderState
     if( cState.depthTest!=0 )
       {
       cState.depthTest = 0;
-      //android.util.Log.d("State", "depth test off");
+      //DistortedLibrary.logMessage("InternalRenderState: depth test off");
       GLES30.glDisable(GLES30.GL_DEPTH_TEST);
       }
 
@@ -297,7 +297,7 @@ public class InternalRenderState
       cState.colorMaskG = 0;
       cState.colorMaskB = 0;
       cState.colorMaskA = 0;
-      //android.util.Log.d("State", "switch off color writing");
+      //DistortedLibrary.logMessage("InternalRenderState: switch off color writing");
       GLES30.glColorMask(false,false,false,false);
       }
 
@@ -306,7 +306,7 @@ public class InternalRenderState
     if( cState.depthMask!=0 )
       {
       cState.depthMask = 0;
-      //android.util.Log.d("State", "switch off depth writing");
+      //DistortedLibrary.logMessage("InternalRenderState: switch off depth writing");
       GLES30.glDepthMask(false);
       }
 
@@ -315,7 +315,7 @@ public class InternalRenderState
     if( cState.stencilMask!= 0x00 )
       {
       cState.stencilMask = 0x00;
-      //android.util.Log.d("State", "stencil mask off");
+      //DistortedLibrary.logMessage("InternalRenderState: stencil mask off");
       GLES30.glStencilMask(cState.stencilMask);
       }
     }
@@ -329,7 +329,7 @@ public class InternalRenderState
     if( cState.stencilTest!=0 )
       {
       cState.stencilTest = 0;
-      //android.util.Log.d("State", "stencil test off");
+      //DistortedLibrary.logMessage("InternalRenderState: stencil test off");
       GLES30.glDisable(GLES30.GL_STENCIL_TEST);
       }
 
@@ -338,7 +338,7 @@ public class InternalRenderState
     if( cState.depthTest!=0 )
       {
       cState.depthTest = 0;
-      //android.util.Log.d("State", "depth test off");
+      //DistortedLibrary.logMessage("InternalRenderState: depth test off");
       GLES30.glDisable(GLES30.GL_DEPTH_TEST);
       }
 
@@ -353,7 +353,7 @@ public class InternalRenderState
       cState.colorMaskG = 1;
       cState.colorMaskB = 1;
       cState.colorMaskA = 1;
-      //android.util.Log.d("State", "switch on color writing");
+      //DistortedLibrary.logMessage("InternalRenderState: switch on color writing");
       GLES30.glColorMask(true,true,true,true);
       }
 
@@ -362,7 +362,7 @@ public class InternalRenderState
     if( cState.depthMask!=1 )
       {
       cState.depthMask = 1;
-      //android.util.Log.d("State", "switch on depth writing");
+      //DistortedLibrary.logMessage("InternalRenderState: switch on depth writing");
       GLES30.glDepthMask(true);
       }
 
@@ -371,7 +371,7 @@ public class InternalRenderState
     if( cState.stencilMask!= 0x00 )
       {
       cState.stencilMask = 0x00;
-      //android.util.Log.d("State", "stencil mask off");
+      //DistortedLibrary.logMessage("InternalRenderState: stencil mask off");
       GLES30.glStencilMask(cState.stencilMask);
       }
     }
@@ -435,7 +435,7 @@ public class InternalRenderState
     if( cState.stencilTest!=1 )
       {
       cState.stencilTest = 1;
-      //android.util.Log.d("State", "stencil test on");
+      //DistortedLibrary.logMessage("InternalRenderState: stencil test on");
       GLES30.glEnable(GLES30.GL_STENCIL_TEST);
       }
 
@@ -448,7 +448,7 @@ public class InternalRenderState
       cState.stencilFuncFunc = GLES30.GL_ALWAYS;
       cState.stencilFuncRef  = 1;
       cState.stencilFuncMask = STENCIL_MASK;
-      //android.util.Log.d("State", "stencil func on");
+      //DistortedLibrary.logMessage("InternalRenderState: stencil func on");
       GLES30.glStencilFunc(cState.stencilFuncFunc,cState.stencilFuncRef,cState.stencilFuncMask);
       }
 
@@ -461,7 +461,7 @@ public class InternalRenderState
       cState.stencilOpSfail = GLES30.GL_KEEP;
       cState.stencilOpDpfail= GLES30.GL_KEEP;
       cState.stencilOpDppass= GLES30.GL_REPLACE;
-      //android.util.Log.d("State", "stencil op on");
+      //DistortedLibrary.logMessage("InternalRenderState: stencil op on");
       GLES30.glStencilOp(cState.stencilOpSfail,cState.stencilOpDpfail,cState.stencilOpDppass);
       }
 
@@ -478,7 +478,7 @@ public class InternalRenderState
       cState.colorMaskG = clr;
       cState.colorMaskB = clr;
       cState.colorMaskA = clr;
-      //android.util.Log.d("State", "switch off color writing");
+      //DistortedLibrary.logMessage("InternalRenderState: switch off color writing");
       GLES30.glColorMask(color,color,color,color);
       }
 
@@ -487,7 +487,7 @@ public class InternalRenderState
     if( cState.depthMask!=1 )
       {
       cState.depthMask = 1;
-      //android.util.Log.d("State", "switch on depth writing");
+      //DistortedLibrary.logMessage("InternalRenderState: switch on depth writing");
       GLES30.glDepthMask(true);
       }
 
@@ -496,7 +496,7 @@ public class InternalRenderState
     if( cState.stencilMask!= STENCIL_MASK )
       {
       cState.stencilMask = STENCIL_MASK;
-      //android.util.Log.d("State", "stencil mask on");
+      //DistortedLibrary.logMessage("InternalRenderState: stencil mask on");
       GLES30.glStencilMask(cState.stencilMask);
       }
     }
@@ -648,13 +648,13 @@ public class InternalRenderState
 
   void apply()
     {
-    //android.util.Log.e("State", "APPLYING STATE");
+    //DistortedLibrary.logMessage("InternalRenderState: APPLYING STATE");
 
     /////////////////////////////////////////////////////
     // 1. Write to color buffer?
     if( mState.colorMaskR!=cState.colorMaskR || mState.colorMaskG!=cState.colorMaskG || mState.colorMaskB!=cState.colorMaskB || mState.colorMaskA!=cState.colorMaskA)
       {
-      //android.util.Log.d("State", "setting color mask");
+      //DistortedLibrary.logMessage("InternalRenderState: setting color mask");
       cState.colorMaskR = mState.colorMaskR;
       cState.colorMaskG = mState.colorMaskG;
       cState.colorMaskB = mState.colorMaskB;
@@ -670,12 +670,12 @@ public class InternalRenderState
 
       if (cState.depthTest == 0)
         {
-        //android.util.Log.d("State", "disabling depth test");
+        //DistortedLibrary.logMessage("InternalRenderState: disabling depth test");
         GLES30.glDisable(GLES30.GL_DEPTH_TEST);
         }
       else
         {
-        //android.util.Log.d("State", "enable depth test");
+        //DistortedLibrary.logMessage("InternalRenderState: enable depth test");
         GLES30.glEnable(GLES30.GL_DEPTH_TEST);
         }
       }
@@ -684,7 +684,7 @@ public class InternalRenderState
     // 3. Change Depth Function?
     if( mState.depthFunc!=cState.depthFunc )
       {
-      //android.util.Log.d("State", "setting depth func");
+      //DistortedLibrary.logMessage("InternalRenderState: setting depth func");
       cState.depthFunc = mState.depthFunc;
       GLES30.glDepthFunc(cState.depthFunc);
       }
@@ -693,7 +693,7 @@ public class InternalRenderState
     // 4. Write to Depth buffer?
     if( mState.depthMask!=cState.depthMask )
       {
-      //android.util.Log.d("State", "setting depth mask");
+      //DistortedLibrary.logMessage("InternalRenderState: setting depth mask");
       cState.depthMask = mState.depthMask;
       GLES30.glDepthMask(cState.depthMask==1);
       }
@@ -706,12 +706,12 @@ public class InternalRenderState
 
       if (cState.blend == 0)
         {
-        //android.util.Log.d("State", "disabling blending");
+        //DistortedLibrary.logMessage("InternalRenderState: disabling blending");
         GLES30.glDisable(GLES30.GL_BLEND);
         }
       else
         {
-        //android.util.Log.d("State", "enabling blending");
+        //DistortedLibrary.logMessage("InternalRenderState: enabling blending");
         GLES30.glEnable(GLES30.GL_BLEND);
         }
       }
@@ -720,7 +720,7 @@ public class InternalRenderState
     // 6. Change Blend function?
     if( mState.blendSrc!=cState.blendSrc || mState.blendDst!=cState.blendDst )
       {
-      //android.util.Log.d("State", "setting blend function");
+      //DistortedLibrary.logMessage("InternalRenderState: setting blend function");
       cState.blendSrc = mState.blendSrc;
       cState.blendDst = mState.blendDst;
       GLES30.glBlendFunc(cState.blendSrc,cState.blendDst);
@@ -734,12 +734,12 @@ public class InternalRenderState
 
       if (cState.stencilTest == 0)
         {
-        //android.util.Log.d("State", "disabling stencil test");
+        //DistortedLibrary.logMessage("InternalRenderState: disabling stencil test");
         GLES30.glDisable(GLES30.GL_STENCIL_TEST);
         }
       else
         {
-        //android.util.Log.d("State", "enabling stencil test");
+        //DistortedLibrary.logMessage("InternalRenderState: enabling stencil test");
         GLES30.glEnable(GLES30.GL_STENCIL_TEST);
         }
       }
@@ -748,7 +748,7 @@ public class InternalRenderState
     // 8. Adjust Stencil function?
     if( mState.stencilFuncFunc!=cState.stencilFuncFunc || mState.stencilFuncRef!=cState.stencilFuncRef || mState.stencilFuncMask!=cState.stencilFuncMask )
       {
-      //android.util.Log.d("State", "setting stencil function");
+      //DistortedLibrary.logMessage("InternalRenderState: setting stencil function");
       cState.stencilFuncFunc = mState.stencilFuncFunc;
       cState.stencilFuncRef  = mState.stencilFuncRef ;
       cState.stencilFuncMask = mState.stencilFuncMask;
@@ -759,7 +759,7 @@ public class InternalRenderState
     // 9. Adjust Stencil operation?
     if( mState.stencilOpSfail!=cState.stencilOpSfail || mState.stencilOpDpfail!=cState.stencilOpDpfail || mState.stencilOpDppass!=cState.stencilOpDppass )
       {
-      //android.util.Log.d("State", "setting stencil op");
+      //DistortedLibrary.logMessage("InternalRenderState: setting stencil op");
       cState.stencilOpSfail = mState.stencilOpSfail;
       cState.stencilOpDpfail= mState.stencilOpDpfail;
       cState.stencilOpDppass= mState.stencilOpDppass;
@@ -770,7 +770,7 @@ public class InternalRenderState
     // 10. Write to Stencil buffer?
     if( mState.stencilMask!=cState.stencilMask )
       {
-      //android.util.Log.d("State", "setting stencil mask");
+      //DistortedLibrary.logMessage("InternalRenderState: setting stencil mask");
       cState.stencilMask = mState.stencilMask;
       GLES30.glStencilMask(cState.stencilMask);
       }
@@ -779,7 +779,7 @@ public class InternalRenderState
     // 11. Clear buffers?
     if( mClear!=0 )
       {
-      //android.util.Log.d("State", "clearing buffer");
+      //DistortedLibrary.logMessage("InternalRenderState: clearing buffer");
       GLES30.glClear(mClear);
       }
     }
diff --git a/src/main/java/org/distorted/library/main/InternalStackFrame.java b/src/main/java/org/distorted/library/main/InternalStackFrame.java
index 8addad1..2efc825 100644
--- a/src/main/java/org/distorted/library/main/InternalStackFrame.java
+++ b/src/main/java/org/distorted/library/main/InternalStackFrame.java
@@ -371,15 +371,15 @@ public class InternalStackFrame
 
   static void debugListsGeneric(LinkedList<InternalObject> list, HashMap<Long,Job> map,String frameMarker)
     {
-    android.util.Log.e("Object", frameMarker);
-    android.util.Log.e("Object", "  Done list:");
+    DistortedLibrary.logMessage("InternalStackFrame: "+frameMarker);
+    DistortedLibrary.logMessage("InternalStackFrame:  Done list:");
 
     for(InternalObject object : list)
       {
       object.print("  ");
       }
 
-    android.util.Log.e("Object", "  ToDo list:");
+    DistortedLibrary.logMessage("InternalStackFrame: ToDo list:");
 
     Job job;
 
@@ -394,13 +394,13 @@ public class InternalStackFrame
 
   void debugMap(String frameMarker)
     {
-    android.util.Log.e("Object", frameMarker);
+    DistortedLibrary.logMessage("InternalStackFrame: "+frameMarker);
     InternalNodeData tmp;
 
     for(ArrayList<Long> key: mMapNodeID.keySet())
       {
       tmp = mMapNodeID.get(key);
-      android.util.Log.e("NodeData", "NodeID: "+tmp.ID+" <-- "+key);
+      DistortedLibrary.logMessage("InternalStackFrame: NodeID: "+tmp.ID+" <-- "+key);
       }
     }
   }
diff --git a/src/main/java/org/distorted/library/mesh/DeferredJobs.java b/src/main/java/org/distorted/library/mesh/DeferredJobs.java
index 0018695..ce8e5a3 100644
--- a/src/main/java/org/distorted/library/mesh/DeferredJobs.java
+++ b/src/main/java/org/distorted/library/mesh/DeferredJobs.java
@@ -28,8 +28,6 @@ import org.distorted.library.type.Static4D;
 
 import java.util.ArrayList;
 
-import android.util.Log;
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Not part of public API, do not document (public only because has to be cleaned from the main package)
@@ -201,7 +199,7 @@ public class DeferredJobs
 
       str += (" next: "+numNext+" prev: "+numPrev);
 
-      Log.e("job", str);
+      DistortedLibrary.logMessage("DeferredJobs: "+str);
 
       for(int i=0; i<numPrev; i++)
         {
diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.java b/src/main/java/org/distorted/library/mesh/MeshBase.java
index 7a80acd..5fd645b 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.java
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.java
@@ -22,11 +22,11 @@ package org.distorted.library.mesh;
 
 import android.opengl.GLES30;
 import android.opengl.Matrix;
-import android.util.Log;
 
 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;
@@ -462,7 +462,7 @@ public abstract class MeshBase
 
      if( origVertices!=mNumVertices )
        {
-       android.util.Log.e("mesh", "join: origVertices: "+origVertices+" numVertices: "+mNumVertices);
+       DistortedLibrary.logMessage("MeshBase: join: origVertices: "+origVertices+" numVertices: "+mNumVertices);
        }
 
      int endIndex, index=0, numEffComp = mEffComponent.size();
@@ -621,7 +621,7 @@ public abstract class MeshBase
      else
        {
        int error = GLES30.glGetError();
-       Log.e("mesh", "failed to map tf buffer, error="+error);
+       DistortedLibrary.logMessage("MeshBase: failed to map tf buffer, error="+error);
        }
 
      GLES30.glUnmapBuffer(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER);
@@ -641,7 +641,7 @@ public abstract class MeshBase
        }
      else
        {
-       android.util.Log.e("mesh", "JobNode null");
+       DistortedLibrary.logMessage("MeshBase: JobNode null");
        }
      }
 
@@ -670,7 +670,7 @@ public abstract class MeshBase
          sb.append(") ");
          }
 
-       Log.d("mesh", sb.toString() );
+       DistortedLibrary.logMessage("MeshBase: "+sb.toString() );
        }
      }
 
@@ -694,7 +694,7 @@ public abstract class MeshBase
        sb.append(mVertAttribs1[VERT1_ATTRIBS*i+POS_ATTRIB+2]);
        sb.append(") ");
        }
-     Log.d("mesh", sb.toString());
+     DistortedLibrary.logMessage("MeshBase: "+sb.toString());
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -713,7 +713,7 @@ public abstract class MeshBase
        sb.append(' ');
        }
 
-     Log.d("mesh", sb.toString());
+     DistortedLibrary.logMessage("MeshBase: "+sb.toString());
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -745,7 +745,7 @@ public abstract class MeshBase
          }
        }
 
-     Log.d("mesh", sb.toString());
+     DistortedLibrary.logMessage("MeshBase: "+sb.toString());
      }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -907,13 +907,13 @@ public abstract class MeshBase
          }
        else
          {
-         android.util.Log.e("mesh", "Error: unknown mesh file version "+String.format("0x%08X", version));
+         DistortedLibrary.logMessage("MeshBase: Error: unknown mesh file version "+String.format("0x%08X", version));
          return 0;
          }
        }
      catch(Exception e)
        {
-       android.util.Log.e("mesh", "Exception1 trying to read file: "+e.toString());
+       DistortedLibrary.logMessage("MeshBase: Exception1 trying to read file: "+e.toString());
        return 0;
        }
 
@@ -941,7 +941,7 @@ public abstract class MeshBase
          }
        catch(Exception e)
          {
-         android.util.Log.e("mesh", "Exception2 trying to read file: "+e.toString());
+         DistortedLibrary.logMessage("MeshBase: Exception2 trying to read file: "+e.toString());
          return 0;
          }
 
@@ -1008,7 +1008,7 @@ public abstract class MeshBase
 
                   floatBuf.get(mVertAttribs1, 0, VERT1_ATTRIBS*mNumVertices);
                   break;
-         default: android.util.Log.e("mesh", "Error: unknown mesh file version "+String.format("0x%08X", version));
+         default: DistortedLibrary.logMessage("MeshBase: Error: unknown mesh file version "+String.format("0x%08X", version));
                   return 0;
          }
 
@@ -1101,7 +1101,7 @@ public abstract class MeshBase
        }
      catch(IOException ex)
        {
-       android.util.Log.e("mesh", "IOException trying to write a mesh: "+ex.toString());
+       DistortedLibrary.logMessage("MeshBase: IOException trying to write a mesh: "+ex.toString());
        }
      }
 
diff --git a/src/main/java/org/distorted/library/mesh/MeshCubes.java b/src/main/java/org/distorted/library/mesh/MeshCubes.java
index 0e888d2..7f26711 100644
--- a/src/main/java/org/distorted/library/mesh/MeshCubes.java
+++ b/src/main/java/org/distorted/library/mesh/MeshCubes.java
@@ -20,6 +20,7 @@
 
 package org.distorted.library.mesh;
 
+import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.type.Static4D;
 
 import java.util.ArrayList;
@@ -771,7 +772,7 @@ public class MeshCubes extends MeshBase
      mInflateY = null;
 
      if( currVert!=numVertices )
-       android.util.Log.e("MeshCubes", "currVert " +currVert+" numVertices="+numVertices );
+       DistortedLibrary.logMessage("MeshCubes: currVert " +currVert+" numVertices="+numVertices );
 
      setAttribs(attribs1,attribs2);
      }
diff --git a/src/main/java/org/distorted/library/mesh/MeshPolygon.java b/src/main/java/org/distorted/library/mesh/MeshPolygon.java
index 4714786..719c189 100644
--- a/src/main/java/org/distorted/library/mesh/MeshPolygon.java
+++ b/src/main/java/org/distorted/library/mesh/MeshPolygon.java
@@ -21,6 +21,9 @@
 package org.distorted.library.mesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+import org.distorted.library.main.DistortedLibrary;
+
 /**
  * Create a polygon of any shape and varying elevations from the edges towards the center.
  * <p>
@@ -278,7 +281,7 @@ public class MeshPolygon extends MeshBase
     buildGrid(attribs1,attribs2);
 
     if( remainingVert!=0 )
-      android.util.Log.d("MeshPolygon", "remainingVert " +remainingVert );
+      DistortedLibrary.logMessage("MeshPolygon: remainingVert " +remainingVert );
 
     if( centerX!=0.0f || centerY!=0.0f )
       {
diff --git a/src/main/java/org/distorted/library/mesh/MeshSphere.java b/src/main/java/org/distorted/library/mesh/MeshSphere.java
index dbbcd3e..ed06b12 100644
--- a/src/main/java/org/distorted/library/mesh/MeshSphere.java
+++ b/src/main/java/org/distorted/library/mesh/MeshSphere.java
@@ -21,6 +21,9 @@
 package org.distorted.library.mesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+import org.distorted.library.main.DistortedLibrary;
+
 /**
  * Create a Mesh which approximates a sphere.
  * <p>
@@ -258,7 +261,7 @@ public class MeshSphere extends MeshBase
       }
 
     if( currentVert!=numVertices )
-      android.util.Log.d("MeshSphere", "currentVert= " +currentVert+" numVertices="+numVertices );
+      DistortedLibrary.logMessage("MeshSphere: currentVert= " +currentVert+" numVertices="+numVertices );
 
     setAttribs(attribs1, attribs2);
     }
diff --git a/src/main/java/org/distorted/library/mesh/MeshSquare.java b/src/main/java/org/distorted/library/mesh/MeshSquare.java
index b1e84e3..f4ef9f8 100644
--- a/src/main/java/org/distorted/library/mesh/MeshSquare.java
+++ b/src/main/java/org/distorted/library/mesh/MeshSquare.java
@@ -21,6 +21,9 @@
 package org.distorted.library.mesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+import org.distorted.library.main.DistortedLibrary;
+
 /**
  * Create a flat, rectangular grid.
  * <p>
@@ -195,7 +198,7 @@ public class MeshSquare extends MeshBase
     buildGrid(attribs1,attribs2);
 
     if( remainingVert!=0 )
-      android.util.Log.d("MeshRectangles", "remainingVert " +remainingVert );
+      DistortedLibrary.logMessage("MeshSquare: remainingVert " +remainingVert );
 
     setAttribs(attribs1,attribs2);
     }
@@ -220,7 +223,7 @@ public class MeshSquare extends MeshBase
     buildGrid(attribs1,attribs2,xLoc,yLoc);
 
     if( remainingVert!=0 )
-      android.util.Log.d("MeshRectangles", "remainingVert " +remainingVert );
+      DistortedLibrary.logMessage("MeshSquare: remainingVert " +remainingVert );
 
     setAttribs(attribs1,attribs2);
     }
diff --git a/src/main/java/org/distorted/library/mesh/MeshTriangle.java b/src/main/java/org/distorted/library/mesh/MeshTriangle.java
index 91e43ef..bf86e74 100644
--- a/src/main/java/org/distorted/library/mesh/MeshTriangle.java
+++ b/src/main/java/org/distorted/library/mesh/MeshTriangle.java
@@ -21,6 +21,9 @@
 package org.distorted.library.mesh;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+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)
  */
@@ -114,7 +117,7 @@ public class MeshTriangle extends MeshBase
      buildGrid(attribs1, attribs2, level);
 
      if( remainingVert!=0 )
-       android.util.Log.d("MeshTriangles", "remainingVert " +remainingVert );
+       DistortedLibrary.logMessage("MeshTriangle: remainingVert " +remainingVert );
 
      setAttribs(attribs1, attribs2);
      }
diff --git a/src/main/java/org/distorted/library/program/DistortedProgram.java b/src/main/java/org/distorted/library/program/DistortedProgram.java
index fad1d04..5747d5e 100644
--- a/src/main/java/org/distorted/library/program/DistortedProgram.java
+++ b/src/main/java/org/distorted/library/program/DistortedProgram.java
@@ -22,6 +22,8 @@ package org.distorted.library.program;
 
 import android.opengl.GLES30;
 
+import org.distorted.library.main.DistortedLibrary;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
@@ -91,7 +93,7 @@ public class DistortedProgram
       final int[] numberOfUniforms = new int[1];
       GLES30.glGetProgramiv(programHandle, GLES30.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
 
-      //android.util.Log.d("program", "number of active uniforms="+numberOfUniforms[0]);
+      //DistortedLibrary.logMessage("DistortedProgram: number of active uniforms="+numberOfUniforms[0]);
       }
 
     return programHandle;
@@ -137,7 +139,7 @@ public class DistortedProgram
 
       if( subline.startsWith(mUniformStr))
         {
-        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
+        //DistortedLibrary.logMessage("DistortedProgram: GOOD LINE: " +subline+" subLen="+subLen);
 
         for(nameBegin=subLen-1; nameBegin>mUniformLen-2; nameBegin--)
           {
@@ -259,7 +261,7 @@ public class DistortedProgram
 
           if( attribute!=null )
             {
-            //android.util.Log.d("program", "new attribute: "+attribute);
+            //DistortedLibrary.logMessage("DistortedProgram: new attribute: "+attribute);
             if( attrList.length()>0 ) attrList+=" ";
             attrList += attribute;
             }
@@ -330,7 +332,7 @@ public class DistortedProgram
       }
     else
       {
-      android.util.Log.e("Program", "Error: marker string not found in SHADER!");
+      DistortedLibrary.logMessage("DistortedProgram: Error: marker string not found in SHADER!");
       }
 
     return null;
diff --git a/src/main/java/org/distorted/library/type/Dynamic.java b/src/main/java/org/distorted/library/type/Dynamic.java
index 7356bb5..124cdb7 100644
--- a/src/main/java/org/distorted/library/type/Dynamic.java
+++ b/src/main/java/org/distorted/library/type/Dynamic.java
@@ -20,6 +20,8 @@
 
 package org.distorted.library.type;
 
+import org.distorted.library.main.DistortedLibrary;
+
 import java.util.Random;
 import java.util.Vector;
 
@@ -404,7 +406,7 @@ public abstract class Dynamic
         t = ((int)(1000*baseV[i][j]))/(1000.0f);
         s+=(" "+t);
         }
-      android.util.Log.e("dynamic", str+" base "+i+" : " + s);
+      DistortedLibrary.logMessage("Dynamic: "+str+" base "+i+" : " + s);
       }
     }
 
@@ -429,7 +431,7 @@ public abstract class Dynamic
 
       if( len[i] == 0.0f || len[0]/len[i] < 0.95f || len[0]/len[i]>1.05f )
         {
-        android.util.Log.e("dynamic", "length of vector "+i+" : "+Math.sqrt(len[i]));
+        DistortedLibrary.logMessage("Dynamic: length of vector "+i+" : "+Math.sqrt(len[i]));
         error = true;
         }
       }
@@ -448,7 +450,7 @@ public abstract class Dynamic
 
         if( cosA > 0.05f || cosA < -0.05f )
           {
-          android.util.Log.e("dynamic", "cos angle between vectors "+i+" and "+j+" : "+cosA);
+          DistortedLibrary.logMessage("Dynamic: cos angle between vectors "+i+" and "+j+" : "+cosA);
           error = true;
           }
         }
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.java b/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.java
index 1f584b1..55df663 100644
--- a/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.java
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.java
@@ -22,6 +22,7 @@ package org.distorted.library.uniformblock;
 
 import android.opengl.GLES30;
 
+import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.InternalBuffer;
 import org.distorted.library.mesh.MeshBase;
 
@@ -152,6 +153,6 @@ public class UniformBlockAssociation
 
     String res = builder.toString();
 
-    android.util.Log.e("uba", res);
+    DistortedLibrary.logMessage("UniformBlockAssociation: "+res);
     }
   }
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.java b/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.java
index f9335ed..c638973 100644
--- a/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.java
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.java
@@ -22,6 +22,7 @@ package org.distorted.library.uniformblock;
 
 import android.opengl.GLES30;
 
+import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.InternalBuffer;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -143,6 +144,6 @@ public class UniformBlockFloatUniforms
 
     String res = builder.toString();
 
-    android.util.Log.e("ubp", res);
+    DistortedLibrary.logMessage("UniformBlockFloatUniforms: "+res);
     }
   }
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.java b/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.java
index feba3cd..a5355b5 100644
--- a/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.java
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.java
@@ -23,6 +23,7 @@ package org.distorted.library.uniformblock;
 import android.opengl.GLES30;
 
 import org.distorted.library.effect.Effect;
+import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.InternalBuffer;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -169,6 +170,6 @@ public class UniformBlockIntUniforms
 
     String res = builder.toString();
 
-    android.util.Log.e("ubp", res);
+    DistortedLibrary.logMessage("UniformBlockIntUniforms: "+res);
     }
   }
