Allow specifying configurations for mesh primitives

This lets you specify separate alpha, depth read, and depth
write behaviors for each primitive in your mesh. It will be
used by Books to allow drawing shadows separately from the
main model.

Change-Id: I9351f720e6e09260b5bcd335e305922ff48cb373
diff --git a/carousel/java/com/android/ex/carousel/CarouselController.java b/carousel/java/com/android/ex/carousel/CarouselController.java
index b722583..fd94a7d 100644
--- a/carousel/java/com/android/ex/carousel/CarouselController.java
+++ b/carousel/java/com/android/ex/carousel/CarouselController.java
@@ -19,9 +19,7 @@
 import com.android.ex.carousel.CarouselRS.CarouselCallback;
 import com.android.ex.carousel.CarouselView.DetailAlignment;
 
-import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.renderscript.FileA3D;
 import android.renderscript.Float4;
 import android.renderscript.Mesh;
 import android.renderscript.RenderScriptGL;
@@ -724,4 +722,39 @@
     public void onTouchStopped(float x, float y, long t) {
         mRenderScript.doStop(x, y, t);
     }
+
+    /**
+     * Whether to use alpha when drawing a primitive: on for translucent, off for opaque.
+     */
+    public static final int STORE_CONFIG_ALPHA = 1;
+
+    /**
+     * Whether to read from the depth buffer when rendering. Determines with glDepthFunc()
+     * is given GL_LESS or GL_ALWAYS. On for GL_LESS, off for GL_ALWAYS.
+     */
+    public static final int STORE_CONFIG_DEPTH_READS = 2;
+
+    /**
+     * Whether to write to the depth buffer when rendering. Passed to glDepthMask().
+     */
+    public static final int STORE_CONFIG_DEPTH_WRITES = 4;
+
+    /**
+     * Set the StoreConfig parameters that will be used for each mesh primitive.
+     *
+     * Each integer in the array is a bitfield composed of
+     * {@link CarouselController#STORE_CONFIG_ALPHA},
+     * {@link CarouselController#STORE_CONFIG_DEPTH_READS}, and
+     * {@link CarouselController#STORE_CONFIG_DEPTH_WRITES}.
+     *
+     * These parameters MUST correspond to primitives in geometry previously set in
+     * {@link CarouselController#setDefaultGeometry(int)} or
+     * {@link CarouselController#setLoadingGeometry(int)} or
+     * {@link CarouselController#setGeometryForItem(int,Mesh)}.
+     *
+     * @param configs An array, each element of which corresponds to an ordered mesh primitive
+     */
+    public void setStoreConfigs(int configs[]) {
+        mRenderScript.setStoreConfigs(configs);
+    }
 }
diff --git a/carousel/java/com/android/ex/carousel/CarouselRS.java b/carousel/java/com/android/ex/carousel/CarouselRS.java
index ea4be35..4b41d7c 100644
--- a/carousel/java/com/android/ex/carousel/CarouselRS.java
+++ b/carousel/java/com/android/ex/carousel/CarouselRS.java
@@ -68,15 +68,13 @@
     private ScriptC_carousel mScript;
     private ScriptField_Card mCards;
     private ScriptField_FragmentShaderConstants_s mFSConst;
-    private ProgramStore mProgramStoreAlphaZ;
-    private ProgramStore mProgramStoreAlphaNoZ;
-    private ProgramStore mProgramStoreNoAlphaZ;
-    private ProgramStore mProgramStoreNoAlphaNoZ;
+    private ScriptField_ProgramStore_s mProgramStoresCard;
     private ProgramFragment mSingleTextureFragmentProgram;
     private ProgramFragment mMultiTextureFragmentProgram;
     private ProgramVertex mVertexProgram;
     private ProgramRaster mRasterProgram;
     private Allocation[] mAllocationPool;
+    private boolean mForceBlendCardsWithZ;
     private int mVisibleSlots;
     private int mRowCount;
     private int mPrefetchCardCount;
