blob: 02d47379453f4f005c533cdce8d42cdb02e0cd88 [file] [log] [blame]
/*
* 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.fall.rs;
import android.content.res.Resources;
import android.renderscript.RenderScript;
import android.renderscript.ScriptC;
import android.renderscript.ProgramFragment;
import android.renderscript.ProgramStore;
import android.renderscript.ProgramVertex;
import android.renderscript.Allocation;
import android.renderscript.Sampler;
import android.renderscript.Element;
import android.renderscript.Light;
import android.renderscript.Type;
import static android.renderscript.Sampler.Value.LINEAR;
import static android.renderscript.Sampler.Value.WRAP;
import static android.renderscript.ProgramStore.DepthFunc.*;
import static android.renderscript.ProgramStore.BlendDstFunc;
import static android.renderscript.ProgramStore.BlendSrcFunc;
import static android.renderscript.ProgramFragment.EnvMode.*;
import static android.renderscript.Element.*;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap;
import static android.util.MathUtils.*;
import java.util.TimeZone;
class FallRS {
private static final int MESH_RESOLUTION = 48;
private static final int RSID_STATE = 0;
private static final int TEXTURES_COUNT = 3;
private static final int LEAVES_TEXTURES_COUNT = 4;
private static final int RSID_TEXTURE_RIVERBED = 0;
private static final int RSID_TEXTURE_LEAVES = 1;
private static final int RSID_TEXTURE_SKY = 2;
private static final int RSID_RIPPLE_MAP = 1;
private static final int RSID_REFRACTION_MAP = 2;
private static final int RSID_LEAVES = 3;
private static final int LEAVES_COUNT = 14;
private static final int LEAF_STRUCT_FIELDS_COUNT = 11;
private static final int LEAF_STRUCT_X = 0;
private static final int LEAF_STRUCT_Y = 1;
private static final int LEAF_STRUCT_SCALE = 2;
private static final int LEAF_STRUCT_ANGLE = 3;
private static final int LEAF_STRUCT_SPIN = 4;
private static final int LEAF_STRUCT_U1 = 5;
private static final int LEAF_STRUCT_U2 = 6;
private static final int LEAF_STRUCT_ALTITUDE = 7;
private static final int LEAF_STRUCT_RIPPLED = 8;
private static final int LEAF_STRUCT_DELTAX = 9;
private static final int LEAF_STRUCT_DELTAY = 10;
private static final int RSID_DROP = 4;
private Resources mResources;
private RenderScript mRS;
private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
private final int mWidth;
private final int mHeight;
@SuppressWarnings({"FieldCanBeLocal"})
private ProgramFragment mPfBackground;
@SuppressWarnings({"FieldCanBeLocal"})
private ProgramFragment mPfLighting;
@SuppressWarnings({"FieldCanBeLocal"})
private ProgramFragment mPfSky;
@SuppressWarnings({"FieldCanBeLocal"})
private ProgramStore mPfsBackground;
@SuppressWarnings({"FieldCanBeLocal"})
private ProgramStore mPfsLeaf;
@SuppressWarnings({"FieldCanBeLocal"})
private ProgramVertex mPvLight;
@SuppressWarnings({"FieldCanBeLocal"})
private ProgramVertex mPvSky;
private Allocation mState;
private Allocation mDropState;
private DropState mDrop;
private Type mStateType;
private Type mDropType;
private int mMeshWidth;
private int mMeshHeight;
private Allocation mRippleMap;
private Allocation mRefractionMap;
private Allocation mLeaves;
private float mGlHeight;
public FallRS(int width, int height) {
mWidth = width;
mHeight = height;
mOptionsARGB.inScaled = false;
mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
}
public void init(RenderScript rs, Resources res) {
mRS = rs;
mResources = res;
initRS();
}
private void initRS() {
createProgramVertex();
createProgramFragmentStore();
createProgramFragment();
createMesh();
createScriptStructures();
loadTextures();
ScriptC.Builder sb = new ScriptC.Builder(mRS);
sb.setType(mStateType, "State", RSID_STATE);
sb.setType(mDropType, "Drop", RSID_DROP);
sb.setScript(mResources, R.raw.fall);
sb.setRoot(true);
ScriptC script = sb.create();
script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
script.setTimeZone(TimeZone.getDefault().getID());
script.bindAllocation(mState, RSID_STATE);
script.bindAllocation(mRippleMap, RSID_RIPPLE_MAP);
script.bindAllocation(mRefractionMap, RSID_REFRACTION_MAP);
script.bindAllocation(mLeaves, RSID_LEAVES);
script.bindAllocation(mDropState, RSID_DROP);
mRS.contextBindRootScript(script);
}
private void createMesh() {
final RenderScript rs = mRS;
rs.triangleMeshBegin(Element.NORM_ST_XYZ_F32, Element.INDEX_16);
int wResolution;
int hResolution;
final int width = mWidth;
final int height = mHeight;
if (width < height) {
wResolution = MESH_RESOLUTION;
hResolution = (int) (MESH_RESOLUTION * height / (float) width);
} else {
wResolution = (int) (MESH_RESOLUTION * width / (float) height);
hResolution = MESH_RESOLUTION;
}
mGlHeight = 2.0f * height / (float) width;
final float glHeight = mGlHeight;
float quadWidth = 2.0f / (float) wResolution;
float quadHeight = glHeight / (float) hResolution;
wResolution += 2;
hResolution += 2;
for (int y = 0; y <= hResolution; y++) {
final float yOffset = y * quadHeight - glHeight / 2.0f - quadHeight;
final float t = 1.0f - y / (float) hResolution;
for (int x = 0; x <= wResolution; x++) {
rs.triangleMeshAddVertex_XYZ_ST_NORM(
-1.0f + x * quadWidth - quadWidth, yOffset, 0.0f,
x / (float) wResolution, t,
0.0f, 0.0f, -1.0f);
}
}
for (int y = 0; y < hResolution; y++) {
for (int x = 0; x < wResolution; x++) {
final int index = y * (wResolution + 1) + x;
final int iWR1 = index + wResolution + 1;
rs.triangleMeshAddTriangle(index, index + 1, iWR1);
rs.triangleMeshAddTriangle(index + 1, iWR1, iWR1 + 1);
}
}
RenderScript.TriangleMesh mesh = rs.triangleMeshCreate();
mesh.setName("WaterMesh");
mMeshWidth = wResolution + 1;
mMeshHeight = hResolution + 1;
}
private void createScriptStructures() {
final int rippleMapSize = (mMeshWidth + 2) * (mMeshHeight + 2);
createState(rippleMapSize);
createRippleMap(rippleMapSize);
createRefractionMap();
createLeaves();
}
private void createLeaves() {
final float[] leaves = new float[LEAVES_COUNT * LEAF_STRUCT_FIELDS_COUNT];
mLeaves = Allocation.createSized(mRS, USER_FLOAT, leaves.length);
for (int i = 0; i < leaves.length; i += LEAF_STRUCT_FIELDS_COUNT) {
createLeaf(leaves, i);
}
mLeaves.data(leaves);
}
private void createRefractionMap() {
final int[] refractionMap = new int[513];
float ir = 1.0f / 1.333f;
for (int i = 0; i < refractionMap.length; i++) {
float d = (float) Math.tan(Math.asin(Math.sin(Math.atan(i * (1.0f / 256.0f))) * ir));
refractionMap[i] = (int) Math.floor(d * (1 << 16) + 0.5f);
}
mRefractionMap = Allocation.createSized(mRS, USER_I32, refractionMap.length);
mRefractionMap.data(refractionMap);
}
private void createRippleMap(int rippleMapSize) {
final int[] rippleMap = new int[rippleMapSize * 2];
mRippleMap = Allocation.createSized(mRS, USER_I32, rippleMap.length);
mRippleMap.data(rippleMap);
}
static class WorldState {
public int frameCount;
public int width;
public int height;
public int meshWidth;
public int meshHeight;
public int rippleMapSize;
public int rippleIndex;
public int leavesCount;
public float glWidth;
public float glHeight;
public float skyOffsetX;
public float skyOffsetY;
public float skySpeedX;
public float skySpeedY;
}
static class DropState {
public int dropX;
public int dropY;
}
private void createState(int rippleMapSize) {
WorldState worldState = new WorldState();
worldState.width = mWidth;
worldState.height = mHeight;
worldState.meshWidth = mMeshWidth;
worldState.meshHeight = mMeshHeight;
worldState.rippleMapSize = rippleMapSize;
worldState.rippleIndex = 0;
worldState.leavesCount = LEAVES_COUNT;
worldState.glWidth = 2.0f;
worldState.glHeight = mGlHeight;
worldState.skySpeedX = random(-0.001f, 0.001f);
worldState.skySpeedY = random(0.00008f, 0.0002f);
mStateType = Type.createFromClass(mRS, WorldState.class, 1, "WorldState");
mState = Allocation.createTyped(mRS, mStateType);
mState.data(worldState);
mDrop = new DropState();
mDrop.dropX = -1;
mDrop.dropY = -1;
mDropType = Type.createFromClass(mRS, DropState.class, 1, "DropState");
mDropState = Allocation.createTyped(mRS, mDropType);
mDropState.data(mDrop);
}
private void createLeaf(float[] leaves, int index) {
int sprite = random(LEAVES_TEXTURES_COUNT);
//noinspection PointlessArithmeticExpression
leaves[index + LEAF_STRUCT_X] = random(-1.0f, 1.0f);
leaves[index + LEAF_STRUCT_Y] = random(-mGlHeight / 2.0f, mGlHeight / 2.0f);
leaves[index + LEAF_STRUCT_SCALE] = random(0.4f, 0.5f);
leaves[index + LEAF_STRUCT_ANGLE] = random(0.0f, 360.0f);
leaves[index + LEAF_STRUCT_SPIN] = degrees(random(-0.02f, 0.02f)) / 4.0f;
leaves[index + LEAF_STRUCT_U1] = sprite / (float) LEAVES_TEXTURES_COUNT;
leaves[index + LEAF_STRUCT_U2] = (sprite + 1) / (float) LEAVES_TEXTURES_COUNT;
leaves[index + LEAF_STRUCT_ALTITUDE] = -1.0f;
leaves[index + LEAF_STRUCT_RIPPLED] = 1.0f;
leaves[index + LEAF_STRUCT_DELTAX] = random(-0.02f, 0.02f) / 60.0f;
leaves[index + LEAF_STRUCT_DELTAY] = -0.08f * random(0.9f, 1.1f) / 60.0f;
}
private void loadTextures() {
final Allocation[] textures = new Allocation[TEXTURES_COUNT];
textures[RSID_TEXTURE_RIVERBED] = loadTexture(R.drawable.riverbed, "TRiverbed");
textures[RSID_TEXTURE_LEAVES] = loadTextureARGB(R.drawable.leaves, "TLeaves");
textures[RSID_TEXTURE_SKY] = loadTextureARGB(R.drawable.sky, "TSky");
final int count = textures.length;
for (int i = 0; i < count; i++) {
final Allocation texture = textures[i];
texture.uploadToTexture(0);
}
}
private Allocation loadTexture(int id, String name) {
final Allocation allocation = Allocation.createFromBitmapResource(mRS, mResources,
id, RGB_565, false);
allocation.setName(name);
return allocation;
}
private Allocation loadTextureARGB(int id, String name) {
Bitmap b = BitmapFactory.decodeResource(mResources, id, mOptionsARGB);
final Allocation allocation = Allocation.createFromBitmap(mRS, b, RGBA_8888, false);
allocation.setName(name);
return allocation;
}
private void createProgramFragment() {
Sampler.Builder sampleBuilder = new Sampler.Builder(mRS);
sampleBuilder.setMin(LINEAR);
sampleBuilder.setMag(LINEAR);
sampleBuilder.setWrapS(WRAP);
sampleBuilder.setWrapT(WRAP);
Sampler sampler = sampleBuilder.create();
ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS, null, null);
builder.setTexEnable(true, 0);
builder.setTexEnvMode(REPLACE, 0);
mPfBackground = builder.create();
mPfBackground.setName("PFBackground");
mPfBackground.bindSampler(sampler, 0);
builder = new ProgramFragment.Builder(mRS, null, null);
builder.setTexEnable(false, 0);
mPfLighting = builder.create();
mPfLighting.setName("PFLighting");
mPfLighting.bindSampler(sampler, 0);
builder = new ProgramFragment.Builder(mRS, null, null);
builder.setTexEnable(true, 0);
builder.setTexEnvMode(MODULATE, 0);
mPfSky = builder.create();
mPfSky.setName("PFSky");
mPfSky.bindSampler(sampler, 0);
}
private void createProgramFragmentStore() {
ProgramStore.Builder builder = new ProgramStore.Builder(mRS, null, null);
builder.setDepthFunc(ALWAYS);
builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
builder.setDitherEnable(false);
builder.setDepthMask(true);
mPfsBackground = builder.create();
mPfsBackground.setName("PFSBackground");
builder = new ProgramStore.Builder(mRS, null, null);
builder.setDepthFunc(ALWAYS);
builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
builder.setDitherEnable(false);
builder.setDepthMask(true);
mPfsLeaf = builder.create();
mPfsLeaf.setName("PFSLeaf");
}
private void createProgramVertex() {
ProgramVertex.MatrixAllocation pvOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS);
pvOrthoAlloc.setupProjectionNormalized(mWidth, mHeight);
Light light = new Light.Builder(mRS).create();
light.setPosition(0.0f, 2.0f, -8.0f);
ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS, null, null);
builder.addLight(light);
mPvLight = builder.create();
mPvLight.bindAllocation(pvOrthoAlloc);
mPvLight.setName("PVLight");
builder = new ProgramVertex.Builder(mRS, null, null);
builder.setTextureMatrixEnable(true);
mPvSky = builder.create();
mPvSky.bindAllocation(pvOrthoAlloc);
mPvSky.setName("PVSky");
}
void addDrop(float x, float y) {
mDrop.dropX = (int) ((x / mWidth) * mMeshWidth);
mDrop.dropY = (int) ((y / mHeight) * mMeshHeight);
mDropState.data(mDrop);
}
}