commit 46b26cdc06dead2c0cf319d1497b129a8f644734
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Mar 20 23:06:54 2019 +0000

    Rename the 'Effects3D' app files.

diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index c45297d..26cafa1 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -35,8 +35,8 @@
         <activity android:name=".inflate.InflateActivity" />
         <activity android:name=".inflate.InflateActivity2" />
         <activity android:name=".quaternion.QuaternionActivity" />          
-        <activity android:name=".generic.Effects3DActivity" />
-        <activity android:name=".generic.Effects3DActivity2"/>
+        <activity android:name=".generic.GenericActivity" />
+        <activity android:name=".generic.GenericActivity2"/>
         <activity android:name=".plainmonalisa.PlainMonaLisaActivity" />
         <activity android:name=".save.SaveActivity"/>
         <activity android:name=".flag.FlagActivity"/>
diff --git a/src/main/java/org/distorted/examples/TableOfContents.java b/src/main/java/org/distorted/examples/TableOfContents.java
index dab49a2..d57cf00 100644
--- a/src/main/java/org/distorted/examples/TableOfContents.java
+++ b/src/main/java/org/distorted/examples/TableOfContents.java
@@ -53,7 +53,7 @@ import org.distorted.examples.objecttree.ObjectTreeActivity;
 import org.distorted.examples.starwars.StarWarsActivity;
 import org.distorted.examples.inflate.InflateActivity;
 import org.distorted.examples.quaternion.QuaternionActivity;
-import org.distorted.examples.generic.Effects3DActivity;
+import org.distorted.examples.generic.GenericActivity;
 import org.distorted.examples.plainmonalisa.PlainMonaLisaActivity;
 import org.distorted.examples.save.SaveActivity;
 import org.distorted.examples.flag.FlagActivity;
@@ -269,7 +269,7 @@ public class TableOfContents extends ListActivity
       item.put(ITEM_TITLE, (i+1)+". "+getText(R.string.example_effects3d));
       item.put(ITEM_SUBTITLE, getText(R.string.example_effects3d_subtitle));
       data.add(item);
