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.ScriptC; 21 import android.renderscript.ProgramFragment; 22 import android.renderscript.ProgramStore; 23 import android.renderscript.ProgramVertex; 24 import android.renderscript.Allocation; 25 import android.renderscript.Sampler; 26 import android.renderscript.Light; 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.WRAP; 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.ProgramFragment.EnvMode.*; 36 import static android.renderscript.Element.*; 37 import android.graphics.BitmapFactory; 38 import android.graphics.Bitmap; 39 import static android.util.MathUtils.*; 40 41 import java.util.TimeZone; 42 43 import com.android.wallpaper.R; 44 import com.android.wallpaper.RenderScriptScene; 45 46 class FallRS extends RenderScriptScene { 47 private static final int MESH_RESOLUTION = 48; 48 49 private static final int RSID_STATE = 0; 50 51 private static final int TEXTURES_COUNT = 2; 52 private static final int RSID_TEXTURE_RIVERBED = 0; 53 private static final int RSID_TEXTURE_LEAVES = 1; 54 private static final int RSID_TEXTURE_SKY = 2; 55 56 private static final int RSID_RIPPLE_MAP = 1; 57 private static final int RSID_LEAVES = 3; 58 private static final int RSID_DROP = 4; 59 60 private static final int LEAVES_COUNT = 14; 61 62 private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); 63 64 @SuppressWarnings({"FieldCanBeLocal"}) 65 private ProgramFragment mPfBackground; 66 @SuppressWarnings({"FieldCanBeLocal"}) 67 private ProgramFragment mPfSky; 68 @SuppressWarnings({"FieldCanBeLocal"}) 69 private ProgramStore mPfsBackground; 70 @SuppressWarnings({"FieldCanBeLocal"}) 71 private ProgramStore mPfsLeaf; 72 @SuppressWarnings({"FieldCanBeLocal"}) 73 private ProgramVertex mPvSky; 74 private ProgramVertex.MatrixAllocation mPvOrthoAlloc; 75 @SuppressWarnings({"FieldCanBeLocal"}) 76 private Sampler mSampler; 77 78 private Allocation mState; 79 private Allocation mDropState; 80 private DropState mDrop; 81 private Type mStateType; 82 private Type mDropType; 83 private int mMeshWidth; 84 85 private int mMeshHeight; 86 @SuppressWarnings({"FieldCanBeLocal"}) 87 private SimpleMesh mMesh; 88 private WorldState mWorldState; 89 90 private Allocation mRippleMap; 91 92 private Allocation mLeaves; 93 private Type mLeavesType; 94 95 private float mGlHeight; 96 FallRS(int width, int height)97 public FallRS(int width, int height) { 98 super(width, height); 99 100 mOptionsARGB.inScaled = false; 101 mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888; 102 } 103 104 @Override setOffset(float xOffset, float yOffset, int xPixels, int yPixels)105 public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) { 106 mWorldState.xOffset = xOffset; 107 mState.data(mWorldState); 108 } 109 110 @Override onCommand(String action, int x, int y, int z, Bundle extras, boolean resultRequested)111 public Bundle onCommand(String action, int x, int y, int z, Bundle extras, 112 boolean resultRequested) { 113 if ("android.wallpaper.tap".equals(action)) { 114 addDrop(x + (mWorldState.width * mWorldState.xOffset), y); 115 } else if ("android.home.drop".equals(action)) { 116 addDrop(x + (mWorldState.width * mWorldState.xOffset), y); 117 } 118 return null; 119 } 120 121 @Override start()122 public void start() { 123 super.start(); 124 final WorldState worldState = mWorldState; 125 final int width = worldState.width; 126 final int x = width / 4 + (int)(Math.random() * (width / 2)); 127 final int y = worldState.height / 4 + (int)(Math.random() * (worldState.height / 2)); 128 addDrop(x + (width * worldState.xOffset), y); 129 } 130 131 @Override resize(int width, int height)132 public void resize(int width, int height) { 133 super.resize(width, height); 134 135 mWorldState.width = width; 136 mWorldState.height = height; 137 mWorldState.rotate = width > height ? 1 : 0; 138 mState.data(mWorldState); 139 140 mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight); 141 } 142 143 @Override createScript()144 protected ScriptC createScript() { 145 createProgramVertex(); 146 createProgramFragmentStore(); 147 createProgramFragment(); 148 createMesh(); 149 createScriptStructures(); 150 loadTextures(); 151 152 ScriptC.Builder sb = new ScriptC.Builder(mRS); 153 sb.setType(mStateType, "State", RSID_STATE); 154 sb.setType(mDropType, "Drop", RSID_DROP); 155 sb.setType(mLeavesType, "Leaves", RSID_LEAVES); 156 sb.setScript(mResources, R.raw.fall); 157 Script.Invokable invokable = sb.addInvokable("initLeaves"); 158 sb.setRoot(true); 159 160 ScriptC script = sb.create(); 161 script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f); 162 script.setTimeZone(TimeZone.getDefault().getID()); 163 164 script.bindAllocation(mState, RSID_STATE); 165 script.bindAllocation(mRippleMap, RSID_RIPPLE_MAP); 166 script.bindAllocation(mLeaves, RSID_LEAVES); 167 script.bindAllocation(mDropState, RSID_DROP); 168 169 invokable.execute(); 170 171 return script; 172 } 173 createMesh()174 private void createMesh() { 175 SimpleMesh.TriangleMeshBuilder tmb = new SimpleMesh.TriangleMeshBuilder(mRS, 3, 176 SimpleMesh.TriangleMeshBuilder.TEXTURE_0); 177 178 final int width = mWidth > mHeight ? mHeight : mWidth; 179 final int height = mWidth > mHeight ? mWidth : mHeight; 180 181 int wResolution = MESH_RESOLUTION; 182 int hResolution = (int) (MESH_RESOLUTION * height / (float) width); 183 184 mGlHeight = 2.0f * height / (float) width; 185 final float glHeight = mGlHeight; 186 187 float quadWidth = 2.0f / (float) wResolution; 188 float quadHeight = glHeight / (float) hResolution; 189 190 wResolution += 2; 191 hResolution += 2; 192 193 for (int y = 0; y <= hResolution; y++) { 194 final float yOffset = y * quadHeight - glHeight / 2.0f - quadHeight; 195 final float t = 1.0f - y / (float) hResolution; 196 for (int x = 0; x <= wResolution; x++) { 197 tmb.setTexture(x / (float) wResolution, t); 198 tmb.addVertex(-1.0f + x * quadWidth - quadWidth, yOffset, 0.0f); 199 } 200 } 201 202 for (int y = 0; y < hResolution; y++) { 203 final boolean shift = (y & 0x1) == 0; 204 final int yOffset = y * (wResolution + 1); 205 for (int x = 0; x < wResolution; x++) { 206 final int index = yOffset + x; 207 final int iWR1 = index + wResolution + 1; 208 if (shift) { 209 tmb.addTriangle(index, index + 1, iWR1); 210 tmb.addTriangle(index + 1, iWR1 + 1, iWR1); 211 } else { 212 tmb.addTriangle(index, iWR1 + 1, iWR1); 213 tmb.addTriangle(index, index + 1, iWR1 + 1); 214 } 215 } 216 } 217 218 mMesh = tmb.create(); 219 mMesh.setName("WaterMesh"); 220 221 mMeshWidth = wResolution + 1; 222 mMeshHeight = hResolution + 1; 223 } 224 createScriptStructures()225 private void createScriptStructures() { 226 final int rippleMapSize = (mMeshWidth + 2) * (mMeshHeight + 2); 227 228 createState(rippleMapSize); 229 createRippleMap(rippleMapSize); 230 createLeaves(); 231 } 232 createLeaves()233 private void createLeaves() { 234 mLeavesType = Type.createFromClass(mRS, Leaf.class, LEAVES_COUNT, "Leaf"); 235 mLeaves = Allocation.createTyped(mRS, mLeavesType); 236 } 237 createRippleMap(int rippleMapSize)238 private void createRippleMap(int rippleMapSize) { 239 final int[] rippleMap = new int[rippleMapSize * 2]; 240 mRippleMap = Allocation.createSized(mRS, USER_I32(mRS), rippleMap.length); 241 mRippleMap.data(rippleMap); 242 } 243 244 static class WorldState { 245 public int frameCount; 246 public int width; 247 public int height; 248 public int meshWidth; 249 public int meshHeight; 250 public int rippleMapSize; 251 public int rippleIndex; 252 public int leavesCount; 253 public float glWidth; 254 public float glHeight; 255 public float skySpeedX; 256 public float skySpeedY; 257 public int rotate; 258 public int isPreview; 259 public float xOffset; 260 } 261 262 static class DropState { 263 public int dropX; 264 public int dropY; 265 } 266 createState(int rippleMapSize)267 private void createState(int rippleMapSize) { 268 mWorldState = new WorldState(); 269 mWorldState.width = mWidth; 270 mWorldState.height = mHeight; 271 mWorldState.meshWidth = mMeshWidth; 272 mWorldState.meshHeight = mMeshHeight; 273 mWorldState.rippleMapSize = rippleMapSize; 274 mWorldState.rippleIndex = 0; 275 mWorldState.leavesCount = LEAVES_COUNT; 276 mWorldState.glWidth = 2.0f; 277 mWorldState.glHeight = mGlHeight; 278 mWorldState.skySpeedX = random(-0.001f, 0.001f); 279 mWorldState.skySpeedY = random(0.00008f, 0.0002f); 280 mWorldState.rotate = mWidth > mHeight ? 1 : 0; 281 mWorldState.isPreview = isPreview() ? 1 : 0; 282 283 mStateType = Type.createFromClass(mRS, WorldState.class, 1, "WorldState"); 284 mState = Allocation.createTyped(mRS, mStateType); 285 mState.data(mWorldState); 286 287 mDrop = new DropState(); 288 mDrop.dropX = -1; 289 mDrop.dropY = -1; 290 291 mDropType = Type.createFromClass(mRS, DropState.class, 1, "DropState"); 292 mDropState = Allocation.createTyped(mRS, mDropType); 293 mDropState.data(mDrop); 294 } 295 296 static class Leaf { 297 public float x; 298 public float y; 299 public float scale; 300 public float angle; 301 public float spin; 302 public float u1; 303 public float u2; 304 public float altitude; 305 public float rippled; 306 public float deltaX; 307 public float deltaY; 308 } 309 loadTextures()310 private void loadTextures() { 311 final Allocation[] textures = new Allocation[TEXTURES_COUNT]; 312 textures[RSID_TEXTURE_RIVERBED] = loadTexture(R.drawable.pond, "TRiverbed"); 313 textures[RSID_TEXTURE_LEAVES] = loadTextureARGB(R.drawable.leaves, "TLeaves"); 314 // textures[RSID_TEXTURE_SKY] = loadTextureARGB(R.drawable.clouds, "TSky"); 315 316 final int count = textures.length; 317 for (int i = 0; i < count; i++) { 318 textures[i].uploadToTexture(0); 319 } 320 } 321 loadTexture(int id, String name)322 private Allocation loadTexture(int id, String name) { 323 final Allocation allocation = Allocation.createFromBitmapResource(mRS, mResources, 324 id, RGB_565(mRS), false); 325 allocation.setName(name); 326 return allocation; 327 } 328 loadTextureARGB(int id, String name)329 private Allocation loadTextureARGB(int id, String name) { 330 Bitmap b = BitmapFactory.decodeResource(mResources, id, mOptionsARGB); 331 final Allocation allocation = Allocation.createFromBitmap(mRS, b, RGBA_8888(mRS), false); 332 allocation.setName(name); 333 return allocation; 334 } 335 createProgramFragment()336 private void createProgramFragment() { 337 Sampler.Builder sampleBuilder = new Sampler.Builder(mRS); 338 sampleBuilder.setMin(LINEAR); 339 sampleBuilder.setMag(LINEAR); 340 sampleBuilder.setWrapS(WRAP); 341 sampleBuilder.setWrapT(WRAP); 342 mSampler = sampleBuilder.create(); 343 344 ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS, null, null); 345 builder.setTexEnable(true, 0); 346 builder.setTexEnvMode(REPLACE, 0); 347 mPfBackground = builder.create(); 348 mPfBackground.setName("PFBackground"); 349 mPfBackground.bindSampler(mSampler, 0); 350 351 builder = new ProgramFragment.Builder(mRS, null, null); 352 builder.setTexEnable(true, 0); 353 builder.setTexEnvMode(MODULATE, 0); 354 mPfSky = builder.create(); 355 mPfSky.setName("PFSky"); 356 mPfSky.bindSampler(mSampler, 0); 357 } 358 createProgramFragmentStore()359 private void createProgramFragmentStore() { 360 ProgramStore.Builder builder = new ProgramStore.Builder(mRS, null, null); 361 builder.setDepthFunc(ALWAYS); 362 builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE); 363 builder.setDitherEnable(false); 364 builder.setDepthMask(true); 365 mPfsBackground = builder.create(); 366 mPfsBackground.setName("PFSBackground"); 367 368 builder = new ProgramStore.Builder(mRS, null, null); 369 builder.setDepthFunc(ALWAYS); 370 builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA); 371 builder.setDitherEnable(false); 372 builder.setDepthMask(true); 373 mPfsLeaf = builder.create(); 374 mPfsLeaf.setName("PFSLeaf"); 375 } 376 createProgramVertex()377 private void createProgramVertex() { 378 mPvOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS); 379 mPvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight); 380 381 ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS, null, null); 382 builder.setTextureMatrixEnable(true); 383 mPvSky = builder.create(); 384 mPvSky.bindAllocation(mPvOrthoAlloc); 385 mPvSky.setName("PVSky"); 386 } 387 addDrop(float x, float y)388 void addDrop(float x, float y) { 389 if (mWorldState.rotate == 0) { 390 mDrop.dropX = (int) ((x / mWidth) * mMeshWidth); 391 mDrop.dropY = (int) ((y / mHeight) * mMeshHeight); 392 } else { 393 mDrop.dropY = (int) ((x / mWidth) * mMeshHeight); 394 mDrop.dropX = mMeshWidth - (int) ((y / mHeight) * mMeshWidth); 395 } 396 mDropState.data(mDrop); 397 } 398 }