• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GrassRS(Context context, int width, int height)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
start()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
stop()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
resize(int width, int height)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
createScript()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
setOffset(float xOffset, float yOffset, int xPixels, int yPixels)171     public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
172         mScript.set_gXOffset(xOffset);
173     }
174 
createBlades()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 
createMesh()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 
createBlade(ScriptField_Blade.Item blades)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 
loadTextures()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 
generateTextureAlpha()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 
loadTexture(int id)278     private Allocation loadTexture(int id) {
279         return Allocation.createFromBitmapResource(mRS, mResources, id);
280     }
281 
createProgramFragment()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 
createProgramFragmentStore()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 
createProgramVertex()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 
updateLocation()331     private void updateLocation() {
332         updateLocation(mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
333     }
334 
updateLocation(Location location)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 {
onLocationChanged(Location location)358         public void onLocationChanged(Location location) {
359             updateLocation(location);
360         }
361 
onStatusChanged(String provider, int status, Bundle extras)362         public void onStatusChanged(String provider, int status, Bundle extras) {
363         }
364 
onProviderEnabled(String provider)365         public void onProviderEnabled(String provider) {
366         }
367 
onProviderDisabled(String provider)368         public void onProviderDisabled(String provider) {
369         }
370     }
371 
372     private class TimezoneTracker extends BroadcastReceiver {
onReceive(Context context, Intent intent)373         public void onReceive(Context context, Intent intent) {
374             getScript().setTimeZone(Time.getCurrentTimezone());
375             updateLocation();
376         }
377     }
378 }
379