| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.wallpaper.fall; |
| |
| import android.os.Bundle; |
| import android.renderscript.*; |
| import static android.renderscript.Sampler.Value.LINEAR; |
| import static android.renderscript.Sampler.Value.CLAMP; |
| import static android.renderscript.ProgramStore.DepthFunc.*; |
| import static android.renderscript.ProgramStore.BlendDstFunc; |
| import static android.renderscript.ProgramStore.BlendSrcFunc; |
| import static android.renderscript.Element.*; |
| |
| import android.util.Log; |
| |
| import android.app.WallpaperManager; |
| import android.graphics.BitmapFactory; |
| import android.graphics.Bitmap; |
| import static android.util.MathUtils.*; |
| |
| import java.util.TimeZone; |
| |
| import com.android.wallpaper.R; |
| import com.android.wallpaper.RenderScriptScene; |
| |
| class FallRS extends RenderScriptScene { |
| private static final int MESH_RESOLUTION = 48; |
| |
| private static final int RSID_STATE = 0; |
| private static final int RSID_CONSTANTS = 1; |
| private static final int RSID_DROP = 2; |
| |
| private static final int TEXTURES_COUNT = 2; |
| private static final int RSID_TEXTURE_RIVERBED = 0; |
| private static final int RSID_TEXTURE_LEAVES = 1; |
| |
| private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); |
| |
| @SuppressWarnings({"FieldCanBeLocal"}) |
| private ProgramFragment mPfBackground; |
| @SuppressWarnings({"FieldCanBeLocal"}) |
| private ProgramFragment mPfSky; |
| @SuppressWarnings({"FieldCanBeLocal"}) |
| private ProgramStore mPfsBackground; |
| @SuppressWarnings({"FieldCanBeLocal"}) |
| private ProgramStore mPfsLeaf; |
| @SuppressWarnings({"FieldCanBeLocal"}) |
| private ProgramVertex mPvSky; |
| @SuppressWarnings({"FieldCanBeLocal"}) |
| private ProgramVertex mPvWater; |
| private ProgramVertexFixedFunction.Constants mPvOrthoAlloc; |
| @SuppressWarnings({"FieldCanBeLocal"}) |
| private Sampler mSampler; |
| |
| private int mMeshWidth; |
| private Allocation mUniformAlloc; |
| |
| private int mMeshHeight; |
| @SuppressWarnings({"FieldCanBeLocal"}) |
| private Mesh mMesh; |
| private WorldState mWorldState; |
| |
| private ScriptC_fall mScript; |
| |
| private ScriptField_Constants mConstants; |
| |
| private float mGlHeight; |
| |
| public FallRS(int width, int height) { |
| super(width, height); |
| |
| mOptionsARGB.inScaled = false; |
| mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888; |
| } |
| |
| @Override |
| public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) { |
| mWorldState.xOffset = xOffset; |
| mScript.set_g_xOffset(mWorldState.xOffset); |
| } |
| |
| @Override |
| public Bundle onCommand(String action, int x, int y, int z, Bundle extras, |
| boolean resultRequested) { |
| if (WallpaperManager.COMMAND_TAP.equals(action) |
| || WallpaperManager.COMMAND_SECONDARY_TAP.equals(action) |
| || WallpaperManager.COMMAND_DROP.equals(action)) { |
| addDrop(x + (mWorldState.rotate == 0 ? (mWorldState.width * mWorldState.xOffset) : 0), y); |
| } |
| return null; |
| } |
| |
| @Override |
| public void start() { |
| super.start(); |
| final WorldState worldState = mWorldState; |
| final int width = worldState.width; |
| final int x = width / 4 + (int)(Math.random() * (width / 2)); |
| final int y = worldState.height / 4 + (int)(Math.random() * (worldState.height / 2)); |
| addDrop(x + (mWorldState.rotate == 0 ? (width * worldState.xOffset) : 0), y); |
| } |
| |
| @Override |
| public void resize(int width, int height) { |
| super.resize(width, height); |
| |
| mWorldState.width = width; |
| mWorldState.height = height; |
| mWorldState.rotate = width > height ? 1 : 0; |
| |
| mScript.set_g_glWidth(mWorldState.width); |
| mScript.set_g_glHeight(mWorldState.height); |
| mScript.set_g_rotate(mWorldState.rotate); |
| |
| mScript.invoke_initLeaves(); |
| |
| Matrix4f proj = new Matrix4f(); |
| proj.loadProjectionNormalized(mWidth, mHeight); |
| mPvOrthoAlloc.setProjection(proj); |
| } |
| |
| @Override |
| protected ScriptC createScript() { |
| mScript = new ScriptC_fall(mRS, mResources, R.raw.fall); |
| |
| createMesh(); |
| createState(); |
| createProgramVertex(); |
| createProgramFragmentStore(); |
| createProgramFragment(); |
| loadTextures(); |
| |
| mScript.setTimeZone(TimeZone.getDefault().getID()); |
| |
| mScript.bind_g_Constants(mConstants); |
| |
| return mScript; |
| } |
| |
| private void createMesh() { |
| Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, 2, 0); |
| |
| final int width = mWidth > mHeight ? mHeight : mWidth; |
| final int height = mWidth > mHeight ? mWidth : mHeight; |
| |
| int wResolution = MESH_RESOLUTION; |
| int hResolution = (int) (MESH_RESOLUTION * height / (float) width); |
| |
| mGlHeight = 2.0f * height / (float) width; |
| |
| wResolution += 2; |
| hResolution += 2; |
| |
| for (int y = 0; y <= hResolution; y++) { |
| final float yOffset = (((float)y / hResolution) * 2.f - 1.f) * height / width; |
| for (int x = 0; x <= wResolution; x++) { |
| tmb.addVertex(((float)x / wResolution) * 2.f - 1.f, yOffset); |
| } |
| } |
| |
| for (int y = 0; y < hResolution; y++) { |
| final boolean shift = (y & 0x1) == 0; |
| final int yOffset = y * (wResolution + 1); |
| for (int x = 0; x < wResolution; x++) { |
| final int index = yOffset + x; |
| final int iWR1 = index + wResolution + 1; |
| if (shift) { |
| tmb.addTriangle(index, index + 1, iWR1); |
| tmb.addTriangle(index + 1, iWR1 + 1, iWR1); |
| } else { |
| tmb.addTriangle(index, iWR1 + 1, iWR1); |
| tmb.addTriangle(index, index + 1, iWR1 + 1); |
| } |
| } |
| } |
| |
| mMesh = tmb.create(true); |
| |
| mMeshWidth = wResolution + 1; |
| mMeshHeight = hResolution + 1; |
| |
| mScript.set_g_WaterMesh(mMesh); |
| } |
| |
| static class WorldState { |
| public int frameCount; |
| public int width; |
| public int height; |
| public int meshWidth; |
| public int meshHeight; |
| public int rippleIndex; |
| public float glWidth; |
| public float glHeight; |
| public float skySpeedX; |
| public float skySpeedY; |
| public int rotate; |
| public int isPreview; |
| public float xOffset; |
| } |
| |
| private void createState() { |
| mWorldState = new WorldState(); |
| mWorldState.width = mWidth; |
| mWorldState.height = mHeight; |
| mWorldState.meshWidth = mMeshWidth; |
| mWorldState.meshHeight = mMeshHeight; |
| mWorldState.rippleIndex = 0; |
| mWorldState.glWidth = 2.0f; |
| mWorldState.glHeight = mGlHeight; |
| mWorldState.skySpeedX = random(-0.001f, 0.001f); |
| mWorldState.skySpeedY = random(0.00008f, 0.0002f); |
| mWorldState.rotate = mWidth > mHeight ? 1 : 0; |
| mWorldState.isPreview = isPreview() ? 1 : 0; |
| |
| mScript.set_g_glWidth(mWorldState.glWidth); |
| mScript.set_g_glHeight(mWorldState.glHeight); |
| mScript.set_g_meshWidth(mWorldState.meshWidth); |
| mScript.set_g_meshHeight(mWorldState.meshHeight); |
| mScript.set_g_xOffset(0); |
| mScript.set_g_rotate(mWorldState.rotate); |
| } |
| |
| private void loadTextures() { |
| mScript.set_g_TLeaves(loadTextureARGB(R.drawable.leaves)); |
| mScript.set_g_TRiverbed(loadTexture(R.drawable.pond)); |
| } |
| |
| private Allocation loadTexture(int id) { |
| final Allocation allocation = Allocation.createFromBitmapResource(mRS, mResources, id); |
| return allocation; |
| } |
| |
| private Allocation loadTextureARGB(int id) { |
| Bitmap b = BitmapFactory.decodeResource(mResources, id, mOptionsARGB); |
| final Allocation allocation = Allocation.createFromBitmap(mRS, b); |
| return allocation; |
| } |
| |
| private void createProgramFragment() { |
| Sampler.Builder sampleBuilder = new Sampler.Builder(mRS); |
| sampleBuilder.setMinification(LINEAR); |
| sampleBuilder.setMagnification(LINEAR); |
| sampleBuilder.setWrapS(CLAMP); |
| sampleBuilder.setWrapT(CLAMP); |
| mSampler = sampleBuilder.create(); |
| |
| ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS); |
| builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, |
| ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); |
| mPfBackground = builder.create(); |
| mPfBackground.bindSampler(mSampler, 0); |
| |
| mScript.set_g_PFBackground(mPfBackground); |
| |
| builder = new ProgramFragmentFixedFunction.Builder(mRS); |
| builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE, |
| ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); |
| mPfSky = builder.create(); |
| mPfSky.bindSampler(mSampler, 0); |
| |
| mScript.set_g_PFSky(mPfSky); |
| } |
| |
| private void createProgramFragmentStore() { |
| ProgramStore.Builder builder = new ProgramStore.Builder(mRS); |
| builder.setDepthFunc(ALWAYS); |
| builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE); |
| builder.setDitherEnabled(false); |
| builder.setDepthMaskEnabled(true); |
| mPfsBackground = builder.create(); |
| |
| builder = new ProgramStore.Builder(mRS); |
| builder.setDepthFunc(ALWAYS); |
| builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA); |
| builder.setDitherEnabled(false); |
| builder.setDepthMaskEnabled(true); |
| mPfsLeaf = builder.create(); |
| |
| mScript.set_g_PFSLeaf(mPfsLeaf); |
| mScript.set_g_PFSBackground(mPfsBackground); |
| } |
| |
| private void createProgramVertex() { |
| mPvOrthoAlloc = new ProgramVertexFixedFunction.Constants(mRS); |
| Matrix4f proj = new Matrix4f(); |
| proj.loadProjectionNormalized(mWidth, mHeight); |
| mPvOrthoAlloc.setProjection(proj); |
| |
| |
| ProgramVertexFixedFunction.Builder builder = new ProgramVertexFixedFunction.Builder(mRS); |
| mPvSky = builder.create(); |
| ((ProgramVertexFixedFunction)mPvSky).bindConstants(mPvOrthoAlloc); |
| |
| mScript.set_g_PVSky(mPvSky); |
| |
| mConstants = new ScriptField_Constants(mRS, 1); |
| mUniformAlloc = mConstants.getAllocation(); |
| |
| ProgramVertex.Builder sb = new ProgramVertex.Builder(mRS); |
| |
| String t = "\n" + |
| "varying vec4 varColor;\n" + |
| "varying vec2 varTex0;\n" + |
| |
| "vec2 addDrop(vec4 d, vec2 pos, float dxMul) {\n" + |
| " vec2 ret = vec2(0.0, 0.0);\n" + |
| " vec2 delta = d.xy - pos;\n" + |
| " delta.x *= dxMul;\n" + |
| " float dist = length(delta);\n" + |
| " if (dist < d.w) { \n" + |
| " float amp = d.z * dist;\n" + |
| " amp /= d.w * d.w;\n" + |
| " amp *= sin(d.w - dist);\n" + |
| " ret = delta * amp;\n" + |
| " }\n" + |
| " return ret;\n" + |
| "}\n" + |
| |
| "void main() {\n" + |
| " vec2 pos = ATTRIB_position.xy;\n" + |
| " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);\n" + |
| " float dxMul = 1.0;\n" + |
| |
| " varTex0 = vec2((pos.x + 1.0), (pos.y + 1.6666));\n" + |
| |
| " if (UNI_Rotate < 0.9) {\n" + |
| " varTex0.xy *= vec2(0.25, 0.33);\n" + |
| " varTex0.x += UNI_Offset.x * 0.5;\n" + |
| " pos.x += UNI_Offset.x * 2.0;\n" + |
| " } else {\n" + |
| " varTex0.xy *= vec2(0.5, 0.3125);\n" + |
| " dxMul = 2.5;\n" + |
| " }\n" + |
| |
| " varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" + |
| " pos.xy += vec2(1.0, 1.0);\n" + |
| " pos.xy *= vec2(25.0, 42.0);\n" + |
| |
| " varTex0.xy += addDrop(UNI_Drop01, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop02, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop03, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop04, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop05, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop06, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop07, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop08, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop09, pos, dxMul);\n" + |
| " varTex0.xy += addDrop(UNI_Drop10, pos, dxMul);\n" + |
| "}\n"; |
| |
| sb.setShader(t); |
| sb.addConstant(mUniformAlloc.getType()); |
| sb.addInput(mMesh.getVertexAllocation(0).getType().getElement()); |
| mPvWater = sb.create(); |
| mPvWater.bindConstants(mUniformAlloc, 0); |
| |
| mScript.set_g_PVWater(mPvWater); |
| |
| } |
| |
| void addDrop(float x, float y) { |
| int dropX = (int) ((x / mWidth) * mMeshWidth); |
| int dropY = (int) ((y / mHeight) * mMeshHeight); |
| |
| mScript.invoke_addDrop(dropX, dropY); |
| } |
| } |