commit df77c72cc0edc557002ea66b1317d474639f4ceb
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Mon Jul 4 01:23:03 2016 +0100

    New Fragment3D app.

diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index c5f8723..094b05d 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -36,6 +36,7 @@
         <activity android:name=".quaternion.QuaternionActivity" />          
         <activity android:name=".matrix3d.Matrix3DActivity" />
         <activity android:name=".vertex3d.Vertex3DActivity" />
+        <activity android:name=".fragment3d.Fragment3DActivity" />
         <activity android:name=".plainmonalisa.PlainMonaLisaActivity" />
         <activity android:name=".save.SaveActivity"/>
     </application>
diff --git a/src/main/java/org/distorted/examples/TableOfContents.java b/src/main/java/org/distorted/examples/TableOfContents.java
index 5405ce5..61a8283 100644
--- a/src/main/java/org/distorted/examples/TableOfContents.java
+++ b/src/main/java/org/distorted/examples/TableOfContents.java
@@ -55,6 +55,7 @@ import org.distorted.examples.cubes.CubesActivity;
 import org.distorted.examples.quaternion.QuaternionActivity;
 import org.distorted.examples.matrix3d.Matrix3DActivity;
 import org.distorted.examples.vertex3d.Vertex3DActivity;
+import org.distorted.examples.fragment3d.Fragment3DActivity;
 import org.distorted.examples.plainmonalisa.PlainMonaLisaActivity;
 import org.distorted.examples.save.SaveActivity;
 
@@ -268,6 +269,15 @@ public class TableOfContents extends ListActivity
       activityMapping.put(i++, Vertex3DActivity.class);
    }
 