@@ -379,40 +377,37 @@
     }
 
     private void initProgramStore() {
+        resizeProgramStoresCard(1);
+
         final boolean dither = true;
-        mProgramStoreAlphaZ = new ProgramStore.Builder(mRS)
-                .setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
-                        ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA)
-                .setDitherEnable(dither)
-                .setDepthFunc(ProgramStore.DepthFunc.LESS)
-                .setDepthMask(true)
-                .create();
-        mScript.set_programStoreAlphaZ(mProgramStoreAlphaZ);
+        final ProgramStore.DepthFunc depthFunc = mForceBlendCardsWithZ ?
+                ProgramStore.DepthFunc.LESS : ProgramStore.DepthFunc.ALWAYS;
 
-        mProgramStoreAlphaNoZ = new ProgramStore.Builder(mRS)
-                .setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
-                        ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA)
-                .setDitherEnable(dither)
-                .setDepthFunc(ProgramStore.DepthFunc.ALWAYS)
-                .setDepthMask(false)
-                .create();
-        mScript.set_programStoreAlphaNoZ(mProgramStoreAlphaNoZ);
+        // Background: Alpha disabled, depth optional
+        mScript.set_programStoreBackground(new ProgramStore.Builder(mRS)
+            .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ZERO)
+            .setDitherEnable(dither)
+            .setDepthFunc(depthFunc)
+            .setDepthMask(mForceBlendCardsWithZ)
+            .create());
 
-        mProgramStoreNoAlphaZ = new ProgramStore.Builder(mRS)
-                .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ZERO)
-                .setDitherEnable(dither)
-                .setDepthFunc(ProgramStore.DepthFunc.LESS)
-                .setDepthMask(true)
-                .create();
-        mScript.set_programStoreNoAlphaZ(mProgramStoreNoAlphaZ);
+        // Card: Alpha enabled, depth optional
+        setProgramStoreCard(0, new ProgramStore.Builder(mRS)
+            .setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
+                ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA)
+            .setDitherEnable(dither)
+            .setDepthFunc(depthFunc)
+            .setDepthMask(mForceBlendCardsWithZ)
+            .create());
 
-        mProgramStoreNoAlphaNoZ = new ProgramStore.Builder(mRS)
-                .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ZERO)
-                .setDitherEnable(dither)
-                .setDepthFunc(ProgramStore.DepthFunc.ALWAYS)
-                .setDepthMask(false)
-                .create();
-        mScript.set_programStoreNoAlphaNoZ(mProgramStoreNoAlphaNoZ);
+        // Detail: Alpha enabled, depth disabled
+        mScript.set_programStoreDetail(new ProgramStore.Builder(mRS)
+            .setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
+                ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA)
+            .setDitherEnable(dither)
+            .setDepthFunc(ProgramStore.DepthFunc.ALWAYS)
+            .setDepthMask(false)
+            .create());
     }
 
     public void createCards(int count)
@@ -466,8 +461,74 @@
         mScript.set_detailTextureAlignment(alignment);
     }
 
