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                                            Allocation.MipmapControl.MIPMAP_NONE,
    281                                            Allocation.USAGE_GRAPHICS_TEXTURE);
    282     }
    283 
    284     private void createProgramFragment() {
    285         Sampler.Builder samplerBuilder = new Sampler.Builder(mRS);
    286         samplerBuilder.setMinification(LINEAR_MIP_LINEAR);
    287         samplerBuilder.setMagnification(LINEAR);
    288         samplerBuilder.setWrapS(WRAP);
    289         samplerBuilder.setWrapT(WRAP);
    290         Sampler sl = samplerBuilder.create();
    291 
    292         samplerBuilder.setMinification(NEAREST);
    293         samplerBuilder.setMagnification(NEAREST);
    294         Sampler sn = samplerBuilder.create();
    295 
    296         ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
    297         builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
    298                            ProgramFragmentFixedFunction.Builder.Format.ALPHA, 0);
    299         builder.setVaryingColor(true);
    300         ProgramFragment pf = builder.create();
    301         mScript.set_gPFGrass(pf);
    302         pf.bindSampler(sl, 0);
    303 
    304         builder = new ProgramFragmentFixedFunction.Builder(mRS);
    305         builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
    306                            ProgramFragmentFixedFunction.Builder.Format.RGB, 0);
    307         pf = builder.create();
    308         mScript.set_gPFBackground(pf);
    309         pf.bindSampler(sn, 0);
    310     }
    311 
    312     private void createProgramFragmentStore() {
    313         ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
    314         builder.setDepthFunc(ALWAYS);
    315         builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
    316         builder.setDitherEnabled(false);
    317         builder.setDepthMaskEnabled(false);
    318         mScript.set_gPSBackground(builder.create());
    319     }
    320 
    321     private void createProgramVertex() {
    322         mPvOrthoAlloc = new ProgramVertexFixedFunction.Constants(mRS);
    323         Matrix4f proj = new Matrix4f();
    324         proj.loadOrthoWindow(mWidth, mHeight);
    325         mPvOrthoAlloc.setProjection(proj);
    326 
    327         ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
    328         ProgramVertex pv = pvb.create();
    329         ((ProgramVertexFixedFunction)pv).bindConstants(mPvOrthoAlloc);
    330         mScript.set_gPVBackground(pv);
    331     }
    332 
    333     private void updateLocation() {
    334         updateLocation(mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    335     }
    336 
    337     private void updateLocation(Location location) {
    338         float dawn = 0.3f;
    339         float dusk = 0.75f;
    340 
    341         if (location != null) {
    342             final String timeZone = Time.getCurrentTimezone();
    343             final SunCalculator calculator = new SunCalculator(location, timeZone);
    344             final Calendar now = Calendar.getInstance();
    345 
    346             final double sunrise = calculator.computeSunriseTime(SunCalculator.ZENITH_CIVIL, now);
    347             dawn = SunCalculator.timeToDayFraction(sunrise);
    348 
    349             final double sunset = calculator.computeSunsetTime(SunCalculator.ZENITH_CIVIL, now);
    350             dusk = SunCalculator.timeToDayFraction(sunset);
    351         }
    352 
    353         mScript.set_gDawn(dawn);
    354         mScript.set_gDusk(dusk);
    355         mScript.set_gMorning(dawn + 1.0f / 12.0f); // 2 hours for sunrise
    356         mScript.set_gAfternoon(dusk - 1.0f / 12.0f); // 2 hours for sunset
    357     }
    358 
    359     private class LocationUpdater implements LocationListener {
    360         public void onLocationChanged(Location location) {
    361             updateLocation(location);
    362         }
    363 
    364         public void onStatusChanged(String provider, int status, Bundle extras) {
    365         }
    366 
    367         public void onProviderEnabled(String provider) {
    368         }
    369 
    370         public void onProviderDisabled(String provider) {
    371         }
    372     }
    373 
    374     private class TimezoneTracker extends BroadcastReceiver {
    375         public void onReceive(Context context, Intent intent) {
    376             getScript().setTimeZone(Time.getCurrentTimezone());
    377             updateLocation();
    378         }
    379     }
    380 }
    381