1package com.android.noisefield;
2
3import android.content.res.Resources;
4import android.graphics.Bitmap;
5import android.graphics.BitmapFactory;
6import android.renderscript.Allocation;
7import android.renderscript.Float3;
8import android.renderscript.Float4;
9import android.renderscript.Matrix4f;
10import android.renderscript.Mesh;
11import android.renderscript.Program;
12import android.renderscript.ProgramFragment;
13import android.renderscript.ProgramFragmentFixedFunction;
14import android.renderscript.ProgramRaster;
15import android.renderscript.ProgramStore;
16import android.renderscript.ProgramVertex;
17import android.renderscript.ProgramVertexFixedFunction;
18import android.renderscript.RenderScriptGL;
19import android.renderscript.Sampler;
20import android.renderscript.Mesh.Primitive;
21import android.renderscript.ProgramStore.BlendDstFunc;
22import android.renderscript.ProgramStore.BlendSrcFunc;
23import android.os.Bundle;
24import android.app.WallpaperManager;
25import android.util.Log;
26import android.view.MotionEvent;
27import java.io.InputStreamReader;
28import java.io.InputStream;
29import java.io.BufferedReader;
30import java.io.IOException;
31import java.util.ArrayList;
32
33public class NoiseFieldRS {
34    public static String LOG_TAG = "NoiseField";
35
36    private Resources mRes;
37    private RenderScriptGL mRS;
38    private ScriptC_noisefield mScript;
39    private int mHeight;
40    private int mWidth;
41    private boolean mTouchDown;
42    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
43
44    private ScriptField_VpConsts mPvConsts;
45    private Allocation mDotAllocation;
46    private ScriptField_VertexColor_s mVertexColors;
47    private ScriptField_Particle mDotParticles;
48    private Mesh mDotMesh;
49    private int mDensityDPI;
50
51    public void init(int dpi, RenderScriptGL rs,
52                     Resources res, int width, int height) {
53        mDensityDPI = dpi;
54        mRS = rs;
55        mRes = res;
56
57        mWidth = width;
58        mHeight = height;
59
60        mOptionsARGB.inScaled = false;
61        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
62
63        Mesh.AllocationBuilder smb2 = new Mesh.AllocationBuilder(mRS);
64
65        mDotParticles = new ScriptField_Particle(mRS, 83);
66        smb2.addVertexAllocation(mDotParticles.getAllocation());
67
68        smb2.addIndexSetType(Mesh.Primitive.POINT);
69        mScript = new ScriptC_noisefield(mRS, mRes, R.raw.noisefield);
70
71        mDotMesh = smb2.create();
72        mScript.set_dotMesh(mDotMesh);
73        mScript.bind_dotParticles(mDotParticles);
74
75        mPvConsts = new ScriptField_VpConsts(mRS, 1);
76
77        createProgramVertex();
78        createProgramRaster();
79        createProgramFragmentStore();
80        createProgramFragment();
81        createBackgroundMesh();
82        loadTextures();
83
84        mScript.set_densityDPI(mDensityDPI);
85        mScript.invoke_positionParticles();
86    }
87
88    private Matrix4f getProjectionNormalized(int w, int h) {
89        // range -1,1 in the narrow axis at z = 0.
90        Matrix4f m1 = new Matrix4f();
91        Matrix4f m2 = new Matrix4f();
92
93        if (w > h) {
94            float aspect = ((float) w) / h;
95            m1.loadFrustum(-aspect, aspect, -1, 1, 1, 100);
96        } else {
97            float aspect = ((float) h) / w;
98            m1.loadFrustum(-0.5f, 1, -aspect, aspect, 1, 100);
99        }
100
101        m2.loadRotate(180, 0, 1, 0);
102        m1.loadMultiply(m1, m2);
103
104        m2.loadScale(-1, 1, 1);
105        m1.loadMultiply(m1, m2);
106
107        m2.loadTranslate(0, 0, 1);
108        m1.loadMultiply(m1, m2);
109        return m1;
110    }
111
112    private void updateProjectionMatrices() {
113        Matrix4f projNorm = getProjectionNormalized(mWidth, mHeight);
114        ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item();
115        i.MVP = projNorm;
116        i.scaleSize = mDensityDPI/240.0f;
117        mPvConsts.set(i, 0, true);
118    }
119
120    private void createBackgroundMesh() {
121        // The composition and colors of the background mesh were plotted on paper and photoshop
122        // first then translated to the csv file in raw. Points and colors are not random.
123        ArrayList<String> meshData = new ArrayList<String>();
124        InputStream inputStream = mRes.openRawResource(R.raw.bgmesh);
125        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
126        try {
127            String line;
128            while ((line = reader.readLine()) != null) {
129                meshData.add(line);
130            }
131        } catch (IOException e) {
132            Log.e(LOG_TAG, "Unable to load background mesh from csv file.");
133        } finally {
134            try {
135                inputStream.close();
136            } catch (IOException e) {
137                Log.e(LOG_TAG, "Unable to close background mesh csv file.");
138            }
139        }
140
141        int meshDataSize = meshData.size();
142        mVertexColors = new ScriptField_VertexColor_s(mRS, meshDataSize);
143        for (int i=0; i<meshDataSize; i++) {
144            String line = (String) meshData.get(i);
145            String[] values = line.split(",");
146            float xPos = Float.parseFloat(values[0]);
147            float yPos = Float.parseFloat(values[1]);
148            float red = Float.parseFloat(values[2]);
149            float green = Float.parseFloat(values[3]);
150            float blue = Float.parseFloat(values[4]);
151            mVertexColors.set_position(i, new Float3(xPos, yPos, 0.0f), false);
152            mVertexColors.set_color(i, new Float4(red, green, blue, 1.0f), false);
153        }
154        mVertexColors.copyAll();
155
156        Mesh.AllocationBuilder backgroundBuilder = new Mesh.AllocationBuilder(mRS);
157        backgroundBuilder.addIndexSetType(Primitive.TRIANGLE);
158        backgroundBuilder.addVertexAllocation(mVertexColors.getAllocation());
159        mScript.set_gBackgroundMesh(backgroundBuilder.create());
160        mScript.bind_vertexColors(mVertexColors);
161    }
162
163    private Allocation loadTexture(int id) {
164        final Allocation allocation = Allocation.createFromBitmapResource(mRS, mRes, id,
165                                           Allocation.MipmapControl.MIPMAP_NONE,
166                                           Allocation.USAGE_GRAPHICS_TEXTURE);
167        return allocation;
168    }
169
170    private void loadTextures() {
171        mDotAllocation = loadTexture(R.drawable.dot);
172        mScript.set_textureDot(mDotAllocation);
173    }
174
175    private void createProgramVertex() {
176        ProgramVertex.Builder backgroundBuilder = new ProgramVertex.Builder(mRS);
177        backgroundBuilder.setShader(mRes, R.raw.bg_vs);
178        backgroundBuilder.addInput(ScriptField_VertexColor_s.createElement(mRS));
179        ProgramVertex programVertexBackground = backgroundBuilder.create();
180        mScript.set_vertBg(programVertexBackground);
181
182        updateProjectionMatrices();
183
184        ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS);
185        builder = new ProgramVertex.Builder(mRS);
186        builder.setShader(mRes, R.raw.noisefield_vs);
187        builder.addConstant(mPvConsts.getType());
188        builder.addInput(mDotMesh.getVertexAllocation(0).getType().getElement());
189
190        ProgramVertex pvs = builder.create();
191        pvs.bindConstants(mPvConsts.getAllocation(), 0);
192        mRS.bindProgramVertex(pvs);
193        mScript.set_vertDots(pvs);
194    }
195
196    private void createProgramFragment() {
197        ProgramFragment.Builder backgroundBuilder = new ProgramFragment.Builder(mRS);
198        backgroundBuilder.setShader(mRes, R.raw.bg_fs);
199        ProgramFragment programFragmentBackground = backgroundBuilder.create();
200        mScript.set_fragBg(programFragmentBackground);
201
202        ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS);
203        builder.setShader(mRes, R.raw.noisefield_fs);
204        builder.addTexture(Program.TextureType.TEXTURE_2D);
205        ProgramFragment pf = builder.create();
206        pf.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
207        mScript.set_fragDots(pf);
208    }
209
210    private void createProgramRaster() {
211        ProgramRaster.Builder builder = new ProgramRaster.Builder(mRS);
212        builder.setPointSpriteEnabled(true);
213        ProgramRaster pr = builder.create();
214        mRS.bindProgramRaster(pr);
215    }
216
217    private void createProgramFragmentStore() {
218        ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
219        builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE );
220        mRS.bindProgramStore(builder.create());
221    }
222
223    public void start() {
224        mRS.bindRootScript(mScript);
225    }
226
227    public void stop() {
228        mRS.bindRootScript(null);
229    }
230
231    public void resize(int w, int h) {
232
233    }
234
235    public void onTouchEvent(MotionEvent ev) {
236        int act = ev.getActionMasked();
237        if (act == MotionEvent.ACTION_UP || act == MotionEvent.ACTION_POINTER_UP) {
238            if(mTouchDown){
239                mTouchDown = false;
240                mScript.set_touchDown(mTouchDown);
241            }
242            return;
243        } else if(   act == MotionEvent.ACTION_DOWN
244                  || act == MotionEvent.ACTION_MOVE
245                  || act == MotionEvent.ACTION_POINTER_DOWN) {
246            int pcount = ev.getPointerCount();
247
248            if(!mTouchDown){
249                mTouchDown = true;
250                mScript.set_touchDown(mTouchDown);
251            }
252            if(pcount > 0){
253                // just send first pointer position
254                mScript.invoke_touch(ev.getX(0), ev.getY(0));
255            }
256        }
257    }
258}