Add refraction and bettr normals computation to FallRS
diff --git a/java/Fall/AndroidManifest.xml b/java/Fall/AndroidManifest.xml
index f4f96a1..f646d0d 100644
--- a/java/Fall/AndroidManifest.xml
+++ b/java/Fall/AndroidManifest.xml
@@ -5,6 +5,7 @@
     <application android:label="FallRS">
 
         <activity
+            android:screenOrientation="portrait"
             android:name="Fall"
             android:theme="@android:style/Theme.NoTitleBar">
 
diff --git a/java/Fall/res/drawable-hdpi/riverbed.jpg b/java/Fall/res/drawable-hdpi/riverbed.jpg
new file mode 100644
index 0000000..1698f28
--- /dev/null
+++ b/java/Fall/res/drawable-hdpi/riverbed.jpg
Binary files differ
diff --git a/java/Fall/res/raw/fall.c b/java/Fall/res/raw/fall.c
index 267b3ea..ba9dd25 100644
--- a/java/Fall/res/raw/fall.c
+++ b/java/Fall/res/raw/fall.c
@@ -27,6 +27,7 @@
 #define RSID_RIPPLE_INDEX 6
 #define RSID_DROP_X 7
 #define RSID_DROP_Y 8
+#define RSID_RUNNING 9
     
 #define RSID_TEXTURES 1
 
@@ -35,7 +36,7 @@
 #define RSID_REFRACTION_MAP 3
 
 #define REFRACTION 1.333f
-#define DAMP 4
+#define DAMP 3
 
 #define DROP_RADIUS 2
 // The higher, the smaller the ripple
@@ -113,6 +114,20 @@
     }
 }
 
+int refraction(int d, int wave) {
+    int* map = loadArrayI32(RSID_REFRACTION_MAP, 0);
+    int i = d;
+    if (i < 0) i = -i;
+    if (i > 512) i = 512;
+    int w = (wave + 0x10000) >> 8;
+    w &= ~(w >> 31);
+    int r = (map[i] * w) >> 3;
+    if (d < 0) {
+        return -r;
+    }
+    return r;
+}
+
 void generateRipples() {
     int rippleMapSize = loadI32(RSID_STATE, RSID_RIPPLE_MAP_SIZE);
     int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
@@ -135,6 +150,19 @@
             int dx = nextWave - wave;
             int dy = current[b] - wave;
 
+            int offsetx = refraction(dx, wave) >> 16;
+            int u = (width - w) + offsetx;
+            u &= ~(u >> 31);
+            if (u >= width) u = width - 1;
+
+            int offsety = refraction(dy, wave) >> 16;
+            int v = (height - h) + offsety;
+            v &= ~(v >> 31);
+            if (v >= height) v = height - 1;
+
+            vertices[(offset + w) * 8 + 3] = u / (float) width;
+            vertices[(offset + w) * 8 + 4] = v / (float) height;
+
             // Update Z coordinate of the vertex
             vertices[(offset + w) * 8 + 7] = (dy / 512.0f) / RIPPLE_HEIGHT;
             
@@ -187,13 +215,67 @@
             n3x /= len;
             n3y /= len;
             n3z /= len;
+            
+            // V2
+            v2x = vertices[(yOffset + width + x + 1) * 8 + 5];
+            v2y = vertices[(yOffset + width + x + 1) * 8 + 6];
+            v2z = vertices[(yOffset + width + x + 1) * 8 + 7];
 
-            vertices[(yOffset + x) * 8 + 0] = -n3x;
-            vertices[(yOffset + x) * 8 + 1] = -n3y;
+            // N1
+            n1x = v2x - v1x;
+            n1y = v2y - v1y;
+            n1z = v2z - v1z;
+
+            // N2
+            n2x = v3x - v1x;
+            n2y = v3y - v1y;
+            n2z = v3z - v1z;
+
+            // Avegare of previous normal and N1 x N2
+            n3x = n3x / 2.0f + (n1y * n2z - n1z * n2y) / 2.0f;
+            n3y = n3y / 2.0f + (n1z * n2x - n1x * n2z) / 2.0f;
+            n3z = n3z / 2.0f + (n1x * n2y - n1y * n2x) / 2.0f;
+
+            // Normalize
+            len = magf3(n3x, n3y, n3z);
+            n3x /= len;
+            n3y /= len;
+            n3z /= len;
+
+            vertices[(yOffset + x) * 8 + 0] = n3x;
+            vertices[(yOffset + x) * 8 + 1] = n3y;
             vertices[(yOffset + x) * 8 + 2] = -n3z;
+            
+            // reset Z
+            vertices[(yOffset + x) * 8 + 7] = 0.0f;
         }
     }
