1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.wallpaper.fall;
18
19import android.os.Bundle;
20import android.renderscript.*;
21import static android.renderscript.Sampler.Value.LINEAR;
22import static android.renderscript.Sampler.Value.CLAMP;
23import static android.renderscript.ProgramStore.DepthFunc.*;
24import static android.renderscript.ProgramStore.BlendDstFunc;
25import static android.renderscript.ProgramStore.BlendSrcFunc;
26import static android.renderscript.Element.*;
27
28import android.util.Log;
29
30import android.app.WallpaperManager;
31import android.graphics.BitmapFactory;
32import android.graphics.Bitmap;
33import static android.util.MathUtils.*;
34
35import java.util.TimeZone;
36
37import com.android.wallpaper.R;
38import com.android.wallpaper.RenderScriptScene;
39
40class FallRS extends RenderScriptScene {
41    private static final int MESH_RESOLUTION = 48;
42
43    private static final int RSID_STATE = 0;
44    private static final int RSID_CONSTANTS = 1;
45    private static final int RSID_DROP = 2;
46
47    private static final int TEXTURES_COUNT = 2;
48    private static final int RSID_TEXTURE_RIVERBED = 0;
49    private static final int RSID_TEXTURE_LEAVES = 1;
50
51    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
52
53    @SuppressWarnings({"FieldCanBeLocal"})
54    private ProgramFragment mPfBackground;
55    @SuppressWarnings({"FieldCanBeLocal"})
56    private ProgramFragment mPfSky;
57    @SuppressWarnings({"FieldCanBeLocal"})
58    private ProgramStore mPfsBackground;
59    @SuppressWarnings({"FieldCanBeLocal"})
60    private ProgramStore mPfsLeaf;
61    @SuppressWarnings({"FieldCanBeLocal"})
62    private ProgramVertex mPvSky;
63    @SuppressWarnings({"FieldCanBeLocal"})
64    private ProgramVertex mPvWater;
65    private ProgramVertexFixedFunction.Constants mPvOrthoAlloc;
66    @SuppressWarnings({"FieldCanBeLocal"})
67    private Sampler mSampler;
68
69    private int mMeshWidth;
70    private Allocation mUniformAlloc;
71
72    private int mMeshHeight;
73    @SuppressWarnings({"FieldCanBeLocal"})
74    private Mesh mMesh;
75    private WorldState mWorldState;
76
77    private ScriptC_fall mScript;
78
79    private ScriptField_Constants mConstants;
80
81    private float mGlHeight;
82
83    public FallRS(int width, int height) {
84        super(width, height);
85
86        mOptionsARGB.inScaled = false;
87        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
88    }
89
90    @Override
91    public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
92        mWorldState.xOffset = xOffset;
93        mScript.set_g_xOffset(mWorldState.xOffset);
94    }
95
96    @Override
97    public Bundle onCommand(String action, int x, int y, int z, Bundle extras,
98            boolean resultRequested) {
99        if (WallpaperManager.COMMAND_TAP.equals(action)
100                || WallpaperManager.COMMAND_SECONDARY_TAP.equals(action)
101                || WallpaperManager.COMMAND_DROP.equals(action)) {
102            addDrop(x + (mWorldState.rotate == 0 ? (mWorldState.width * mWorldState.xOffset) : 0), y);
103        }
104        return null;
105    }
106
107    @Override
108    public void start() {
109        super.start();
110        final WorldState worldState = mWorldState;
111        final int width = worldState.width;
112        final int x = width / 4 + (int)(Math.random() * (width / 2));
113        final int y = worldState.height / 4 + (int)(Math.random() * (worldState.height / 2));
114        addDrop(x + (mWorldState.rotate == 0 ? (width * worldState.xOffset) : 0), y);
115    }
116
117    @Override
118    public void resize(int width, int height) {
119        super.resize(width, height);
120
121        mWorldState.width = width;
122        mWorldState.height = height;
123        mWorldState.rotate = width > height ? 1 : 0;
124
125        mScript.set_g_glWidth(mWorldState.width);
126        mScript.set_g_glHeight(mWorldState.height);
127        mScript.set_g_rotate(mWorldState.rotate);
128
129        mScript.invoke_initLeaves();
130
131        Matrix4f proj = new Matrix4f();
132        proj.loadProjectionNormalized(mWidth, mHeight);
133        mPvOrthoAlloc.setProjection(proj);
134    }
135
136    @Override
137    protected ScriptC createScript() {
138        mScript = new ScriptC_fall(mRS, mResources, R.raw.fall);
139
140        createMesh();
141        createState();
142        createProgramVertex();
143        createProgramFragmentStore();
144        createProgramFragment();
145        loadTextures();
146
147        mScript.setTimeZone(TimeZone.getDefault().getID());
148
149        mScript.bind_g_Constants(mConstants);
150
151        return mScript;
152    }
153
154    private void createMesh() {
155        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, 2, 0);
156
157        final int width = mWidth > mHeight ? mHeight : mWidth;
158        final int height = mWidth > mHeight ? mWidth : mHeight;
159
160        int wResolution = MESH_RESOLUTION;
161        int hResolution = (int) (MESH_RESOLUTION * height / (float) width);
162
163        mGlHeight = 2.0f * height / (float) width;
164
165        wResolution += 2;
166        hResolution += 2;
167
168        for (int y = 0; y <= hResolution; y++) {
169            final float yOffset = (((float)y / hResolution) * 2.f - 1.f) * height / width;
170            for (int x = 0; x <= wResolution; x++) {
171                tmb.addVertex(((float)x / wResolution) * 2.f - 1.f, yOffset);
172            }
173        }
174
175        for (int y = 0; y < hResolution; y++) {
176            final boolean shift = (y & 0x1) == 0;
177            final int yOffset = y * (wResolution + 1);
178            for (int x = 0; x < wResolution; x++) {
179                final int index = yOffset + x;
180                final int iWR1 = index + wResolution + 1;
181                if (shift) {
182                    tmb.addTriangle(index, index + 1, iWR1);
183                    tmb.addTriangle(index + 1, iWR1 + 1, iWR1);
184                } else {
185                    tmb.addTriangle(index, iWR1 + 1, iWR1);
186                    tmb.addTriangle(index, index + 1, iWR1 + 1);
187                }
188            }
189        }
190
191        mMesh = tmb.create(true);
192
193        mMeshWidth = wResolution + 1;
194        mMeshHeight = hResolution + 1;
195
196        mScript.set_g_WaterMesh(mMesh);
197    }
198
199    static class WorldState {
200        public int frameCount;
201        public int width;
202        public int height;
203        public int meshWidth;
204        public int meshHeight;
205        public int rippleIndex;
206        public float glWidth;
207        public float glHeight;
208        public float skySpeedX;
209        public float skySpeedY;
210        public int rotate;
211        public int isPreview;
212        public float xOffset;
213    }
214
215    private void createState() {
216        mWorldState = new WorldState();
217        mWorldState.width = mWidth;
218        mWorldState.height = mHeight;
219        mWorldState.meshWidth = mMeshWidth;
220        mWorldState.meshHeight = mMeshHeight;
221        mWorldState.rippleIndex = 0;
222        mWorldState.glWidth = 2.0f;
223        mWorldState.glHeight = mGlHeight;
224        mWorldState.skySpeedX = random(-0.001f, 0.001f);
225        mWorldState.skySpeedY = random(0.00008f, 0.0002f);
226        mWorldState.rotate = mWidth > mHeight ? 1 : 0;
227        mWorldState.isPreview = isPreview() ? 1 : 0;
228
229        mScript.set_g_glWidth(mWorldState.glWidth);
230        mScript.set_g_glHeight(mWorldState.glHeight);
231        mScript.set_g_meshWidth(mWorldState.meshWidth);
232        mScript.set_g_meshHeight(mWorldState.meshHeight);
233        mScript.set_g_xOffset(0);
234        mScript.set_g_rotate(mWorldState.rotate);
235    }
236
237    private void loadTextures() {
238        mScript.set_g_TLeaves(loadTextureARGB(R.drawable.leaves));
239        mScript.set_g_TRiverbed(loadTexture(R.drawable.pond));
240    }
241
242    private Allocation loadTexture(int id) {
243        final Allocation allocation = Allocation.createFromBitmapResource(mRS, mResources, id,
244                                           Allocation.MipmapControl.MIPMAP_NONE,
245                                           Allocation.USAGE_GRAPHICS_TEXTURE);
246        return allocation;
247    }
248
249    private Allocation loadTextureARGB(int id) {
250        Bitmap b = BitmapFactory.decodeResource(mResources, id, mOptionsARGB);
251        final Allocation allocation = Allocation.createFromBitmap(mRS, b,
252                                           Allocation.MipmapControl.MIPMAP_NONE,
253                                           Allocation.USAGE_GRAPHICS_TEXTURE);
254        return allocation;
255    }
256
257    private void createProgramFragment() {
258        Sampler.Builder sampleBuilder = new Sampler.Builder(mRS);
259        sampleBuilder.setMinification(LINEAR);
260        sampleBuilder.setMagnification(LINEAR);
261        sampleBuilder.setWrapS(CLAMP);
262        sampleBuilder.setWrapT(CLAMP);
263        mSampler = sampleBuilder.create();
264
265        ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
266        builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
267                           ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
268        mPfBackground = builder.create();
269        mPfBackground.bindSampler(mSampler, 0);
270
271        mScript.set_g_PFBackground(mPfBackground);
272
273        builder = new ProgramFragmentFixedFunction.Builder(mRS);
274        builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE,
275                           ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
276        mPfSky = builder.create();
277        mPfSky.bindSampler(mSampler, 0);
278
279        mScript.set_g_PFSky(mPfSky);
280    }
281
282    private void createProgramFragmentStore() {
283        ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
284        builder.setDepthFunc(ALWAYS);
285        builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
286        builder.setDitherEnabled(false);
287        builder.setDepthMaskEnabled(true);
288        mPfsBackground = builder.create();
289
290        builder = new ProgramStore.Builder(mRS);
291        builder.setDepthFunc(ALWAYS);
292        builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
293        builder.setDitherEnabled(false);
294        builder.setDepthMaskEnabled(true);
295        mPfsLeaf = builder.create();
296
297        mScript.set_g_PFSLeaf(mPfsLeaf);
298        mScript.set_g_PFSBackground(mPfsBackground);
299    }
300
301    private void createProgramVertex() {
302        mPvOrthoAlloc = new ProgramVertexFixedFunction.Constants(mRS);
303        Matrix4f proj = new Matrix4f();
304        proj.loadProjectionNormalized(mWidth, mHeight);
305        mPvOrthoAlloc.setProjection(proj);
306
307
308        ProgramVertexFixedFunction.Builder builder = new ProgramVertexFixedFunction.Builder(mRS);
309        mPvSky = builder.create();
310        ((ProgramVertexFixedFunction)mPvSky).bindConstants(mPvOrthoAlloc);
311
312        mScript.set_g_PVSky(mPvSky);
313
314        mConstants = new ScriptField_Constants(mRS, 1);
315        mUniformAlloc = mConstants.getAllocation();
316
317        ProgramVertex.Builder sb = new ProgramVertex.Builder(mRS);
318
319        String t = "\n" +
320                "varying vec4 varColor;\n" +
321                "varying vec2 varTex0;\n" +
322
323                "vec2 addDrop(vec4 d, vec2 pos, float dxMul) {\n" +
324                "  vec2 ret = vec2(0.0, 0.0);\n" +
325                "  vec2 delta = d.xy - pos;\n" +
326                "  delta.x *= dxMul;\n" +
327                "  float dist = length(delta);\n" +
328                "  if (dist < d.w) { \n" +
329                "    float amp = d.z * dist;\n" +
330                "    amp /= d.w * d.w;\n" +
331                "    amp *= sin(d.w - dist);\n" +
332                "    ret = delta * amp;\n" +
333                "  }\n" +
334                "  return ret;\n" +
335                "}\n" +
336
337                "void main() {\n" +
338                "  vec2 pos = ATTRIB_position.xy;\n" +
339                "  gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);\n" +
340                "  float dxMul = 1.0;\n" +
341
342                "  varTex0 = vec2((pos.x + 1.0), (pos.y + 1.6666));\n" +
343
344                "  if (UNI_Rotate < 0.9) {\n" +
345                "    varTex0.xy *= vec2(0.25, 0.33);\n" +
346                "    varTex0.x += UNI_Offset.x * 0.5;\n" +
347                "    pos.x += UNI_Offset.x * 2.0;\n" +
348                "  } else {\n" +
349                "    varTex0.xy *= vec2(0.5, 0.3125);\n" +
350                "    dxMul = 2.5;\n" +
351                "  }\n" +
352
353                "  varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +
354                "  pos.xy += vec2(1.0, 1.0);\n" +
355                "  pos.xy *= vec2(25.0, 42.0);\n" +
356
357                "  varTex0.xy += addDrop(UNI_Drop01, pos, dxMul);\n" +
358                "  varTex0.xy += addDrop(UNI_Drop02, pos, dxMul);\n" +
359                "  varTex0.xy += addDrop(UNI_Drop03, pos, dxMul);\n" +
360                "  varTex0.xy += addDrop(UNI_Drop04, pos, dxMul);\n" +
361                "  varTex0.xy += addDrop(UNI_Drop05, pos, dxMul);\n" +
362                "  varTex0.xy += addDrop(UNI_Drop06, pos, dxMul);\n" +
363                "  varTex0.xy += addDrop(UNI_Drop07, pos, dxMul);\n" +
364                "  varTex0.xy += addDrop(UNI_Drop08, pos, dxMul);\n" +
365                "  varTex0.xy += addDrop(UNI_Drop09, pos, dxMul);\n" +
366                "  varTex0.xy += addDrop(UNI_Drop10, pos, dxMul);\n" +
367                "}\n";
368
369        sb.setShader(t);
370        sb.addConstant(mUniformAlloc.getType());
371        sb.addInput(mMesh.getVertexAllocation(0).getType().getElement());
372        mPvWater = sb.create();
373        mPvWater.bindConstants(mUniformAlloc, 0);
374
375        mScript.set_g_PVWater(mPvWater);
376
377    }
378
379    void addDrop(float x, float y) {
380        int dropX = (int) ((x / mWidth) * mMeshWidth);
381        int dropY = (int) ((y / mHeight) * mMeshHeight);
382
383        mScript.invoke_addDrop(dropX, dropY);
384    }
385}
386