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 17 package com.android.wallpaper.fall; 18 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.Type; 28 import android.renderscript.SimpleMesh; 29 import android.renderscript.Script; 30 import static android.renderscript.Sampler.Value.LINEAR; 31 import static android.renderscript.Sampler.Value.CLAMP; 32 import static android.renderscript.ProgramStore.DepthFunc.*; 33 import static android.renderscript.ProgramStore.BlendDstFunc; 34 import static android.renderscript.ProgramStore.BlendSrcFunc; 35 import static android.renderscript.Element.*; 36 37 import android.app.WallpaperManager; 38 import android.graphics.BitmapFactory; 39 import android.graphics.Bitmap; 40 import static android.util.MathUtils.*; 41 42 import java.util.TimeZone; 43 44 import com.android.wallpaper.R; 45 import com.android.wallpaper.RenderScriptScene; 46 47 class FallRS extends RenderScriptScene { 48 private static final int MESH_RESOLUTION = 48; 49 50 private static final int RSID_STATE = 0; 51 private static final int RSID_CONSTANTS = 1; 52 private static final int RSID_DROP = 2; 53 54 private static final int TEXTURES_COUNT = 2; 55 private static final int RSID_TEXTURE_RIVERBED = 0; 56 private static final int RSID_TEXTURE_LEAVES = 1; 57 58 private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); 59 60 @SuppressWarnings({"FieldCanBeLocal"}) 61 private ProgramFragment mPfBackground; 62 @SuppressWarnings({"FieldCanBeLocal"}) 63 private ProgramFragment mPfSky; 64 @SuppressWarnings({"FieldCanBeLocal"}) 65 private ProgramStore mPfsBackground; 66 @SuppressWarnings({"FieldCanBeLocal"}) 67 private ProgramStore mPfsLeaf; 68 @SuppressWarnings({"FieldCanBeLocal"}) 69 private ProgramVertex mPvSky; 70 @SuppressWarnings({"FieldCanBeLocal"}) 71 private ProgramVertex mPvWater; 72 private ProgramVertex.MatrixAllocation mPvOrthoAlloc; 73 @SuppressWarnings({"FieldCanBeLocal"}) 74 private Sampler mSampler; 75 76 private Allocation mState; 77 private Allocation mDropState; 78 private DropState mDrop; 79 private Type mStateType; 80 private Type mDropType; 81 private int mMeshWidth; 82 private Allocation mUniformAlloc; 83 84 private int mMeshHeight; 85 @SuppressWarnings({"FieldCanBeLocal"}) 86 private SimpleMesh mMesh; 87 private WorldState mWorldState; 88 89 private float mGlHeight; 90 91 public FallRS(int width, int height) { 92 super(width, height); 93 94 mOptionsARGB.inScaled = false; 95 mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888; 96 } 97 98 @Override 99 public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) { 100 mWorldState.xOffset = xOffset; 101 mState.data(mWorldState); 102 } 103 104 @Override 105 public Bundle onCommand(String action, int x, int y, int z, Bundle extras, 106 boolean resultRequested) { 107 if (WallpaperManager.COMMAND_TAP.equals(action)) { 108 addDrop(x + (mWorldState.rotate == 0 ? (mWorldState.width * mWorldState.xOffset) : 0), y); 109 } else if (WallpaperManager.COMMAND_DROP.equals(action)) { 110 addDrop(x + (mWorldState.rotate == 0 ? (mWorldState.width * mWorldState.xOffset) : 0), y); 111 } 112 return null; 113 } 114 115 @Override 116 public void start() { 117 super.start(); 118 final WorldState worldState = mWorldState; 119 final int width = worldState.width; 120 final int x = width / 4 + (int)(Math.random() * (width / 2)); 121 final int y = worldState.height / 4 + (int)(Math.random() * (worldState.height / 2)); 122 addDrop(x + (mWorldState.rotate == 0 ? (width * worldState.xOffset) : 0), y); 123 } 124 125 @Override 126 public void resize(int width, int height) { 127 super.resize(width, height); 128 129 mWorldState.width = width; 130 mWorldState.height = height; 131 mWorldState.rotate = width > height ? 1 : 0; 132 mState.data(mWorldState); 133 134 mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight); 135 } 136 137 @Override 138 protected ScriptC createScript() { 139 createMesh(); 140 createState(); 141 createProgramVertex(); 142 createProgramFragmentStore(); 143 createProgramFragment(); 144 loadTextures(); 145 146 ScriptC.Builder sb = new ScriptC.Builder(mRS); 147 sb.setType(mStateType, "State", RSID_STATE); 148 sb.setType(mDropType, "Drop", RSID_DROP); 149 sb.setType(mUniformAlloc.getType(), "Constants", RSID_CONSTANTS); 150 sb.setScript(mResources, R.raw.fall); 151 Script.Invokable invokable = sb.addInvokable("initLeaves"); 152 sb.setRoot(true); 153 154 ScriptC script = sb.create(); 155 script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f); 156 script.setTimeZone(TimeZone.getDefault().getID()); 157 158 script.bindAllocation(mState, RSID_STATE); 159 script.bindAllocation(mUniformAlloc, RSID_CONSTANTS); 160 script.bindAllocation(mDropState, RSID_DROP); 161 162 invokable.execute(); 163 164 return script; 165 } 166 167 private void createMesh() { 168 SimpleMesh.TriangleMeshBuilder tmb = new SimpleMesh.TriangleMeshBuilder(mRS, 2, 0); 169 170 final int width = mWidth > mHeight ? mHeight : mWidth; 171 final int height = mWidth > mHeight ? mWidth : mHeight; 172 173 int wResolution = MESH_RESOLUTION; 174 int hResolution = (int) (MESH_RESOLUTION * height / (float) width); 175 176 mGlHeight = 2.0f * height / (float) width; 177 178 wResolution += 2; 179 hResolution += 2; 180 181 for (int y = 0; y <= hResolution; y++) { 182 final float yOffset = (((float)y / hResolution) * 2.f - 1.f) * height / width; 183 for (int x = 0; x <= wResolution; x++) { 184 tmb.addVertex(((float)x / wResolution) * 2.f - 1.f, yOffset); 185 } 186 } 187 188 for (int y = 0; y < hResolution; y++) { 189 final boolean shift = (y & 0x1) == 0; 190 final int yOffset = y * (wResolution + 1); 191 for (int x = 0; x < wResolution; x++) { 192 final int index = yOffset + x; 193 final int iWR1 = index + wResolution + 1; 194 if (shift) { 195 tmb.addTriangle(index, index + 1, iWR1); 196 tmb.addTriangle(index + 1, iWR1 + 1, iWR1); 197 } else { 198 tmb.addTriangle(index, iWR1 + 1, iWR1); 199 tmb.addTriangle(index, index + 1, iWR1 + 1); 200 } 201 } 202 } 203 204 mMesh = tmb.create(); 205 mMesh.setName("WaterMesh"); 206 207 mMeshWidth = wResolution + 1; 208 mMeshHeight = hResolution + 1; 209 } 210 211 static class WorldState { 212 public int frameCount; 213 public int width; 214 public int height; 215 public int meshWidth; 216 public int meshHeight; 217 public int rippleIndex; 218 public float glWidth; 219 public float glHeight; 220 public float skySpeedX; 221 public float skySpeedY; 222 public int rotate; 223 public int isPreview; 224 public float xOffset; 225 } 226 227 static class DropState { 228 public int dropX; 229 public int dropY; 230 } 231 232 private void createState() { 233 mWorldState = new WorldState(); 234 mWorldState.width = mWidth; 235 mWorldState.height = mHeight; 236 mWorldState.meshWidth = mMeshWidth; 237 mWorldState.meshHeight = mMeshHeight; 238 mWorldState.rippleIndex = 0; 239 mWorldState.glWidth = 2.0f; 240 mWorldState.glHeight = mGlHeight; 241 mWorldState.skySpeedX = random(-0.001f, 0.001f); 242 mWorldState.skySpeedY = random(0.00008f, 0.0002f); 243 mWorldState.rotate = mWidth > mHeight ? 1 : 0; 244 mWorldState.isPreview = isPreview() ? 1 : 0; 245 246 mStateType = Type.createFromClass(mRS, WorldState.class, 1, "WorldState"); 247 mState = Allocation.createTyped(mRS, mStateType); 248 mState.data(mWorldState); 249 250 mDrop = new DropState(); 251 mDrop.dropX = -1; 252 mDrop.dropY = -1; 253 254 mDropType = Type.createFromClass(mRS, DropState.class, 1, "DropState"); 255 mDropState = Allocation.createTyped(mRS, mDropType); 256 mDropState.data(mDrop); 257 } 258 259 private void loadTextures() { 260 final Allocation[] textures = new Allocation[TEXTURES_COUNT]; 261 textures[RSID_TEXTURE_RIVERBED] = loadTexture(R.drawable.pond, "TRiverbed"); 262 textures[RSID_TEXTURE_LEAVES] = loadTextureARGB(R.drawable.leaves, "TLeaves"); 263 264 final int count = textures.length; 265 for (int i = 0; i < count; i++) { 266 textures[i].uploadToTexture(0); 267 } 268 } 269 270 private Allocation loadTexture(int id, String name) { 271 final Allocation allocation = Allocation.createFromBitmapResource(mRS, mResources, 272 id, RGB_565(mRS), false); 273 allocation.setName(name); 274 return allocation; 275 } 276 277 private Allocation loadTextureARGB(int id, String name) { 278 Bitmap b = BitmapFactory.decodeResource(mResources, id, mOptionsARGB); 279 final Allocation allocation = Allocation.createFromBitmap(mRS, b, RGBA_8888(mRS), false); 280 allocation.setName(name); 281 return allocation; 282 } 283 284 private void createProgramFragment() { 285 Sampler.Builder sampleBuilder = new Sampler.Builder(mRS); 286 sampleBuilder.setMin(LINEAR); 287 sampleBuilder.setMag(LINEAR); 288 sampleBuilder.setWrapS(CLAMP); 289 sampleBuilder.setWrapT(CLAMP); 290 mSampler = sampleBuilder.create(); 291 292 ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS); 293 builder.setTexture(ProgramFragment.Builder.EnvMode.REPLACE, 294 ProgramFragment.Builder.Format.RGBA, 0); 295 mPfBackground = builder.create(); 296 mPfBackground.setName("PFBackground"); 297 mPfBackground.bindSampler(mSampler, 0); 298 299 builder = new ProgramFragment.Builder(mRS); 300 builder.setTexture(ProgramFragment.Builder.EnvMode.MODULATE, 301 ProgramFragment.Builder.Format.RGBA, 0); 302 mPfSky = builder.create(); 303 mPfSky.setName("PFSky"); 304 mPfSky.bindSampler(mSampler, 0); 305 } 306 307 private void createProgramFragmentStore() { 308 ProgramStore.Builder builder = new ProgramStore.Builder(mRS, null, null); 309 builder.setDepthFunc(ALWAYS); 310 builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE); 311 builder.setDitherEnable(false); 312 builder.setDepthMask(true); 313 mPfsBackground = builder.create(); 314 mPfsBackground.setName("PFSBackground"); 315 316 builder = new ProgramStore.Builder(mRS, null, null); 317 builder.setDepthFunc(ALWAYS); 318 builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA); 319 builder.setDitherEnable(false); 320 builder.setDepthMask(true); 321 mPfsLeaf = builder.create(); 322 mPfsLeaf.setName("PFSLeaf"); 323 } 324 325 private void createProgramVertex() { 326 mPvOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS); 327 mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight); 328 329 ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS, null, null); 330 mPvSky = builder.create(); 331 mPvSky.bindAllocation(mPvOrthoAlloc); 332 mPvSky.setName("PVSky"); 333 334 Element.Builder eb = new Element.Builder(mRS); 335 // Make this an array when we can. 336 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop01"); 337 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop02"); 338 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop03"); 339 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop04"); 340 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop05"); 341 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop06"); 342 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop07"); 343 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop08"); 344 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop09"); 345 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Drop10"); 346 eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 4), "Offset"); 347 eb.add(Element.USER_F32(mRS), "Rotate"); 348 Element e = eb.create(); 349 350 mUniformAlloc = Allocation.createSized(mRS, e, 1); 351 352 ProgramVertex.ShaderBuilder sb = new ProgramVertex.ShaderBuilder(mRS); 353 354 String t = "\n" + 355 "vec2 addDrop(vec4 d, vec2 pos, float dxMul) {\n" + 356 " vec2 ret = vec2(0.0, 0.0);\n" + 357 " vec2 delta = d.xy - pos;\n" + 358 " delta.x *= dxMul;\n" + 359 " float dist = length(delta);\n" + 360 " if (dist < d.w) { \n" + 361 " float amp = d.z * dist;\n" + 362 " amp /= d.w * d.w;\n" + 363 " amp *= sin(d.w - dist);\n" + 364 " ret = delta * amp;\n" + 365 " }\n" + 366 " return ret;\n" + 367 "}\n" + 368 369 "void main() {\n" + 370 " vec2 pos = ATTRIB_position.xy;\n" + 371 " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);\n" + 372 " float dxMul = 1.0;\n" + 373 374 " varTex0 = vec4((pos.x + 1.0), (pos.y + 1.6666), 0.0, 0.0);\n" + 375 376 " if (UNI_Rotate < 0.9) {\n" + 377 " varTex0.xy *= vec2(0.25, 0.33);\n" + 378 " varTex0.x += UNI_Offset.x * 0.5;\n" + 379 " pos.x += UNI_Offset.x * 2.0;\n" + 380 " } else {\n" + 381 " varTex0.xy *= vec2(0.5, 0.3125);\n" + 382 " dxMul = 2.5;\n" + 383 " }\n" + 384 385 " varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" + 386 " pos.xy += vec2(1.0, 1.0);\n" + 387 " pos.xy *= vec2(25.0, 42.0);\n" + 388 389 " varTex0.xy += addDrop(UNI_Drop01, pos, dxMul);\n" + 390 " varTex0.xy += addDrop(UNI_Drop02, pos, dxMul);\n" + 391 " varTex0.xy += addDrop(UNI_Drop03, pos, dxMul);\n" + 392 " varTex0.xy += addDrop(UNI_Drop04, pos, dxMul);\n" + 393 " varTex0.xy += addDrop(UNI_Drop05, pos, dxMul);\n" + 394 " varTex0.xy += addDrop(UNI_Drop06, pos, dxMul);\n" + 395 " varTex0.xy += addDrop(UNI_Drop07, pos, dxMul);\n" + 396 " varTex0.xy += addDrop(UNI_Drop08, pos, dxMul);\n" + 397 " varTex0.xy += addDrop(UNI_Drop09, pos, dxMul);\n" + 398 " varTex0.xy += addDrop(UNI_Drop10, pos, dxMul);\n" + 399 "}\n"; 400 sb.setShader(t); 401 sb.addConstant(mUniformAlloc.getType()); 402 sb.addInput(mMesh.getVertexType(0).getElement()); 403 mPvWater = sb.create(); 404 mPvWater.bindAllocation(mPvOrthoAlloc); 405 mPvWater.setName("PVWater"); 406 mPvWater.bindConstants(mUniformAlloc, 1); 407 408 } 409 410 void addDrop(float x, float y) { 411 mDrop.dropX = (int) ((x / mWidth) * mMeshWidth); 412 mDrop.dropY = (int) ((y / mHeight) * mMeshHeight); 413 mDropState.data(mDrop); 414 } 415 }