-}   
+}
+
+void drawNormals() {
+    int width = loadI32(RSID_STATE, RSID_MESH_WIDTH);
+    int height = loadI32(RSID_STATE, RSID_MESH_HEIGHT);
+
+    float *vertices = loadTriangleMeshVerticesF(NAMED_mesh);
+    
+    bindProgramVertex(NAMED_PVLines);
+    color(1.0f, 0.0f, 0.0f, 1.0f);
+
+    int y = 0;
+    for ( ; y < height; y++) {
+        int yOffset = y * width;
+        int x = 0;
+        for ( ; x < width; x++) {
+            float vx = vertices[(yOffset + x) * 8 + 5];
+            float vy = vertices[(yOffset + x) * 8 + 6];
+            float vz = vertices[(yOffset + x) * 8 + 7];
+            float nx = vertices[(yOffset + x) * 8 + 0];
+            float ny = vertices[(yOffset + x) * 8 + 1];
+            float nz = vertices[(yOffset + x) * 8 + 2];
+            drawLine(vx, vy, vz, vx + nx / 10.0f, vy + ny / 10.0f, vz + nz / 10.0f);
+        }
+    }
+}
 
 int main(int index) {
     int dropX = loadI32(RSID_STATE, RSID_DROP_X);
@@ -204,13 +286,33 @@
         storeI32(RSID_STATE, RSID_DROP_Y, -1);
     }
 
-    updateRipples();
-    generateRipples();
-    updateTriangleMesh(NAMED_mesh);
+    int isRunning = loadI32(RSID_STATE, RSID_RUNNING);
+    if (isRunning) {
+        updateRipples();
+        generateRipples();
+        updateTriangleMesh(NAMED_mesh);
+    }
 
-    ambient(0.0f, 0.1f, 0.9f, 1.0f);
-    diffuse(0.0f, 0.1f, 0.9f, 1.0f);
-    drawTriangleMesh(NAMED_mesh);    
+    bindTexture(NAMED_PFBackground, 0, NAMED_TRiverbed);
+    drawTriangleMesh(NAMED_mesh);
+    
+    ambient(0.0f, 0.0f, 0.0f, 1.0f);
+    diffuse(0.0f, 0.0f, 0.0f, 1.0f);
+    specular(0.44f, 0.44f, 0.44f, 1.0f);
+    shininess(40.0f);
+    bindProgramFragment(NAMED_PFLighting);
+    drawTriangleMesh(NAMED_mesh);
+
+//    bindProgramVertex(NAMED_PVSky);
+//    bindProgramFragment(NAMED_PFSky);
+//    bindProgramFragmentStore(NAMED_PFSSky);
+//    color(1.0f, 1.0f, 1.0f, 0.30f);
+//    bindTexture(NAMED_PFSky, 0, NAMED_TSky);
+//    drawTriangleMesh(NAMED_mesh);
+
+    if (!isRunning) {
+        drawNormals();
+    }
 
     return 1;
 }
diff --git a/java/Fall/src/com/android/fall/rs/FallRS.java b/java/Fall/src/com/android/fall/rs/FallRS.java
index 542f67c..1685a62 100644
--- a/java/Fall/src/com/android/fall/rs/FallRS.java
+++ b/java/Fall/src/com/android/fall/rs/FallRS.java
@@ -33,8 +33,6 @@
 import static android.renderscript.ProgramStore.BlendSrcFunc;
 import static android.renderscript.ProgramFragment.EnvMode.*;
 import static android.renderscript.Element.*;
-import android.graphics.BitmapFactory;
-import android.graphics.Bitmap;
 
 import java.util.TimeZone;
 
@@ -51,17 +49,20 @@
     private static final int RSID_STATE_RIPPLE_INDEX = 6;
     private static final int RSID_STATE_DROP_X = 7;
     private static final int RSID_STATE_DROP_Y = 8;
+    private static final int RSID_STATE_RUNNING = 9;
     
     private static final int RSID_TEXTURES = 1;
-    private static final int TEXTURES_COUNT = 0;
+    private static final int TEXTURES_COUNT = 1;
+    private static final int RSID_TEXTURE_RIVERBED = 0;
 
     private static final int RSID_RIPPLE_MAP = 2;
 
     private static final int RSID_REFRACTION_MAP = 3;
 
+    private boolean mIsRunning = true;    
+    
     private Resources mResources;
     private RenderScript mRS;
-    private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
 
     private final int mWidth;
     private final int mHeight;
