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.*; 21 import static android.renderscript.Sampler.Value.LINEAR; 22 import static android.renderscript.Sampler.Value.CLAMP; 23 import static android.renderscript.ProgramStore.DepthFunc.*; 24 import static android.renderscript.ProgramStore.BlendDstFunc; 25 import static android.renderscript.ProgramStore.BlendSrcFunc; 26 import static android.renderscript.Element.*; 27 28 import android.util.Log; 29 30 import android.app.WallpaperManager; 31 import android.graphics.BitmapFactory; 32 import android.graphics.Bitmap; 33 import static android.util.MathUtils.*; 34 35 import java.util.TimeZone; 36 37 import com.android.wallpaper.R; 38 import com.android.wallpaper.RenderScriptScene; 39 40 class 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