-      activityMapping.put(i++, Effects3DActivity.class);
+      activityMapping.put(i++, GenericActivity.class);
    }
 
    {
diff --git a/src/main/java/org/distorted/examples/generic/Effects3DActivity.java b/src/main/java/org/distorted/examples/generic/Effects3DActivity.java
deleted file mode 100644
index 8f1ac65..0000000
--- a/src/main/java/org/distorted/examples/generic/Effects3DActivity.java
+++ /dev/null
@@ -1,325 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.generic;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.NumberPicker;
-import android.widget.Spinner;
-import android.widget.TableRow;
-
-import org.distorted.examples.R;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class Effects3DActivity extends Activity
-                               implements View.OnClickListener, AdapterView.OnItemSelectedListener
-  {
-  private static final int COLOR_OFF = 0xffffe81f;
-  private static final int COLOR_ON  = 0xff0000ff;
-  private static final int COLOR_INAC= 0xff999999;
-
-  private int mNumCols = 1;
-  private int mNumRows = 1;
-  private int mNumSlic = 1;
-  private boolean mGridInitialized;
-  private NumberPicker mColsPicker, mRowsPicker, mSlicPicker;
-  private boolean[] mShape;
-  private int mObjectType;
-  private int mBitmapID;
-  private LinearLayout mLay;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  protected void onCreate(Bundle savedState)
-    {
-    super.onCreate(savedState);
-
-    setContentView(R.layout.objectpickerlayout);
-
-    mLay = findViewById(R.id.objectpicker_buttongrid);
-
-    mColsPicker = findViewById(R.id.objectpicker_cols);
-    mRowsPicker = findViewById(R.id.objectpicker_rows);
-    mSlicPicker = findViewById(R.id.objectpicker_slices);
-
-    mColsPicker.setMaxValue(40);
-    mColsPicker.setMinValue( 1);
-    mRowsPicker.setMaxValue(40);
-    mRowsPicker.setMinValue( 1);
-    mSlicPicker.setMaxValue(40);
-    mSlicPicker.setMinValue( 0);
-
-    mColsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
-         {
-         @Override
-         public void onValueChange(NumberPicker picker, int oldVal, int newVal)
-           {
-           setGrid();
-           }
-         });
-
-    mRowsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
-         {
-         @Override
-         public void onValueChange(NumberPicker picker, int oldVal, int newVal)
-           {
-           setGrid();
-           }
-         });
-
-    mSlicPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
-         {
-         @Override
-         public void onValueChange(NumberPicker picker, int oldVal, int newVal)
-           {
-           mNumSlic = mSlicPicker.getValue();
-           }
-         });
-
-    mObjectType = 0;
-    mGridInitialized = false;
-
-    Spinner typeSpinner  = findViewById(R.id.objectpicker_spinnerType);
-    typeSpinner.setOnItemSelectedListener(this);
-
-    String[] objectType = new String[] {"Mesh: Cubes", "Mesh: Flat", "Mesh: Sphere", "Mesh: Quad"};
-
-    ArrayAdapter<String> adapterType = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectType);
-    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    typeSpinner.setAdapter(adapterType);
-
-    Spinner bitmapSpinner  = findViewById(R.id.objectpicker_spinnerBitmap);
-    bitmapSpinner.setOnItemSelectedListener(this);
-
-    String[] objectBitmap = new String[] { "Texture: Grid", "Texture: Girl", "Texture: Dog", "Texture: Cat",
-                                           "Texture: Squares", "Texture: Bean", "Texture: Lisa", "Texture: World" };
-
-    ArrayAdapter<String> adapterBitmap = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectBitmap);
-    adapterBitmap.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    bitmapSpinner.setAdapter(adapterBitmap);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setGrid()
-    {
-    mGridInitialized = true;
-
-    mNumCols = mColsPicker.getValue();
-    mNumRows = mRowsPicker.getValue();
-
-    int width = mLay.getWidth();
-    int height= mLay.getHeight();
-    int w = mNumCols>0 ? (int)( 0.9f*width / mNumCols) : 0;
-    int h = mNumRows>0 ? (int)( 0.9f*height/ mNumRows) : 0;
-    int size= w<h ? w:h;
-    int pad = size<20 ? 1 : size/20;
-
-    mLay.removeAllViews();
-
-    mShape = new boolean[mNumRows*mNumCols];
-
-    TableRow.LayoutParams p = new TableRow.LayoutParams();
-
-    p.rightMargin  = pad;
-    p.leftMargin   = pad;
-    p.topMargin    = pad;
-    p.bottomMargin = pad;
-    p.height       = size;
-    p.width        = size;
-
-    for (int rows=0; rows<mNumRows; rows++)
-      {
-      TableRow tr = new TableRow(this);
-      tr.setGravity(Gravity.CENTER);
-
-      for(int cols=0; cols<mNumCols; cols++)
-        {
-        Button b = new Button(this);
-        b.setOnClickListener(this);
-        b.setId(rows*mNumCols+cols);
-        b.setLayoutParams(p);
-        b.setBackgroundColor(mObjectType==0 ? COLOR_ON:COLOR_INAC);
-        tr.addView(b, p);
-        mShape[rows*mNumCols+cols] = true;
-        }
-
-      mLay.addView(tr);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onClick(View view)
-    {
-    if( mObjectType==0 )  // cubes
-      {
-      Button tmp = (Button)view;
-      int id = tmp.getId();
-      mShape[id] = !mShape[id];
-      tmp.setBackgroundColor(mShape[id] ? COLOR_ON:COLOR_OFF);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void uncheckAll()
-    {
-    TableRow tr;
-    Button butt;
-
-    for (int row=0; row<mNumRows; row++)
-      {
-      tr = (TableRow)mLay.getChildAt(row);
-
-      for(int col=0; col<mNumCols; col++)
-        {
-        butt = (Button)tr.getVirtualChildAt(col);
-        butt.setBackgroundColor(mObjectType==0 ? COLOR_ON : COLOR_INAC);
-        mShape[row*mNumCols+col] = true;
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void Create(View v)
-    {
-    Intent mainInt = new Intent( getApplicationContext(), Effects3DActivity2.class);
-    Bundle b = new Bundle();
-
-    int rows=0, cols=0;
-
-    switch(mObjectType)
-      {
-      case 0:
-      case 1: rows = mNumRows;
-              cols = mNumCols;
-              break;
-      case 2: rows = mNumRows;   // always make the sphere equal in X and Y
-              cols = mNumRows;   //
-              break;
-      case 3: rows = 1;          // a quad is always 1x1
-              cols = 1;
-              break;
-      }
-
-    b.putInt("type", mObjectType);
-    b.putInt("cols", cols);
-    b.putInt("rows", rows);
-    b.putInt("slices", mNumSlic);
-    b.putInt("bitmap", mBitmapID);
-
-    if( mObjectType==0 )   // cubes
-      {
-      String str = "";
-
-      for(int i=0; i<mNumRows*mNumCols; i++)
-        str += mShape[i] ? "1" : "0";
-
-      b.putString("string", str);
-      }
-    else
-      {
-      b.putString("string", "");
-      }
-
-    mainInt.putExtras(b);
-    startActivity(mainInt);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
-    {
-    switch(parent.getId())
-      {
-      case R.id.objectpicker_spinnerType  : if( mObjectType!=pos )
-                                              {
-                                              mObjectType = pos;
-                                              uncheckAll();
-
-                                              switch(mObjectType)
-                                                {
-                                                case 0: mColsPicker.setEnabled(true);
-                                                        mRowsPicker.setEnabled(true);
-                                                        mSlicPicker.setEnabled(true);
-                                                        break;
-                                                case 1: mColsPicker.setEnabled(true);
-                                                        mRowsPicker.setEnabled(true);
-                                                        mSlicPicker.setEnabled(false);
-                                                        break;
-                                                case 2: mColsPicker.setEnabled(false);
-                                                        mRowsPicker.setEnabled(true);
-                                                        mSlicPicker.setEnabled(false);
-                                                        break;
-                                                case 3: mColsPicker.setEnabled(false);
-                                                        mRowsPicker.setEnabled(false);
-                                                        mSlicPicker.setEnabled(false);
-                                                        break;
-                                                }
-                                              }
-                                            break;
-      case R.id.objectpicker_spinnerBitmap: switch(pos)
-                                              {
-                                              case 0: mBitmapID = -1            ; break;
-                                              case 1: mBitmapID = R.raw.face    ; break;
-                                              case 2: mBitmapID = R.raw.dog     ; break;
-                                              case 3: mBitmapID = R.raw.cat     ; break;
-                                              case 4: mBitmapID = R.raw.grid    ; break;
-                                              case 5: mBitmapID = R.raw.bean    ; break;
-                                              case 6: mBitmapID = R.raw.monalisa; break;
-                                              case 7: mBitmapID = R.raw.world   ; break;
-                                              }
-                                            break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onNothingSelected(AdapterView<?> parent)
-    {
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Overrides
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onWindowFocusChanged(boolean hasFocus)
-    {
-    super.onWindowFocusChanged(hasFocus);
-
-    mColsPicker.setValue(mNumCols);
-    mRowsPicker.setValue(mNumRows);
-    mSlicPicker.setValue(mNumSlic);
-
-    if( !mGridInitialized ) setGrid();
-    }
-  }
diff --git a/src/main/java/org/distorted/examples/generic/Effects3DActivity2.java b/src/main/java/org/distorted/examples/generic/Effects3DActivity2.java
deleted file mode 100644
index dd650ab..0000000
--- a/src/main/java/org/distorted/examples/generic/Effects3DActivity2.java
+++ /dev/null
@@ -1,464 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.generic;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-import android.support.design.widget.TabLayout;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-import android.widget.CheckBox;
-
-import org.distorted.examples.R;
-import org.distorted.library.main.Distorted;
-import org.distorted.library.main.DistortedEffects;
-import org.distorted.library.main.DistortedTexture;
-import org.distorted.library.mesh.MeshCubes;
-import org.distorted.library.mesh.MeshFlat;
-import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.mesh.MeshQuad;
-import org.distorted.library.mesh.MeshSphere;
-import org.distorted.library.type.Static4D;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class Effects3DActivity2 extends AppCompatActivity
-  {
-  public static final int NUM_TABS = 4;
-
-  private DistortedTexture mTexture;
-  private MeshBase mMesh;
-  private Bitmap mBitmap;
-
-  private float mCenterX, mCenterY, mCenterZ;
-  private float mRegionX, mRegionY, mRegionZ, mRegionR;
-  private DistortedEffects mEffects;
-  private ViewPager mViewPager;
-
-  private boolean mShowCenter;
-  private boolean mShowRegion;
-  private boolean mShowNormal;
-  private boolean mUseOIT    ;
-
-  private Effects3DTab[] mTab;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  protected void onCreate(Bundle savedState)
-    {
-    super.onCreate(savedState);
-
-    setTheme(R.style.CustomActivityThemeNoActionBar);
-
-    mTab = new Effects3DTab[NUM_TABS];
-
-    Bundle b = getIntent().getExtras();
-
-    int objectType = b.getInt("type");
-    int numCols    = b.getInt("cols");
-    int numRows    = b.getInt("rows");
-    int numSlic    = b.getInt("slices");
-    int bitmapID   = b.getInt("bitmap");
-    String str     = b.getString("string");
-
-    mShowCenter = false;
-    mShowRegion = false;
-    mShowNormal = false;
-    mUseOIT     = false;
-
-    int maxsize = numCols > numRows ? (numCols>numSlic ? numCols:numSlic) : (numRows>numSlic ? numRows:numSlic) ;
-
-    createBitmap(maxsize, bitmapID);
-
-    switch(objectType)
-      {
-      case 0: if( bitmapID!=-1 )
-                {
-                mMesh = new MeshCubes(numCols, str, numSlic);
-                }
-              else
-                {
-                Static4D mapFB = new Static4D(0.0f,0.0f, (float)numCols/maxsize, (float)numRows/maxsize);
-                Static4D mapLR = new Static4D(0.0f,0.0f, (float)numSlic/maxsize, (float)numRows/maxsize);
-                Static4D mapTB = new Static4D(0.0f,0.0f, (float)numCols/maxsize, (float)numSlic/maxsize);
-
-                mMesh = new MeshCubes(numCols, str, numSlic, mapFB, mapFB, mapLR, mapLR, mapTB, mapTB);
-                }
-              break;
-      case 1: mMesh = new MeshFlat(numCols,numRows);
-              break;
-      case 2: mMesh = new MeshSphere(numRows);
-              break;
-      case 3: mMesh = new MeshQuad();
-              break;
-      }
-
-    mMesh.setShowNormals(mShowNormal);
-    mTexture= new DistortedTexture(numCols,numRows);
-    mEffects= new DistortedEffects();
-
-    final View view = getLayoutInflater().inflate(R.layout.effects3dlayout, null);
-
-    setContentView(view);
-
-    mViewPager = findViewById(R.id.effects3d_viewpager);
-    mViewPager.setOffscreenPageLimit( NUM_TABS-1 );
-    Effects3DTabViewPager pager = new Effects3DTabViewPager(this, getSupportFragmentManager() );
-    mViewPager.setAdapter(pager);
-    TabLayout tabLayout = findViewById(R.id.effects3d_sliding_tabs);
-    tabLayout.setupWithViewPager(mViewPager);
-
-    resetData();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createBitmap(int size, int bitmapID)
-    {
-    if( bitmapID!=-1)
-      {
-      InputStream is = getResources().openRawResource(bitmapID);
-
-      try
-        {
-        mBitmap = BitmapFactory.decodeStream(is);
-        }
-      finally
-        {
-        try
-          {
-          is.close();
-          }
-        catch(IOException e) { }
-        }
-      }
-    else
-      {
-      final int T = 64;
-      final int S = T*size;
-
-      Paint paint = new Paint();
-      mBitmap = Bitmap.createBitmap(S,S, Bitmap.Config.ARGB_8888);
-      Canvas canvas = new Canvas(mBitmap);
-
-      paint.setAntiAlias(true);
-      paint.setTextAlign(Paint.Align.CENTER);
-      paint.setColor(0xff008800);
-      paint.setStyle(Paint.Style.FILL);
-      canvas.drawRect(0, 0, S, S, paint);
-      paint.setColor(0xffffffff);
-
-      for(int i=0; i<=size ; i++ )
-        {
-        canvas.drawRect( T*i-1, 0, T*i+1, S, paint);
-        canvas.drawRect( 0, T*i-1, S, T*i+1, paint);
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public DistortedTexture getTexture()
-    {
-    return mTexture;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public MeshBase getMesh()
-    {
-    return mMesh;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Bitmap getBitmap()
-    {
-    return mBitmap;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setRegion(float x, float y, float z, float r)
-    {
-    mRegionX = x;
-    mRegionY =-y;
-    mRegionZ = z;
-    mRegionR = r;
-
-    Effects3DSurfaceView view = findViewById(R.id.effects3dSurfaceView);
-    view.getRenderer().setRegion(mRegionX, mRegionY, mRegionZ, mRegionR);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void setCenter(float x, float y, float z)
-    {
-    mCenterX = x;
-    mCenterY = y;
-    mCenterZ = z;
-
-    Effects3DSurfaceView view = findViewById(R.id.effects3dSurfaceView);
-    view.getRenderer().setCenter( mCenterX, mCenterY, mCenterZ );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getCenterX()
-    {
-    return mCenterX;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getCenterY()
-    {
-    return mCenterY;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getRegionX()
-    {
-    return mRegionX;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getRegionY()
-    {
-    return mRegionY;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getRegionZ()
-    {
-    return mRegionZ;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public float getRegionR()
-    {
-    return mRegionR;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getWidth()
-    {
-    return mTexture==null ? 0: mTexture.getWidth();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getHeight()
-    {
-    return mTexture==null ? 0: mTexture.getHeight();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getDepth()
-    {
-    return mTexture==null ? 0: mTexture.getDepth(mMesh);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public int getScreenWidth()
-    {
-    Effects3DRenderer r = ((Effects3DSurfaceView)findViewById(R.id.effects3dSurfaceView)).getRenderer();
-    return r.getWidth();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void resetData()
-    {
-    mCenterX = 0.5f*getWidth();
-    mCenterY = 0.5f*getHeight();
-    mRegionX = 0;
-    mRegionY = 0;
-    mRegionZ = 0;
-    mRegionR = getWidth()/2;
-
-    Effects3DSurfaceView view = findViewById(R.id.effects3dSurfaceView);
-    Effects3DRenderer renderer= view.getRenderer();
-
-    renderer.setCenter( mCenterX, mCenterY, mCenterZ );
-    renderer.setRegion( mRegionX, mRegionY, mRegionZ, mRegionR );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void resetMatrixEffects()
-    {
-    Effects3DSurfaceView view = findViewById(R.id.effects3dSurfaceView);
-    Effects3DRenderer renderer= view.getRenderer();
-
-    renderer.resetMatrixEffects();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public DistortedEffects getEffects()
-    {
-    return mEffects;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void newEffect(View v)
-    {
-    int pos = mViewPager.getCurrentItem();
-
-    if( pos>=0 && pos<NUM_TABS ) mTab[pos].newEffect();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void removeAll(View v)
-    {
-    int pos = mViewPager.getCurrentItem();
-
-    if( pos>=0 && pos<NUM_TABS ) mTab[pos].removeAll();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void remove(View v)
-    {
-    int pos = mViewPager.getCurrentItem();
-
-    if( pos>=0 && pos<NUM_TABS ) mTab[pos].remove(v);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setTab(int pos, Effects3DTab tab)
-    {
-    if( pos>=0 && pos<NUM_TABS ) mTab[pos] = tab;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean getShowCenter()
-    {
-    return mShowCenter;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean getShowRegion()
-    {
-    return mShowRegion;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void showCenter(View view)
-    {
-    CheckBox box = (CheckBox)view;
-    mShowCenter = box.isChecked();
-
-    Effects3DSurfaceView sv = findViewById(R.id.effects3dSurfaceView);
-    sv.getRenderer().showRegionAndCenter(mShowRegion,mShowCenter);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void showRegion(View view)
-    {
-    CheckBox box = (CheckBox)view;
-    mShowRegion = box.isChecked();
-
-    Effects3DSurfaceView sv = findViewById(R.id.effects3dSurfaceView);
-    sv.getRenderer().showRegionAndCenter(mShowRegion,mShowCenter);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void showNormal(View view)
-    {
-    CheckBox box = (CheckBox)view;
-    mShowNormal = box.isChecked();
-
-    if ( mMesh!=null )
-      {
-      mMesh.setShowNormals(mShowNormal);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void triggerOIT(View view)
-    {
-    CheckBox box = (CheckBox)view;
-    mUseOIT = box.isChecked();
-
-    Effects3DSurfaceView sv = findViewById(R.id.effects3dSurfaceView);
-    sv.getRenderer().useOIT(mUseOIT);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Overrides
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  protected void onPause()
-    {
-    GLSurfaceView mView = findViewById(R.id.effects3dSurfaceView);
-    if( mView!=null ) mView.onPause();
-
-    Distorted.onPause();
-    super.onPause();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  protected void onResume()
-    {
-    super.onResume();
-    GLSurfaceView mView = findViewById(R.id.effects3dSurfaceView);
-    if( mView!=null ) mView.onResume();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-    
-  @Override
-  protected void onDestroy()
-    {
-    Distorted.onDestroy();
-    super.onDestroy();
-    }
-
-  }
diff --git a/src/main/java/org/distorted/examples/generic/Effects3DEffect.java b/src/main/java/org/distorted/examples/generic/Effects3DEffect.java
deleted file mode 100644
index 5e585cc..0000000
--- a/src/main/java/org/distorted/examples/generic/Effects3DEffect.java
+++ /dev/null
@@ -1,827 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.generic;
-
-import android.view.View;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import org.distorted.examples.R;
-import org.distorted.library.effect.Effect;
-import org.distorted.library.effect.EffectName;
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.effect.FragmentEffectAlpha;
-import org.distorted.library.effect.FragmentEffectBrightness;
-import org.distorted.library.effect.FragmentEffectChroma;
-import org.distorted.library.effect.FragmentEffectContrast;
-import org.distorted.library.effect.FragmentEffectSaturation;
-import org.distorted.library.effect.MatrixEffectMove;
-import org.distorted.library.effect.MatrixEffectQuaternion;
-import org.distorted.library.effect.MatrixEffectRotate;
-import org.distorted.library.effect.MatrixEffectScale;
-import org.distorted.library.effect.MatrixEffectShear;
-import org.distorted.library.effect.PostprocessEffectBlur;
-import org.distorted.library.effect.PostprocessEffectGlow;
-import org.distorted.library.effect.VertexEffectDeform;
-import org.distorted.library.effect.VertexEffectDistort;
-import org.distorted.library.effect.VertexEffectPinch;
-import org.distorted.library.effect.VertexEffectSink;
-import org.distorted.library.effect.VertexEffectSwirl;
-import org.distorted.library.effect.VertexEffectWave;
-import org.distorted.library.main.DistortedEffects;
-import org.distorted.library.type.Dynamic1D;
-import org.distorted.library.type.Dynamic2D;
-import org.distorted.library.type.Dynamic3D;
-import org.distorted.library.type.Dynamic4D;
-import org.distorted.library.type.Dynamic5D;
-import org.distorted.library.type.Static1D;
-import org.distorted.library.type.Static2D;
-import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
-import org.distorted.library.type.Static5D;
-
-import java.lang.ref.WeakReference;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Effects3DEffect implements SeekBar.OnSeekBarChangeListener
-  {
-  private static final int BACKGROUND_ODD = 0xff555555;
-  private static final int BACKGROUND_EVEN= 0xff333333;
-
-  private WeakReference<Effects3DActivity2> mAct;
-
-  private EffectName mName;
-  private int[] mInter;
-  private int[] mInterRegion;
-  private int[] mInterCenter;
-  private int[] mSeekID;
-  private int[] mSeekRegionID;
-  private int[] mSeekCenterID;
-  private int mDimension, mRegionDimension;
-  private TextView mText,mTextRegion,mTextCenter;
-
-  private Dynamic1D mDyn1;
-  private Dynamic2D mDyn2;
-  private Dynamic3D mDyn3;
-  private Dynamic4D mDyn4;
-  private Dynamic5D mDyn5;
-  private Static1D  mSta1;
-  private Static2D  mSta2;
-  private Static3D  mSta3;
-  private Static4D  mSta4;
-  private Static5D  mSta5;
-  private Dynamic4D mRegion4Dyn;
-  private Static4D  mRegion4Sta;
-  private Dynamic3D mRegion3Dyn;
-  private Static3D  mRegion3Sta;
-  private Dynamic3D mCenterDyn;
-  private Static3D  mCenterSta;
-
-  private View mButton, mEffect, mCenter, mRegion;
-  private long mId;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// requires knowledge about effect nature
-
-  void apply(DistortedEffects effects)
-    {
-    Effect effect=null;
-
-    switch(mName)
-      {
-      case ROTATE           : effect = new MatrixEffectRotate      (mDyn1, mDyn3, mCenterDyn); break;
-      case QUATERNION       : effect = new MatrixEffectQuaternion  (mDyn4, mCenterDyn); break;
-      case MOVE             : effect = new MatrixEffectMove        (mDyn3)            ; break;
-      case SCALE            : effect = new MatrixEffectScale       (mDyn3)            ; break;
-      case SHEAR            : effect = new MatrixEffectShear       (mDyn3, mCenterDyn); break;
-
-      case DISTORT          : effect = new VertexEffectDistort     (mDyn3, mCenterDyn, mRegion4Dyn); break;
-      case DEFORM           : effect = new VertexEffectDeform      (mDyn3, mCenterDyn, mRegion4Dyn); break;
-      case SINK             : effect = new VertexEffectSink        (mDyn1, mCenterDyn, mRegion4Dyn); break;
-      case PINCH            : effect = new VertexEffectPinch       (mDyn2, mCenterDyn, mRegion4Dyn); break;
-      case SWIRL            : effect = new VertexEffectSwirl       (mDyn1, mCenterDyn, mRegion4Dyn); break;
-      case WAVE             : effect = new VertexEffectWave        (mDyn5, mCenterDyn, mRegion4Dyn); break;
-
-      case ALPHA            : effect = new FragmentEffectAlpha     (mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
-      case SMOOTH_ALPHA     : effect = new FragmentEffectAlpha     (mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
-      case CHROMA           : effect = new FragmentEffectChroma    (mDyn1, mDyn3, mCenterDyn, mRegion3Dyn, false); break;
-      case SMOOTH_CHROMA    : effect = new FragmentEffectChroma    (mDyn1, mDyn3, mCenterDyn, mRegion3Dyn, true ); break;
-      case BRIGHTNESS       : effect = new FragmentEffectBrightness(mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
-      case SMOOTH_BRIGHTNESS: effect = new FragmentEffectBrightness(mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
-      case SATURATION       : effect = new FragmentEffectSaturation(mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
-      case SMOOTH_SATURATION: effect = new FragmentEffectSaturation(mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
-      case CONTRAST         : effect = new FragmentEffectContrast  (mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
-      case SMOOTH_CONTRAST  : effect = new FragmentEffectContrast  (mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
-
-      case BLUR             : effect = new PostprocessEffectBlur   (mDyn1       ); break;
-      case GLOW             : effect = new PostprocessEffectGlow   (mDyn1, mDyn4); break;
-      }
-
-    if( effect!=null )
-      {
-      effects.apply(effect);
-      mId = effect.getID();
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// requires knowledge about effect nature
-
-  private void fillStatics()
-    {
-    switch(mName)
-      {
-      ///////////////////////////////////////////////////////////////////////////////////////
-      // MATRIX
-      ///////////////////////////////////////////////////////////////////////////////////////
-
-      case ROTATE           : float an = (mInter[0]-50)*180/50;
-                              float rx = (mInter[1]-50)/ 50.0f;
-                              float ry = (mInter[2]-50)/ 50.0f;
-                              float rz = (mInter[3]-50)/ 50.0f;
-                              mSta1.set(an);
-                              mSta3.set(rx,ry,rz);
-                              break;
-      case QUATERNION       : float qx = (mInter[0]-50)/ 50.0f;
-                              float qy = (mInter[1]-50)/ 50.0f;
-                              float qz = (mInter[2]-50)/ 50.0f;
-                              float qa = (mInter[3]-50)*3.1415f/50;
-                              float cosA = (float)Math.cos(qa/2);
-                              float len = (float)Math.sqrt(qx*qx+qy*qy+qz*qz);
-                              float sinAnorm = (float)Math.sin(qa/2)/len;
-                              mSta4.set(sinAnorm*qx,sinAnorm*qy,sinAnorm*qz, cosA);
-                              break;
-      case MOVE             : float scr= mAct.get().getScreenWidth();
-                              float sw = scr/20000.0f;
-                              float sh = scr/20000.0f;
-                              float xm = (mInter[0]-50)*sw;
-                              float ym = (mInter[1]-50)*sh;
-                              float zm = (mInter[2]-50)*(sw+sh)/2;
-                              mSta3.set(xm,ym,zm);
-                              break;
-      case SCALE            : float xs = (mInter[0]>50 ? 0.18f : 0.018f)*(mInter[0]-50) + 1;
-                              float ys = (mInter[1]>50 ? 0.18f : 0.018f)*(mInter[1]-50) + 1;
-                              float zs = (mInter[2]>50 ? 0.18f : 0.018f)*(mInter[2]-50) + 1;
-                              mSta3.set(xs,ys,zs);
-                              break;
-      case SHEAR            : float xsh = (mInter[0]-50)/25.0f;
-                              float ysh = (mInter[1]-50)/25.0f;
-                              float zsh = (mInter[2]-50)/25.0f;
-                              mSta3.set(xsh,ysh,zsh);
-                              break;
-
-      ///////////////////////////////////////////////////////////////////////////////////////
-      // VERTEX
-      ///////////////////////////////////////////////////////////////////////////////////////
-
-      case DISTORT          :
-      case DEFORM           : float ld = mAct.get().getWidth()/50.0f;
-                              float xd = (mInter[0]-50)*ld;
-                              float yd = (mInter[1]-50)*ld;
-                              float zd = (mInter[2]-50)*ld;
-                              mSta3.set(xd,yd,zd);
-                              break;
-      case WAVE             : float l2 = mAct.get().getWidth()/50.0f;
-                              float x2 = (mInter[0]-50)*l2;
-                              float y2 = (mInter[1]-50)*l2;
-                              float z2 = (mInter[2]-50)*180 / 50;
-                              float w2 = (mInter[3]-50)*180 / 50;
-                              float v2 = (mInter[4]-50)*180 / 50;
-                              mSta5.set(x2,y2,z2,w2,v2);
-                              break;
-      case SWIRL            : mSta1.set( 3.6f*(mInter[0]-50) );
-                              break;
-      case SINK             : mSta1.set(mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f);
-                              break;
-      case PINCH            : float dp = mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f;
-                              float ap = (mInter[1]-50)*180 / 50;
-                              mSta2.set(dp,ap);
-                              break;
-
-      ///////////////////////////////////////////////////////////////////////////////////////
-      // FRAGMENT
-      ///////////////////////////////////////////////////////////////////////////////////////
-
-      case ALPHA            :
-      case SMOOTH_ALPHA     : mSta1.set(mInter[0]/100.0f);
-                              break;
-      case SATURATION       :
-      case SMOOTH_SATURATION:
-      case CONTRAST         :
-      case SMOOTH_CONTRAST  :
-      case BRIGHTNESS       :
-      case SMOOTH_BRIGHTNESS: mSta1.set(mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f);
-                              break;
-      case CHROMA           :
-      case SMOOTH_CHROMA    : mSta1.set(mInter[0]/100.0f);
-                              mSta3.set(mInter[1]/100.0f,
-                                        mInter[2]/100.0f,
-                                        mInter[3]/100.0f);
-                              break;
-
-      ///////////////////////////////////////////////////////////////////////////////////////
-      // POSTPROCESS
-      ///////////////////////////////////////////////////////////////////////////////////////
-
-      case BLUR             : mSta1.set(mInter[0]/2.0f);
-                              break;
-      case GLOW             : mSta1.set(mInter[0]/2.0f);
-                              mSta4.set(mInter[1]/100.0f,
-                                        mInter[2]/100.0f,
-                                        mInter[3]/100.0f,
-                                        mInter[4]/100.0f );
-                              break;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setDefaultInter()
-    {
-    switch(mDimension)
-      {
-      case 5: mInter[4] = 50;
-      case 4: mInter[3] = 50;
-      case 3: mInter[2] = 50;
-      case 2: mInter[1] = 50;
-      case 1: mInter[0] = 50;
-      }
-
-    if( mName==EffectName.ROTATE || mName==EffectName.QUATERNION ) mInter[1]= 100;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setText()
-    {
-    String text = mName.name();
-
-    if( mSta1 !=null )
-      {
-      float f1 = ((int)(mSta1.get1()*100))/100.0f;
-      text += " "+f1;
-      }
-
-    if( mSta2 !=null )
-      {
-      float f1 = ((int)(mSta2.get1()*100))/100.0f;
-      float f2 = ((int)(mSta2.get2()*100))/100.0f;
-      text += " ("+f1+","+f2+")";
-      }
-
-    if( mSta3 !=null )
-      {
-      float f1 = ((int)(mSta3.get1()*100))/100.0f;
-      float f2 = ((int)(mSta3.get2()*100))/100.0f;
-      float f3 = ((int)(mSta3.get3()*100))/100.0f;
-      text += " ("+f1+","+f2+","+f3+")";
-      }
-
-    if( mSta4 !=null )
-      {
-      float f1 = ((int)(mSta4.get1()*100))/100.0f;
-      float f2 = ((int)(mSta4.get2()*100))/100.0f;
-      float f3 = ((int)(mSta4.get3()*100))/100.0f;
-      float f4 = ((int)(mSta4.get4()*100))/100.0f;
-      text += " ("+f1+","+f2+","+f3+","+f4+")";
-      }
-
-    if( mSta5 !=null )
-      {
-      float f1 = ((int)(mSta5.get1()*100))/100.0f;
-      float f2 = ((int)(mSta5.get2()*100))/100.0f;
-      float f3 = ((int)(mSta5.get3()*100))/100.0f;
-      float f4 = ((int)(mSta5.get4()*100))/100.0f;
-      float f5 = ((int)(mSta5.get5()*100))/100.0f;
-      text += " ("+f1+","+f2+","+f3+","+f4+","+f5+")";
-      }
-
-    mText.setText(text);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void fillCenterStatics()
-    {
-    Effects3DActivity2 act = mAct.get();
-
-    float x = (mInterCenter[0]*0.012f - 0.1f)*act.getWidth();
-    float y = (mInterCenter[1]*0.012f - 0.1f)*act.getHeight();
-    float z = (mInterCenter[2]*0.012f - 0.1f)*act.getDepth();
-
-    mCenterSta.set(x,y,z);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setDefaultCenterInter()
-    {
-    mInterCenter[0] = 50;
-    mInterCenter[1] = 50;
-    mInterCenter[2] = 50;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setCenterText()
-    {
-    float f0 = ((int)(mCenterSta.get1()*100))/100.0f;
-    float f1 = ((int)(mCenterSta.get2()*100))/100.0f;
-    float f2 = ((int)(mCenterSta.get3()*100))/100.0f;
-
-    mTextCenter.setText("center ("+f0+","+f1+","+f2+")");
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void fillRegionStatics()
-    {
-    Effects3DActivity2 act = mAct.get();
-
-    float factorX = act.getWidth() / 100.0f;
-    float factorY = act.getHeight()/ 100.0f;
-   // float factorZ = act.getDepth() / 100.0f;
-
-    int deduct = (mName.getType() == EffectType.VERTEX ? 50:0);
-
-    float x = (mInterRegion[0]-deduct)*factorX;
-    float y = (mInterRegion[1]-deduct)*factorY;
-    float z = (mInterRegion[2]-deduct)*factorX;   // ??
-    float r =  mInterRegion[3]        *(factorX+factorY)/2;
-
-    mRegion4Sta.set(x,y,z,r);
-    mRegion3Sta.set(x,y,z);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setDefaultRegionInter()
-    {
-    mInterRegion[0] = 50;
-    mInterRegion[1] = 50;
-    mInterRegion[2] = 50;
-    mInterRegion[3] = 50;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void setRegionText()
-    {
-    if( mRegionDimension==4 )
-      {
-      float f0 = ((int)(mRegion4Sta.get1()*100))/100.0f;
-      float f1 = ((int)(mRegion4Sta.get2()*100))/100.0f;
-      float f2 = ((int)(mRegion4Sta.get3()*100))/100.0f;
-      float f3 = ((int)(mRegion4Sta.get4()*100))/100.0f;
-
-      mTextRegion.setText("region (" + f0 + "," + f1 + "," + f2 + "," + f3 + ")");
-      }
-
-    if( mRegionDimension==3 )
-      {
-      float f0 = ((int)(mRegion4Sta.get1()*100))/100.0f;
-      float f1 = ((int)(mRegion4Sta.get2()*100))/100.0f;
-      float f2 = ((int)(mRegion4Sta.get3()*100))/100.0f;
-
-      mTextRegion.setText("region (" + f0 + "," + f1 + "," + f2 + ")");
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setBackground(int pos)
-    {
-    int color = (pos%2==1 ? BACKGROUND_ODD:BACKGROUND_EVEN);
-
-    if( mEffect!=null ) mEffect.setBackgroundColor(color);
-    if( mCenter!=null ) mCenter.setBackgroundColor(color);
-    if( mRegion!=null ) mRegion.setBackgroundColor(color);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  Effects3DEffect(EffectName name, Effects3DActivity2 act)
-    {
-    mAct = new WeakReference<>(act);
-    mName = name;
-
-    mDyn1 = null;
-    mDyn2 = null;
-    mDyn3 = null;
-    mDyn4 = null;
-    mDyn5 = null;
-    mSta1 = null;
-    mSta2 = null;
-    mSta3 = null;
-    mSta4 = null;
-    mSta5 = null;
-
-    mDimension = mName.getEffectDimension();
-    mRegionDimension = mName.getRegionDimension();
-
-    switch(mDimension)
-      {
-      case 1 : mDyn1 = new Dynamic1D();
-               mSta1 = new Static1D(0);
-               mDyn1.add(mSta1);
-               break;
-      case 2 : mDyn2 = new Dynamic2D();
-               mSta2 = new Static2D(0,0);
-               mDyn2.add(mSta2);
-               break;
-      case 3 : mDyn3 = new Dynamic3D();
-               mSta3 = new Static3D(0,0,0);
-               mDyn3.add(mSta3);
-               break;
-      case 4 : if( mName == EffectName.QUATERNION )
-                 {
-                 mDyn4 = new Dynamic4D();
-                 mSta4 = new Static4D(0,0,0,0);
-                 mDyn4.add(mSta4);
-                 }
-               else
-                 {
-                 mDyn3 = new Dynamic3D();
-                 mSta3 = new Static3D(0,0,0);
-                 mDyn3.add(mSta3);
-                 mDyn1 = new Dynamic1D();
-                 mSta1 = new Static1D(0);
-                 mDyn1.add(mSta1);
-                 }
-               break;
-      case 5 : if( mName == EffectName.WAVE )
-                 {
-                 mDyn5 = new Dynamic5D();
-                 mSta5 = new Static5D(0, 0, 0, 0, 0);
-                 mDyn5.add(mSta5);
-                 }
-               else
-                 {
-                 mDyn4 = new Dynamic4D();
-                 mSta4 = new Static4D(0,0,0,0);
-                 mDyn4.add(mSta4);
-                 mDyn1 = new Dynamic1D();
-                 mSta1 = new Static1D(0);
-                 mDyn1.add(mSta1);
-                 }
-               break;
-      default: throw new RuntimeException("unsupported effect");
-      }
-
-    mInter = new int[mDimension];
-    mSeekID= new int[mDimension];
-
-    mInterRegion = new int[4];
-    mSeekRegionID= new int[4];
-    mRegion4Dyn  = new Dynamic4D();
-    mRegion4Sta  = new Static4D(0,0,0,0);
-    mRegion4Dyn.add(mRegion4Sta);
-    mRegion3Dyn  = new Dynamic3D();
-    mRegion3Sta  = new Static3D(0,0,0);
-    mRegion3Dyn.add(mRegion3Sta);
-
-    mInterCenter = new int[3];
-    mSeekCenterID= new int[3];
-    mCenterDyn   = new Dynamic3D();
-    mCenterSta   = new Static3D(0,0,0);
-    mCenterDyn.add(mCenterSta);
-
-    mButton = null;
-    mEffect = null;
-    mCenter = null;
-    mRegion = null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  View createView(int num)
-    {
-    SeekBar[] seek = new SeekBar[mDimension];
-
-    Effects3DActivity2 act = mAct.get();
-
-    switch(mDimension)
-      {
-      case 1 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect1d, null);
-               mText      = mEffect.findViewById(R.id.effect1dText);
-               seek[0]    = mEffect.findViewById(R.id.effect1dbar1);
-               mSeekID[0] = seek[0].getId();
-               mButton    = mEffect.findViewById(R.id.button1dRemove);
-               break;
-      case 2 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect2d, null);
-               mText      = mEffect.findViewById(R.id.effect2dText);
-               seek[0]    = mEffect.findViewById(R.id.effect2dbar1);
-               seek[1]    = mEffect.findViewById(R.id.effect2dbar2);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               mButton    = mEffect.findViewById(R.id.button2dRemove);
-               break;
-      case 3 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect3d, null);
-               mText      = mEffect.findViewById(R.id.effect3dText);
-               seek[0]    = mEffect.findViewById(R.id.effect3dbar1);
-               seek[1]    = mEffect.findViewById(R.id.effect3dbar2);
-               seek[2]    = mEffect.findViewById(R.id.effect3dbar3);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               mSeekID[2] = seek[2].getId();
-               mButton    = mEffect.findViewById(R.id.button3dRemove);
-               break;
-      case 4 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect4d, null);
-               mText      = mEffect.findViewById(R.id.effect4dText);
-               seek[0]    = mEffect.findViewById(R.id.effect4dbar1);
-               seek[1]    = mEffect.findViewById(R.id.effect4dbar2);
-               seek[2]    = mEffect.findViewById(R.id.effect4dbar3);
-               seek[3]    = mEffect.findViewById(R.id.effect4dbar4);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               mSeekID[2] = seek[2].getId();
-               mSeekID[3] = seek[3].getId();
-               mButton    = mEffect.findViewById(R.id.button4dRemove);
-               break;
-      case 5 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect5d, null);
-               mText      = mEffect.findViewById(R.id.effect5dText);
-               seek[0]    = mEffect.findViewById(R.id.effect5dbar1);
-               seek[1]    = mEffect.findViewById(R.id.effect5dbar2);
-               seek[2]    = mEffect.findViewById(R.id.effect5dbar3);
-               seek[3]    = mEffect.findViewById(R.id.effect5dbar4);
-               seek[4]    = mEffect.findViewById(R.id.effect5dbar5);
-               mSeekID[0] = seek[0].getId();
-               mSeekID[1] = seek[1].getId();
-               mSeekID[2] = seek[2].getId();
-               mSeekID[3] = seek[3].getId();
-               mSeekID[4] = seek[4].getId();
-               mButton    = mEffect.findViewById(R.id.button5dRemove);
-               break;
-      default: android.util.Log.e("Effects3DEffect", "dimension "+mDimension+" not supported!");
-               return null;
-      }
-
-    mEffect.setBackgroundColor( num%2==1 ? BACKGROUND_EVEN: BACKGROUND_ODD );
-
-    setDefaultInter();
-
-    for(int i=0; i<mDimension; i++)
-      {
-      seek[i].setOnSeekBarChangeListener(this);
-      seek[i].setProgress( mInter[i] );
-      }
-
-    return mEffect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  View createRegion(int num)
-    {
-    Effects3DActivity2 act = mAct.get();
-
-    if( mRegionDimension== 4 )
-      {
-      mRegion = act.getLayoutInflater().inflate(R.layout.effectregion4, null);
-      mRegion.setBackgroundColor(num % 2 == 1 ? BACKGROUND_EVEN : BACKGROUND_ODD);
-
-      SeekBar[] seek = new SeekBar[4];
-
-      seek[0] = mRegion.findViewById(R.id.effectRegion4BarX);
-      seek[1] = mRegion.findViewById(R.id.effectRegion4BarY);
-      seek[2] = mRegion.findViewById(R.id.effectRegion4BarZ);
-      seek[3] = mRegion.findViewById(R.id.effectRegion4BarR);
-
-      mSeekRegionID[0] = seek[0].getId();
-      mSeekRegionID[1] = seek[1].getId();
-      mSeekRegionID[2] = seek[2].getId();
-      mSeekRegionID[3] = seek[3].getId();
-
-      mTextRegion = mRegion.findViewById(R.id.effectRegion4Text);
-
-      setDefaultRegionInter();
-
-      for (int i = 0; i < 4; i++)
-        {
-        seek[i].setOnSeekBarChangeListener(this);
-        seek[i].setProgress(mInterRegion[i]);
-        }
-
-      act.setRegion(mRegion4Sta.get1(), mRegion4Sta.get2(), mRegion4Sta.get3(), mRegion4Sta.get4());
-      }
-    else
-      {
-      mRegion = act.getLayoutInflater().inflate(R.layout.effectregion3, null);
-      mRegion.setBackgroundColor(num % 2 == 1 ? BACKGROUND_EVEN : BACKGROUND_ODD);
-
-      SeekBar[] seek = new SeekBar[3];
-
-      seek[0] = mRegion.findViewById(R.id.effectRegion3BarRX);
-      seek[1] = mRegion.findViewById(R.id.effectRegion3BarRY);
-      seek[2] = mRegion.findViewById(R.id.effectRegion3BarRZ);
-
-      mSeekRegionID[0] = seek[0].getId();
-      mSeekRegionID[1] = seek[1].getId();
-      mSeekRegionID[2] = seek[2].getId();
-
-      mTextRegion = mRegion.findViewById(R.id.effectRegion3Text);
-
-      setDefaultRegionInter();
-
-      for (int i = 0; i < 3; i++)
-        {
-        seek[i].setOnSeekBarChangeListener(this);
-        seek[i].setProgress(mInterRegion[i]);
-        }
-
-      act.setRegion(mRegion3Sta.get1(), mRegion3Sta.get2(), mRegion3Sta.get3(), 1.0f );
-      }
-
-    return mRegion;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  View createCenter(int num)
-    {
-    Effects3DActivity2 act = mAct.get();
-
-    mCenter = act.getLayoutInflater().inflate(R.layout.effectcenter, null);
-    mCenter.setBackgroundColor( num%2==1 ? BACKGROUND_EVEN: BACKGROUND_ODD );
-
-    SeekBar[] seek = new SeekBar[3];
-
-    seek[0] = mCenter.findViewById(R.id.effectCenterBarX );
-    seek[1] = mCenter.findViewById(R.id.effectCenterBarY );
-    seek[2] = mCenter.findViewById(R.id.effectCenterBarZ );
-
-    mSeekCenterID[0] = seek[0].getId();
-    mSeekCenterID[1] = seek[1].getId();
-    mSeekCenterID[2] = seek[2].getId();
-
-    mTextCenter = mCenter.findViewById(R.id.effectCenterText);
-
-    setDefaultCenterInter();
-
-    for(int i=0; i<3; i++)
-      {
-      seek[i].setOnSeekBarChangeListener(this);
-      seek[i].setProgress( mInterCenter[i] );
-      }
-
-    act.setCenter(mCenterSta.get1(),mCenterSta.get2(),mCenterSta.get3());
-
-    return mCenter;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
-    {
-    if ( mDimension>=1 && bar.getId()==mSeekID[0] )
-      {
-      mInter[0] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=2 && bar.getId()==mSeekID[1] )
-      {
-      mInter[1] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=3 && bar.getId()==mSeekID[2] )
-      {
-      mInter[2] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=4 && bar.getId()==mSeekID[3] )
-      {
-      mInter[3] = progress;
-      fillStatics();
-      setText();
-      }
-    if ( mDimension>=5 && bar.getId()==mSeekID[4] )
-      {
-      mInter[4] = progress;
-      fillStatics();
-      setText();
-      }
-
-    if( mRegionDimension>=1 && bar.getId() == mSeekRegionID[0] )
-      {
-      mInterRegion[0] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( mRegionDimension>=2 && bar.getId() == mSeekRegionID[1] )
-      {
-      mInterRegion[1] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( mRegionDimension>=3 && bar.getId() == mSeekRegionID[2] )
-      {
-      mInterRegion[2] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-    if( mRegionDimension>=4 && bar.getId() == mSeekRegionID[3] )
-      {
-      mInterRegion[3] = progress;
-      fillRegionStatics();
-      setRegionText();
-      }
-
-    if( bar.getId() == mSeekCenterID[0] )
-      {
-      mInterCenter[0] = progress;
-      fillCenterStatics();
-      setCenterText();
-      }
-    if( bar.getId() == mSeekCenterID[1] )
-      {
-      mInterCenter[1] = progress;
-      fillCenterStatics();
-      setCenterText();
-      }
-    if( bar.getId() == mSeekCenterID[2] )
-      {
-      mInterCenter[2] = progress;
-      fillCenterStatics();
-      setCenterText();
-      }
-
-    if( fromUser )
-      {
-      Effects3DActivity2 act = mAct.get();
-
-      boolean show = (mName.getType()==EffectType.VERTEX);
-      boolean showR= (show && act.getShowRegion());
-      boolean showC= (show && act.getShowCenter());
-
-      Effects3DSurfaceView view = act.findViewById(R.id.effects3dSurfaceView);
-      view.getRenderer().showRegionAndCenter( showR,showC );
-
-      act.setCenter(mCenterSta.get1(),mCenterSta.get2(),mCenterSta.get3());
-      act.setRegion(mRegion4Sta.get1(),mRegion4Sta.get2(),mRegion4Sta.get3(), mRegion4Sta.get4());
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean thisView(View v)
-    {
-    return v==mButton;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public long getId()
-    {
-    return mId;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View getEffect()
-    {
-    return mEffect;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View getRegion()
-    {
-    return mRegion;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public View getCenter()
-    {
-    return mCenter;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onStartTrackingTouch(SeekBar bar) { }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onStopTrackingTouch(SeekBar bar)  { }
-  }
diff --git a/src/main/java/org/distorted/examples/generic/Effects3DRenderer.java b/src/main/java/org/distorted/examples/generic/Effects3DRenderer.java
deleted file mode 100644
index 05a8d9f..0000000
--- a/src/main/java/org/distorted/examples/generic/Effects3DRenderer.java
+++ /dev/null
@@ -1,325 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.generic;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.opengl.GLSurfaceView;
-
-import org.distorted.examples.R;
-import org.distorted.library.effect.Effect;
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.effect.MatrixEffectMove;
-import org.distorted.library.effect.MatrixEffectQuaternion;
-import org.distorted.library.effect.MatrixEffectScale;
-import org.distorted.library.main.Distorted;
-import org.distorted.library.main.DistortedEffects;
-import org.distorted.library.main.DistortedNode;
-import org.distorted.library.main.DistortedScreen;
-import org.distorted.library.main.DistortedTexture;
-import org.distorted.library.mesh.MeshBase;
-import org.distorted.library.mesh.MeshFlat;
-import org.distorted.library.mesh.MeshSphere;
-import org.distorted.library.type.Static3D;
-import org.distorted.library.type.Static4D;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Effects3DRenderer implements GLSurfaceView.Renderer
-{
-    private static final float FOV = 30.0f;
-    private static final float NEAR = 0.1f;
-
-    private GLSurfaceView mView;
-    private DistortedTexture mObjectTexture, mBackgroundTexture, mCenterTexture, mRegionTexture;
-    private DistortedScreen mScreen;
-    private DistortedNode mCenterNode, mRegionNode;
-    private int mObjWidth, mObjHeight, mObjDepth;
-    private Static3D mCenterPoint, mRegionPoint, mRegionScalePoint;
-    private Static3D mRotateCen, mMoveObject, mScaleObject, mMoveCenter, mScaleCenter, mMoveRegion, mMoveBackground, mScaleBackground;
-    private boolean mShowingCenter=false;
-    private boolean mShowingRegion=false;
-    private float mFactorObj, mFactorReg;
-    private int mWidth;
-    private float mFactor;
-
-    Static4D mQuat1, mQuat2;
-    int mScreenMin;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    Effects3DRenderer(GLSurfaceView v)
-      {
-      mView = v;
-
-      Effects3DActivity2 act = (Effects3DActivity2)v.getContext();
-
-      mRotateCen      = new Static3D(0,0,0);
-      mMoveObject     = new Static3D(0,0,0);
-      mScaleObject    = new Static3D(1,1,1);
-      mMoveCenter     = new Static3D(0,0,0);
-      mScaleCenter    = new Static3D(1,1,1);
-      mMoveRegion     = new Static3D(0,0,0);
-      mMoveBackground = new Static3D(0,0,0);
-      mScaleBackground= new Static3D(1,1,1);
-
-      mObjectTexture     = act.getTexture();
-      mBackgroundTexture = new DistortedTexture(100,100);
-      mCenterTexture     = new DistortedTexture(100,100);
-      mRegionTexture     = new DistortedTexture(100,100);
-
-      DistortedEffects objectEffects     = act.getEffects();
-      DistortedEffects backgroundEffects = new DistortedEffects();
-      DistortedEffects centerEffects     = new DistortedEffects();
-      DistortedEffects regionEffects     = new DistortedEffects();
-
-      MeshBase mesh    = act.getMesh();
-      MeshFlat quad    = new MeshFlat(1,1);
-
-      mFactor = mesh instanceof MeshSphere ? 1.0f : 0.7f;
-
-      mObjWidth = mObjectTexture.getWidth();
-      mObjHeight= mObjectTexture.getHeight();
-      mObjDepth = mObjectTexture.getDepth(mesh);
-
-      mQuat1 = new Static4D(0,0,0,1);  // unity
-      mQuat2 = new Static4D(0,0,0,1);  // quaternions
-
-      mCenterPoint= new Static3D(0,0,0);
-      mRegionPoint= new Static3D(0,0,0);
-      mRegionScalePoint = new Static3D(0,0,0);
-
-      mCenterNode = new DistortedNode(mCenterTexture, centerEffects, quad);
-      mRegionNode = new DistortedNode(mRegionTexture, regionEffects, quad);
-
-      mScreen = new DistortedScreen();
-      mScreen.setProjection(FOV, NEAR);
-      mScreen.attach(mBackgroundTexture, backgroundEffects, quad );
-      mScreen.attach(mObjectTexture    , objectEffects    , mesh );
-
-      int regionSize = mRegionTexture.getWidth();
-      mRotateCen = new Static3D(0 ,0, 0);
-
-      MatrixEffectQuaternion quat1cen = new MatrixEffectQuaternion(mQuat1, mRotateCen);
-      MatrixEffectQuaternion quat2cen = new MatrixEffectQuaternion(mQuat2, mRotateCen);
-      MatrixEffectMove centerMove = new MatrixEffectMove(mCenterPoint);
-
-      centerEffects.apply(quat1cen);
-      centerEffects.apply(quat2cen);
-      centerEffects.apply( new MatrixEffectMove(mMoveCenter) );
-      centerEffects.apply( centerMove );
-      centerEffects.apply( new MatrixEffectScale(mScaleCenter) );
-
-      regionEffects.apply(quat1cen);
-      regionEffects.apply(quat2cen);
-      regionEffects.apply( new MatrixEffectMove(mMoveRegion) );
-      regionEffects.apply( centerMove );
-      regionEffects.apply( new MatrixEffectMove(mRegionPoint) );
-      regionEffects.apply( new MatrixEffectScale(mRegionScalePoint) );
-      regionEffects.apply( new MatrixEffectMove(new Static3D( -regionSize/2 , -regionSize/2 , 0)) );
-
-      resetMatrixEffects();
-
-      // quite tricky: move the background exactly to the FAR plane! (see DistortedOutputSurface.setProjection() )
-      backgroundEffects.apply(new MatrixEffectMove(mMoveBackground) );
-      backgroundEffects.apply(new MatrixEffectScale(mScaleBackground) );
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void resetMatrixEffects()
-      {
-      Effects3DActivity2 act = (Effects3DActivity2)mView.getContext();
-      DistortedEffects objectEffects= act.getEffects();
-      Static3D rotateObj = new Static3D( (float)mObjWidth/2, (float)mObjHeight/2, (float)mObjDepth/2 );
-
-      MatrixEffectQuaternion quat1obj = new MatrixEffectQuaternion(mQuat1,  rotateObj);
-      MatrixEffectQuaternion quat2obj = new MatrixEffectQuaternion(mQuat2,  rotateObj);
-
-      objectEffects.apply( new MatrixEffectMove(mMoveObject));
-      objectEffects.apply( new MatrixEffectScale(mScaleObject) );
-      objectEffects.apply(quat1obj);
-      objectEffects.apply(quat2obj);
-
-      mQuat1.set(0,0,0,1);
-      mQuat2.set(0,0,0,1);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void showRegionAndCenter(boolean showRegion, boolean showCenter)
-      {
-      if( mShowingCenter!=showCenter  )
-        {
-        if( showCenter ) mScreen.attach(mCenterNode);
-        else             mScreen.detach(mCenterNode);
-
-        mShowingCenter = showCenter;
-        }
-
-      if( mShowingRegion!=showRegion  )
-        {
-        if( showRegion ) mScreen.attach(mRegionNode);
-        else             mScreen.detach(mRegionNode);
-
-        mShowingRegion = showRegion;
-        }
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public int getWidth()
-      {
-      return mWidth;
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void setCenter(float x, float y, float z)
-      {
-      mCenterPoint.set(mFactorObj*x,mFactorObj*y,mFactorObj*z);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void setRegion(float x, float y, float z, float r)
-      {
-      mFactorReg = 2*mFactorObj*r/mRegionTexture.getWidth();
-      mRegionPoint.set(mFactorObj*x,-mFactorObj*y, mFactorObj*z);
-      mRegionScalePoint.set(mFactorReg,mFactorReg,mFactorReg);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    void useOIT(boolean use)
-      {
-      mScreen.setOrderIndependentTransparency(use);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void onDrawFrame(GL10 glUnused)
-      {
-      mScreen.render(System.currentTimeMillis());
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void onSurfaceChanged(GL10 glUnused, int width, int height)
-      {
-      mWidth = width;
-      mScreenMin = width<height ? width:height;
-
-      float factorCen;
-      int centerSize = mCenterTexture.getWidth();
-      int regionSize = mRegionTexture.getWidth();
-
-      if( width*mObjHeight > height*mObjWidth ) // screen is more 'horizontal' than the Object
-        {
-        mFactorObj = (mFactor*height)/mObjHeight;
-        factorCen  = (0.08f  *height)/centerSize;
-        }
-      else
-        {
-        mFactorObj = (mFactor*width)/mObjWidth;
-        factorCen  = (0.08f  *width)/centerSize;
-        }
-
-      Effects3DActivity2 act = (Effects3DActivity2)mView.getContext();
-      mCenterPoint.set(mFactorObj*act.getCenterX(),+mFactorObj*act.getCenterY(),0);
-      mRegionPoint.set(mFactorObj*act.getRegionX(),-mFactorObj*act.getRegionY(),0);
-      mFactorReg = 2*mFactorObj*act.getRegionR()/regionSize;
-      mRegionScalePoint.set(mFactorReg,mFactorReg,mFactorReg);
-      mMoveObject.set( (width-mFactorObj*mObjWidth)/2 , (height-mFactorObj*mObjHeight)/2 , -mFactorObj*mObjDepth );
-      mRotateCen.set(width/2,height/2, 0);
-      mScaleObject.set(mFactorObj,mFactorObj,mFactorObj);
-      mMoveCenter.set( (width -factorCen*centerSize-mFactorObj*mObjWidth )/2 ,
-                       (height-factorCen*centerSize-mFactorObj*mObjHeight)/2 , 15 );
-      mScaleCenter.set(factorCen,factorCen,factorCen);
-      mMoveRegion.set( (width -mFactorObj*mObjWidth )/2 ,(height-mFactorObj*mObjHeight)/2 , 12 );
-
-      int backgroundSize = mBackgroundTexture.getWidth();
-      float factorBackX = ((float)width)/backgroundSize;
-      float factorBackY = ((float)height)/backgroundSize;
-
-      // quite tricky: move the background exactly to the FAR plane! (see DistortedOutputSurface.setProjection() )
-      mMoveBackground.set( -width/2, -height/2, -0.9f*height*(1.0f-NEAR)/(2.0f*(float)Math.tan(FOV*Math.PI/360)) );
-      mScaleBackground.set( 2*factorBackX, 2*factorBackY, 1.0f );
-
-      mScreen.resize(width, height);
-      }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
-      {
-      Effects3DActivity2 act = (Effects3DActivity2)mView.getContext();
-
-      InputStream isB = act.getResources().openRawResource(R.raw.water);
-      InputStream isC = act.getResources().openRawResource(R.raw.center);
-      InputStream isR = act.getResources().openRawResource(R.raw.region);
-
-      Bitmap bitmapB,bitmapC,bitmapR;
-        
-      try 
-        {
-        bitmapB = BitmapFactory.decodeStream(isB);
-        bitmapC = BitmapFactory.decodeStream(isC);
-        bitmapR = BitmapFactory.decodeStream(isR);
-        }
-      finally 
-        {
-        try 
-          {
-          isB.close();
-          isC.close();
-          isR.close();
-          }
-        catch(IOException e) { }
-        }  
-      
-      mObjectTexture.setTexture( act.getBitmap() );
-      mBackgroundTexture.setTexture(bitmapB);
-      mCenterTexture.setTexture(bitmapC);
-      mRegionTexture.setTexture(bitmapR);
-
-      DistortedEffects.setMax(EffectType.VERTEX  ,10);
-      DistortedEffects.setMax(EffectType.FRAGMENT,10);
-
-      Effect.enableEffects(EffectType.VERTEX);
-      Effect.enableEffects(EffectType.FRAGMENT);
-      Effect.enableEffects(EffectType.POSTPROCESS);
-
-      try
-        {
-        Distorted.onCreate(mView.getContext());
-        }
-      catch(Exception ex)
-        {
-        android.util.Log.e("Effects3D", ex.getMessage() );
-        }
-      }
-}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/examples/generic/Effects3DSurfaceView.java b/src/main/java/org/distorted/examples/generic/Effects3DSurfaceView.java
deleted file mode 100644
index 7cf5ca0..0000000
--- a/src/main/java/org/distorted/examples/generic/Effects3DSurfaceView.java
+++ /dev/null
@@ -1,122 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.generic;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.pm.ConfigurationInfo;
-import android.opengl.GLSurfaceView;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class Effects3DSurfaceView extends GLSurfaceView
-  {
-  private int mX, mY;
-  private Effects3DRenderer mRenderer;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-   
-  public Effects3DSurfaceView(Context context, AttributeSet attrs)
-    {
-    super(context, attrs);
-      
-    if(!isInEditMode())
-      {
-      mRenderer = new Effects3DRenderer(this);
-      final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-      final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
-      setEGLContextClientVersion( (configurationInfo.reqGlEsVersion>>16) >= 3 ? 3:2 );
-      setRenderer(mRenderer);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Effects3DRenderer getRenderer()
-    {
-    return mRenderer;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public boolean onTouchEvent(MotionEvent event)
-    {
-    int action = event.getAction();
-    int x = (int)event.getX();
-    int y = (int)event.getY();
-
-    switch(action)
-      {
-      case MotionEvent.ACTION_DOWN: mX = x;
-                                    mY = y;
-                                    break;
-
-      case MotionEvent.ACTION_MOVE: if( mX>=0 && mY>= 0 )
-                                      {
-                                      float px = mY-y;
-                                      float py = mX-x;
-                                      float pz = 0;
-                                      float plen = (float)Math.sqrt(px*px + py*py + pz*pz);
-
-                                      if( plen>0 )
-                                        {
-                                        px /= plen;
-                                        py /= plen;
-                                        pz /= plen;
-
-                                        float cosA = (float)Math.cos(plen*3.14f/mRenderer.mScreenMin);
-                                        float sinA = (float)Math.sqrt(1-cosA*cosA);
-
-                                        mRenderer.mQuat1.set(px*sinA, py*sinA, pz*sinA, cosA);
-                                        }
-                                      }
-                                    break;
-
-      case MotionEvent.ACTION_UP  : mX = -1;
-                                    mY = -1;
-
-                                    float qx = mRenderer.mQuat1.get1();
-                                    float qy = mRenderer.mQuat1.get2();
-                                    float qz = mRenderer.mQuat1.get3();
-                                    float qw = mRenderer.mQuat1.get4();
-
-                                    float rx = mRenderer.mQuat2.get1();
-                                    float ry = mRenderer.mQuat2.get2();
-                                    float rz = mRenderer.mQuat2.get3();
-                                    float rw = mRenderer.mQuat2.get4();
-
-                                    float tx = rw*qx - rz*qy + ry*qz + rx*qw;
-                                    float ty = rw*qy + rz*qx + ry*qw - rx*qz;
-                                    float tz = rw*qz + rz*qw - ry*qx + rx*qy;
-                                    float tw = rw*qw - rz*qz - ry*qy - rx*qx;
-
-                                    mRenderer.mQuat1.set(0f, 0f, 0f, 1f);
-                                    mRenderer.mQuat2.set(tx, ty, tz, tw);
-
-                                    break;
-      }
-
-    return true;
-    }
-}
-
diff --git a/src/main/java/org/distorted/examples/generic/Effects3DTab.java b/src/main/java/org/distorted/examples/generic/Effects3DTab.java
deleted file mode 100644
index 117422f..0000000
--- a/src/main/java/org/distorted/examples/generic/Effects3DTab.java
+++ /dev/null
@@ -1,277 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2018 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.generic;
-
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.LinearLayout;
-import android.widget.Spinner;
-
-import org.distorted.examples.R;
-import org.distorted.library.effect.EffectName;
-import org.distorted.library.effect.EffectType;
-import org.distorted.library.main.DistortedEffects;
-
-import java.util.ArrayList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class Effects3DTab extends Fragment implements AdapterView.OnItemSelectedListener
-  {
-  private EffectType mType;
-  private ArrayList<Effects3DEffect> mList;
-  private int mEffectAdd;
-  private EffectName[] mEffectNames;
-  private DistortedEffects mEffects;
-  private String[] mEffectStrings;
-  private int mTab, mLayout, mSpinner;
-  private int mChildren;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public Effects3DTab()
-    {
-    mList = new ArrayList<>();
-    mEffectAdd = 0;
-    mChildren  = 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
-    {
-    Bundle args = getArguments();
-
-    int position = args.getInt("position");
-
-    switch(position)
-      {
-      case 0: mTab     = R.layout.effects3dtab0;
-              mLayout  = R.id.effects3dlayout0;
-              mSpinner = R.id.effects3dspinner0;
-              mType    = EffectType.MATRIX;
-              break;
-      case 1: mTab     = R.layout.effects3dtab1;
-              mLayout  = R.id.effects3dlayout1;
-              mSpinner = R.id.effects3dspinner1;
-              mType    = EffectType.VERTEX;
-              break;
-      case 2: mTab     = R.layout.effects3dtab2;
-              mLayout  = R.id.effects3dlayout2;
-              mSpinner = R.id.effects3dspinner2;
-              mType    = EffectType.FRAGMENT;
-              break;
-      case 3: mTab     = R.layout.effects3dtab3;
-              mLayout  = R.id.effects3dlayout3;
-              mSpinner = R.id.effects3dspinner3;
-              mType    = EffectType.POSTPROCESS;
-              break;
-      }
-
-    Effects3DActivity2 act = (Effects3DActivity2)getActivity();
-
-    if( act!=null )
-      {
-      act.setTab(position, this);
-      mEffects = act.getEffects();
-      }
-
-    createEffectNames(mType);
-    mEffectStrings = new String[mEffectNames.length];
-
-    for (int i = 0; i < mEffectNames.length; i++)
-      {
-      mEffectStrings[i] = mEffectNames[i].name();
-      }
-
-    return inflater.inflate( mTab, container, false);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public void onStart()
-    {
-    super.onStart();
-
-    Effects3DActivity2 act = (Effects3DActivity2)getActivity();
-
-    ArrayAdapter<String> adapterEffect = new ArrayAdapter<>( act, android.R.layout.simple_spinner_item, mEffectStrings);
-    adapterEffect.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-
-    Spinner effectSpinner = act.findViewById(mSpinner);
-
-    effectSpinner.setOnItemSelectedListener(this);
-    effectSpinner.setAdapter(adapterEffect);
-
-    resetData();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
-    {
-    if( parent.getId() == mSpinner )
-      {
-      mEffectAdd = pos;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public void onNothingSelected(AdapterView<?> parent)
-    {
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void resetData()
-    {
-    Effects3DActivity2 act = (Effects3DActivity2)getActivity();
-    Effects3DSurfaceView view = act.findViewById(R.id.effects3dSurfaceView);
-    view.getRenderer().showRegionAndCenter(false,false);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void newEffect()
-    {
-    Effects3DActivity2 act = (Effects3DActivity2)getActivity();
-    Effects3DEffect eff = new Effects3DEffect(mEffectNames[mEffectAdd], act );
-    mList.add(eff);
-
-    mChildren++;
-
-    LinearLayout layout = act.findViewById(mLayout);
-    View view = eff.createView(mChildren);
-    layout.addView(view);
-
-    if( mEffectNames[mEffectAdd].getCenterDimension() > 0 )
-      {
-      View center = eff.createCenter(mChildren);
-      layout.addView(center);
-      }
-
-    if( mEffectNames[mEffectAdd].getRegionDimension() > 0 )
-      {
-      View region = eff.createRegion(mChildren);
-      layout.addView(region);
-      }
-
-    eff.apply(mEffects);
-
-    boolean region = act.getShowRegion();
-    boolean center = act.getShowCenter();
-
-    boolean show = (mEffectNames[mEffectAdd].getType()==EffectType.VERTEX);
-    Effects3DSurfaceView sv = act.findViewById(R.id.effects3dSurfaceView);
-    sv.getRenderer().showRegionAndCenter( (show && region) , (show && center) );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private void createEffectNames(EffectType type)
-    {
-    EffectName[] names = EffectName.values();
-
-    int numEffects=0;
-
-    for(int i=0; i<names.length; i++)
-      if( names[i].getType() == type ) numEffects++;
-
-    mEffectNames = new EffectName[numEffects];
-
-    numEffects=0;
-
-    for(int i=0; i<names.length; i++)
-      if( names[i].getType() == type )
-        {
-        mEffectNames[numEffects++] = names[i];
-        }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void removeAll()
-    {
-    Effects3DActivity2 act = (Effects3DActivity2)getActivity();
-
-    mChildren = 0;
-
-    mList.clear();
-    LinearLayout layout = act.findViewById(mLayout);
-    layout.removeAllViews();
-    mEffects.abortByType(mType);
-
-    resetData();
-    act.resetData();
-
-    if( mType==EffectType.MATRIX )
-      {
-      act.resetMatrixEffects();
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void remove(View v)
-    {
-    for(Effects3DEffect effect: mList)
-      {
-      if( effect.thisView(v) )
-        {
-        Effects3DActivity2 act = (Effects3DActivity2)getActivity();
-        LinearLayout layout = act.findViewById(mLayout);
-        View view;
-
-        view = effect.getEffect();
-        if( view!=null ) layout.removeView(view);
-        view = effect.getCenter();
-        if( view!=null ) layout.removeView(view);
-        view = effect.getRegion();
-        if( view!=null ) layout.removeView(view);
-
-        mEffects.abortById(effect.getId());
-
-        int index = mList.indexOf(effect);
-        int capac = mList.size();
-
-        for(int i=index+1; i<capac; i++)
-          {
-          mList.get(i).setBackground(i-1);
-          }
-
-        mList.remove(effect);
-        mChildren--;
-
-        resetData();
-
-        break;
-        }
-      }
-    }
-
-  }
diff --git a/src/main/java/org/distorted/examples/generic/Effects3DTabViewPager.java b/src/main/java/org/distorted/examples/generic/Effects3DTabViewPager.java
deleted file mode 100644
index ce4579f..0000000
--- a/src/main/java/org/distorted/examples/generic/Effects3DTabViewPager.java
+++ /dev/null
@@ -1,86 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2018 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.examples.generic;
-
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.os.Bundle;
-
-import org.distorted.examples.R;
-
-import java.lang.ref.WeakReference;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-public class Effects3DTabViewPager extends FragmentPagerAdapter
-  {
-  private WeakReference<Effects3DActivity2> mAct;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  Effects3DTabViewPager(Effects3DActivity2 act, FragmentManager fm)
-    {
-    super(fm);
-    mAct = new WeakReference<>(act);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public Fragment getItem(int position)
-    {
-    if( position>=0 && position<Effects3DActivity2.NUM_TABS )
-      {
-      Effects3DTab tab = new Effects3DTab();
-
-      Bundle bundle = new Bundle();
-      bundle.putInt("position", position);
-      tab.setArguments(bundle);
-
-      return tab;
-      }
-
-    return null;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public int getCount()
-    {
-    return Effects3DActivity2.NUM_TABS;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  @Override
-  public CharSequence getPageTitle(int position)
-    {
-    switch (position)
-      {
-      case 0 : return mAct.get().getString(R.string.type_matrix);
-      case 1 : return mAct.get().getString(R.string.type_vertex);
-      case 2 : return mAct.get().getString(R.string.type_fragment);
-      case 3 : return mAct.get().getString(R.string.type_postprocess);
-      default: return null;
-      }
-    }
-  }
diff --git a/src/main/java/org/distorted/examples/generic/GenericActivity.java b/src/main/java/org/distorted/examples/generic/GenericActivity.java
new file mode 100644
index 0000000..7ba7b4f
--- /dev/null
+++ b/src/main/java/org/distorted/examples/generic/GenericActivity.java
@@ -0,0 +1,325 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.generic;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.NumberPicker;
+import android.widget.Spinner;
+import android.widget.TableRow;
+
+import org.distorted.examples.R;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class GenericActivity extends Activity
+                               implements View.OnClickListener, AdapterView.OnItemSelectedListener
+  {
+  private static final int COLOR_OFF = 0xffffe81f;
+  private static final int COLOR_ON  = 0xff0000ff;
+  private static final int COLOR_INAC= 0xff999999;
+
+  private int mNumCols = 1;
+  private int mNumRows = 1;
+  private int mNumSlic = 1;
+  private boolean mGridInitialized;
+  private NumberPicker mColsPicker, mRowsPicker, mSlicPicker;
+  private boolean[] mShape;
+  private int mObjectType;
+  private int mBitmapID;
+  private LinearLayout mLay;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  protected void onCreate(Bundle savedState)
+    {
+    super.onCreate(savedState);
+
+    setContentView(R.layout.objectpickerlayout);
+
+    mLay = findViewById(R.id.objectpicker_buttongrid);
+
+    mColsPicker = findViewById(R.id.objectpicker_cols);
+    mRowsPicker = findViewById(R.id.objectpicker_rows);
+    mSlicPicker = findViewById(R.id.objectpicker_slices);
+
+    mColsPicker.setMaxValue(40);
+    mColsPicker.setMinValue( 1);
+    mRowsPicker.setMaxValue(40);
+    mRowsPicker.setMinValue( 1);
+    mSlicPicker.setMaxValue(40);
+    mSlicPicker.setMinValue( 0);
+
+    mColsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
+         {
+         @Override
+         public void onValueChange(NumberPicker picker, int oldVal, int newVal)
+           {
+           setGrid();
+           }
+         });
+
+    mRowsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
+         {
+         @Override
+         public void onValueChange(NumberPicker picker, int oldVal, int newVal)
+           {
+           setGrid();
+           }
+         });
+
+    mSlicPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
+         {
+         @Override
+         public void onValueChange(NumberPicker picker, int oldVal, int newVal)
+           {
+           mNumSlic = mSlicPicker.getValue();
+           }
+         });
+
+    mObjectType = 0;
+    mGridInitialized = false;
+
+    Spinner typeSpinner  = findViewById(R.id.objectpicker_spinnerType);
+    typeSpinner.setOnItemSelectedListener(this);
+
+    String[] objectType = new String[] {"Mesh: Cubes", "Mesh: Flat", "Mesh: Sphere", "Mesh: Quad"};
+
+    ArrayAdapter<String> adapterType = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectType);
+    adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    typeSpinner.setAdapter(adapterType);
+
+    Spinner bitmapSpinner  = findViewById(R.id.objectpicker_spinnerBitmap);
+    bitmapSpinner.setOnItemSelectedListener(this);
+
+    String[] objectBitmap = new String[] { "Texture: Grid", "Texture: Girl", "Texture: Dog", "Texture: Cat",
+                                           "Texture: Squares", "Texture: Bean", "Texture: Lisa", "Texture: World" };
+
+    ArrayAdapter<String> adapterBitmap = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, objectBitmap);
+    adapterBitmap.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+    bitmapSpinner.setAdapter(adapterBitmap);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setGrid()
+    {
+    mGridInitialized = true;
+
+    mNumCols = mColsPicker.getValue();
+    mNumRows = mRowsPicker.getValue();
+
+    int width = mLay.getWidth();
+    int height= mLay.getHeight();
+    int w = mNumCols>0 ? (int)( 0.9f*width / mNumCols) : 0;
+    int h = mNumRows>0 ? (int)( 0.9f*height/ mNumRows) : 0;
+    int size= w<h ? w:h;
+    int pad = size<20 ? 1 : size/20;
+
+    mLay.removeAllViews();
+
+    mShape = new boolean[mNumRows*mNumCols];
+
+    TableRow.LayoutParams p = new TableRow.LayoutParams();
+
+    p.rightMargin  = pad;
+    p.leftMargin   = pad;
+    p.topMargin    = pad;
+    p.bottomMargin = pad;
+    p.height       = size;
+    p.width        = size;
+
+    for (int rows=0; rows<mNumRows; rows++)
+      {
+      TableRow tr = new TableRow(this);
+      tr.setGravity(Gravity.CENTER);
+
+      for(int cols=0; cols<mNumCols; cols++)
+        {
+        Button b = new Button(this);
+        b.setOnClickListener(this);
+        b.setId(rows*mNumCols+cols);
+        b.setLayoutParams(p);
+        b.setBackgroundColor(mObjectType==0 ? COLOR_ON:COLOR_INAC);
+        tr.addView(b, p);
+        mShape[rows*mNumCols+cols] = true;
+        }
+
+      mLay.addView(tr);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onClick(View view)
+    {
+    if( mObjectType==0 )  // cubes
+      {
+      Button tmp = (Button)view;
+      int id = tmp.getId();
+      mShape[id] = !mShape[id];
+      tmp.setBackgroundColor(mShape[id] ? COLOR_ON:COLOR_OFF);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void uncheckAll()
+    {
+    TableRow tr;
+    Button butt;
+
+    for (int row=0; row<mNumRows; row++)
+      {
+      tr = (TableRow)mLay.getChildAt(row);
+
+      for(int col=0; col<mNumCols; col++)
+        {
+        butt = (Button)tr.getVirtualChildAt(col);
+        butt.setBackgroundColor(mObjectType==0 ? COLOR_ON : COLOR_INAC);
+        mShape[row*mNumCols+col] = true;
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void Create(View v)
+    {
+    Intent mainInt = new Intent( getApplicationContext(), GenericActivity2.class);
+    Bundle b = new Bundle();
+
+    int rows=0, cols=0;
+
+    switch(mObjectType)
+      {
+      case 0:
+      case 1: rows = mNumRows;
+              cols = mNumCols;
+              break;
+      case 2: rows = mNumRows;   // always make the sphere equal in X and Y
+              cols = mNumRows;   //
+              break;
+      case 3: rows = 1;          // a quad is always 1x1
+              cols = 1;
+              break;
+      }
+
+    b.putInt("type", mObjectType);
+    b.putInt("cols", cols);
+    b.putInt("rows", rows);
+    b.putInt("slices", mNumSlic);
+    b.putInt("bitmap", mBitmapID);
+
+    if( mObjectType==0 )   // cubes
+      {
+      String str = "";
+
+      for(int i=0; i<mNumRows*mNumCols; i++)
+        str += mShape[i] ? "1" : "0";
+
+      b.putString("string", str);
+      }
+    else
+      {
+      b.putString("string", "");
+      }
+
+    mainInt.putExtras(b);
+    startActivity(mainInt);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    switch(parent.getId())
+      {
+      case R.id.objectpicker_spinnerType  : if( mObjectType!=pos )
+                                              {
+                                              mObjectType = pos;
+                                              uncheckAll();
+
+                                              switch(mObjectType)
+                                                {
+                                                case 0: mColsPicker.setEnabled(true);
+                                                        mRowsPicker.setEnabled(true);
+                                                        mSlicPicker.setEnabled(true);
+                                                        break;
+                                                case 1: mColsPicker.setEnabled(true);
+                                                        mRowsPicker.setEnabled(true);
+                                                        mSlicPicker.setEnabled(false);
+                                                        break;
+                                                case 2: mColsPicker.setEnabled(false);
+                                                        mRowsPicker.setEnabled(true);
+                                                        mSlicPicker.setEnabled(false);
+                                                        break;
+                                                case 3: mColsPicker.setEnabled(false);
+                                                        mRowsPicker.setEnabled(false);
+                                                        mSlicPicker.setEnabled(false);
+                                                        break;
+                                                }
+                                              }
+                                            break;
+      case R.id.objectpicker_spinnerBitmap: switch(pos)
+                                              {
+                                              case 0: mBitmapID = -1            ; break;
+                                              case 1: mBitmapID = R.raw.face    ; break;
+                                              case 2: mBitmapID = R.raw.dog     ; break;
+                                              case 3: mBitmapID = R.raw.cat     ; break;
+                                              case 4: mBitmapID = R.raw.grid    ; break;
+                                              case 5: mBitmapID = R.raw.bean    ; break;
+                                              case 6: mBitmapID = R.raw.monalisa; break;
+                                              case 7: mBitmapID = R.raw.world   ; break;
+                                              }
+                                            break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onNothingSelected(AdapterView<?> parent)
+    {
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Overrides
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onWindowFocusChanged(boolean hasFocus)
+    {
+    super.onWindowFocusChanged(hasFocus);
+
+    mColsPicker.setValue(mNumCols);
+    mRowsPicker.setValue(mNumRows);
+    mSlicPicker.setValue(mNumSlic);
+
+    if( !mGridInitialized ) setGrid();
+    }
+  }
diff --git a/src/main/java/org/distorted/examples/generic/GenericActivity2.java b/src/main/java/org/distorted/examples/generic/GenericActivity2.java
new file mode 100644
index 0000000..7836c31
--- /dev/null
+++ b/src/main/java/org/distorted/examples/generic/GenericActivity2.java
@@ -0,0 +1,464 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.generic;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.support.design.widget.TabLayout;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.CheckBox;
+
+import org.distorted.examples.R;
+import org.distorted.library.main.Distorted;
+import org.distorted.library.main.DistortedEffects;
+import org.distorted.library.main.DistortedTexture;
+import org.distorted.library.mesh.MeshCubes;
+import org.distorted.library.mesh.MeshFlat;
+import org.distorted.library.mesh.MeshBase;
+import org.distorted.library.mesh.MeshQuad;
+import org.distorted.library.mesh.MeshSphere;
+import org.distorted.library.type.Static4D;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class GenericActivity2 extends AppCompatActivity
+  {
+  public static final int NUM_TABS = 4;
+
+  private DistortedTexture mTexture;
+  private MeshBase mMesh;
+  private Bitmap mBitmap;
+
+  private float mCenterX, mCenterY, mCenterZ;
+  private float mRegionX, mRegionY, mRegionZ, mRegionR;
+  private DistortedEffects mEffects;
+  private ViewPager mViewPager;
+
+  private boolean mShowCenter;
+  private boolean mShowRegion;
+  private boolean mShowNormal;
+  private boolean mUseOIT    ;
+
+  private GenericTab[] mTab;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  protected void onCreate(Bundle savedState)
+    {
+    super.onCreate(savedState);
+
+    setTheme(R.style.CustomActivityThemeNoActionBar);
+
+    mTab = new GenericTab[NUM_TABS];
+
+    Bundle b = getIntent().getExtras();
+
+    int objectType = b.getInt("type");
+    int numCols    = b.getInt("cols");
+    int numRows    = b.getInt("rows");
+    int numSlic    = b.getInt("slices");
+    int bitmapID   = b.getInt("bitmap");
+    String str     = b.getString("string");
+
+    mShowCenter = false;
+    mShowRegion = false;
+    mShowNormal = false;
+    mUseOIT     = false;
+
+    int maxsize = numCols > numRows ? (numCols>numSlic ? numCols:numSlic) : (numRows>numSlic ? numRows:numSlic) ;
+
+    createBitmap(maxsize, bitmapID);
+
+    switch(objectType)
+      {
+      case 0: if( bitmapID!=-1 )
+                {
+                mMesh = new MeshCubes(numCols, str, numSlic);
+                }
+              else
+                {
+                Static4D mapFB = new Static4D(0.0f,0.0f, (float)numCols/maxsize, (float)numRows/maxsize);
+                Static4D mapLR = new Static4D(0.0f,0.0f, (float)numSlic/maxsize, (float)numRows/maxsize);
+                Static4D mapTB = new Static4D(0.0f,0.0f, (float)numCols/maxsize, (float)numSlic/maxsize);
+
+                mMesh = new MeshCubes(numCols, str, numSlic, mapFB, mapFB, mapLR, mapLR, mapTB, mapTB);
+                }
+              break;
+      case 1: mMesh = new MeshFlat(numCols,numRows);
+              break;
+      case 2: mMesh = new MeshSphere(numRows);
+              break;
+      case 3: mMesh = new MeshQuad();
+              break;
+      }
+
+    mMesh.setShowNormals(mShowNormal);
+    mTexture= new DistortedTexture(numCols,numRows);
+    mEffects= new DistortedEffects();
+
+    final View view = getLayoutInflater().inflate(R.layout.genericlayout, null);
+
+    setContentView(view);
+
+    mViewPager = findViewById(R.id.generic_viewpager);
+    mViewPager.setOffscreenPageLimit( NUM_TABS-1 );
+    GenericTabViewPager pager = new GenericTabViewPager(this, getSupportFragmentManager() );
+    mViewPager.setAdapter(pager);
+    TabLayout tabLayout = findViewById(R.id.generic_sliding_tabs);
+    tabLayout.setupWithViewPager(mViewPager);
+
+    resetData();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createBitmap(int size, int bitmapID)
+    {
+    if( bitmapID!=-1)
+      {
+      InputStream is = getResources().openRawResource(bitmapID);
+
+      try
+        {
+        mBitmap = BitmapFactory.decodeStream(is);
+        }
+      finally
+        {
+        try
+          {
+          is.close();
+          }
+        catch(IOException e) { }
+        }
+      }
+    else
+      {
+      final int T = 64;
+      final int S = T*size;
+
+      Paint paint = new Paint();
+      mBitmap = Bitmap.createBitmap(S,S, Bitmap.Config.ARGB_8888);
+      Canvas canvas = new Canvas(mBitmap);
+
+      paint.setAntiAlias(true);
+      paint.setTextAlign(Paint.Align.CENTER);
+      paint.setColor(0xff008800);
+      paint.setStyle(Paint.Style.FILL);
+      canvas.drawRect(0, 0, S, S, paint);
+      paint.setColor(0xffffffff);
+
+      for(int i=0; i<=size ; i++ )
+        {
+        canvas.drawRect( T*i-1, 0, T*i+1, S, paint);
+        canvas.drawRect( 0, T*i-1, S, T*i+1, paint);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DistortedTexture getTexture()
+    {
+    return mTexture;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public MeshBase getMesh()
+    {
+    return mMesh;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Bitmap getBitmap()
+    {
+    return mBitmap;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setRegion(float x, float y, float z, float r)
+    {
+    mRegionX = x;
+    mRegionY =-y;
+    mRegionZ = z;
+    mRegionR = r;
+
+    GenericSurfaceView view = findViewById(R.id.genericSurfaceView);
+    view.getRenderer().setRegion(mRegionX, mRegionY, mRegionZ, mRegionR);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setCenter(float x, float y, float z)
+    {
+    mCenterX = x;
+    mCenterY = y;
+    mCenterZ = z;
+
+    GenericSurfaceView view = findViewById(R.id.genericSurfaceView);
+    view.getRenderer().setCenter( mCenterX, mCenterY, mCenterZ );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getCenterX()
+    {
+    return mCenterX;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getCenterY()
+    {
+    return mCenterY;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getRegionX()
+    {
+    return mRegionX;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getRegionY()
+    {
+    return mRegionY;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getRegionZ()
+    {
+    return mRegionZ;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getRegionR()
+    {
+    return mRegionR;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getWidth()
+    {
+    return mTexture==null ? 0: mTexture.getWidth();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getHeight()
+    {
+    return mTexture==null ? 0: mTexture.getHeight();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getDepth()
+    {
+    return mTexture==null ? 0: mTexture.getDepth(mMesh);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getScreenWidth()
+    {
+    GenericRenderer r = ((GenericSurfaceView)findViewById(R.id.genericSurfaceView)).getRenderer();
+    return r.getWidth();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void resetData()
+    {
+    mCenterX = 0.5f*getWidth();
+    mCenterY = 0.5f*getHeight();
+    mRegionX = 0;
+    mRegionY = 0;
+    mRegionZ = 0;
+    mRegionR = getWidth()/2;
+
+    GenericSurfaceView view = findViewById(R.id.genericSurfaceView);
+    GenericRenderer renderer= view.getRenderer();
+
+    renderer.setCenter( mCenterX, mCenterY, mCenterZ );
+    renderer.setRegion( mRegionX, mRegionY, mRegionZ, mRegionR );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void resetMatrixEffects()
+    {
+    GenericSurfaceView view = findViewById(R.id.genericSurfaceView);
+    GenericRenderer renderer= view.getRenderer();
+
+    renderer.resetMatrixEffects();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DistortedEffects getEffects()
+    {
+    return mEffects;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void newEffect(View v)
+    {
+    int pos = mViewPager.getCurrentItem();
+
+    if( pos>=0 && pos<NUM_TABS ) mTab[pos].newEffect();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void removeAll(View v)
+    {
+    int pos = mViewPager.getCurrentItem();
+
+    if( pos>=0 && pos<NUM_TABS ) mTab[pos].removeAll();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void remove(View v)
+    {
+    int pos = mViewPager.getCurrentItem();
+
+    if( pos>=0 && pos<NUM_TABS ) mTab[pos].remove(v);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setTab(int pos, GenericTab tab)
+    {
+    if( pos>=0 && pos<NUM_TABS ) mTab[pos] = tab;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean getShowCenter()
+    {
+    return mShowCenter;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean getShowRegion()
+    {
+    return mShowRegion;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void showCenter(View view)
+    {
+    CheckBox box = (CheckBox)view;
+    mShowCenter = box.isChecked();
+
+    GenericSurfaceView sv = findViewById(R.id.genericSurfaceView);
+    sv.getRenderer().showRegionAndCenter(mShowRegion,mShowCenter);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void showRegion(View view)
+    {
+    CheckBox box = (CheckBox)view;
+    mShowRegion = box.isChecked();
+
+    GenericSurfaceView sv = findViewById(R.id.genericSurfaceView);
+    sv.getRenderer().showRegionAndCenter(mShowRegion,mShowCenter);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void showNormal(View view)
+    {
+    CheckBox box = (CheckBox)view;
+    mShowNormal = box.isChecked();
+
+    if ( mMesh!=null )
+      {
+      mMesh.setShowNormals(mShowNormal);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void triggerOIT(View view)
+    {
+    CheckBox box = (CheckBox)view;
+    mUseOIT = box.isChecked();
+
+    GenericSurfaceView sv = findViewById(R.id.genericSurfaceView);
+    sv.getRenderer().useOIT(mUseOIT);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Overrides
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  protected void onPause()
+    {
+    GLSurfaceView mView = findViewById(R.id.genericSurfaceView);
+    if( mView!=null ) mView.onPause();
+
+    Distorted.onPause();
+    super.onPause();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  protected void onResume()
+    {
+    super.onResume();
+    GLSurfaceView mView = findViewById(R.id.genericSurfaceView);
+    if( mView!=null ) mView.onResume();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  protected void onDestroy()
+    {
+    Distorted.onDestroy();
+    super.onDestroy();
+    }
+
+  }
diff --git a/src/main/java/org/distorted/examples/generic/GenericEffect.java b/src/main/java/org/distorted/examples/generic/GenericEffect.java
new file mode 100644
index 0000000..c91ef61
--- /dev/null
+++ b/src/main/java/org/distorted/examples/generic/GenericEffect.java
@@ -0,0 +1,827 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.generic;
+
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.distorted.examples.R;
+import org.distorted.library.effect.Effect;
+import org.distorted.library.effect.EffectName;
+import org.distorted.library.effect.EffectType;
+import org.distorted.library.effect.FragmentEffectAlpha;
+import org.distorted.library.effect.FragmentEffectBrightness;
+import org.distorted.library.effect.FragmentEffectChroma;
+import org.distorted.library.effect.FragmentEffectContrast;
+import org.distorted.library.effect.FragmentEffectSaturation;
+import org.distorted.library.effect.MatrixEffectMove;
+import org.distorted.library.effect.MatrixEffectQuaternion;
+import org.distorted.library.effect.MatrixEffectRotate;
+import org.distorted.library.effect.MatrixEffectScale;
+import org.distorted.library.effect.MatrixEffectShear;
+import org.distorted.library.effect.PostprocessEffectBlur;
+import org.distorted.library.effect.PostprocessEffectGlow;
+import org.distorted.library.effect.VertexEffectDeform;
+import org.distorted.library.effect.VertexEffectDistort;
+import org.distorted.library.effect.VertexEffectPinch;
+import org.distorted.library.effect.VertexEffectSink;
+import org.distorted.library.effect.VertexEffectSwirl;
+import org.distorted.library.effect.VertexEffectWave;
+import org.distorted.library.main.DistortedEffects;
+import org.distorted.library.type.Dynamic1D;
+import org.distorted.library.type.Dynamic2D;
+import org.distorted.library.type.Dynamic3D;
+import org.distorted.library.type.Dynamic4D;
+import org.distorted.library.type.Dynamic5D;
+import org.distorted.library.type.Static1D;
+import org.distorted.library.type.Static2D;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+import org.distorted.library.type.Static5D;
+
+import java.lang.ref.WeakReference;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class GenericEffect implements SeekBar.OnSeekBarChangeListener
+  {
+  private static final int BACKGROUND_ODD = 0xff555555;
+  private static final int BACKGROUND_EVEN= 0xff333333;
+
+  private WeakReference<GenericActivity2> mAct;
+
+  private EffectName mName;
+  private int[] mInter;
+  private int[] mInterRegion;
+  private int[] mInterCenter;
+  private int[] mSeekID;
+  private int[] mSeekRegionID;
+  private int[] mSeekCenterID;
+  private int mDimension, mRegionDimension;
+  private TextView mText,mTextRegion,mTextCenter;
+
+  private Dynamic1D mDyn1;
+  private Dynamic2D mDyn2;
+  private Dynamic3D mDyn3;
+  private Dynamic4D mDyn4;
+  private Dynamic5D mDyn5;
+  private Static1D  mSta1;
+  private Static2D  mSta2;
+  private Static3D  mSta3;
+  private Static4D  mSta4;
+  private Static5D  mSta5;
+  private Dynamic4D mRegion4Dyn;
+  private Static4D  mRegion4Sta;
+  private Dynamic3D mRegion3Dyn;
+  private Static3D  mRegion3Sta;
+  private Dynamic3D mCenterDyn;
+  private Static3D  mCenterSta;
+
+  private View mButton, mEffect, mCenter, mRegion;
+  private long mId;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  void apply(DistortedEffects effects)
+    {
+    Effect effect=null;
+
+    switch(mName)
+      {
+      case ROTATE           : effect = new MatrixEffectRotate      (mDyn1, mDyn3, mCenterDyn); break;
+      case QUATERNION       : effect = new MatrixEffectQuaternion  (mDyn4, mCenterDyn); break;
+      case MOVE             : effect = new MatrixEffectMove        (mDyn3)            ; break;
+      case SCALE            : effect = new MatrixEffectScale       (mDyn3)            ; break;
+      case SHEAR            : effect = new MatrixEffectShear       (mDyn3, mCenterDyn); break;
+
+      case DISTORT          : effect = new VertexEffectDistort     (mDyn3, mCenterDyn, mRegion4Dyn); break;
+      case DEFORM           : effect = new VertexEffectDeform      (mDyn3, mCenterDyn, mRegion4Dyn); break;
+      case SINK             : effect = new VertexEffectSink        (mDyn1, mCenterDyn, mRegion4Dyn); break;
+      case PINCH            : effect = new VertexEffectPinch       (mDyn2, mCenterDyn, mRegion4Dyn); break;
+      case SWIRL            : effect = new VertexEffectSwirl       (mDyn1, mCenterDyn, mRegion4Dyn); break;
+      case WAVE             : effect = new VertexEffectWave        (mDyn5, mCenterDyn, mRegion4Dyn); break;
+
+      case ALPHA            : effect = new FragmentEffectAlpha     (mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_ALPHA     : effect = new FragmentEffectAlpha     (mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
+      case CHROMA           : effect = new FragmentEffectChroma    (mDyn1, mDyn3, mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_CHROMA    : effect = new FragmentEffectChroma    (mDyn1, mDyn3, mCenterDyn, mRegion3Dyn, true ); break;
+      case BRIGHTNESS       : effect = new FragmentEffectBrightness(mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_BRIGHTNESS: effect = new FragmentEffectBrightness(mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
+      case SATURATION       : effect = new FragmentEffectSaturation(mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_SATURATION: effect = new FragmentEffectSaturation(mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
+      case CONTRAST         : effect = new FragmentEffectContrast  (mDyn1,        mCenterDyn, mRegion3Dyn, false); break;
+      case SMOOTH_CONTRAST  : effect = new FragmentEffectContrast  (mDyn1,        mCenterDyn, mRegion3Dyn, true ); break;
+
+      case BLUR             : effect = new PostprocessEffectBlur   (mDyn1       ); break;
+      case GLOW             : effect = new PostprocessEffectGlow   (mDyn1, mDyn4); break;
+      }
+
+    if( effect!=null )
+      {
+      effects.apply(effect);
+      mId = effect.getID();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// requires knowledge about effect nature
+
+  private void fillStatics()
+    {
+    switch(mName)
+      {
+      ///////////////////////////////////////////////////////////////////////////////////////
+      // MATRIX
+      ///////////////////////////////////////////////////////////////////////////////////////
+
+      case ROTATE           : float an = (mInter[0]-50)*180/50;
+                              float rx = (mInter[1]-50)/ 50.0f;
+                              float ry = (mInter[2]-50)/ 50.0f;
+                              float rz = (mInter[3]-50)/ 50.0f;
+                              mSta1.set(an);
+                              mSta3.set(rx,ry,rz);
+                              break;
+      case QUATERNION       : float qx = (mInter[0]-50)/ 50.0f;
+                              float qy = (mInter[1]-50)/ 50.0f;
+                              float qz = (mInter[2]-50)/ 50.0f;
+                              float qa = (mInter[3]-50)*3.1415f/50;
+                              float cosA = (float)Math.cos(qa/2);
+                              float len = (float)Math.sqrt(qx*qx+qy*qy+qz*qz);
+                              float sinAnorm = (float)Math.sin(qa/2)/len;
+                              mSta4.set(sinAnorm*qx,sinAnorm*qy,sinAnorm*qz, cosA);
+                              break;
+      case MOVE             : float scr= mAct.get().getScreenWidth();
+                              float sw = scr/20000.0f;
+                              float sh = scr/20000.0f;
+                              float xm = (mInter[0]-50)*sw;
+                              float ym = (mInter[1]-50)*sh;
+                              float zm = (mInter[2]-50)*(sw+sh)/2;
+                              mSta3.set(xm,ym,zm);
+                              break;
+      case SCALE            : float xs = (mInter[0]>50 ? 0.18f : 0.018f)*(mInter[0]-50) + 1;
+                              float ys = (mInter[1]>50 ? 0.18f : 0.018f)*(mInter[1]-50) + 1;
+                              float zs = (mInter[2]>50 ? 0.18f : 0.018f)*(mInter[2]-50) + 1;
+                              mSta3.set(xs,ys,zs);
+                              break;
+      case SHEAR            : float xsh = (mInter[0]-50)/25.0f;
+                              float ysh = (mInter[1]-50)/25.0f;
+                              float zsh = (mInter[2]-50)/25.0f;
+                              mSta3.set(xsh,ysh,zsh);
+                              break;
+
+      ///////////////////////////////////////////////////////////////////////////////////////
+      // VERTEX
+      ///////////////////////////////////////////////////////////////////////////////////////
+
+      case DISTORT          :
+      case DEFORM           : float ld = mAct.get().getWidth()/50.0f;
+                              float xd = (mInter[0]-50)*ld;
+                              float yd = (mInter[1]-50)*ld;
+                              float zd = (mInter[2]-50)*ld;
+                              mSta3.set(xd,yd,zd);
+                              break;
+      case WAVE             : float l2 = mAct.get().getWidth()/50.0f;
+                              float x2 = (mInter[0]-50)*l2;
+                              float y2 = (mInter[1]-50)*l2;
+                              float z2 = (mInter[2]-50)*180 / 50;
+                              float w2 = (mInter[3]-50)*180 / 50;
+                              float v2 = (mInter[4]-50)*180 / 50;
+                              mSta5.set(x2,y2,z2,w2,v2);
+                              break;
+      case SWIRL            : mSta1.set( 3.6f*(mInter[0]-50) );
+                              break;
+      case SINK             : mSta1.set(mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f);
+                              break;
+      case PINCH            : float dp = mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f;
+                              float ap = (mInter[1]-50)*180 / 50;
+                              mSta2.set(dp,ap);
+                              break;
+
+      ///////////////////////////////////////////////////////////////////////////////////////
+      // FRAGMENT
+      ///////////////////////////////////////////////////////////////////////////////////////
+
+      case ALPHA            :
+      case SMOOTH_ALPHA     : mSta1.set(mInter[0]/100.0f);
+                              break;
+      case SATURATION       :
+      case SMOOTH_SATURATION:
+      case CONTRAST         :
+      case SMOOTH_CONTRAST  :
+      case BRIGHTNESS       :
+      case SMOOTH_BRIGHTNESS: mSta1.set(mInter[0] > 50 ? 50.0f/(100.01f-mInter[0]) : mInter[0] / 50.0f);
+                              break;
+      case CHROMA           :
+      case SMOOTH_CHROMA    : mSta1.set(mInter[0]/100.0f);
+                              mSta3.set(mInter[1]/100.0f,
+                                        mInter[2]/100.0f,
+                                        mInter[3]/100.0f);
+                              break;
+
+      ///////////////////////////////////////////////////////////////////////////////////////
+      // POSTPROCESS
+      ///////////////////////////////////////////////////////////////////////////////////////
+
+      case BLUR             : mSta1.set(mInter[0]/2.0f);
+                              break;
+      case GLOW             : mSta1.set(mInter[0]/2.0f);
+                              mSta4.set(mInter[1]/100.0f,
+                                        mInter[2]/100.0f,
+                                        mInter[3]/100.0f,
+                                        mInter[4]/100.0f );
+                              break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultInter()
+    {
+    switch(mDimension)
+      {
+      case 5: mInter[4] = 50;
+      case 4: mInter[3] = 50;
+      case 3: mInter[2] = 50;
+      case 2: mInter[1] = 50;
+      case 1: mInter[0] = 50;
+      }
+
+    if( mName==EffectName.ROTATE || mName==EffectName.QUATERNION ) mInter[1]= 100;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setText()
+    {
+    String text = mName.name();
+
+    if( mSta1 !=null )
+      {
+      float f1 = ((int)(mSta1.get1()*100))/100.0f;
+      text += " "+f1;
+      }
+
+    if( mSta2 !=null )
+      {
+      float f1 = ((int)(mSta2.get1()*100))/100.0f;
+      float f2 = ((int)(mSta2.get2()*100))/100.0f;
+      text += " ("+f1+","+f2+")";
+      }
+
+    if( mSta3 !=null )
+      {
+      float f1 = ((int)(mSta3.get1()*100))/100.0f;
+      float f2 = ((int)(mSta3.get2()*100))/100.0f;
+      float f3 = ((int)(mSta3.get3()*100))/100.0f;
+      text += " ("+f1+","+f2+","+f3+")";
+      }
+
+    if( mSta4 !=null )
+      {
+      float f1 = ((int)(mSta4.get1()*100))/100.0f;
+      float f2 = ((int)(mSta4.get2()*100))/100.0f;
+      float f3 = ((int)(mSta4.get3()*100))/100.0f;
+      float f4 = ((int)(mSta4.get4()*100))/100.0f;
+      text += " ("+f1+","+f2+","+f3+","+f4+")";
+      }
+
+    if( mSta5 !=null )
+      {
+      float f1 = ((int)(mSta5.get1()*100))/100.0f;
+      float f2 = ((int)(mSta5.get2()*100))/100.0f;
+      float f3 = ((int)(mSta5.get3()*100))/100.0f;
+      float f4 = ((int)(mSta5.get4()*100))/100.0f;
+      float f5 = ((int)(mSta5.get5()*100))/100.0f;
+      text += " ("+f1+","+f2+","+f3+","+f4+","+f5+")";
+      }
+
+    mText.setText(text);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fillCenterStatics()
+    {
+    GenericActivity2 act = mAct.get();
+
+    float x = (mInterCenter[0]*0.012f - 0.1f)*act.getWidth();
+    float y = (mInterCenter[1]*0.012f - 0.1f)*act.getHeight();
+    float z = (mInterCenter[2]*0.012f - 0.1f)*act.getDepth();
+
+    mCenterSta.set(x,y,z);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultCenterInter()
+    {
+    mInterCenter[0] = 50;
+    mInterCenter[1] = 50;
+    mInterCenter[2] = 50;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setCenterText()
+    {
+    float f0 = ((int)(mCenterSta.get1()*100))/100.0f;
+    float f1 = ((int)(mCenterSta.get2()*100))/100.0f;
+    float f2 = ((int)(mCenterSta.get3()*100))/100.0f;
+
+    mTextCenter.setText("center ("+f0+","+f1+","+f2+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void fillRegionStatics()
+    {
+    GenericActivity2 act = mAct.get();
+
+    float factorX = act.getWidth() / 100.0f;
+    float factorY = act.getHeight()/ 100.0f;
+   // float factorZ = act.getDepth() / 100.0f;
+
+    int deduct = (mName.getType() == EffectType.VERTEX ? 50:0);
+
+    float x = (mInterRegion[0]-deduct)*factorX;
+    float y = (mInterRegion[1]-deduct)*factorY;
+    float z = (mInterRegion[2]-deduct)*factorX;   // ??
+    float r =  mInterRegion[3]        *(factorX+factorY)/2;
+
+    mRegion4Sta.set(x,y,z,r);
+    mRegion3Sta.set(x,y,z);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setDefaultRegionInter()
+    {
+    mInterRegion[0] = 50;
+    mInterRegion[1] = 50;
+    mInterRegion[2] = 50;
+    mInterRegion[3] = 50;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setRegionText()
+    {
+    if( mRegionDimension==4 )
+      {
+      float f0 = ((int)(mRegion4Sta.get1()*100))/100.0f;
+      float f1 = ((int)(mRegion4Sta.get2()*100))/100.0f;
+      float f2 = ((int)(mRegion4Sta.get3()*100))/100.0f;
+      float f3 = ((int)(mRegion4Sta.get4()*100))/100.0f;
+
+      mTextRegion.setText("region (" + f0 + "," + f1 + "," + f2 + "," + f3 + ")");
+      }
+
+    if( mRegionDimension==3 )
+      {
+      float f0 = ((int)(mRegion4Sta.get1()*100))/100.0f;
+      float f1 = ((int)(mRegion4Sta.get2()*100))/100.0f;
+      float f2 = ((int)(mRegion4Sta.get3()*100))/100.0f;
+
+      mTextRegion.setText("region (" + f0 + "," + f1 + "," + f2 + ")");
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void setBackground(int pos)
+    {
+    int color = (pos%2==1 ? BACKGROUND_ODD:BACKGROUND_EVEN);
+
+    if( mEffect!=null ) mEffect.setBackgroundColor(color);
+    if( mCenter!=null ) mCenter.setBackgroundColor(color);
+    if( mRegion!=null ) mRegion.setBackgroundColor(color);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  GenericEffect(EffectName name, GenericActivity2 act)
+    {
+    mAct = new WeakReference<>(act);
+    mName = name;
+
+    mDyn1 = null;
+    mDyn2 = null;
+    mDyn3 = null;
+    mDyn4 = null;
+    mDyn5 = null;
+    mSta1 = null;
+    mSta2 = null;
+    mSta3 = null;
+    mSta4 = null;
+    mSta5 = null;
+
+    mDimension = mName.getEffectDimension();
+    mRegionDimension = mName.getRegionDimension();
+
+    switch(mDimension)
+      {
+      case 1 : mDyn1 = new Dynamic1D();
+               mSta1 = new Static1D(0);
+               mDyn1.add(mSta1);
+               break;
+      case 2 : mDyn2 = new Dynamic2D();
+               mSta2 = new Static2D(0,0);
+               mDyn2.add(mSta2);
+               break;
+      case 3 : mDyn3 = new Dynamic3D();
+               mSta3 = new Static3D(0,0,0);
+               mDyn3.add(mSta3);
+               break;
+      case 4 : if( mName == EffectName.QUATERNION )
+                 {
+                 mDyn4 = new Dynamic4D();
+                 mSta4 = new Static4D(0,0,0,0);
+                 mDyn4.add(mSta4);
+                 }
+               else
+                 {
+                 mDyn3 = new Dynamic3D();
+                 mSta3 = new Static3D(0,0,0);
+                 mDyn3.add(mSta3);
+                 mDyn1 = new Dynamic1D();
+                 mSta1 = new Static1D(0);
+                 mDyn1.add(mSta1);
+                 }
+               break;
+      case 5 : if( mName == EffectName.WAVE )
+                 {
+                 mDyn5 = new Dynamic5D();
+                 mSta5 = new Static5D(0, 0, 0, 0, 0);
+                 mDyn5.add(mSta5);
+                 }
+               else
+                 {
+                 mDyn4 = new Dynamic4D();
+                 mSta4 = new Static4D(0,0,0,0);
+                 mDyn4.add(mSta4);
+                 mDyn1 = new Dynamic1D();
+                 mSta1 = new Static1D(0);
+                 mDyn1.add(mSta1);
+                 }
+               break;
+      default: throw new RuntimeException("unsupported effect");
+      }
+
+    mInter = new int[mDimension];
+    mSeekID= new int[mDimension];
+
+    mInterRegion = new int[4];
+    mSeekRegionID= new int[4];
+    mRegion4Dyn  = new Dynamic4D();
+    mRegion4Sta  = new Static4D(0,0,0,0);
+    mRegion4Dyn.add(mRegion4Sta);
+    mRegion3Dyn  = new Dynamic3D();
+    mRegion3Sta  = new Static3D(0,0,0);
+    mRegion3Dyn.add(mRegion3Sta);
+
+    mInterCenter = new int[3];
+    mSeekCenterID= new int[3];
+    mCenterDyn   = new Dynamic3D();
+    mCenterSta   = new Static3D(0,0,0);
+    mCenterDyn.add(mCenterSta);
+
+    mButton = null;
+    mEffect = null;
+    mCenter = null;
+    mRegion = null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createView(int num)
+    {
+    SeekBar[] seek = new SeekBar[mDimension];
+
+    GenericActivity2 act = mAct.get();
+
+    switch(mDimension)
+      {
+      case 1 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect1d, null);
+               mText      = mEffect.findViewById(R.id.effect1dText);
+               seek[0]    = mEffect.findViewById(R.id.effect1dbar1);
+               mSeekID[0] = seek[0].getId();
+               mButton    = mEffect.findViewById(R.id.button1dRemove);
+               break;
+      case 2 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect2d, null);
+               mText      = mEffect.findViewById(R.id.effect2dText);
+               seek[0]    = mEffect.findViewById(R.id.effect2dbar1);
+               seek[1]    = mEffect.findViewById(R.id.effect2dbar2);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mButton    = mEffect.findViewById(R.id.button2dRemove);
+               break;
+      case 3 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect3d, null);
+               mText      = mEffect.findViewById(R.id.effect3dText);
+               seek[0]    = mEffect.findViewById(R.id.effect3dbar1);
+               seek[1]    = mEffect.findViewById(R.id.effect3dbar2);
+               seek[2]    = mEffect.findViewById(R.id.effect3dbar3);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mSeekID[2] = seek[2].getId();
+               mButton    = mEffect.findViewById(R.id.button3dRemove);
+               break;
+      case 4 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect4d, null);
+               mText      = mEffect.findViewById(R.id.effect4dText);
+               seek[0]    = mEffect.findViewById(R.id.effect4dbar1);
+               seek[1]    = mEffect.findViewById(R.id.effect4dbar2);
+               seek[2]    = mEffect.findViewById(R.id.effect4dbar3);
+               seek[3]    = mEffect.findViewById(R.id.effect4dbar4);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mSeekID[2] = seek[2].getId();
+               mSeekID[3] = seek[3].getId();
+               mButton    = mEffect.findViewById(R.id.button4dRemove);
+               break;
+      case 5 : mEffect    = act.getLayoutInflater().inflate(R.layout.effect5d, null);
+               mText      = mEffect.findViewById(R.id.effect5dText);
+               seek[0]    = mEffect.findViewById(R.id.effect5dbar1);
+               seek[1]    = mEffect.findViewById(R.id.effect5dbar2);
+               seek[2]    = mEffect.findViewById(R.id.effect5dbar3);
+               seek[3]    = mEffect.findViewById(R.id.effect5dbar4);
+               seek[4]    = mEffect.findViewById(R.id.effect5dbar5);
+               mSeekID[0] = seek[0].getId();
+               mSeekID[1] = seek[1].getId();
+               mSeekID[2] = seek[2].getId();
+               mSeekID[3] = seek[3].getId();
+               mSeekID[4] = seek[4].getId();
+               mButton    = mEffect.findViewById(R.id.button5dRemove);
+               break;
+      default: android.util.Log.e("GenericEffect", "dimension "+mDimension+" not supported!");
+               return null;
+      }
+
+    mEffect.setBackgroundColor( num%2==1 ? BACKGROUND_EVEN: BACKGROUND_ODD );
+
+    setDefaultInter();
+
+    for(int i=0; i<mDimension; i++)
+      {
+      seek[i].setOnSeekBarChangeListener(this);
+      seek[i].setProgress( mInter[i] );
+      }
+
+    return mEffect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createRegion(int num)
+    {
+    GenericActivity2 act = mAct.get();
+
+    if( mRegionDimension== 4 )
+      {
+      mRegion = act.getLayoutInflater().inflate(R.layout.effectregion4, null);
+      mRegion.setBackgroundColor(num % 2 == 1 ? BACKGROUND_EVEN : BACKGROUND_ODD);
+
+      SeekBar[] seek = new SeekBar[4];
+
+      seek[0] = mRegion.findViewById(R.id.effectRegion4BarX);
+      seek[1] = mRegion.findViewById(R.id.effectRegion4BarY);
+      seek[2] = mRegion.findViewById(R.id.effectRegion4BarZ);
+      seek[3] = mRegion.findViewById(R.id.effectRegion4BarR);
+
+      mSeekRegionID[0] = seek[0].getId();
+      mSeekRegionID[1] = seek[1].getId();
+      mSeekRegionID[2] = seek[2].getId();
+      mSeekRegionID[3] = seek[3].getId();
+
+      mTextRegion = mRegion.findViewById(R.id.effectRegion4Text);
+
+      setDefaultRegionInter();
+
+      for (int i = 0; i < 4; i++)
+        {
+        seek[i].setOnSeekBarChangeListener(this);
+        seek[i].setProgress(mInterRegion[i]);
+        }
+
+      act.setRegion(mRegion4Sta.get1(), mRegion4Sta.get2(), mRegion4Sta.get3(), mRegion4Sta.get4());
+      }
+    else
+      {
+      mRegion = act.getLayoutInflater().inflate(R.layout.effectregion3, null);
+      mRegion.setBackgroundColor(num % 2 == 1 ? BACKGROUND_EVEN : BACKGROUND_ODD);
+
+      SeekBar[] seek = new SeekBar[3];
+
+      seek[0] = mRegion.findViewById(R.id.effectRegion3BarRX);
+      seek[1] = mRegion.findViewById(R.id.effectRegion3BarRY);
+      seek[2] = mRegion.findViewById(R.id.effectRegion3BarRZ);
+
+      mSeekRegionID[0] = seek[0].getId();
+      mSeekRegionID[1] = seek[1].getId();
+      mSeekRegionID[2] = seek[2].getId();
+
+      mTextRegion = mRegion.findViewById(R.id.effectRegion3Text);
+
+      setDefaultRegionInter();
+
+      for (int i = 0; i < 3; i++)
+        {
+        seek[i].setOnSeekBarChangeListener(this);
+        seek[i].setProgress(mInterRegion[i]);
+        }
+
+      act.setRegion(mRegion3Sta.get1(), mRegion3Sta.get2(), mRegion3Sta.get3(), 1.0f );
+      }
+
+    return mRegion;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  View createCenter(int num)
+    {
+    GenericActivity2 act = mAct.get();
+
+    mCenter = act.getLayoutInflater().inflate(R.layout.effectcenter, null);
+    mCenter.setBackgroundColor( num%2==1 ? BACKGROUND_EVEN: BACKGROUND_ODD );
+
+    SeekBar[] seek = new SeekBar[3];
+
+    seek[0] = mCenter.findViewById(R.id.effectCenterBarX );
+    seek[1] = mCenter.findViewById(R.id.effectCenterBarY );
+    seek[2] = mCenter.findViewById(R.id.effectCenterBarZ );
+
+    mSeekCenterID[0] = seek[0].getId();
+    mSeekCenterID[1] = seek[1].getId();
+    mSeekCenterID[2] = seek[2].getId();
+
+    mTextCenter = mCenter.findViewById(R.id.effectCenterText);
+
+    setDefaultCenterInter();
+
+    for(int i=0; i<3; i++)
+      {
+      seek[i].setOnSeekBarChangeListener(this);
+      seek[i].setProgress( mInterCenter[i] );
+      }
+
+    act.setCenter(mCenterSta.get1(),mCenterSta.get2(),mCenterSta.get3());
+
+    return mCenter;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
+    {
+    if ( mDimension>=1 && bar.getId()==mSeekID[0] )
+      {
+      mInter[0] = progress;
+      fillStatics();
+      setText();
+      }
+    if ( mDimension>=2 && bar.getId()==mSeekID[1] )
+      {
+      mInter[1] = progress;
+      fillStatics();
+      setText();
+      }
+    if ( mDimension>=3 && bar.getId()==mSeekID[2] )
+      {
+      mInter[2] = progress;
+      fillStatics();
+      setText();
+      }
+    if ( mDimension>=4 && bar.getId()==mSeekID[3] )
+      {
+      mInter[3] = progress;
+      fillStatics();
+      setText();
+      }
+    if ( mDimension>=5 && bar.getId()==mSeekID[4] )
+      {
+      mInter[4] = progress;
+      fillStatics();
+      setText();
+      }
+
+    if( mRegionDimension>=1 && bar.getId() == mSeekRegionID[0] )
+      {
+      mInterRegion[0] = progress;
+      fillRegionStatics();
+      setRegionText();
+      }
+    if( mRegionDimension>=2 && bar.getId() == mSeekRegionID[1] )
+      {
+      mInterRegion[1] = progress;
+      fillRegionStatics();
+      setRegionText();
+      }
+    if( mRegionDimension>=3 && bar.getId() == mSeekRegionID[2] )
+      {
+      mInterRegion[2] = progress;
+      fillRegionStatics();
+      setRegionText();
+      }
+    if( mRegionDimension>=4 && bar.getId() == mSeekRegionID[3] )
+      {
+      mInterRegion[3] = progress;
+      fillRegionStatics();
+      setRegionText();
+      }
+
+    if( bar.getId() == mSeekCenterID[0] )
+      {
+      mInterCenter[0] = progress;
+      fillCenterStatics();
+      setCenterText();
+      }
+    if( bar.getId() == mSeekCenterID[1] )
+      {
+      mInterCenter[1] = progress;
+      fillCenterStatics();
+      setCenterText();
+      }
+    if( bar.getId() == mSeekCenterID[2] )
+      {
+      mInterCenter[2] = progress;
+      fillCenterStatics();
+      setCenterText();
+      }
+
+    if( fromUser )
+      {
+      GenericActivity2 act = mAct.get();
+
+      boolean show = (mName.getType()==EffectType.VERTEX);
+      boolean showR= (show && act.getShowRegion());
+      boolean showC= (show && act.getShowCenter());
+
+      GenericSurfaceView view = act.findViewById(R.id.genericSurfaceView);
+      view.getRenderer().showRegionAndCenter( showR,showC );
+
+      act.setCenter(mCenterSta.get1(),mCenterSta.get2(),mCenterSta.get3());
+      act.setRegion(mRegion4Sta.get1(),mRegion4Sta.get2(),mRegion4Sta.get3(), mRegion4Sta.get4());
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  boolean thisView(View v)
+    {
+    return v==mButton;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public long getId()
+    {
+    return mId;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getEffect()
+    {
+    return mEffect;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getRegion()
+    {
+    return mRegion;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public View getCenter()
+    {
+    return mCenter;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onStartTrackingTouch(SeekBar bar) { }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onStopTrackingTouch(SeekBar bar)  { }
+  }
diff --git a/src/main/java/org/distorted/examples/generic/GenericRenderer.java b/src/main/java/org/distorted/examples/generic/GenericRenderer.java
new file mode 100644
index 0000000..865438f
--- /dev/null
+++ b/src/main/java/org/distorted/examples/generic/GenericRenderer.java
@@ -0,0 +1,325 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.generic;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLSurfaceView;
+
+import org.distorted.examples.R;
+import org.distorted.library.effect.Effect;
+import org.distorted.library.effect.EffectType;
+import org.distorted.library.effect.MatrixEffectMove;
+import org.distorted.library.effect.MatrixEffectQuaternion;
+import org.distorted.library.effect.MatrixEffectScale;
+import org.distorted.library.main.Distorted;
+import org.distorted.library.main.DistortedEffects;
+import org.distorted.library.main.DistortedNode;
+import org.distorted.library.main.DistortedScreen;
+import org.distorted.library.main.DistortedTexture;
+import org.distorted.library.mesh.MeshBase;
+import org.distorted.library.mesh.MeshFlat;
+import org.distorted.library.mesh.MeshSphere;
+import org.distorted.library.type.Static3D;
+import org.distorted.library.type.Static4D;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class GenericRenderer implements GLSurfaceView.Renderer
+{
+    private static final float FOV = 30.0f;
+    private static final float NEAR = 0.1f;
+
+    private GLSurfaceView mView;
+    private DistortedTexture mObjectTexture, mBackgroundTexture, mCenterTexture, mRegionTexture;
+    private DistortedScreen mScreen;
+    private DistortedNode mCenterNode, mRegionNode;
+    private int mObjWidth, mObjHeight, mObjDepth;
+    private Static3D mCenterPoint, mRegionPoint, mRegionScalePoint;
+    private Static3D mRotateCen, mMoveObject, mScaleObject, mMoveCenter, mScaleCenter, mMoveRegion, mMoveBackground, mScaleBackground;
+    private boolean mShowingCenter=false;
+    private boolean mShowingRegion=false;
+    private float mFactorObj, mFactorReg;
+    private int mWidth;
+    private float mFactor;
+
+    Static4D mQuat1, mQuat2;
+    int mScreenMin;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    GenericRenderer(GLSurfaceView v)
+      {
+      mView = v;
+
+      GenericActivity2 act = (GenericActivity2)v.getContext();
+
+      mRotateCen      = new Static3D(0,0,0);
+      mMoveObject     = new Static3D(0,0,0);
+      mScaleObject    = new Static3D(1,1,1);
+      mMoveCenter     = new Static3D(0,0,0);
+      mScaleCenter    = new Static3D(1,1,1);
+      mMoveRegion     = new Static3D(0,0,0);
+      mMoveBackground = new Static3D(0,0,0);
+      mScaleBackground= new Static3D(1,1,1);
+
+      mObjectTexture     = act.getTexture();
+      mBackgroundTexture = new DistortedTexture(100,100);
+      mCenterTexture     = new DistortedTexture(100,100);
+      mRegionTexture     = new DistortedTexture(100,100);
+
+      DistortedEffects objectEffects     = act.getEffects();
+      DistortedEffects backgroundEffects = new DistortedEffects();
+      DistortedEffects centerEffects     = new DistortedEffects();
+      DistortedEffects regionEffects     = new DistortedEffects();
+
+      MeshBase mesh    = act.getMesh();
+      MeshFlat quad    = new MeshFlat(1,1);
+
+      mFactor = mesh instanceof MeshSphere ? 1.0f : 0.7f;
+
+      mObjWidth = mObjectTexture.getWidth();
+      mObjHeight= mObjectTexture.getHeight();
+      mObjDepth = mObjectTexture.getDepth(mesh);
+
+      mQuat1 = new Static4D(0,0,0,1);  // unity
+      mQuat2 = new Static4D(0,0,0,1);  // quaternions
+
+      mCenterPoint= new Static3D(0,0,0);
+      mRegionPoint= new Static3D(0,0,0);
+      mRegionScalePoint = new Static3D(0,0,0);
+
+      mCenterNode = new DistortedNode(mCenterTexture, centerEffects, quad);
+      mRegionNode = new DistortedNode(mRegionTexture, regionEffects, quad);
+
+      mScreen = new DistortedScreen();
+      mScreen.setProjection(FOV, NEAR);
+      mScreen.attach(mBackgroundTexture, backgroundEffects, quad );
+      mScreen.attach(mObjectTexture    , objectEffects    , mesh );
+
+      int regionSize = mRegionTexture.getWidth();
+      mRotateCen = new Static3D(0 ,0, 0);
+
+      MatrixEffectQuaternion quat1cen = new MatrixEffectQuaternion(mQuat1, mRotateCen);
+      MatrixEffectQuaternion quat2cen = new MatrixEffectQuaternion(mQuat2, mRotateCen);
+      MatrixEffectMove centerMove = new MatrixEffectMove(mCenterPoint);
+
+      centerEffects.apply(quat1cen);
+      centerEffects.apply(quat2cen);
+      centerEffects.apply( new MatrixEffectMove(mMoveCenter) );
+      centerEffects.apply( centerMove );
+      centerEffects.apply( new MatrixEffectScale(mScaleCenter) );
+
+      regionEffects.apply(quat1cen);
+      regionEffects.apply(quat2cen);
+      regionEffects.apply( new MatrixEffectMove(mMoveRegion) );
+      regionEffects.apply( centerMove );
+      regionEffects.apply( new MatrixEffectMove(mRegionPoint) );
+      regionEffects.apply( new MatrixEffectScale(mRegionScalePoint) );
+      regionEffects.apply( new MatrixEffectMove(new Static3D( -regionSize/2 , -regionSize/2 , 0)) );
+
+      resetMatrixEffects();
+
+      // quite tricky: move the background exactly to the FAR plane! (see DistortedOutputSurface.setProjection() )
+      backgroundEffects.apply(new MatrixEffectMove(mMoveBackground) );
+      backgroundEffects.apply(new MatrixEffectScale(mScaleBackground) );
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void resetMatrixEffects()
+      {
+      GenericActivity2 act = (GenericActivity2)mView.getContext();
+      DistortedEffects objectEffects= act.getEffects();
+      Static3D rotateObj = new Static3D( (float)mObjWidth/2, (float)mObjHeight/2, (float)mObjDepth/2 );
+
+      MatrixEffectQuaternion quat1obj = new MatrixEffectQuaternion(mQuat1,  rotateObj);
+      MatrixEffectQuaternion quat2obj = new MatrixEffectQuaternion(mQuat2,  rotateObj);
+
+      objectEffects.apply( new MatrixEffectMove(mMoveObject));
+      objectEffects.apply( new MatrixEffectScale(mScaleObject) );
+      objectEffects.apply(quat1obj);
+      objectEffects.apply(quat2obj);
+
+      mQuat1.set(0,0,0,1);
+      mQuat2.set(0,0,0,1);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void showRegionAndCenter(boolean showRegion, boolean showCenter)
+      {
+      if( mShowingCenter!=showCenter  )
+        {
+        if( showCenter ) mScreen.attach(mCenterNode);
+        else             mScreen.detach(mCenterNode);
+
+        mShowingCenter = showCenter;
+        }
+
+      if( mShowingRegion!=showRegion  )
+        {
+        if( showRegion ) mScreen.attach(mRegionNode);
+        else             mScreen.detach(mRegionNode);
+
+        mShowingRegion = showRegion;
+        }
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public int getWidth()
+      {
+      return mWidth;
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void setCenter(float x, float y, float z)
+      {
+      mCenterPoint.set(mFactorObj*x,mFactorObj*y,mFactorObj*z);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void setRegion(float x, float y, float z, float r)
+      {
+      mFactorReg = 2*mFactorObj*r/mRegionTexture.getWidth();
+      mRegionPoint.set(mFactorObj*x,-mFactorObj*y, mFactorObj*z);
+      mRegionScalePoint.set(mFactorReg,mFactorReg,mFactorReg);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    void useOIT(boolean use)
+      {
+      mScreen.setOrderIndependentTransparency(use);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void onDrawFrame(GL10 glUnused)
+      {
+      mScreen.render(System.currentTimeMillis());
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void onSurfaceChanged(GL10 glUnused, int width, int height)
+      {
+      mWidth = width;
+      mScreenMin = width<height ? width:height;
+
+      float factorCen;
+      int centerSize = mCenterTexture.getWidth();
+      int regionSize = mRegionTexture.getWidth();
+
+      if( width*mObjHeight > height*mObjWidth ) // screen is more 'horizontal' than the Object
+        {
+        mFactorObj = (mFactor*height)/mObjHeight;
+        factorCen  = (0.08f  *height)/centerSize;
+        }
+      else
+        {
+        mFactorObj = (mFactor*width)/mObjWidth;
+        factorCen  = (0.08f  *width)/centerSize;
+        }
+
+      GenericActivity2 act = (GenericActivity2)mView.getContext();
+      mCenterPoint.set(mFactorObj*act.getCenterX(),+mFactorObj*act.getCenterY(),0);
+      mRegionPoint.set(mFactorObj*act.getRegionX(),-mFactorObj*act.getRegionY(),0);
+      mFactorReg = 2*mFactorObj*act.getRegionR()/regionSize;
+      mRegionScalePoint.set(mFactorReg,mFactorReg,mFactorReg);
+      mMoveObject.set( (width-mFactorObj*mObjWidth)/2 , (height-mFactorObj*mObjHeight)/2 , -mFactorObj*mObjDepth );
+      mRotateCen.set(width/2,height/2, 0);
+      mScaleObject.set(mFactorObj,mFactorObj,mFactorObj);
+      mMoveCenter.set( (width -factorCen*centerSize-mFactorObj*mObjWidth )/2 ,
+                       (height-factorCen*centerSize-mFactorObj*mObjHeight)/2 , 15 );
+      mScaleCenter.set(factorCen,factorCen,factorCen);
+      mMoveRegion.set( (width -mFactorObj*mObjWidth )/2 ,(height-mFactorObj*mObjHeight)/2 , 12 );
+
+      int backgroundSize = mBackgroundTexture.getWidth();
+      float factorBackX = ((float)width)/backgroundSize;
+      float factorBackY = ((float)height)/backgroundSize;
+
+      // quite tricky: move the background exactly to the FAR plane! (see DistortedOutputSurface.setProjection() )
+      mMoveBackground.set( -width/2, -height/2, -0.9f*height*(1.0f-NEAR)/(2.0f*(float)Math.tan(FOV*Math.PI/360)) );
+      mScaleBackground.set( 2*factorBackX, 2*factorBackY, 1.0f );
+
+      mScreen.resize(width, height);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
+      {
+      GenericActivity2 act = (GenericActivity2)mView.getContext();
+
+      InputStream isB = act.getResources().openRawResource(R.raw.water);
+      InputStream isC = act.getResources().openRawResource(R.raw.center);
+      InputStream isR = act.getResources().openRawResource(R.raw.region);
+
+      Bitmap bitmapB,bitmapC,bitmapR;
+        
+      try 
+        {
+        bitmapB = BitmapFactory.decodeStream(isB);
+        bitmapC = BitmapFactory.decodeStream(isC);
+        bitmapR = BitmapFactory.decodeStream(isR);
+        }
+      finally 
+        {
+        try 
+          {
+          isB.close();
+          isC.close();
+          isR.close();
+          }
+        catch(IOException e) { }
+        }  
+      
+      mObjectTexture.setTexture( act.getBitmap() );
+      mBackgroundTexture.setTexture(bitmapB);
+      mCenterTexture.setTexture(bitmapC);
+      mRegionTexture.setTexture(bitmapR);
+
+      DistortedEffects.setMax(EffectType.VERTEX  ,10);
+      DistortedEffects.setMax(EffectType.FRAGMENT,10);
+
+      Effect.enableEffects(EffectType.VERTEX);
+      Effect.enableEffects(EffectType.FRAGMENT);
+      Effect.enableEffects(EffectType.POSTPROCESS);
+
+      try
+        {
+        Distorted.onCreate(mView.getContext());
+        }
+      catch(Exception ex)
+        {
+        android.util.Log.e("Effects3D", ex.getMessage() );
+        }
+      }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/examples/generic/GenericSurfaceView.java b/src/main/java/org/distorted/examples/generic/GenericSurfaceView.java
new file mode 100644
index 0000000..2d753ec
--- /dev/null
+++ b/src/main/java/org/distorted/examples/generic/GenericSurfaceView.java
@@ -0,0 +1,122 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.generic;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class GenericSurfaceView extends GLSurfaceView
+  {
+  private int mX, mY;
+  private GenericRenderer mRenderer;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+   
+  public GenericSurfaceView(Context context, AttributeSet attrs)
+    {
+    super(context, attrs);
+      
+    if(!isInEditMode())
+      {
+      mRenderer = new GenericRenderer(this);
+      final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+      final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
+      setEGLContextClientVersion( (configurationInfo.reqGlEsVersion>>16) >= 3 ? 3:2 );
+      setRenderer(mRenderer);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public GenericRenderer getRenderer()
+    {
+    return mRenderer;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public boolean onTouchEvent(MotionEvent event)
+    {
+    int action = event.getAction();
+    int x = (int)event.getX();
+    int y = (int)event.getY();
+
+    switch(action)
+      {
+      case MotionEvent.ACTION_DOWN: mX = x;
+                                    mY = y;
+                                    break;
+
+      case MotionEvent.ACTION_MOVE: if( mX>=0 && mY>= 0 )
+                                      {
+                                      float px = mY-y;
+                                      float py = mX-x;
+                                      float pz = 0;
+                                      float plen = (float)Math.sqrt(px*px + py*py + pz*pz);
+
+                                      if( plen>0 )
+                                        {
+                                        px /= plen;
+                                        py /= plen;
+                                        pz /= plen;
+
+                                        float cosA = (float)Math.cos(plen*3.14f/mRenderer.mScreenMin);
+                                        float sinA = (float)Math.sqrt(1-cosA*cosA);
+
+                                        mRenderer.mQuat1.set(px*sinA, py*sinA, pz*sinA, cosA);
+                                        }
+                                      }
+                                    break;
+
+      case MotionEvent.ACTION_UP  : mX = -1;
+                                    mY = -1;
+
+                                    float qx = mRenderer.mQuat1.get1();
+                                    float qy = mRenderer.mQuat1.get2();
+                                    float qz = mRenderer.mQuat1.get3();
+                                    float qw = mRenderer.mQuat1.get4();
+
+                                    float rx = mRenderer.mQuat2.get1();
+                                    float ry = mRenderer.mQuat2.get2();
+                                    float rz = mRenderer.mQuat2.get3();
+                                    float rw = mRenderer.mQuat2.get4();
+
+                                    float tx = rw*qx - rz*qy + ry*qz + rx*qw;
+                                    float ty = rw*qy + rz*qx + ry*qw - rx*qz;
+                                    float tz = rw*qz + rz*qw - ry*qx + rx*qy;
+                                    float tw = rw*qw - rz*qz - ry*qy - rx*qx;
+
+                                    mRenderer.mQuat1.set(0f, 0f, 0f, 1f);
+                                    mRenderer.mQuat2.set(tx, ty, tz, tw);
+
+                                    break;
+      }
+
+    return true;
+    }
+}
+
diff --git a/src/main/java/org/distorted/examples/generic/GenericTab.java b/src/main/java/org/distorted/examples/generic/GenericTab.java
new file mode 100644
index 0000000..9adb392
--- /dev/null
+++ b/src/main/java/org/distorted/examples/generic/GenericTab.java
@@ -0,0 +1,277 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2018 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.generic;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+
+import org.distorted.examples.R;
+import org.distorted.library.effect.EffectName;
+import org.distorted.library.effect.EffectType;
+import org.distorted.library.main.DistortedEffects;
+
+import java.util.ArrayList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class GenericTab extends Fragment implements AdapterView.OnItemSelectedListener
+  {
+  private EffectType mType;
+  private ArrayList<GenericEffect> mList;
+  private int mEffectAdd;
+  private EffectName[] mEffectNames;
+  private DistortedEffects mEffects;
+  private String[] mEffectStrings;
+  private int mTab, mLayout, mSpinner;
+  private int mChildren;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public GenericTab()
+    {
+    mList = new ArrayList<>();
+    mEffectAdd = 0;
+    mChildren  = 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
+    {
+    Bundle args = getArguments();
+
+    int position = args.getInt("position");
+
+    switch(position)
+      {
+      case 0: mTab     = R.layout.effects3dtab0;
+              mLayout  = R.id.effects3dlayout0;
+              mSpinner = R.id.effects3dspinner0;
+              mType    = EffectType.MATRIX;
+              break;
+      case 1: mTab     = R.layout.effects3dtab1;
+              mLayout  = R.id.effects3dlayout1;
+              mSpinner = R.id.effects3dspinner1;
+              mType    = EffectType.VERTEX;
+              break;
+      case 2: mTab     = R.layout.effects3dtab2;
+              mLayout  = R.id.effects3dlayout2;
+              mSpinner = R.id.effects3dspinner2;
+              mType    = EffectType.FRAGMENT;
+              break;
+      case 3: mTab     = R.layout.effects3dtab3;
+              mLayout  = R.id.effects3dlayout3;
+              mSpinner = R.id.effects3dspinner3;
+              mType    = EffectType.POSTPROCESS;
+              break;
+      }
+
+    GenericActivity2 act = (GenericActivity2)getActivity();
+
+    if( act!=null )
+      {
+      act.setTab(position, this);
+      mEffects = act.getEffects();
+      }
+
+    createEffectNames(mType);
+    mEffectStrings = new String[mEffectNames.length];
+
+    for (int i = 0; i < mEffectNames.length; i++)
+      {
+      mEffectStrings[i] = mEffectNames[i].name();
+      }
+
+    return inflater.inflate( mTab, container, false);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public void onStart()
+    {
+    super.onStart();
+
+    GenericActivity2 act = (GenericActivity2)getActivity();
+
+    ArrayAdapter<String> adapterEffect = new ArrayAdapter<>( act, android.R.layout.simple_spinner_item, mEffectStrings);
+    adapterEffect.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+    Spinner effectSpinner = act.findViewById(mSpinner);
+
+    effectSpinner.setOnItemSelectedListener(this);
+    effectSpinner.setAdapter(adapterEffect);
+
+    resetData();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    if( parent.getId() == mSpinner )
+      {
+      mEffectAdd = pos;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onNothingSelected(AdapterView<?> parent)
+    {
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void resetData()
+    {
+    GenericActivity2 act = (GenericActivity2)getActivity();
+    GenericSurfaceView view = act.findViewById(R.id.genericSurfaceView);
+    view.getRenderer().showRegionAndCenter(false,false);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void newEffect()
+    {
+    GenericActivity2 act = (GenericActivity2)getActivity();
+    GenericEffect eff = new GenericEffect(mEffectNames[mEffectAdd], act );
+    mList.add(eff);
+
+    mChildren++;
+
+    LinearLayout layout = act.findViewById(mLayout);
+    View view = eff.createView(mChildren);
+    layout.addView(view);
+
+    if( mEffectNames[mEffectAdd].getCenterDimension() > 0 )
+      {
+      View center = eff.createCenter(mChildren);
+      layout.addView(center);
+      }
+
+    if( mEffectNames[mEffectAdd].getRegionDimension() > 0 )
+      {
+      View region = eff.createRegion(mChildren);
+      layout.addView(region);
+      }
+
+    eff.apply(mEffects);
+
+    boolean region = act.getShowRegion();
+    boolean center = act.getShowCenter();
+
+    boolean show = (mEffectNames[mEffectAdd].getType()==EffectType.VERTEX);
+    GenericSurfaceView sv = act.findViewById(R.id.genericSurfaceView);
+    sv.getRenderer().showRegionAndCenter( (show && region) , (show && center) );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void createEffectNames(EffectType type)
+    {
+    EffectName[] names = EffectName.values();
+
+    int numEffects=0;
+
+    for(int i=0; i<names.length; i++)
+      if( names[i].getType() == type ) numEffects++;
+
+    mEffectNames = new EffectName[numEffects];
+
+    numEffects=0;
+
+    for(int i=0; i<names.length; i++)
+      if( names[i].getType() == type )
+        {
+        mEffectNames[numEffects++] = names[i];
+        }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void removeAll()
+    {
+    GenericActivity2 act = (GenericActivity2)getActivity();
+
+    mChildren = 0;
+
+    mList.clear();
+    LinearLayout layout = act.findViewById(mLayout);
+    layout.removeAllViews();
+    mEffects.abortByType(mType);
+
+    resetData();
+    act.resetData();
+
+    if( mType==EffectType.MATRIX )
+      {
+      act.resetMatrixEffects();
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void remove(View v)
+    {
+    for(GenericEffect effect: mList)
+      {
+      if( effect.thisView(v) )
+        {
+        GenericActivity2 act = (GenericActivity2)getActivity();
+        LinearLayout layout = act.findViewById(mLayout);
+        View view;
+
+        view = effect.getEffect();
+        if( view!=null ) layout.removeView(view);
+        view = effect.getCenter();
+        if( view!=null ) layout.removeView(view);
+        view = effect.getRegion();
+        if( view!=null ) layout.removeView(view);
+
+        mEffects.abortById(effect.getId());
+
+        int index = mList.indexOf(effect);
+        int capac = mList.size();
+
+        for(int i=index+1; i<capac; i++)
+          {
+          mList.get(i).setBackground(i-1);
+          }
+
+        mList.remove(effect);
+        mChildren--;
+
+        resetData();
+
+        break;
+        }
+      }
+    }
+
+  }
diff --git a/src/main/java/org/distorted/examples/generic/GenericTabViewPager.java b/src/main/java/org/distorted/examples/generic/GenericTabViewPager.java
new file mode 100644
index 0000000..69fb0d1
--- /dev/null
+++ b/src/main/java/org/distorted/examples/generic/GenericTabViewPager.java
@@ -0,0 +1,86 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2018 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.examples.generic;
+
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.os.Bundle;
+
+import org.distorted.examples.R;
+
+import java.lang.ref.WeakReference;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class GenericTabViewPager extends FragmentPagerAdapter
+  {
+  private WeakReference<GenericActivity2> mAct;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  GenericTabViewPager(GenericActivity2 act, FragmentManager fm)
+    {
+    super(fm);
+    mAct = new WeakReference<>(act);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public Fragment getItem(int position)
+    {
+    if( position>=0 && position< GenericActivity2.NUM_TABS )
+      {
+      GenericTab tab = new GenericTab();
+
+      Bundle bundle = new Bundle();
+      bundle.putInt("position", position);
+      tab.setArguments(bundle);
+
+      return tab;
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public int getCount()
+    {
+    return GenericActivity2.NUM_TABS;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public CharSequence getPageTitle(int position)
+    {
+    switch (position)
+      {
+      case 0 : return mAct.get().getString(R.string.type_matrix);
+      case 1 : return mAct.get().getString(R.string.type_vertex);
+      case 2 : return mAct.get().getString(R.string.type_fragment);
+      case 3 : return mAct.get().getString(R.string.type_postprocess);
+      default: return null;
+      }
+    }
+  }
diff --git a/src/main/res/layout/effects3dlayout.xml b/src/main/res/layout/effects3dlayout.xml
deleted file mode 100644
index 4a6fbd1..0000000
--- a/src/main/res/layout/effects3dlayout.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:layout_weight="1"
-    android:orientation="vertical" >
-
-    <org.distorted.examples.generic.Effects3DSurfaceView
-        android:id="@+id/effects3dSurfaceView"
-        android:layout_width="fill_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="32dp"
-        android:orientation="horizontal">
-
-        <CheckBox
-            android:id="@+id/effects3dCheckBoxCenter"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:checked="false"
-            android:onClick="showCenter"
-            android:text="@string/show_center"
-            android:textSize="12sp"/>
-
-        <CheckBox
-            android:id="@+id/effects3dCheckBoxRegion"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:checked="false"
-            android:onClick="showRegion"
-            android:text="@string/show_region"
-            android:textSize="12sp"/>
-
-        <CheckBox
-            android:id="@+id/effects3dCheckBoxNormal"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:checked="false"
-            android:onClick="showNormal"
-            android:text="@string/show_normal"
-            android:textSize="12sp"/>
-
-        <CheckBox
-            android:id="@+id/effects3dCheckBoxOIT"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:checked="false"
-            android:onClick="triggerOIT"
-            android:text="@string/oit"
-            android:textSize="12sp"/>
-
-    </LinearLayout>
-
-    <android.support.design.widget.TabLayout
-        android:id="@+id/effects3d_sliding_tabs"
-        android:layout_width="match_parent"
-        android:layout_height="32dp"
-        android:theme="@style/Theme.AppCompat.NoActionBar">
-    </android.support.design.widget.TabLayout>
-
-    <android.support.v4.view.ViewPager
-        android:id="@+id/effects3d_viewpager"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:background="@android:color/black" />
-
-</LinearLayout>
diff --git a/src/main/res/layout/genericlayout.xml b/src/main/res/layout/genericlayout.xml
new file mode 100644
index 0000000..75ade70
--- /dev/null
+++ b/src/main/res/layout/genericlayout.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:layout_weight="1"
+    android:orientation="vertical" >
+
+    <org.distorted.examples.generic.GenericSurfaceView
+        android:id="@+id/genericSurfaceView"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="32dp"
+        android:orientation="horizontal">
+
+        <CheckBox
+            android:id="@+id/genericCheckBoxCenter"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:checked="false"
+            android:onClick="showCenter"
+            android:text="@string/show_center"
+            android:textSize="12sp"/>
+
+        <CheckBox
+            android:id="@+id/genericCheckBoxRegion"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:checked="false"
+            android:onClick="showRegion"
+            android:text="@string/show_region"
+            android:textSize="12sp"/>
+
+        <CheckBox
+            android:id="@+id/genericCheckBoxNormal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:checked="false"
+            android:onClick="showNormal"
+            android:text="@string/show_normal"
+            android:textSize="12sp"/>
+
+        <CheckBox
+            android:id="@+id/genericCheckBoxOIT"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:checked="false"
+            android:onClick="triggerOIT"
+            android:text="@string/oit"
+            android:textSize="12sp"/>
+
+    </LinearLayout>
+
+    <android.support.design.widget.TabLayout
+        android:id="@+id/generic_sliding_tabs"
+        android:layout_width="match_parent"
+        android:layout_height="32dp"
+        android:theme="@style/Theme.AppCompat.NoActionBar">
+    </android.support.design.widget.TabLayout>
+
+    <android.support.v4.view.ViewPager
+        android:id="@+id/generic_viewpager"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:background="@android:color/black" />
+
+</LinearLayout>