@@ -69,8 +70,10 @@
     private ScriptC mScript;
     private Sampler mSampler;
     private ProgramFragment mPfBackground;
+    private ProgramFragment mPfLighting;
     private ProgramStore mPfsBackground;
     private ProgramVertex mPvBackground;
+    private ProgramVertex mPvLines;
     private ProgramVertex.MatrixAllocation mPvOrthoAlloc;
     private Light mLight;
 
@@ -89,8 +92,6 @@
     public FallRS(int width, int height) {
         mWidth = width;
         mHeight = height;
-        mBitmapOptions.inScaled = false;
-        mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
     }
 
     public void init(RenderScript rs, Resources res) {
@@ -116,6 +117,8 @@
         mLight.destroy();
         mRippleMap.destroy();
         mRefractionMap.destroy();
+        mPvLines.destroy();
+        mPfLighting.destroy();
     }
 
     @Override
@@ -177,10 +180,11 @@
         
         for (int y = 0; y <= hResolution; y++) {
             final float yOffset = y * quadHeight - glHeight / 2.0f - quadHeight;
+            final float t = 1.0f - y / (float) hResolution;
             for (int x = 0; x <= wResolution; x++) {
                 rs.triangleMeshAddVertex_XYZ_ST_NORM(
                         -1.0f + x * quadWidth - quadWidth, yOffset, 0.0f,
-                        x / (float) wResolution, y / (float) wResolution,
+                        x / (float) wResolution, t,
                         0.0f, 0.0f, -1.0f);
             }
         }
@@ -204,7 +208,7 @@
     private void createScriptStructures() {
         final int rippleMapSize = (mMeshWidth + 2) * (mMeshHeight + 2);
 
-        final int[] data = new int[9];
+        final int[] data = new int[10];
         mState = Allocation.createSized(mRS, USER_I32, data.length);
         data[RSID_STATE_FRAMECOUNT] = 0;
         data[RSID_STATE_WIDTH] = mWidth;
@@ -215,6 +219,7 @@
         data[RSID_STATE_RIPPLE_INDEX] = 0;
         data[RSID_STATE_DROP_X] = mMeshWidth / 2;
         data[RSID_STATE_DROP_Y] = mMeshHeight / 2;
+        data[RSID_STATE_RUNNING] = 1;
         mState.data(data);
 
         final int[] rippleMap = new int[rippleMapSize * 2];
@@ -236,7 +241,7 @@
         mTexturesIDs = Allocation.createSized(mRS, USER_FLOAT, TEXTURES_COUNT);
 
         final Allocation[] textures = mTextures;
-        // TOOD: Load textures
+        textures[RSID_TEXTURE_RIVERBED] = loadTexture(R.drawable.riverbed, "TRiverbed");
 
         final int[] bufferIds = mTextureBufferIDs;
         final int count = textures.length;
@@ -257,15 +262,6 @@
         return allocation;
     }
 
-    private Allocation loadTextureARGB(int id, String name) {
-        // Forces ARGB 32 bits, because pngcrush sometimes optimize our PNGs to
-        // indexed pictures, which are not well supported
-        final Bitmap b = BitmapFactory.decodeResource(mResources, id, mBitmapOptions);
-        final Allocation allocation = Allocation.createFromBitmap(mRS, b, RGBA_8888, false);
-        allocation.setName(name);
-        return allocation;
-    }
-
     private void createProgramFragment() {
         Sampler.Builder sampleBuilder = new Sampler.Builder(mRS);
         sampleBuilder.setMin(LINEAR);
@@ -280,12 +276,18 @@
         mPfBackground = builder.create();
         mPfBackground.setName("PFBackground");
         mPfBackground.bindSampler(mSampler, 0);
+        
+        builder = new ProgramFragment.Builder(mRS, null, null);
+        builder.setTexEnable(false, 0);
+        mPfLighting = builder.create();
+        mPfLighting.setName("PFLighting");
+        mPfLighting.bindSampler(mSampler, 0);
     }
 
     private void createProgramFragmentStore() {
         ProgramStore.Builder builder = new ProgramStore.Builder(mRS, null, null);
-        builder.setDepthFunc(LESS);
-        builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+        builder.setDepthFunc(ALWAYS);
+        builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
         builder.setDitherEnable(true);
         builder.setDepthMask(true);
         mPfsBackground = builder.create();
@@ -297,7 +299,7 @@
         mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight);
 
         mLight = new Light.Builder(mRS).create();