+    private void resizeProgramStoresCard(int count) {
+        // enableResize works around a Renderscript bug that keeps resizes from being propagated.
+        // TODO(jshuma): Remove enableResize once the Renderscript bug is fixed
+        final boolean enableResize = false;
+
+        if (mProgramStoresCard != null && enableResize) {
+            int newSize = count > 0 ? count : 1;
+            mProgramStoresCard.resize(newSize);
+        } else {
+            mProgramStoresCard = new ScriptField_ProgramStore_s(mRS, count > 0 ? count : 1);
+            mScript.bind_programStoresCard(mProgramStoresCard);
+        }
+    }
+
+    private void setProgramStoreCard(int n, ProgramStore programStore) {
+        ScriptField_ProgramStore_s.Item item = mProgramStoresCard.get(n);
+        if (item == null) {
+            item = new ScriptField_ProgramStore_s.Item();
+        }
+        item.programStore = programStore;
+        mProgramStoresCard.set(item, n, false);
+        mScript.invoke_setProgramStoresCard(n, programStore);
+    }
+
+    public void setStoreConfigs(int configs[]) {
+        final int count = configs.length;
+
+        resizeProgramStoresCard(count);
+        for (int i=0; i<count; ++i) {
+            final int config = configs[i];
+
+            final boolean alpha = (config & CarouselController.STORE_CONFIG_ALPHA) != 0;
+            final boolean depthReads = (config & CarouselController.STORE_CONFIG_DEPTH_READS) != 0;
+            final boolean depthWrites =
+                    (config & CarouselController.STORE_CONFIG_DEPTH_WRITES) != 0;
+
+            final boolean dither = true;
+            final ProgramStore.BlendDstFunc dstFunc = alpha ?
+                    ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA :
+                    ProgramStore.BlendDstFunc.ZERO;
+            final ProgramStore.DepthFunc depthFunc = depthReads ?
+                    ProgramStore.DepthFunc.LESS :
+                    ProgramStore.DepthFunc.ALWAYS;
+
+            final ProgramStore ps = new ProgramStore.Builder(mRS)
+                    .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, dstFunc)
+                    .setDitherEnable(dither)
+                    .setDepthFunc(depthFunc)
+                    .setDepthMask(depthWrites)
+                    .create();
+
+            setProgramStoreCard(i, ps);
+        }
+    }
+
+    /**
+     * Sets whether the background texture and default card geometry are to be drawn with respect
+     * to the depth buffer (both reading from it and writing to it).
+     *
+     * This method is a specialization of functionality that can be done with greater flexibility
+     * by setStoreConfigs. Calling setForceBlendCardsWithZ() after calling setStoreConfigs()
+     * results in the values set in setStoreConfigs() being discarded.
+     *
+     * @param enabled true to read from and write to the depth buffer, false to ignore it
+     */
     public void setForceBlendCardsWithZ(boolean enabled) {
-        mScript.set_forceBlendCardsWithZ(enabled);
+        mForceBlendCardsWithZ = enabled;
+        initProgramStore();
     }
 
     public void setDrawRuler(boolean drawRuler) {
diff --git a/carousel/java/com/android/ex/carousel/carousel.rs b/carousel/java/com/android/ex/carousel/carousel.rs
index e35cc1a..4de05d9 100644
--- a/carousel/java/com/android/ex/carousel/carousel.rs
+++ b/carousel/java/com/android/ex/carousel/carousel.rs
@@ -64,6 +64,10 @@
     float  far;
 } PerspectiveCamera;
 
+typedef struct ProgramStore_s {
+    rs_program_store programStore;
+} ProgramStore_t;
+
 typedef struct FragmentShaderConstants_s {
     float fadeAmount;
 } FragmentShaderConstants;
@@ -153,11 +157,11 @@
 float startAngle; // position of initial card, in radians
 int slotCount; // number of positions where a card can be
 int cardCount; // number of cards in stack
+int programStoresCardCount; // number of program fragment stores
 int visibleSlotCount; // number of visible slots (for culling)
 int visibleDetailCount; // number of visible detail textures to show
 int prefetchCardCount; // how many cards to keep in memory
 int detailTextureAlignment; // How to align detail texture with respect to card
-bool forceBlendCardsWithZ; // Enable depth buffer while blending
 bool drawRuler; // whether to draw a ruler from the card to the detail texture
 float radius; // carousel radius. Cards will be centered on a circle with this radius
 float cardRotation; // rotation of card in XY plane relative to Z=1
@@ -175,10 +179,9 @@
 
 int dragModel = DRAG_MODEL_SCREEN_DELTA;
 int fillDirection; // the order in which to lay out cards: +1 for CCW (default), -1 for CW
-rs_program_store programStoreAlphaZ;
-rs_program_store programStoreAlphaNoZ;
-rs_program_store programStoreNoAlphaZ;
-rs_program_store programStoreNoAlphaNoZ;
+ProgramStore_t *programStoresCard;
+rs_program_store programStoreBackground;
+rs_program_store programStoreDetail;
 rs_program_fragment singleTextureFragmentProgram;
 rs_program_fragment multiTextureFragmentProgram;
 rs_program_vertex vertexProgram;
@@ -224,9 +227,10 @@
 };
 
 // Because allocations can't have 0 dimensions, we have to track whether or not
-// cards are valid separately.
+// cards and program stores are valid separately.
 // TODO: Remove this dependency once allocations can have a zero dimension.
 static bool cardAllocationValid = false;
+static bool programStoresAllocationValid = false;
 
 // Default geometry when card.geometry is not set.
 static const float3 cardVertices[4] = {
@@ -273,6 +277,7 @@
     updateCamera = true;
     backgroundColor = (float4) { 0.0f, 0.0f, 0.0f, 1.0f };
     cardAllocationValid = false;
+    programStoresAllocationValid = false;
     cardCount = 0;
     rowCount = 1;
     rowSpacing = 0.0f;
@@ -283,12 +288,18 @@
     rsMatrixLoadIdentity(&defaultCardMatrix);
 }
 
