Home | History | Annotate | Download | only in grass
      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.grass;
     18 
     19 import android.renderscript.Sampler;
     20 import static android.renderscript.ProgramStore.DepthFunc.*;
     21 import static android.renderscript.ProgramStore.BlendSrcFunc;
     22 import static android.renderscript.ProgramStore.BlendDstFunc;
     23 import android.renderscript.*;
     24 import static android.renderscript.Element.*;
     25 import static android.util.MathUtils.*;
     26 import android.renderscript.Mesh.Primitive;
     27 import static android.renderscript.Sampler.Value.*;
     28 import android.content.Context;
     29 import android.content.IntentFilter;
     30 import android.content.Intent;
     31 import android.content.BroadcastReceiver;
     32 import android.location.LocationManager;
     33 import android.location.LocationListener;
     34 import android.location.Location;
     35 import android.os.Bundle;
     36 import android.text.format.Time;
     37 import com.android.wallpaper.R;
     38 import com.android.wallpaper.RenderScriptScene;
     39 
     40 import java.util.TimeZone;
     41 import java.util.Calendar;
     42 
     43 class GrassRS extends RenderScriptScene {
     44     @SuppressWarnings({"UnusedDeclaration"})
     45     private static final String LOG_TAG = "Grass";
     46     private static final boolean DEBUG = false;
     47 
     48     private static final int LOCATION_UPDATE_MIN_TIME = DEBUG ? 5 * 60 * 1000 : 60 * 60 * 1000; // 1 hour
     49     private static final int LOCATION_UPDATE_MIN_DISTANCE = DEBUG ? 10 : 150 * 1000; // 150 km
     50     private static final float TESSELATION = 0.5f;
     51     private static final int TEXTURES_COUNT = 5;
     52     private static final int BLADES_COUNT = 200;
     53 
     54     private ScriptField_Blade mBlades;
     55     private ScriptField_Vertex mVertexBuffer;
     56     private ProgramVertexFixedFunction.Constants mPvOrthoAlloc;
     57 
     58     //private Allocation mBladesBuffer;
     59     private Allocation mBladesIndicies;
     60     private Mesh mBladesMesh;
     61 
     62     private ScriptC_grass mScript;
     63 
     64     private int mVerticies;
     65     private int mIndicies;
     66     private int[] mBladeSizes;
     67 
     68     private final Context mContext;
     69     private final LocationManager mLocationManager;
     70 
     71     private LocationUpdater mLocationUpdater;
     72     private GrassRS.TimezoneTracker mTimezoneTracker;
     73 
     74     GrassRS(Context context, int width, int height) {
     75         super(width, height);
     76 
     77         mContext = context;
     78         mLocationManager = (LocationManager)
     79                 context.getSystemService(Context.LOCATION_SERVICE);
     80     }
     81 
     82     @Override
     83     public void start() {
     84         super.start();
     85 
     86         if (mTimezoneTracker == null) {
     87             mTimezoneTracker = new TimezoneTracker();
     88             IntentFilter filter = new IntentFilter();
     89             filter.addAction(Intent.ACTION_DATE_CHANGED);
     90             filter.addAction(Intent.ACTION_TIME_CHANGED);
     91             filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
     92 
     93             mContext.registerReceiver(mTimezoneTracker, filter);
     94         }
     95 
     96         if (mLocationUpdater == null) {
     97             mLocationUpdater = new LocationUpdater();
     98             try {
     99               mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
    100                       LOCATION_UPDATE_MIN_TIME, LOCATION_UPDATE_MIN_DISTANCE, mLocationUpdater);
    101             } catch (java.lang.IllegalArgumentException e) {
    102               if (!e.getMessage().equals("provider=network")) {
    103                 throw e;
    104               }
    105             }
    106         }
    107 
    108         updateLocation();
    109     }
    110 
    111     @Override
    112     public void stop() {
    113         super.stop();
    114 
    115         if (mTimezoneTracker != null) {
    116             mContext.unregisterReceiver(mTimezoneTracker);
    117             mTimezoneTracker = null;
    118         }
    119 
    120         if (mLocationUpdater != null) {
    121             mLocationManager.removeUpdates(mLocationUpdater);
    122             mLocationUpdater = null;
    123         }
    124     }
    125 
    126     @Override
    127     public void resize(int width, int height) {
    128         super.resize(width, height);
    129 
    130         mScript.set_gWidth(width);
    131         mScript.set_gHeight(height);
    132         mScript.invoke_updateBlades();
    133         Matrix4f proj = new Matrix4f();
    134         proj.loadOrthoWindow(width, height);
    135         mPvOrthoAlloc.setProjection(proj);
    136     }
    137 
    138     @Override
    139     protected ScriptC createScript() {
    140         mScript = new ScriptC_grass(mRS, mResources, R.raw.grass);
    141 
    142         final boolean isPreview = isPreview();
    143         createProgramVertex();
    144         createProgramFragmentStore();
    145         loadTextures();
    146         createProgramFragment();
    147         createBlades();
    148 
    149         mScript.set_gBladesCount(BLADES_COUNT);
    150         mScript.set_gIndexCount(mIndicies);
    151         mScript.set_gWidth(mWidth);
    152         mScript.set_gHeight(mHeight);
    153         mScript.set_gXOffset(isPreview ? 0.5f : 0.f);
    154         mScript.set_gIsPreview(isPreview ? 1 : 0);
    155         mScript.set_gBladesMesh(mBladesMesh);
    156 
    157         mScript.setTimeZone(TimeZone.getDefault().getID());
    158         mScript.bind_Blades(mBlades);
    159         mScript.bind_Verticies(mVertexBuffer);
    160 
    161         // set these to reasonable defaults.
    162         mScript.set_gDawn(6.f / 24.f);
    163         mScript.set_gDusk(18.f / 24.f);
    164         mScript.set_gMorning(8.f / 24.f); // 2 hours for sunrise
    165         mScript.set_gAfternoon(16.f / 24.f); // 2 hours for sunset
    166 
    167         return mScript;
    168     }
    169 
    170     @Override
    171     public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
    172         mScript.set_gXOffset(xOffset);
    173     }
    174 
    175     private void createBlades() {
    176         mVerticies = 0;
    177         mIndicies = 0;
    178 
    179         mBlades = new ScriptField_Blade(mRS, BLADES_COUNT);
    180 
    181         mBladeSizes = new int[BLADES_COUNT];
    182         for (int i = 0; i < BLADES_COUNT; i++) {
    183             ScriptField_Blade.Item item = new ScriptField_Blade.Item();
    184             createBlade(item);
    185             mBlades.set(item, i, false);
    186 
    187             mIndicies += item.size * 2 * 3;
    188             mVerticies += item.size + 2;
    189             mBladeSizes[i] = item.size;
    190         }
    191         mBlades.copyAll();
    192 
    193         createMesh();
    194     }
    195 
    196     private void createMesh() {
    197         mVertexBuffer = new ScriptField_Vertex(mRS, mVerticies * 2);
    198 
    199         final Mesh.AllocationBuilder meshBuilder = new Mesh.AllocationBuilder(mRS);
    200         meshBuilder.addVertexAllocation(mVertexBuffer.getAllocation());
    201 
    202         mBladesIndicies = Allocation.createSized(mRS, Element.U16(mRS), mIndicies);
    203         meshBuilder.addIndexSetAllocation(mBladesIndicies, Primitive.TRIANGLE);
    204 
    205         mBladesMesh = meshBuilder.create();
    206 
    207         short[] idx = new short[mIndicies];
    208         int idxIdx = 0;
    209         int vtxIdx = 0;
    210         for (int i = 0; i < mBladeSizes.length; i++) {
    211             for (int ct = 0; ct < mBladeSizes[i]; ct ++) {
    212                 idx[idxIdx + 0] = (short)(vtxIdx + 0);
    213                 idx[idxIdx + 1] = (short)(vtxIdx + 1);
    214                 idx[idxIdx + 2] = (short)(vtxIdx + 2);
    215                 idx[idxIdx + 3] = (short)(vtxIdx + 1);
    216                 idx[idxIdx + 4] = (short)(vtxIdx + 3);
    217                 idx[idxIdx + 5] = (short)(vtxIdx + 2);
    218                 idxIdx += 6;
    219                 vtxIdx += 2;
    220             }
    221             vtxIdx += 2;
    222         }
    223 
    224         mBladesIndicies.copyFrom(idx);
    225     }
    226 
    227     private void createBlade(ScriptField_Blade.Item blades) {
    228         final float size = random(4.0f) + 4.0f;
    229         final int xpos = random(-mWidth, mWidth);
    230 
    231         //noinspection PointlessArithmeticExpression
    232         blades.angle = 0.0f;
    233         blades.size = (int)(size / TESSELATION);
    234         blades.xPos = xpos;
    235         blades.yPos = mHeight;
    236         blades.offset = random(0.2f) - 0.1f;
    237         blades.scale = 4.0f / (size / TESSELATION) + (random(0.6f) + 0.2f) * TESSELATION;
    238         blades.lengthX = (random(4.5f) + 3.0f) * TESSELATION * size;
    239         blades.lengthY = (random(5.5f) + 2.0f) * TESSELATION * size;
    240         blades.hardness = (random(1.0f) + 0.2f) * TESSELATION;
    241         blades.h = random(0.02f) + 0.2f;
    242         blades.s = random(0.22f) + 0.78f;
    243         blades.b = random(0.65f) + 0.35f;
    244         blades.turbulencex = xpos * 0.006f;
    245     }
    246 
    247     private void loadTextures() {
    248         mScript.set_gTNight(loadTexture(R.drawable.night));
    249         mScript.set_gTSunrise(loadTexture(R.drawable.sunrise));
    250         mScript.set_gTSky(loadTexture(R.drawable.sky));
    251         mScript.set_gTSunset(loadTexture(R.drawable.sunset));
    252         mScript.set_gTAa(generateTextureAlpha());
    253     }
    254 
    255     private Allocation generateTextureAlpha() {
    256         final Type.Builder builder = new Type.Builder(mRS, A_8(mRS));
    257         builder.setX(4);
    258         builder.setY(1);
    259         builder.setMipmaps(true);
    260 
    261         final Allocation allocation = Allocation.createTyped(mRS, builder.create(),
    262                                                              Allocation.USAGE_GRAPHICS_TEXTURE);
    263         byte[] mip0 = new byte[] {0, -1, -1, 0};
    264         byte[] mip1 = new byte[] {64, 64};
    265         byte[] mip2 = new byte[] {0};
    266 
    267         AllocationAdapter a = AllocationAdapter.create2D(mRS, allocation);
    268         a.setLOD(0);
    269         a.copyFrom(mip0);
    270         a.setLOD(1);
    271         a.copyFrom(mip1);
    272         a.setLOD(2);
    273         a.copyFrom(mip2);
    274 
    275         return allocation;
    276     }
    277 
    278     private Allocation loadTexture(int id) {
    279         return Allocation.createFromBitmapResource(mRS, mResources, id);
    280     }
    281 
    282     private void createProgramFragment() {
    283         Sampler.Builder samplerBuilder = new Sampler.Builder(mRS);
    284         samplerBuilder.setMinification(LINEAR_MIP_LINEAR);
    285         samplerBuilder.setMagnification(LINEAR);
    286         samplerBuilder.setWrapS(WRAP);
    287         samplerBuilder.setWrapT(WRAP);
    288         Sampler sl = samplerBuilder.create();
    289 
    290         samplerBuilder.setMinification(NEAREST);
    291         samplerBuilder.setMagnification(NEAREST);
    292         Sampler sn = samplerBuilder.create();
    293 
    294         ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
    295         builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
    296                            ProgramFragmentFixedFunction.Builder.Format.ALPHA, 0);
    297         builder.setVaryingColor(true);
    298         ProgramFragment pf = builder.create();
    299         mScript.set_gPFGrass(pf);
    300         pf.bindSampler(sl, 0);
    301 
    302         builder = new ProgramFragmentFixedFunction.Builder(mRS);
    303         builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
    304                            ProgramFragmentFixedFunction.Builder.Format.RGB, 0);
    305         pf = builder.create();
    306         mScript.set_gPFBackground(pf);
    307         pf.bindSampler(sn, 0);
    308     }
    309 
    310     private void createProgramFragmentStore() {
    311         ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
    312         builder.setDepthFunc(ALWAYS);
    313         builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
    314         builder.setDitherEnabled(false);
    315         builder.setDepthMaskEnabled(false);
    316         mScript.set_gPSBackground(builder.create());
    317     }
    318 
    319     private void createProgramVertex() {
    320         mPvOrthoAlloc = new ProgramVertexFixedFunction.Constants(mRS);
    321         Matrix4f proj = new Matrix4f();
    322         proj.loadOrthoWindow(mWidth, mHeight);
    323         mPvOrthoAlloc.setProjection(proj);
    324 
    325         ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
    326         ProgramVertex pv = pvb.create();
    327         ((ProgramVertexFixedFunction)pv).bindConstants(mPvOrthoAlloc);
    328         mScript.set_gPVBackground(pv);
    329     }
    330 
    331     private void updateLocation() {
    332         updateLocation(mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    333     }
    334 
    335     private void updateLocation(Location location) {
    336         float dawn = 0.3f;
    337         float dusk = 0.75f;
    338 
    339         if (location != null) {
    340             final String timeZone = Time.getCurrentTimezone();
    341             final SunCalculator calculator = new SunCalculator(location, timeZone);
    342             final Calendar now = Calendar.getInstance();
    343 
    344             final double sunrise = calculator.computeSunriseTime(SunCalculator.ZENITH_CIVIL, now);
    345             dawn = SunCalculator.timeToDayFraction(sunrise);
    346 
    347             final double sunset = calculator.computeSunsetTime(SunCalculator.ZENITH_CIVIL, now);
    348             dusk = SunCalculator.timeToDayFraction(sunset);
    349         }
    350 
    351         mScript.set_gDawn(dawn);
    352         mScript.set_gDusk(dusk);
    353         mScript.set_gMorning(dawn + 1.0f / 12.0f); // 2 hours for sunrise
    354         mScript.set_gAfternoon(dusk - 1.0f / 12.0f); // 2 hours for sunset
    355     }
    356 
    357     private class LocationUpdater implements LocationListener {
    358         public void onLocationChanged(Location location) {
    359             updateLocation(location);
    360         }
    361 
    362         public void onStatusChanged(String provider, int status, Bundle extras) {
    363         }
    364 
    365         public void onProviderEnabled(String provider) {
    366         }
    367 
    368         public void onProviderDisabled(String provider) {
    369         }
    370     }
    371 
    372     private class TimezoneTracker extends BroadcastReceiver {
    373         public void onReceive(Context context, Intent intent) {
    374             getScript().setTimeZone(Time.getCurrentTimezone());
    375             updateLocation();
    376         }
    377     }
    378 }
    379