summaryrefslogtreecommitdiffstats
path: root/tests/RenderScriptTests/ShadersTest
diff options
context:
space:
mode:
authorRomain Guy <romainguy@google.com>2011-08-17 15:51:02 -0700
committerRomain Guy <romainguy@google.com>2011-08-17 15:52:15 -0700
commit185268942b946028b65f5cff9b335af97f9aeab8 (patch)
treea735256911c1c8202a25f03b85fd7c61da7b61fb /tests/RenderScriptTests/ShadersTest
parent7d8fc3c911ea8e4cd1e6531118da4f72e521a944 (diff)
downloadframeworks_base-185268942b946028b65f5cff9b335af97f9aeab8.zip
frameworks_base-185268942b946028b65f5cff9b335af97f9aeab8.tar.gz
frameworks_base-185268942b946028b65f5cff9b335af97f9aeab8.tar.bz2
Add RenderScript fragment shader test
Change-Id: I03328a610c8eae6bb6d63ecbc4104d55997664cc
Diffstat (limited to 'tests/RenderScriptTests/ShadersTest')
-rw-r--r--tests/RenderScriptTests/ShadersTest/Android.mk26
-rw-r--r--tests/RenderScriptTests/ShadersTest/AndroidManifest.xml32
-rw-r--r--tests/RenderScriptTests/ShadersTest/res/drawable-nodpi/robot.pngbin0 -> 292580 bytes
-rw-r--r--tests/RenderScriptTests/ShadersTest/res/raw/depth_fs.glsl13
-rw-r--r--tests/RenderScriptTests/ShadersTest/res/raw/robot.a3dbin0 -> 144528 bytes
-rw-r--r--tests/RenderScriptTests/ShadersTest/res/raw/vignette_fs.glsl31
-rw-r--r--tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTest.java46
-rw-r--r--tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestRS.java202
-rw-r--r--tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestView.java138
-rw-r--r--tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs193
10 files changed, 681 insertions, 0 deletions
diff --git a/tests/RenderScriptTests/ShadersTest/Android.mk b/tests/RenderScriptTests/ShadersTest/Android.mk
new file mode 100644
index 0000000..109b0a0
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2011 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := ShadersTest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ShadersTest/AndroidManifest.xml b/tests/RenderScriptTests/ShadersTest/AndroidManifest.xml
new file mode 100644
index 0000000..871200d
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.shaderstest">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application android:label="_ShadersTest">
+ <activity android:name="ShadersTest"
+ android:label="_ShadersTest"
+ android:theme="@android:style/Theme.Black.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/RenderScriptTests/ShadersTest/res/drawable-nodpi/robot.png b/tests/RenderScriptTests/ShadersTest/res/drawable-nodpi/robot.png
new file mode 100644
index 0000000..f7353fd
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/ShadersTest/res/raw/depth_fs.glsl b/tests/RenderScriptTests/ShadersTest/res/raw/depth_fs.glsl
new file mode 100644
index 0000000..096843b
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/res/raw/depth_fs.glsl
@@ -0,0 +1,13 @@
+void main() {
+ // Non-linear depth value
+ float z = gl_FragCoord.z;
+ // Near and far planes from the projection
+ // In practice, these values can be used to tweak
+ // the focus range
+ float n = UNI_near;
+ float f = UNI_far;
+ // Linear depth value
+ z = (2.0 * n) / (f + n - z * (f - n));
+
+ gl_FragColor = vec4(z, z, z, 1.0);
+}
diff --git a/tests/RenderScriptTests/ShadersTest/res/raw/robot.a3d b/tests/RenderScriptTests/ShadersTest/res/raw/robot.a3d
new file mode 100644
index 0000000..f48895c
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/res/raw/robot.a3d
Binary files differ
diff --git a/tests/RenderScriptTests/ShadersTest/res/raw/vignette_fs.glsl b/tests/RenderScriptTests/ShadersTest/res/raw/vignette_fs.glsl
new file mode 100644
index 0000000..2dc1ea3
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/res/raw/vignette_fs.glsl
@@ -0,0 +1,31 @@
+#define CRT_MASK
+
+varying vec2 varTex0;
+
+void main() {
+ lowp vec4 color = texture2D(UNI_Tex0, varTex0);
+
+ vec2 powers = pow(abs((gl_FragCoord.xy / vec2(UNI_width, UNI_height)) - 0.5), vec2(2.0));
+ float gradient = smoothstep(UNI_size - UNI_feather, UNI_size + UNI_feather,
+ powers.x + powers.y);
+
+ color = vec4(mix(color.rgb, vec3(0.0), gradient), 1.0);
+
+#ifdef CRT_MASK
+ float vShift = gl_FragCoord.y;
+ if (mod(gl_FragCoord.x, 6.0) >= 3.0) {
+ vShift += 2.0;
+ }
+
+ lowp vec3 r = vec3(0.95, 0.0, 0.2);
+ lowp vec3 g = vec3(0.2, 0.95, 0.0);
+ lowp vec3 b = vec3(0.0, 0.2, 0.95);
+ int channel = int(floor(mod(gl_FragCoord.x, 3.0)));
+ lowp vec4 crt = vec4(r[channel], g[channel], b[channel], 1.0);
+ crt *= clamp(floor(mod(vShift, 4.0)), 0.0, 1.0);
+
+ color = (crt * color * 1.25) + 0.05;
+#endif
+
+ gl_FragColor = color;
+}
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTest.java b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTest.java
new file mode 100644
index 0000000..6803fbb
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 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.shaderstest;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ShadersTest extends Activity {
+
+ private ShadersTestView mView;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mView = new ShadersTestView(this);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mView.resume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mView.pause();
+ }
+}
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestRS.java b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestRS.java
new file mode 100644
index 0000000..dad97e2
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestRS.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2011 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.shaderstest;
+
+import android.content.res.Resources;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Element.DataKind;
+import android.renderscript.Element.DataType;
+import android.renderscript.FileA3D;
+import android.renderscript.Mesh;
+import android.renderscript.Program;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramFragmentFixedFunction;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.renderscript.ProgramVertex;
+import android.renderscript.ProgramVertexFixedFunction;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.RenderScriptGL;
+import android.renderscript.Sampler;
+import android.renderscript.Type.Builder;
+
+@SuppressWarnings({"FieldCanBeLocal"})
+public class ShadersTestRS {
+ public ShadersTestRS() {
+ }
+
+ public void init(RenderScriptGL rs, Resources res) {
+ mRS = rs;
+ mRes = res;
+ initRS();
+ }
+
+ public void surfaceChanged() {
+ initBuffers(mRS.getWidth(), mRS.getHeight());
+ }
+
+ private Resources mRes;
+ private RenderScriptGL mRS;
+ private Sampler mLinearClamp;
+ private Sampler mNearestClamp;
+ private ProgramStore mPSBackground;
+ private ProgramFragment mPFBackground;
+ private ProgramVertex mPVBackground;
+ private ProgramVertexFixedFunction.Constants mPVA;
+
+ private ProgramFragment mPFVignette;
+ private ScriptField_VignetteConstants_s mFSVignetteConst;
+
+ private Allocation mMeshTexture;
+ private Allocation mScreen;
+ private Allocation mScreenDepth;
+
+ private ScriptField_MeshInfo mMeshes;
+ private ScriptC_shaderstest mScript;
+
+
+ public void onActionDown(float x, float y) {
+ mScript.invoke_onActionDown(x, y);
+ }
+
+ public void onActionScale(float scale) {
+ mScript.invoke_onActionScale(scale);
+ }
+
+ public void onActionMove(float x, float y) {
+ mScript.invoke_onActionMove(x, y);
+ }
+
+ private void initPFS() {
+ ProgramStore.Builder b = new ProgramStore.Builder(mRS);
+
+ b.setDepthFunc(DepthFunc.LESS);
+ b.setDitherEnabled(false);
+ b.setDepthMaskEnabled(true);
+ mPSBackground = b.create();
+
+ mScript.set_gPFSBackground(mPSBackground);
+ }
+
+ private void initPF() {
+ mLinearClamp = Sampler.CLAMP_LINEAR(mRS);
+ mScript.set_gLinear(mLinearClamp);
+
+ mNearestClamp = Sampler.CLAMP_NEAREST(mRS);
+ mScript.set_gNearest(mNearestClamp);
+
+ ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
+ b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
+ ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
+ mPFBackground = b.create();
+ mPFBackground.bindSampler(mLinearClamp, 0);
+ mScript.set_gPFBackground(mPFBackground);
+
+ mFSVignetteConst = new ScriptField_VignetteConstants_s(mRS, 1);
+ mScript.bind_gFSVignetteConstants(mFSVignetteConst);
+
+ ProgramFragment.Builder fs;
+
+ fs = new ProgramFragment.Builder(mRS);
+ fs.setShader(mRes, R.raw.vignette_fs);
+ fs.addConstant(mFSVignetteConst.getAllocation().getType());
+ fs.addTexture(Program.TextureType.TEXTURE_2D);
+ mPFVignette = fs.create();
+ mPFVignette.bindConstants(mFSVignetteConst.getAllocation(), 0);
+ mScript.set_gPFVignette(mPFVignette);
+ }
+
+ private void initPV() {
+ ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
+ mPVBackground = pvb.create();
+
+ mPVA = new ProgramVertexFixedFunction.Constants(mRS);
+ ((ProgramVertexFixedFunction) mPVBackground).bindConstants(mPVA);
+
+ mScript.set_gPVBackground(mPVBackground);
+ }
+
+ private void loadImage() {
+ mMeshTexture = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
+ Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+ Allocation.USAGE_GRAPHICS_TEXTURE);
+ mScript.set_gTMesh(mMeshTexture);
+ }
+
+ private void initMeshes(FileA3D model) {
+ int numEntries = model.getIndexEntryCount();
+ int numMeshes = 0;
+ for (int i = 0; i < numEntries; i ++) {
+ FileA3D.IndexEntry entry = model.getIndexEntry(i);
+ if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
+ numMeshes ++;
+ }
+ }
+
+ if (numMeshes > 0) {
+ mMeshes = new ScriptField_MeshInfo(mRS, numMeshes);
+
+ for (int i = 0; i < numEntries; i ++) {
+ FileA3D.IndexEntry entry = model.getIndexEntry(i);
+ if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
+ Mesh mesh = entry.getMesh();
+ mMeshes.set_mMesh(i, mesh, false);
+ mMeshes.set_mNumIndexSets(i, mesh.getPrimitiveCount(), false);
+ }
+ }
+ mMeshes.copyAll();
+ } else {
+ throw new RSRuntimeException("No valid meshes in file");
+ }
+
+ mScript.bind_gMeshes(mMeshes);
+ mScript.invoke_updateMeshInfo();
+ }
+
+ private void initRS() {
+ mScript = new ScriptC_shaderstest(mRS, mRes, R.raw.shaderstest);
+
+ initPFS();
+ initPF();
+ initPV();
+
+ loadImage();
+
+ initBuffers(1, 1);
+
+ FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
+ initMeshes(model);
+
+ mRS.bindRootScript(mScript);
+ }
+
+ private void initBuffers(int width, int height) {
+ Builder b;
+ b = new Builder(mRS, Element.RGBA_8888(mRS));
+ b.setX(width).setY(height);
+ mScreen = Allocation.createTyped(mRS, b.create(),
+ Allocation.USAGE_GRAPHICS_TEXTURE | Allocation.USAGE_GRAPHICS_RENDER_TARGET);
+ mScript.set_gScreen(mScreen);
+
+ b = new Builder(mRS, Element.createPixel(mRS, DataType.UNSIGNED_16, DataKind.PIXEL_DEPTH));
+ b.setX(width).setY(height);
+ mScreenDepth = Allocation.createTyped(mRS, b.create(),
+ Allocation.USAGE_GRAPHICS_RENDER_TARGET);
+ mScript.set_gScreenDepth(mScreenDepth);
+ }
+}
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestView.java b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestView.java
new file mode 100644
index 0000000..e0a540f
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestView.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2011 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.shaderstest;
+
+import android.content.Context;
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScriptGL;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.SurfaceHolder;
+
+public class ShadersTestView extends RSSurfaceView {
+
+ private RenderScriptGL mRS;
+ private ShadersTestRS mRender;
+
+ private ScaleGestureDetector mScaleDetector;
+
+ private static final int INVALID_POINTER_ID = -1;
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ public ShadersTestView(Context context) {
+ super(context);
+ ensureRenderScript();
+ mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
+ }
+
+ private void ensureRenderScript() {
+ if (mRS == null) {
+ RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+ sc.setDepth(16, 24);
+ mRS = createRenderScriptGL(sc);
+ mRender = new ShadersTestRS();
+ mRender.init(mRS, getResources());
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ ensureRenderScript();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
+ mRender.surfaceChanged();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ mRender = null;
+ if (mRS != null) {
+ mRS = null;
+ destroyRenderScriptGL();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ mScaleDetector.onTouchEvent(ev);
+
+ boolean ret = false;
+ float x = ev.getX();
+ float y = ev.getY();
+
+ final int action = ev.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ mRender.onActionDown(x, y);
+ mActivePointerId = ev.getPointerId(0);
+ ret = true;
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ if (!mScaleDetector.isInProgress()) {
+ mRender.onActionMove(x, y);
+ }
+ mRender.onActionDown(x, y);
+ ret = true;
+ break;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ mActivePointerId = INVALID_POINTER_ID;
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER_ID;
+ break;
+ }
+
+ case MotionEvent.ACTION_POINTER_UP: {
+ final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ x = ev.getX(newPointerIndex);
+ y = ev.getY(newPointerIndex);
+ mRender.onActionDown(x, y);
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ }
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ mRender.onActionScale(detector.getScaleFactor());
+ return true;
+ }
+ }
+}
+
+
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs
new file mode 100644
index 0000000..53f10f9
--- /dev/null
+++ b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs
@@ -0,0 +1,193 @@
+// Copyright (C) 2011 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.shaderstest)
+
+#include "rs_graphics.rsh"
+
+rs_program_vertex gPVBackground;
+rs_program_fragment gPFBackground;
+
+typedef struct VignetteConstants_s {
+ float size;
+ float feather;
+ float width;
+ float height;
+} VignetteConstants;
+VignetteConstants *gFSVignetteConstants;
+rs_program_fragment gPFVignette;
+
+rs_allocation gTMesh;
+
+rs_sampler gLinear;
+rs_sampler gNearest;
+
+rs_program_store gPFSBackground;
+
+rs_allocation gScreenDepth;
+rs_allocation gScreen;
+
+typedef struct MeshInfo {
+ rs_mesh mMesh;
+ int mNumIndexSets;
+ float3 bBoxMin;
+ float3 bBoxMax;
+} MeshInfo_t;
+MeshInfo_t *gMeshes;
+
+static float3 gLookAt;
+
+static float gRotateX;
+static float gRotateY;
+static float gZoom;
+
+static float gLastX;
+static float gLastY;
+
+void onActionDown(float x, float y) {
+ gLastX = x;
+ gLastY = y;
+}
+
+void onActionScale(float scale) {
+
+ gZoom *= 1.0f / scale;
+ gZoom = max(0.1f, min(gZoom, 500.0f));
+}
+
+void onActionMove(float x, float y) {
+ float dx = gLastX - x;
+ float dy = gLastY - y;
+
+ if (fabs(dy) <= 2.0f) {
+ dy = 0.0f;
+ }
+ if (fabs(dx) <= 2.0f) {
+ dx = 0.0f;
+ }
+
+ gRotateY -= dx;
+ if (gRotateY > 360) {
+ gRotateY -= 360;
+ }
+ if (gRotateY < 0) {
+ gRotateY += 360;
+ }
+
+ gRotateX -= dy;
+ gRotateX = min(gRotateX, 80.0f);
+ gRotateX = max(gRotateX, -80.0f);
+
+ gLastX = x;
+ gLastY = y;
+}
+
+void init() {
+ gRotateX = 0.0f;
+ gRotateY = 0.0f;
+ gZoom = 50.0f;
+ gLookAt = 0.0f;
+}
+
+void updateMeshInfo() {
+ rs_allocation allMeshes = rsGetAllocation(gMeshes);
+ int size = rsAllocationGetDimX(allMeshes);
+ gLookAt = 0.0f;
+ float minX, minY, minZ, maxX, maxY, maxZ;
+ for (int i = 0; i < size; i++) {
+ MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
+ rsgMeshComputeBoundingBox(info->mMesh,
+ &minX, &minY, &minZ,
+ &maxX, &maxY, &maxZ);
+ info->bBoxMin = (minX, minY, minZ);
+ info->bBoxMax = (maxX, maxY, maxZ);
+ gLookAt += (info->bBoxMin + info->bBoxMax)*0.5f;
+ }
+ gLookAt = gLookAt / (float)size;
+}
+
+static void renderAllMeshes() {
+ rs_allocation allMeshes = rsGetAllocation(gMeshes);
+ int size = rsAllocationGetDimX(allMeshes);
+ gLookAt = 0.0f;
+ float minX, minY, minZ, maxX, maxY, maxZ;
+ for (int i = 0; i < size; i++) {
+ MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
+ rsgDrawMesh(info->mMesh);
+ }
+}
+
+static void renderOffscreen() {
+ rsgBindProgramVertex(gPVBackground);
+ rs_matrix4x4 proj;
+ float aspect = (float) rsAllocationGetDimX(gScreen) / (float) rsAllocationGetDimY(gScreen);
+ rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 1000.0f);
+ rsgProgramVertexLoadProjectionMatrix(&proj);
+
+ rsgBindProgramFragment(gPFBackground);
+ rsgBindTexture(gPFBackground, 0, gTMesh);
+
+ rs_matrix4x4 matrix;
+
+ rsMatrixLoadIdentity(&matrix);
+ rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom);
+ rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
+ rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
+ rsgProgramVertexLoadModelMatrix(&matrix);
+
+ renderAllMeshes();
+}
+
+static void drawOffscreenResult(int posX, int posY, float width, float height) {
+ // display the result d
+ rs_matrix4x4 proj, matrix;
+ rsMatrixLoadOrtho(&proj, 0, width, height, 0, -500, 500);
+ rsgProgramVertexLoadProjectionMatrix(&proj);
+ rsMatrixLoadIdentity(&matrix);
+ rsgProgramVertexLoadModelMatrix(&matrix);
+ float startX = posX, startY = posY;
+ rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
+ startX, startY + height, 0, 0, 0,
+ startX + width, startY + height, 0, 1, 0,
+ startX + width, startY, 0, 1, 1);
+}
+
+int root(void) {
+ gFSVignetteConstants->size = 0.58f * 0.58f;
+ gFSVignetteConstants->feather = 0.2f;
+ gFSVignetteConstants->width = (float) rsAllocationGetDimX(gScreen);
+ gFSVignetteConstants->height = (float) rsAllocationGetDimY(gScreen);
+
+ rsgBindProgramStore(gPFSBackground);
+
+ // Render scene to fullscreenbuffer
+ rsgBindColorTarget(gScreen, 0);
+ rsgBindDepthTarget(gScreenDepth);
+ rsgClearDepth(1.0f);
+ rsgClearColor(1.0f, 1.0f, 1.0f, 0.0f);
+ renderOffscreen();
+
+ // Render on screen
+ rsgClearAllRenderTargets();
+ rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ rsgClearDepth(1.0f);
+
+ rsgBindProgramFragment(gPFVignette);
+ rsgBindTexture(gPFVignette, 0, gScreen);
+ drawOffscreenResult(0, 0, rsgGetWidth(), rsgGetHeight());
+
+ return 0;
+}