2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.wallpaper.fall;
19 import android.os.Bundle;
20 import android.renderscript.Element;
21 import android.renderscript.ScriptC;
22 import android.renderscript.ProgramFragment;
23 import android.renderscript.ProgramStore;
24 import android.renderscript.ProgramVertex;
25 import android.renderscript.Allocation;
26 import android.renderscript.Sampler;
27 import android.renderscript.Light;
28 import android.renderscript.Type;
29 import android.renderscript.SimpleMesh;
30 import android.renderscript.Script;
31 import static android.renderscript.Sampler.Value.LINEAR;
32 import static android.renderscript.Sampler.Value.CLAMP;
33 import static android.renderscript.ProgramStore.DepthFunc.*;
34 import static android.renderscript.ProgramStore.BlendDstFunc;
35 import static android.renderscript.ProgramStore.BlendSrcFunc;
36 import static android.renderscript.Element.*;
38 import android.app.WallpaperManager;
39 import android.graphics.BitmapFactory;
40 import android.graphics.Bitmap;
41 import static android.util.MathUtils.*;
43 import java.util.TimeZone;
45 import com.android.wallpaper.R;
46 import com.android.wallpaper.RenderScriptScene;
48 class FallRS extends RenderScriptScene {
49 private static final int MESH_RESOLUTION = 48;
51 private static final int RSID_STATE = 0;
52 private static final int RSID_CONSTANTS = 1;
53 private static final int RSID_DROP = 2;
55 private static final int TEXTURES_COUNT = 2;
56 private static final int RSID_TEXTURE_RIVERBED = 0;
57 private static final int RSID_TEXTURE_LEAVES = 1;
58 private static final int RSID_TEXTURE_SKY = 2;
62 static class Defines {
66 private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
68 @SuppressWarnings({"FieldCanBeLocal"})
69 private ProgramFragment mPfBackground;
70 @SuppressWarnings({"FieldCanBeLocal"})
71 private ProgramFragment mPfSky;
72 @SuppressWarnings({"FieldCanBeLocal"})
73 private ProgramStore mPfsBackground;
74 @SuppressWarnings({"FieldCanBeLocal"})
75 private ProgramStore mPfsLeaf;
76 @SuppressWarnings({"FieldCanBeLocal"})
77 private ProgramVertex mPvSky;
78 private ProgramVertex mPvWater;
79 private ProgramVertex.MatrixAllocation mPvOrthoAlloc;
80 @SuppressWarnings({"FieldCanBeLocal"})
81 private Sampler mSampler;
83 private Allocation mState;
84 private Allocation mDropState;
85 private DropState mDrop;
86 private Type mStateType;
87 private Type mDropType;
88 private int mMeshWidth;
89 private Allocation mUniformAlloc;
91 private int mMeshHeight;
92 @SuppressWarnings({"FieldCanBeLocal"})
93 private SimpleMesh mMesh;
94 private WorldState mWorldState;
96 private float mGlHeight;
98 public FallRS(int width, int height) {
101 mOptionsARGB.inScaled = false;
102 mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
106 public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
107 mWorldState.xOffset = xOffset;
108 mState.data(mWorldState);
112 public Bundle onCommand(String action, int x, int y, int z, Bundle extras,
113 boolean resultRequested) {
114 if (WallpaperManager.COMMAND_TAP.equals(action)) {
115 addDrop(x + (mWorldState.width * mWorldState.xOffset), y);
116 } else if (WallpaperManager.COMMAND_DROP.equals(action)) {
117 addDrop(x + (mWorldState.width * mWorldState.xOffset), y);
123 public void start() {
125 final WorldState worldState = mWorldState;
126 final int width = worldState.width;
127 final int x = width / 4 + (int)(Math.random() * (width / 2));
128 final int y = worldState.height / 4 + (int)(Math.random() * (worldState.height / 2));
129 addDrop(x + (width * worldState.xOffset), y);
133 public void resize(int width, int height) {
134 super.resize(width, height);
136 mWorldState.width = width;
137 mWorldState.height = height;
138 mWorldState.rotate = width > height ? 1 : 0;
139 mState.data(mWorldState);
141 mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight);
145 protected ScriptC createScript() {
148 createProgramVertex();
149 createProgramFragmentStore();
150 createProgramFragment();
155 ScriptC.Builder sb = new ScriptC.Builder(mRS);
156 sb.setType(mStateType, "State", RSID_STATE);
157 sb.setType(mDropType, "Drop", RSID_DROP);
158 sb.setType(mUniformAlloc.getType(), "Constants", RSID_CONSTANTS);
159 sb.setScript(mResources, R.raw.fall);
160 Script.Invokable invokable = sb.addInvokable("initLeaves");
163 ScriptC script = sb.create();
164 script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
165 script.setTimeZone(TimeZone.getDefault().getID());
167 script.bindAllocation(mState, RSID_STATE);
168 script.bindAllocation(mUniformAlloc, RSID_CONSTANTS);
169 script.bindAllocation(mDropState, RSID_DROP);
176 private void createMesh() {
177 SimpleMesh.TriangleMeshBuilder tmb = new SimpleMesh.TriangleMeshBuilder(mRS, 2, 0);
179 final int width = mWidth > mHeight ? mHeight : mWidth;
180 final int height = mWidth > mHeight ? mWidth : mHeight;
182 int wResolution = MESH_RESOLUTION;
183 int hResolution = (int) (MESH_RESOLUTION * height / (float) width);
185 mGlHeight = 2.0f * height / (float) width;
186 final float glHeight = mGlHeight;
188 float quadWidth = 2.0f / (float) wResolution;
189 float quadHeight = glHeight / (float) hResolution;
194 for (int y = 0; y <= hResolution; y++) {
195 final float yOffset = (((float)y / hResolution) * 2.f - 1.f) * height / width;
196 for (int x = 0; x <= wResolution; x++) {
197 tmb.addVertex(((float)x / wResolution) * 2.f - 1.f, yOffset);
201 for (int y = 0; y < hResolution; y++) {
202 final boolean shift = (y & 0x1) == 0;
203 final int yOffset = y * (wResolution + 1);
204 for (int x = 0; x < wResolution; x++) {
205 final int index = yOffset + x;
206 final int iWR1 = index + wResolution + 1;
208 tmb.addTriangle(index, index + 1, iWR1);
209 tmb.addTriangle(index + 1, iWR1 + 1, iWR1);
211 tmb.addTriangle(index, iWR1 + 1, iWR1);
212 tmb.addTriangle(index, index + 1, iWR1 + 1);
217 mMesh = tmb.create();
218 mMesh.setName("WaterMesh");
220 mMeshWidth = wResolution + 1;
221 mMeshHeight = hResolution + 1;
224 static class WorldState {
225 public int frameCount;
228 public int meshWidth;
229 public int meshHeight;
230 public int rippleIndex;
231 public int leavesCount;
232 public float glWidth;
233 public float glHeight;
234 public float skySpeedX;
235 public float skySpeedY;
237 public int isPreview;
238 public float xOffset;
241 static class DropState {
246 private void createState() {
247 mWorldState = new WorldState();
248 mWorldState.width = mWidth;
249 mWorldState.height = mHeight;
250 mWorldState.meshWidth = mMeshWidth;
251 mWorldState.meshHeight = mMeshHeight;
252 mWorldState.rippleIndex = 0;
253 mWorldState.glWidth = 2.0f;
254 mWorldState.glHeight = mGlHeight;
255 mWorldState.skySpeedX = random(-0.001f, 0.001f);
256 mWorldState.skySpeedY = random(0.00008f, 0.0002f);
257 mWorldState.rotate = mWidth > mHeight ? 1 : 0;
258 mWorldState.isPreview = isPreview() ? 1 : 0;
260 mStateType = Type.createFromClass(mRS, WorldState.class, 1, "WorldState");
261 mState = Allocation.createTyped(mRS, mStateType);
262 mState.data(mWorldState);
264 mDrop = new DropState();
268 mDropType = Type.createFromClass(mRS, DropState.class, 1, "DropState");
269 mDropState = Allocation.createTyped(mRS, mDropType);
270 mDropState.data(mDrop);
273 private void loadTextures() {
274 final Allocation[] textures = new Allocation[TEXTURES_COUNT];
275 textures[RSID_TEXTURE_RIVERBED] = loadTexture(R.drawable.pond, "TRiverbed");
276 textures[RSID_TEXTURE_LEAVES] = loadTextureARGB(R.drawable.leaves, "TLeaves");
277 // textures[RSID_TEXTURE_SKY] = loadTextureARGB(R.drawable.clouds, "TSky");
279 final int count = textures.length;
280 for (int i = 0; i < count; i++) {
281 textures[i].uploadToTexture(0);
285 private Allocation loadTexture(int id, String name) {
286 final Allocation allocation = Allocation.createFromBitmapResource(mRS, mResources,
287 id, RGB_565(mRS), false);
288 allocation.setName(name);
292 private Allocation loadTextureARGB(int id, String name) {
293 Bitmap b = BitmapFactory.decodeResource(mResources, id, mOptionsARGB);
294 final Allocation allocation = Allocation.createFromBitmap(mRS, b, RGBA_8888(mRS), false);
295 allocation.setName(name);
299 private void createProgramFragment() {
300 Sampler.Builder sampleBuilder = new Sampler.Builder(mRS);
301 sampleBuilder.setMin(LINEAR);
302 sampleBuilder.setMag(LINEAR);
303 sampleBuilder.setWrapS(CLAMP);
304 sampleBuilder.setWrapT(CLAMP);
305 mSampler = sampleBuilder.create();
307 ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS);
308 builder.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
309 ProgramFragment.Builder.Format.RGBA, 0);
310 mPfBackground = builder.create();
311 mPfBackground.setName("PFBackground");
312 mPfBackground.bindSampler(mSampler, 0);
314 builder = new ProgramFragment.Builder(mRS);
315 builder.setTexture(ProgramFragment.Builder.EnvMode.MODULATE,
316 ProgramFragment.Builder.Format.RGBA, 0);
317 mPfSky = builder.create();
318 mPfSky.setName("PFSky");
319 mPfSky.bindSampler(mSampler, 0);
322 private void createProgramFragmentStore() {
323 ProgramStore.Builder builder = new ProgramStore.Builder(mRS, null, null);
324 builder.setDepthFunc(ALWAYS);
325 builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
326 builder.setDitherEnable(false);
327 builder.setDepthMask(true);
328 mPfsBackground = builder.create();
329 mPfsBackground.setName("PFSBackground");
331 builder = new ProgramStore.Builder(mRS, null, null);
332 builder.setDepthFunc(ALWAYS);
333 builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
334 builder.setDitherEnable(false);
335 builder.setDepthMask(true);
336 mPfsLeaf = builder.create();
337 mPfsLeaf.setName("PFSLeaf");
340 private void createProgramVertex() {
341 mPvOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS);
342 mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight);
344 ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS, null, null);
345 mPvSky = builder.create();
346 mPvSky.bindAllocation(mPvOrthoAlloc);
347 mPvSky.setName("PVSky");
349 float dw = 480.f / mMeshWidth;
350 float dh = 800.f / mMeshHeight;
352 Element.Builder eb = new Element.Builder(mRS);
353 // Make this an array when we can.
354 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop01");
355 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop02");
356 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop03");
357 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop04");
358 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop05");
359 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop06");
360 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop07");
361 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop08");
362 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop09");
363 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop10");
364 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Offset");
365 Element e = eb.create();
367 mUniformAlloc = Allocation.createSized(mRS, e, 1);
370 ProgramVertex.ShaderBuilder sb = new ProgramVertex.ShaderBuilder(mRS);
371 String t = new String("void main() {\n" +
373 " pos.x = ATTRIB_position.x;\n" +
374 " pos.y = ATTRIB_position.y;\n" +
377 " gl_Position = pos;\n" +
379 // When we resize the texture we will need to tweak this.
380 " varTex0.x = (pos.x + 1.0) * 0.25;\n" +
381 " varTex0.x += UNI_Offset.x * 0.5 * 0.85;\n" +
382 " varTex0.y = (pos.y + 1.6666) * 0.33;\n" +
383 " varTex0.w = 0.0;\n" +
384 " varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +
386 " pos.x += UNI_Offset.x * 2.0;\n" +
389 " pos.x *= 25.0;\n" +
390 " pos.y *= 42.0;\n" +
396 " delta = UNI_Drop01.xy - pos.xy;\n" +
397 " dist = length(delta);\n" +
398 " if (dist < UNI_Drop01.w) { \n" +
399 " amp = UNI_Drop01.z * dist;\n" +
400 " amp /= UNI_Drop01.w * UNI_Drop01.w;\n" +
401 " amp *= sin(UNI_Drop01.w - dist);\n" +
402 " varTex0.xy += delta * amp;\n" +
405 " delta = UNI_Drop02.xy - pos.xy;\n" +
406 " dist = length(delta);\n" +
407 " if (dist < UNI_Drop02.w) { \n" +
408 " amp = UNI_Drop02.z * dist;\n" +
409 " amp /= UNI_Drop02.w * UNI_Drop02.w;\n" +
410 " amp *= sin(UNI_Drop02.w - dist);\n" +
411 " varTex0.xy += delta * amp;\n" +
414 " delta = UNI_Drop03.xy - pos.xy;\n" +
415 " dist = length(delta);\n" +
416 " if (dist < UNI_Drop03.w) { \n" +
417 " amp = UNI_Drop03.z * dist;\n" +
418 " amp /= UNI_Drop03.w * UNI_Drop03.w;\n" +
419 " amp *= sin(UNI_Drop03.w - dist);\n" +
420 " varTex0.xy += delta * amp;\n" +
423 " delta = UNI_Drop04.xy - pos.xy;\n" +
424 " dist = length(delta);\n" +
425 " if (dist < UNI_Drop04.w) { \n" +
426 " amp = UNI_Drop04.z * dist;\n" +
427 " amp /= UNI_Drop04.w * UNI_Drop04.w;\n" +
428 " amp *= sin(UNI_Drop04.w - dist);\n" +
429 " varTex0.xy += delta * amp;\n" +
432 " delta = UNI_Drop05.xy - pos.xy;\n" +
433 " dist = length(delta);\n" +
434 " if (dist < UNI_Drop05.w) { \n" +
435 " amp = UNI_Drop05.z * dist;\n" +
436 " amp /= UNI_Drop05.w * UNI_Drop05.w;\n" +
437 " amp *= sin(UNI_Drop05.w - dist);\n" +
438 " varTex0.xy += delta * amp;\n" +
441 " delta = UNI_Drop06.xy - pos.xy;\n" +
442 " dist = length(delta);\n" +
443 " if (dist < UNI_Drop06.w) { \n" +
444 " amp = UNI_Drop06.z * dist;\n" +
445 " amp /= UNI_Drop06.w * UNI_Drop06.w;\n" +
446 " amp *= sin(UNI_Drop06.w - dist);\n" +
447 " varTex0.xy += delta * amp;\n" +
450 " delta = UNI_Drop07.xy - pos.xy;\n" +
451 " dist = length(delta);\n" +
452 " if (dist < UNI_Drop07.w) { \n" +
453 " amp = UNI_Drop07.z * dist;\n" +
454 " amp /= UNI_Drop07.w * UNI_Drop07.w;\n" +
455 " amp *= sin(UNI_Drop07.w - dist);\n" +
456 " varTex0.xy += delta * amp;\n" +
459 " delta = UNI_Drop08.xy - pos.xy;\n" +
460 " dist = length(delta);\n" +
461 " if (dist < UNI_Drop08.w) { \n" +
462 " amp = UNI_Drop08.z * dist;\n" +
463 " amp /= UNI_Drop08.w * UNI_Drop08.w;\n" +
464 " amp *= sin(UNI_Drop08.w - dist);\n" +
465 " varTex0.xy += delta * amp;\n" +
468 " delta = UNI_Drop09.xy - pos.xy;\n" +
469 " dist = length(delta);\n" +
470 " if (dist < UNI_Drop09.w) { \n" +
471 " amp = UNI_Drop09.z * dist;\n" +
472 " amp /= UNI_Drop09.w * UNI_Drop09.w;\n" +
473 " amp *= sin(UNI_Drop09.w - dist);\n" +
474 " varTex0.xy += delta * amp;\n" +
477 " delta = UNI_Drop10.xy - pos.xy;\n" +
478 " dist = length(delta);\n" +
479 " if (dist < UNI_Drop10.w) { \n" +
480 " amp = UNI_Drop10.z * dist;\n" +
481 " amp /= UNI_Drop10.w * UNI_Drop10.w;\n" +
482 " amp *= sin(UNI_Drop10.w - dist);\n" +
483 " varTex0.xy += delta * amp;\n" +
489 sb.addConstant(mUniformAlloc.getType());
490 sb.addInput(mMesh.getVertexType(0).getElement());
491 mPvWater = sb.create();
492 mPvWater.bindAllocation(mPvOrthoAlloc);
493 mPvWater.setName("PVWater");
494 mPvWater.bindConstants(mUniformAlloc, 1);
498 void addDrop(float x, float y) {
499 if (mWorldState.rotate == 0) {
500 mDrop.dropX = (int) ((x / mWidth) * mMeshWidth);
501 mDrop.dropY = (int) ((y / mHeight) * mMeshHeight);
503 mDrop.dropY = (int) ((x / mWidth) * mMeshHeight);
504 mDrop.dropX = mMeshWidth - (int) ((y / mHeight) * mMeshWidth);
506 mDropState.data(mDrop);