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