-static void updateAllocationVars(Card_t* newcards)
+static void updateAllocationVars()
 {
     // Cards
     rs_allocation cardAlloc;
-    rsSetObject(&cardAlloc, rsGetAllocation(newcards));
+    rsSetObject(&cardAlloc, rsGetAllocation(cards));
     cardCount = (cardAllocationValid && rsIsObject(cardAlloc)) ? rsAllocationGetDimX(cardAlloc) : 0;
+
+    // Program stores
+    rs_allocation psAlloc;
+    rsSetObject(&psAlloc, rsGetAllocation(programStoresCard));
+    programStoresCardCount = (programStoresAllocationValid && rsIsObject(psAlloc) ?
+        rsAllocationGetDimX(psAlloc) : 0);
 }
 
 void setRadius(float rad)
@@ -505,6 +516,12 @@
         cards[n].geometryState = STATE_INVALID;
 }
 
+void setProgramStoresCard(int n, rs_program_store programStore)
+{
+    rsSetObject(&programStoresCard[n].programStore, programStore);
+    programStoresAllocationValid = true;
+}
+
 void setCarouselRotationAngle(float carouselRotationAngle) {
     bias = carouselRotationAngleToRadians(carouselRotationAngle);
 }
@@ -592,6 +609,26 @@
 }
 
 /*
+ * Draws the requested mesh, with the appropriate program store in effect.
+ */
+static void drawMesh(rs_mesh mesh)
+{
+    if (programStoresCardCount == 1) {
+        // Draw the entire mesh, with the only available program store
+        rsgBindProgramStore(programStoresCard[0].programStore);
+        rsgDrawMesh(mesh);
+    } else {
+        // Draw each primitive in the mesh with the corresponding program store
+        for (int i=0; i<programStoresCardCount; ++i) {
+            if (programStoresCard[i].programStore.p != 0) {
+                rsgBindProgramStore(programStoresCard[i].programStore);
+                rsgDrawMesh(mesh, i);
+            }
+        }
+    }
+}
+
+/*
  * Draws cards around the Carousel.
  * Returns true if we're still animating any property of the cards (e.g. fades).
  */
@@ -643,13 +680,14 @@
             stillAnimating |= getMatrixForCard(&matrix, i, true);
             rsgProgramVertexLoadModelMatrix(&matrix);
             if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) {
-                rsgDrawMesh(cards[i].geometry);
+                drawMesh(cards[i].geometry);
             } else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) {
-                rsgDrawMesh(loadingGeometry);
+                drawMesh(loadingGeometry);
             } else if (defaultGeometry.p != 0) {
-                rsgDrawMesh(defaultGeometry);
+                drawMesh(defaultGeometry);
             } else {
                 // Draw place-holder geometry
+                rsgBindProgramStore(programStoresCard[0].programStore);
                 rsgDrawQuad(
                     cardVertices[0].x, cardVertices[0].y, cardVertices[0].z,
                     cardVertices[1].x, cardVertices[1].y, cardVertices[1].z,
@@ -1565,7 +1603,7 @@
     // the mask is disabled. We may want to change the following to always draw w/o Z for
     // the background if we can guarantee the depth buffer will get cleared and
     // there's a performance advantage.
-    rsgBindProgramStore(forceBlendCardsWithZ ? programStoreNoAlphaZ : programStoreNoAlphaNoZ);
+    rsgBindProgramStore(programStoreBackground);
     drawBackground();
 
     updateCameraMatrix(rsgGetWidth(), rsgGetHeight());
@@ -1583,13 +1621,8 @@
     updateCardResources(currentTime);
 
     // Draw cards opaque only if requested, and always draw detail textures with blending.
-    if (forceBlendCardsWithZ) {
-        rsgBindProgramStore(programStoreAlphaZ);
-    } else {
-        rsgBindProgramStore(programStoreAlphaNoZ);
-    }
     stillAnimating |= drawCards(currentTime);
-    rsgBindProgramStore(programStoreAlphaNoZ);
+    rsgBindProgramStore(programStoreDetail);
     stillAnimating |= drawDetails(currentTime);
 
     if (stillAnimating != animating) {