+   {
+      final Map<String, Object> item = new HashMap<>();
+      item.put(ITEM_IMAGE, R.drawable.icon_example_fragment3d);
+      item.put(ITEM_TITLE, (i+1)+". "+getText(R.string.example_fragment3d));
+      item.put(ITEM_SUBTITLE, getText(R.string.example_fragment3d_subtitle));
+      data.add(item);
+      activityMapping.put(i++, Fragment3DActivity.class);
+   }
+
    {
       final Map<String, Object> item = new HashMap<>();
       item.put(ITEM_IMAGE, R.drawable.icon_example_monalisa);
diff --git a/src/main/java/org/distorted/examples/fragment3d/Fragment3DActivity.java b/src/main/java/org/distorted/examples/fragment3d/Fragment3DActivity.java
new file mode 100644
index 0000000..9338c06
--- /dev/null
+++ b/src/main/java/org/distorted/examples/fragment3d/Fragment3DActivity.java
@@ -0,0 +1,621 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.fragment3d;
+
+import android.app.Activity;
+import android.opengl.GLSurfaceView;
+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.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.Spinner;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import org.distorted.examples.R;
+import org.distorted.library.Distorted;
+import org.distorted.library.DistortedBitmap;
+import org.distorted.library.DistortedCubes;
+import org.distorted.library.DistortedObject;
+import org.distorted.library.EffectNames;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class Fragment3DActivity extends Activity
+                                implements OnSeekBarChangeListener,
+                                           View.OnClickListener,
+                                           AdapterView.OnItemSelectedListener
+  {
+  private static final int COLOR_OFF = 0xffffe81f;
+  private static final int COLOR_ON  = 0xff0000ff;
+
+  private boolean firstScreen;
+
+  // fields needed for the first 'pick-a-shape' screen
+  //
+  private int mNumCols = 3;
+  private int mNumRows = 3;
+  private NumberPicker mColsPicker, mRowsPicker;
+  private LinearLayout mLay;
+  private boolean[] mShape;
+  private DistortedObject mObject;
+  private int mObjectType;
+  private int mBitmap;
+
+  // fields needed for the second 'apply fragment effects' screen
+  //
+  private SeekBar bar;
+  private TextView textChroma, textAlpha, textBrightness, textSaturation, textCenter;
+  private int chromaL, chromaR, chromaG, chromaB;
+  private int alphaL;
+  private int brightnessL;
+  private int saturationL;
+  private int centerX, centerY;
+
+  private float fchromaL, fchromaR, fchromaG, fchromaB;
+  private float falphaL;
+  private float fbrightnessL;
+  private float fsaturationL;
+  private float fcenterX, fcenterY;
+  private EffectNames[] effects = new EffectNames[4];
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  protected void onCreate(Bundle savedState)
+    {
+    super.onCreate(savedState);
+
+    setContentView(R.layout.objectpickerlayout);
+
+    mColsPicker = (NumberPicker)findViewById(R.id.objectpicker_cols);
+    mRowsPicker = (NumberPicker)findViewById(R.id.objectpicker_rows);
+
+    mColsPicker.setMaxValue(10);
+    mColsPicker.setMinValue( 0);
+    mRowsPicker.setMaxValue(10);
+    mRowsPicker.setMinValue( 0);
+
+    mColsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
+      {
+      @Override
+      public void onValueChange(NumberPicker picker, int oldVal, int newVal)
+        {
+        mNumCols = mColsPicker.getValue();
+        }
+      });
+
+    mRowsPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener()
+      {
+      @Override
+      public void onValueChange(NumberPicker picker, int oldVal, int newVal)
+        {
+        mNumRows = mRowsPicker.getValue();
+        }
+      });
+
+    firstScreen = true;
+
+    mObjectType = 0;
+
+    Spinner typeSpinner  = (Spinner)findViewById(R.id.objectpicker_spinnerType);
+    typeSpinner.setOnItemSelectedListener(this);
+
+    String[] objectType = new String[] {"DistortedCubes", "DistortedBitmap"};
+
+    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  = (Spinner)findViewById(R.id.objectpicker_spinnerBitmap);
+    bitmapSpinner.setOnItemSelectedListener(this);
+
+    String[] objectBitmap = new String[] {"Girl", "Dog", "Cat", "Grid"};
+
+    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()
+    {
+    mLay = (LinearLayout)findViewById(R.id.objectpicker_buttongrid);
+
+    int width = mLay.getWidth();
+    int height= mLay.getHeight();
+    int w = mNumCols>0 ? (width / mNumCols) -10 : 0;
+    int h = mNumRows>0 ? (height/ mNumRows) -10 : 0;
+    int size= w<h ? w:h;
+    int pad = size/20;
+
+    mLay.removeAllViews();
+
+    mShape = new boolean[mNumRows*mNumCols];
+
+    TableRow.LayoutParams p = new android.widget.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(COLOR_ON);
+        tr.addView(b, p);
+        mShape[rows*mNumCols+cols] = true;
+        }
+
+      mLay.addView(tr);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public DistortedObject getObject()
+    {
+    return mObject;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onClick(View view)
+    {
+    Button tmp = (Button)view;
+    int id = tmp.getId();
+    mShape[id] = !mShape[id];
+    tmp.setBackgroundColor(mShape[id] ? COLOR_ON:COLOR_OFF);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getBitmap()
+    {
+    return mBitmap;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void Continue(View v)
+    {
+    if( mObjectType==1 )
+      {
+      firstScreen = false;
+      mObject = new DistortedBitmap(100,100,mNumCols);
+      setContentView(R.layout.fragment3dlayout);
+      Default(null);
+      }
+    else
+      {
+      View view = getLayoutInflater().inflate(R.layout.objectpicker2layout, null);
+
+      setContentView(view);
+
+      view.post(new Runnable() {
+            @Override
+            public void run() {
+              setGrid();
+            }
+        });
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void Create(View v)
+    {
+    firstScreen = false;
+
+    String str = "";
+
+    for(int i=0; i<mNumRows*mNumCols; i++)
+      str += mShape[i] ? "1" : "0";
+
+    mObject = new DistortedCubes(mNumCols, str, 10);
+
+    setContentView(R.layout.fragment3dlayout);
+    Default(null);
+    }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
+    {
+    switch(parent.getId())
+      {
+      case R.id.objectpicker_spinnerType  : mObjectType = pos; break;
+      case R.id.objectpicker_spinnerBitmap: switch(pos)
+                                              {
+                                              case 0: mBitmap = R.raw.face; break;
+                                              case 1: mBitmap = R.raw.dog;  break;
+                                              case 2: mBitmap = R.raw.cat;  break;
+                                              case 3: mBitmap = R.raw.grid; break;
+                                              }
+                                            break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onNothingSelected(AdapterView<?> parent)
+    {
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 'second screen' methods
+
+  public void Default(View view)
+    {
+    effects[0] = EffectNames.CHROMA;
+    effects[1] = EffectNames.ALPHA;
+    effects[2] = EffectNames.BRIGHTNESS;
+    effects[3] = EffectNames.SATURATION;
+    
+    chromaL = 0;
+    chromaR = 0;
+    chromaG = 0;
+    chromaB = 0;
+
+    alphaL     = 0;
+    brightnessL= 0;
+    saturationL= 0;
+
+    centerX = 50;
+    centerY = 50;
+
+    textCenter = (TextView)findViewById(R.id.fragment3dcenterText);
+    computeCenter();
+    setCenterText();
+
+    setBar(R.id.fragment3dcenterX, centerX);
+    setBar(R.id.fragment3dcenterY, centerY);
+
+    addViews();
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  private void addViews()
+    {
+    LinearLayout layout = (LinearLayout)findViewById(R.id.fragment3dlayout);
+    
+    layout.removeAllViews();
+      
+    View chroma    = getLayoutInflater().inflate(R.layout.fragment3dchroma    , null);
+    View alpha     = getLayoutInflater().inflate(R.layout.fragment3dalpha     , null);
+    View brightness= getLayoutInflater().inflate(R.layout.fragment3dbrightness, null);
+    View saturation= getLayoutInflater().inflate(R.layout.fragment3dsaturation, null);
+     
+    for( int i=effects.length-1 ; i>=0 ; i-- )
+      {
+      switch(effects[i])
+        {
+        case CHROMA     : layout.addView(chroma    , 0); break;
+        case ALPHA      : layout.addView(alpha     , 0); break;
+        case BRIGHTNESS : layout.addView(brightness, 0); break;
+        case SATURATION : layout.addView(saturation, 0); break;
+        }
+      }
+      
+    textChroma    = (TextView)findViewById(R.id.fragment3dchromaText);
+    textAlpha     = (TextView)findViewById(R.id.fragment3dalphaText);
+    textBrightness= (TextView)findViewById(R.id.fragment3dbrightnessText);
+    textSaturation= (TextView)findViewById(R.id.fragment3dsaturationText);
+     
+    setChromaText();
+    setAlphaText();
+    setBrightnessText();
+    setSaturationText();
+      
+    setBar(R.id.fragment3dchromaBar1, chromaL);
+    setBar(R.id.fragment3dchromaBar2, chromaR);
+    setBar(R.id.fragment3dchromaBar3, chromaG);
+    setBar(R.id.fragment3dchromaBar4, chromaB);
+
+    setBar(R.id.fragment3dalphaBar1     , alphaL);
+    setBar(R.id.fragment3dbrightnessBar1, brightnessL);
+    setBar(R.id.fragment3dsaturationBar1, saturationL);
+
+    Fragment3DSurfaceView view = (Fragment3DSurfaceView)findViewById(R.id.fragment3dSurfaceView);
+    view.getRenderer().setOrder(effects);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void moveUp(EffectNames name)
+    {
+    int len = effects.length-1;
+    int index = -1;
+
+    for(int i=0; i<=len; i++)
+      {
+      if( effects[i]==name )
+        {
+        index=i;
+        break;
+        }
+      }
+
+    if( index==0 )
+      {
+      for(int i=0; i<len; i++)
+        effects[i] = effects[i+1];
+
+      effects[len] = name;
+      }
+    else if( index>0 )
+      {
+      effects[index]   = effects[index-1];
+      effects[index-1] = name;
+      }
+
+    addViews();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void ButtonChroma(View v)
+    {
+    moveUp(EffectNames.CHROMA);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void ButtonAlpha(View v)
+    {
+    moveUp(EffectNames.ALPHA);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void ButtonBrightness(View v)
+    {
+    moveUp(EffectNames.BRIGHTNESS);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void ButtonSaturation(View v)
+    {
+    moveUp(EffectNames.SATURATION);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setBar(int id, int value)
+    {
+    bar = (SeekBar)findViewById(id);
+    bar.setOnSeekBarChangeListener(this);
+    bar.setProgress(value);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computeChroma()
+    {
+    Fragment3DSurfaceView view = (Fragment3DSurfaceView)findViewById(R.id.fragment3dSurfaceView);
+    Fragment3DRenderer renderer = view.getRenderer();
+
+    fchromaL = chromaL/100.0f;
+    fchromaR = chromaR*255/100.0f;
+    fchromaG = chromaG*255/100.0f;
+    fchromaB = chromaB*255/100.0f;
+
+    renderer.setChroma( fchromaL, fchromaR, fchromaG, fchromaB );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setChromaText()
+    {
+    fchromaL = ((int)(100*fchromaL))/100.0f;
+    fchromaR = (float)((int)fchromaR);
+    fchromaG = (float)((int)fchromaG);
+    fchromaB = (float)((int)fchromaB);
+
+    textChroma.setText("chroma("+fchromaL+"( "+fchromaR+" , "+fchromaG+" , "+fchromaB+" )");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computeAlpha()
+    {
+    Fragment3DSurfaceView view = (Fragment3DSurfaceView)findViewById(R.id.fragment3dSurfaceView);
+
+    falphaL = alphaL/100.0f;
+
+    view.getRenderer().setAlpha(falphaL);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setAlphaText()
+    {
+    falphaL = ((int)(100*falphaL))/100.0f;
+
+    textAlpha.setText("alpha("+falphaL+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computeBrightness()
+    {
+    Fragment3DSurfaceView view = (Fragment3DSurfaceView)findViewById(R.id.fragment3dSurfaceView);
+
+    fbrightnessL = brightnessL/100.0f;
+
+    view.getRenderer().setBrightness( fbrightnessL );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setBrightnessText()
+    {
+    fbrightnessL = ((int)(100*fbrightnessL))/100.0f;
+
+    textBrightness.setText("brightness("+fbrightnessL+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computeSaturation()
+    {
+    Fragment3DSurfaceView view = (Fragment3DSurfaceView)findViewById(R.id.fragment3dSurfaceView);
+
+    fsaturationL = saturationL/100.0f;
+
+    view.getRenderer().setSaturation( fsaturationL );
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setSaturationText()
+    {
+    fsaturationL = ((int)(100*fsaturationL))/100.0f;
+
+    textSaturation.setText("saturation("+fsaturationL+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void computeCenter()
+    {
+    Fragment3DSurfaceView view = (Fragment3DSurfaceView)findViewById(R.id.fragment3dSurfaceView);
+
+    fcenterX = centerX;
+    fcenterY = centerY;
+
+    view.getRenderer().setCenter( fcenterX, fcenterY );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void setCenterText()
+    {
+    fcenterX = ((int)(100*fcenterX))/100.0f;
+    fcenterY = ((int)(100*fcenterY))/100.0f;
+
+    textCenter.setText("center("+fcenterX+","+fcenterY+")");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getCenterX()
+    {
+    return fcenterX;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float getCenterY()
+    {
+    return fcenterY;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Overrides
+
+  @Override
+  protected void onPause()
+    {
+    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.fragment3dSurfaceView);
+    if( mView!=null ) mView.onPause();
+    super.onPause();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  protected void onResume()
+    {
+    super.onResume();
+    GLSurfaceView mView = (GLSurfaceView)findViewById(R.id.fragment3dSurfaceView);
+    if( mView!=null ) mView.onResume();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  public void onWindowFocusChanged(boolean hasFocus)
+    {
+    super.onWindowFocusChanged(hasFocus);
+
+    if( firstScreen )
+      {
+      mColsPicker.setValue(mNumCols);
+      mRowsPicker.setValue(mNumRows);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  @Override
+  protected void onDestroy()
+    {
+    Distorted.onDestroy();
+    super.onDestroy();
+    }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+  public void onProgressChanged(SeekBar bar, int progress, boolean fromUser)
+    {
+    switch (bar.getId())
+      {
+      case R.id.fragment3dcenterX       : centerX    = progress; computeCenter()    ; setCenterText()    ; break;
+      case R.id.fragment3dcenterY       : centerY    = progress; computeCenter()    ; setCenterText()    ; break;
+      case R.id.fragment3dchromaBar1    : chromaL    = progress; computeChroma()    ; setChromaText()    ; break;
+      case R.id.fragment3dchromaBar2    : chromaR    = progress; computeChroma()    ; setChromaText()    ; break;
+      case R.id.fragment3dchromaBar3    : chromaG    = progress; computeChroma()    ; setChromaText()    ; break;
+      case R.id.fragment3dchromaBar4    : chromaB    = progress; computeChroma()    ; setChromaText()    ; break;
+      case R.id.fragment3dalphaBar1     : alphaL     = progress; computeAlpha()     ; setAlphaText()     ; break;
+      case R.id.fragment3dbrightnessBar1: brightnessL= progress; computeBrightness(); setBrightnessText(); break;
+      case R.id.fragment3dsaturationBar1: saturationL= progress; computeSaturation(); setSaturationText(); break;
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onStartTrackingTouch(SeekBar bar) { }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void onStopTrackingTouch(SeekBar bar)  { }
+  }
diff --git a/src/main/java/org/distorted/examples/fragment3d/Fragment3DRenderer.java b/src/main/java/org/distorted/examples/fragment3d/Fragment3DRenderer.java
new file mode 100644
index 0000000..b7224cb
--- /dev/null
+++ b/src/main/java/org/distorted/examples/fragment3d/Fragment3DRenderer.java
@@ -0,0 +1,292 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.fragment3d;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+
+import org.distorted.examples.R;
+import org.distorted.library.Distorted;
+import org.distorted.library.DistortedBitmap;
+import org.distorted.library.DistortedCubes;
+import org.distorted.library.DistortedObject;
+import org.distorted.library.EffectNames;
+import org.distorted.library.EffectTypes;
+import org.distorted.library.type.Dynamic1D;
+import org.distorted.library.type.Dynamic2D;
+import org.distorted.library.type.Dynamic3D;
+import org.distorted.library.type.DynamicQuat;
+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 java.io.IOException;
+import java.io.InputStream;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class Fragment3DRenderer implements GLSurfaceView.Renderer
+{
+    private static final int SIZE = 100;
+
+    private GLSurfaceView mView;
+    private DistortedObject mObject;
+    private DistortedBitmap mCenter;
+    private float mFactorCen, mFactorObj;
+
+    private int mObjWidth, mObjHeight;
+
+    private DynamicQuat mQuatInt1, mQuatInt2;
+
+    private EffectNames[] order;
+
+    private Dynamic2D mCenterInter;
+    private Dynamic3D mChromaInter, mMoveInter;
+    private Dynamic1D mChromaLevelInter, mAlphaInter, mBrightnessInter, mSaturationInter;
+
+    private Static2D mCenterPoint;
+    private Static3D mChromaPoint, mMovePoint;
+    private Static1D mChromaLevel, mAlphaPoint, mBrightnessPoint, mSaturationPoint;
+
+    Static4D mQuat1, mQuat2;
+    int mScreenMin;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setChroma(float level, float r, float g, float b)
+      {
+      mChromaLevel.set(level);
+      mChromaPoint.set(r, g, b);
+      }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setAlpha(float level)
+      {
+      mAlphaPoint.set(level);
+      }
+     
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setBrightness(float level)
+      {
+      mBrightnessPoint.set(level);
+      }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setSaturation(float level)
+      {
+      mSaturationPoint.set(level);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setCenter(float x, float y)
+      {
+      x = (0.012f*x-0.1f)*mObjWidth;
+      y = (0.012f*y-0.1f)*mObjHeight;
+
+      mCenterPoint.set(x,y);
+      mMovePoint.set(mFactorObj*x,mFactorObj*y,0);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void setOrder(EffectNames[] effects)
+      {
+      order = effects;
+      setFragmentEffects();
+      }
+      
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void setFragmentEffects()
+      {
+      mObject.abortEffects(EffectTypes.FRAGMENT);
+
+      for( int i=0; i<=order.length-1 ; i++ )
+        {
+        switch(order[i])
+          {
+          case CHROMA    : mObject.chroma    ( mChromaLevelInter , mChromaInter) ; break;
+          case ALPHA     : mObject.alpha     ( mAlphaInter                     ) ; break;
+          case BRIGHTNESS: mObject.brightness( mBrightnessInter                ) ; break;
+          case SATURATION: mObject.saturation( mSaturationInter                ) ; break;
+          }
+        }
+      }
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public Fragment3DRenderer(GLSurfaceView v)
+      {
+      mView = v;
+
+      mObject = ((Fragment3DActivity)v.getContext()).getObject();
+      mCenter = new DistortedBitmap(SIZE, SIZE, 1);
+
+      mObjWidth = mObject.getWidth();
+      mObjHeight= mObject.getHeight();
+
+      mCenterPoint    = new Static2D(0,0);
+      mMovePoint      = new Static3D(0,0,0);
+      mChromaPoint    = new Static3D(0,0,0);
+      mChromaLevel    = new Static1D(0);
+      mAlphaPoint     = new Static1D(1);
+      mBrightnessPoint= new Static1D(1);
+      mSaturationPoint= new Static1D(0.5f);
+
+      mCenterInter     = new Dynamic2D();
+      mMoveInter       = new Dynamic3D();
+      mChromaInter     = new Dynamic3D();
+      mChromaLevelInter= new Dynamic1D();
+      mAlphaInter      = new Dynamic1D();
+      mBrightnessInter = new Dynamic1D();
+      mSaturationInter = new Dynamic1D();
+
+      mCenterInter.add(mCenterPoint);
+      mMoveInter.add(mMovePoint);
+      mChromaInter.add(mChromaPoint);
+      mChromaLevelInter.add(mChromaLevel);
+      mAlphaInter.add(mAlphaPoint);
+      mBrightnessInter.add(mBrightnessPoint);
+      mSaturationInter.add(mSaturationPoint);
+
+      mQuat1 = new Static4D(0,0,0,1);  // unity
+      mQuat2 = new Static4D(0,0,0,1);  // quaternions
+
+      mQuatInt1 = new DynamicQuat(0,0.5f);
+      mQuatInt2 = new DynamicQuat(0,0.5f);
+
+      mQuatInt1.add(mQuat1);
+      mQuatInt2.add(mQuat2);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+   
+    public void onDrawFrame(GL10 glUnused) 
+      {
+      GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+      GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+
+      long time = System.currentTimeMillis();
+
+      mObject.draw(time);
+      mCenter.draw(time);
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+    public void onSurfaceChanged(GL10 glUnused, int width, int height) 
+      {
+      mScreenMin = width<height ? width:height;
+
+      mObject.abortEffects(EffectTypes.MATRIX);
+      mCenter.abortEffects(EffectTypes.MATRIX);
+
+      int centerSize = mCenter.getWidth();
+
+      if( width*mObjHeight > height*mObjWidth ) // screen is more 'horizontal' than the Object
+        {
+        mFactorObj = (0.70f*height)/mObjHeight;
+        mFactorCen = (0.15f*height)/centerSize;
+        }
+      else
+        {
+        mFactorObj = (0.70f*width)/mObjWidth;
+        mFactorCen = (0.15f*width)/centerSize;
+        }
+
+      Fragment3DActivity act = (Fragment3DActivity)mView.getContext();
+      float cX = act.getCenterX();
+      float cY = act.getCenterY();
+
+      cX = (0.012f*cX-0.1f)*mObjWidth;
+      cY = (0.012f*cY-0.1f)*mObjHeight;
+
+      mMovePoint.set(cX*mFactorObj,cY*mFactorObj,0);
+
+      Static3D rotateObj = new Static3D(mObjWidth/2,mObjHeight/2, 0);
+
+      mObject.move( new Static3D( (width-mFactorObj*mObjWidth)/2 , (height-mFactorObj*mObjHeight)/2 , 0) );
+      mObject.scale(mFactorObj);
+      mObject.quaternion(mQuatInt1, rotateObj);
+      mObject.quaternion(mQuatInt2, rotateObj);
+
+      Static3D rotateCen = new Static3D(width/2,height/2, 0);
+
+      mCenter.quaternion(mQuatInt1, rotateCen);
+      mCenter.quaternion(mQuatInt2, rotateCen);
+      mCenter.move( new Static3D( (width -mFactorCen*centerSize-mFactorObj*mObjWidth )/2 ,
+                                  (height-mFactorCen*centerSize-mFactorObj*mObjHeight)/2 , 10) );
+      mCenter.move(mMoveInter);
+      mCenter.scale(mFactorCen);
+
+      setFragmentEffects();
+
+      Distorted.onSurfaceChanged(width, height); 
+      }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+    
+    public void onSurfaceCreated(GL10 glUnused, EGLConfig config) 
+      {
+      Fragment3DActivity act = (Fragment3DActivity)mView.getContext();
+
+      InputStream is1 = act.getResources().openRawResource(act.getBitmap());
+      InputStream is2 = act.getResources().openRawResource(R.raw.center);
+
+      Bitmap bitmap1,bitmap2;
+        
+      try 
+        {
+        bitmap1 = BitmapFactory.decodeStream(is1);
+        bitmap2 = BitmapFactory.decodeStream(is2);
+        }
+      finally 
+        {
+        try 
+          {
+          is1.close();
+          is2.close();
+          } 
+        catch(IOException e) { }
+        }  
+      
+      mObject.setBitmap(bitmap1);
+      mCenter.setBitmap(bitmap2);
+
+      try
+        {
+        Distorted.onSurfaceCreated(mView.getContext());
+        }
+      catch(Exception ex)
+        {
+        android.util.Log.e("Fragment3D", ex.getMessage() );
+        }
+      }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/examples/fragment3d/Fragment3DSurfaceView.java b/src/main/java/org/distorted/examples/fragment3d/Fragment3DSurfaceView.java
new file mode 100644
index 0000000..e957a31
--- /dev/null
+++ b/src/main/java/org/distorted/examples/fragment3d/Fragment3DSurfaceView.java
@@ -0,0 +1,126 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.fragment3d;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class Fragment3DSurfaceView extends GLSurfaceView
+  {
+  private int mX, mY;
+  private Fragment3DRenderer mRenderer;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+   
+  public Fragment3DSurfaceView(Context c, AttributeSet attrs)
+    {
+    super(c, attrs);
+      
+    if(!isInEditMode())
+      {
+      setEGLContextClientVersion(2);
+        
+      if( Build.FINGERPRINT.startsWith("generic") )
+        {
+        setEGLConfigChooser(8, 8, 8, 8, 16, 0);
+        }
+
+      mRenderer = new Fragment3DRenderer(this);
+
+      setRenderer(mRenderer);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public Fragment3DRenderer 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.getX();
+                                    float qy = mRenderer.mQuat1.getY();
+                                    float qz = mRenderer.mQuat1.getZ();
+                                    float qw = mRenderer.mQuat1.getW();
+
+                                    float rx = mRenderer.mQuat2.getX();
+                                    float ry = mRenderer.mQuat2.getY();
+                                    float rz = mRenderer.mQuat2.getZ();
+                                    float rw = mRenderer.mQuat2.getW();
+
+                                    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/res/drawable-hdpi/icon_example_fragment3d.png b/src/main/res/drawable-hdpi/icon_example_fragment3d.png
new file mode 100644
index 0000000..a378eea
Binary files /dev/null and b/src/main/res/drawable-hdpi/icon_example_fragment3d.png differ
diff --git a/src/main/res/layout/fragment3dalpha.xml b/src/main/res/layout/fragment3dalpha.xml
new file mode 100644
index 0000000..e5ad659
--- /dev/null
+++ b/src/main/res/layout/fragment3dalpha.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                          android:id="@+id/fragment3dRowAlpha"
+                          android:layout_width="match_parent"
+                          android:layout_height="wrap_content"
+                          android:orientation="horizontal">
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="0.8"
+                    android:orientation="vertical" >
+
+                    <TextView
+                        android:id="@+id/fragment3dalphaText"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginEnd="5dp"
+                        android:layout_marginStart="5dp"
+                        android:layout_marginTop="3dp"
+                        />
+
+                    <SeekBar
+                        android:id="@+id/fragment3dalphaBar1"
+                        android:layout_width="fill_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_marginEnd="5dp"
+                        android:layout_marginLeft="5dp"
+                        android:layout_marginRight="5dp" />
+
+                </LinearLayout>
+
+                <Button
+                    android:id="@+id/fragment3dUpAlpha"
+                    android:layout_width="60dp"
+                    android:layout_height="fill_parent"
+                    android:onClick="ButtonAlpha"
+                    android:text="@string/Up" />
+
+            </LinearLayout>
diff --git a/src/main/res/layout/fragment3dbrightness.xml b/src/main/res/layout/fragment3dbrightness.xml
new file mode 100644
index 0000000..825f47d
--- /dev/null
+++ b/src/main/res/layout/fragment3dbrightness.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                          android:id="@+id/fragment3dRowBrightness"
+                          android:orientation="horizontal"
+                          android:layout_width="match_parent"
+                          android:layout_height="wrap_content" >
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="0.70"
+                    android:orientation="vertical" >
+
+                    <TextView
+                        android:id="@+id/fragment3dbrightnessText"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginEnd="5dp"
+                        android:layout_marginStart="5dp"
+                        android:layout_marginTop="3dp"
+                        />
+
+                    <SeekBar
+                        android:id="@+id/fragment3dbrightnessBar1"
+                        android:layout_width="fill_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="end"
+                        android:layout_marginEnd="5dp"
+                        android:layout_marginLeft="5dp"
+                        android:layout_marginRight="5dp" />
+
+                </LinearLayout>
+
+                <Button
+                    android:id="@+id/fragment3dUpBrightness"
+                    android:layout_width="60dp"
+                    android:layout_height="fill_parent"
+                    android:onClick="ButtonBrightness"
+                    android:text="@string/Up" />
+
+            </LinearLayout>
+           
diff --git a/src/main/res/layout/fragment3dchroma.xml b/src/main/res/layout/fragment3dchroma.xml
new file mode 100644
index 0000000..9efccbf
--- /dev/null
+++ b/src/main/res/layout/fragment3dchroma.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                          android:id="@+id/fragment3dRowChroma"
+                          android:orientation="horizontal"
+                          android:layout_width="match_parent"
+                          android:layout_height="wrap_content" >
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="0.70"
+                    android:orientation="vertical" >
+
+                    <TextView
+                        android:id="@+id/fragment3dchromaText"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginEnd="5dp"
+                        android:layout_marginStart="5dp"
+                        android:layout_marginTop="3dp"
+                        />
+
+                    <SeekBar
+                        android:id="@+id/fragment3dchromaBar1"
+                        android:layout_width="fill_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="end"
+                        android:layout_marginEnd="5dp"
+                        android:layout_marginLeft="5dp"
+                        android:layout_marginRight="5dp" />
+
+                    <LinearLayout
+                        android:orientation="horizontal"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent">
+
+                        <SeekBar
+                            android:id="@+id/fragment3dchromaBar2"
+                            android:layout_width="fill_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_marginLeft="5dp"
+                            android:layout_marginRight="5dp"
+                            android:layout_weight="0.33"/>
+
+                        <SeekBar
+                            android:id="@+id/fragment3dchromaBar3"
+                            android:layout_width="fill_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_marginEnd="5dp"
+                            android:layout_marginLeft="5dp"
+                            android:layout_marginRight="5dp"
+                            android:layout_weight="0.33"/>
+
+                        <SeekBar
+                            android:id="@+id/fragment3dchromaBar4"
+                            android:layout_width="fill_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_marginRight="5dp"
+                            android:layout_marginLeft="5dp"
+                            android:layout_weight="0.33"/>
+                    </LinearLayout>
+
+                </LinearLayout>
+
+                <Button
+                    android:id="@+id/fragment3dUpChroma"
+                    android:layout_width="60dp"
+                    android:layout_height="fill_parent"
+                    android:onClick="ButtonChroma"
+                    android:text="@string/Up" />
+
+            </LinearLayout>
diff --git a/src/main/res/layout/fragment3dlayout.xml b/src/main/res/layout/fragment3dlayout.xml
new file mode 100644
index 0000000..844ac60
--- /dev/null
+++ b/src/main/res/layout/fragment3dlayout.xml
@@ -0,0 +1,85 @@
+<?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:orientation="vertical" >
+
+    <org.distorted.examples.fragment3d.Fragment3DSurfaceView
+        android:id="@+id/fragment3dSurfaceView"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="5dp"
+        android:background="@android:color/holo_green_dark">
+
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="0.8">
+
+            <TextView
+                android:id="@+id/fragment3dcenterText"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="5dp"
+                android:layout_marginStart="5dp"
+                android:layout_marginTop="3dp"
+                />
+
+            <LinearLayout
+                android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                >
+
+                <SeekBar
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/fragment3dcenterX"
+                    android:layout_weight="0.5"
+                    android:paddingLeft="5dp"
+                    android:paddingRight="3dp"/>
+
+                <SeekBar
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/fragment3dcenterY"
+                    android:layout_weight="0.5"
+                    android:paddingLeft="3dp"
+                    android:paddingRight="5dp"/>
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/buttonDefault"
+            android:layout_width="60dp"
+            android:layout_height="wrap_content"
+            android:onClick="Default"
+            android:text="@string/reset"
+            android:layout_gravity="right"
+            android:layout_marginTop="3dp"/>
+    </LinearLayout>
+
+    <ScrollView
+        android:id="@+id/fragment3dscrollView"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="0.82" >
+
+        <LinearLayout
+            android:id="@+id/fragment3dlayout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical" >
+        </LinearLayout>
+
+    </ScrollView>
+
+</LinearLayout>
diff --git a/src/main/res/layout/fragment3dsaturation.xml b/src/main/res/layout/fragment3dsaturation.xml
new file mode 100644
index 0000000..48020b1
--- /dev/null
+++ b/src/main/res/layout/fragment3dsaturation.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                          android:id="@+id/fragment3dRowSaturation"
+                          android:orientation="horizontal"
+                          android:layout_width="match_parent"
+                          android:layout_height="wrap_content" >
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="0.70"
+                    android:orientation="vertical" >
+
+                    <TextView
+                        android:id="@+id/fragment3dsaturationText"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginEnd="5dp"
+                        android:layout_marginStart="5dp"
+                        android:layout_marginTop="3dp"
+                        />
+
+                    <SeekBar
+                        android:id="@+id/fragment3dsaturationBar1"
+                        android:layout_width="fill_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="end"
+                        android:layout_marginEnd="5dp"
+                        android:layout_marginLeft="5dp"
+                        android:layout_marginRight="5dp" />
+
+                </LinearLayout>
+
+                <Button
+                    android:id="@+id/fragment3dUpSaturation"
+                    android:layout_width="60dp"
+                    android:layout_height="fill_parent"
+                    android:onClick="ButtonSaturation"
+                    android:text="@string/Up" />
+
+            </LinearLayout>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 77d3a50..61bcd94 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -90,6 +90,8 @@
     <string name="example_matrix3d_subtitle">Test results of Matrix effects on a 3D object.</string>
     <string name="example_vertex3d">3D Vertex Effects</string>
     <string name="example_vertex3d_subtitle">Test results of Vertex effects on a 3D object.</string>
+    <string name="example_fragment3d">3D Fragment Effects</string>
+    <string name="example_fragment3d_subtitle">Test results of Fragment effects on a 3D object.</string>
     <string name="example_plainmonalisa">PlainMonaLisa</string>
     <string name="example_plainmonalisa_subtitle">MonaLisa rendered on a plain SurfaceView</string>
     <string name="example_save">Save to PNG</string>