-        mLight.setPosition(0.0f, 0.0f, -1.0f);
+        mLight.setPosition(0.0f, 2.0f, -8.0f);
 
         ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS, null, null);
         builder.setTextureMatrixEnable(true);
@@ -305,11 +307,21 @@
         mPvBackground = builder.create();
         mPvBackground.bindAllocation(mPvOrthoAlloc);
         mPvBackground.setName("PVBackground");
+        
+        builder = new ProgramVertex.Builder(mRS, null, null);
+        mPvLines = builder.create();
+        mPvLines.bindAllocation(mPvOrthoAlloc);
+        mPvLines.setName("PVLines");
     }
 
-    public void addDrop(float x, float y) {
+    void addDrop(float x, float y) {
         mState.subData1D(RSID_STATE_DROP_X, 2, new int[] {
                 (int) ((x / mWidth) * mMeshWidth), (int) ((y / mHeight) * mMeshHeight)
         });
     }
+    
+    void togglePause() {
+        mIsRunning = !mIsRunning;
+        mState.subData1D(RSID_STATE_RUNNING, 1, new int[] { mIsRunning ? 1 : 0 });
+    }
 }
diff --git a/java/Fall/src/com/android/fall/rs/FallView.java b/java/Fall/src/com/android/fall/rs/FallView.java
index bb793c8..d7573be 100644
--- a/java/Fall/src/com/android/fall/rs/FallView.java
+++ b/java/Fall/src/com/android/fall/rs/FallView.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.view.SurfaceHolder;
 import android.view.MotionEvent;
+import android.view.KeyEvent;
 import android.renderscript.RenderScript;
 import android.renderscript.RSSurfaceView;
 
@@ -28,6 +29,8 @@
 
     public FallView(Context context) {
         super(context);
+        setFocusable(true);
+        setFocusableInTouchMode(true);
     }
 
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
@@ -44,6 +47,15 @@
     }
 
     @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER ||
+                keyCode == KeyEvent.KEYCODE_MENU) {
+            mRender.togglePause();
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent event) {
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
diff --git a/rsScriptC_Lib.cpp b/rsScriptC_Lib.cpp
index b17121f..813af5d 100644
--- a/rsScriptC_Lib.cpp
+++ b/rsScriptC_Lib.cpp
@@ -544,6 +544,24 @@
     glDrawArrays(GL_TRIANGLES, 0, count * 3);
 }
 
+static void SC_drawLine(float x1, float y1, float z1,
+                        float x2, float y2, float z2)
+{
+    GET_TLS();
+    rsc->setupCheck();
+
+    float vtx[] = { x1, y1, z1, x2, y2, z2 };
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(3, GL_FLOAT, 0, vtx);
+
+    glDisableClientState(GL_NORMAL_ARRAY);
+    glDisableClientState(GL_COLOR_ARRAY);
+
+    glDrawArrays(GL_LINES, 0, 2);
+}
+
 static void SC_drawQuad(float x1, float y1, float z1,
                         float x2, float y2, float z2,
                         float x3, float y3, float z3,
@@ -627,10 +645,9 @@
     glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, params);
 }
 
-static void SC_shininess(float r, float g, float b, float a)
+static void SC_shininess(float s)
 {
-    GLfloat params[] = { r, g, b, a };
-    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, params);
+    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, s);
 }
 
 static void SC_hsb(float h, float s, float b, float a)
@@ -686,25 +703,6 @@
     glColor4f(red, green, blue, a);
 }
 
-/*
-extern "C" void materialDiffuse(float r, float g, float b, float a)
-{
-    float v[] = {r, g, b, a};
-    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v);
-}
-
-extern "C" void materialSpecular(float r, float g, float b, float a)
-{
-    float v[] = {r, g, b, a};
-    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v);
-}
-
-extern "C" void materialShininess(float s)
-{
-    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s);
-}
-*/
-
 static void SC_uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
 {
     GET_TLS();
@@ -903,6 +901,8 @@
         "void", "(int mesh)" },
     { "drawTriangleMeshRange", (void *)&SC_drawTriangleMeshRange,
         "void", "(int mesh, int start, int count)" },
+    { "drawLine", (void *)&SC_drawLine,
+        "void", "(float x1, float y1, float z1, float x2, float y2, float z2)" },
 
 
     // misc
@@ -921,7 +921,7 @@
     { "emission", (void *)&SC_emission,
         "void", "(float, float, float, float)" },
     { "shininess", (void *)&SC_shininess,
-        "void", "(float, float, float, float)" },
+        "void", "(float)" },
 
     { "uploadToTexture", (void *)&SC_uploadToTexture,
         "void", "(int, int)" },