Test for surface flinger latching behavior

-Replace sample app with native implementation.
-Added SurfaceFlingerTest to check latch time is after acquire time.

Test: tradefed.sh run commandAndExit AndroidTest.xml
Change-Id: I7b6d0fdd435aaf54c7ec63cddb26de9503225008
diff --git a/benchmark_libs/Android.bp b/benchmark_libs/Android.bp
index bab6280..ea9fa81 100644
--- a/benchmark_libs/Android.bp
+++ b/benchmark_libs/Android.bp
@@ -14,7 +14,7 @@
 
 cc_library_shared {
     name: "libagq",
-    sdk_version: "current",
+    sdk_version: "26",
 
     srcs: [
         "agq.cpp"
diff --git a/hostside/res/com/android/game/qualification/apk-info.xml b/hostside/res/com/android/game/qualification/apk-info.xml
index 0971fa6..08eb025 100644
--- a/hostside/res/com/android/game/qualification/apk-info.xml
+++ b/hostside/res/com/android/game/qualification/apk-info.xml
@@ -15,7 +15,7 @@
             <name>sample</name>
             <fileName>GameQualificationSampleApp.apk</fileName>
             <packageName>com.android.game.qualification.example</packageName>
-            <layerName>^SurfaceView - com.android.game.qualification.example/com.android.game.qualification.example.SampleActivity.*#0$</layerName>
+            <layerName>^com.android.game.qualification.example/android.app.NativeActivity.*#0$</layerName>
             <expectIntents>true</expectIntents>
         </apk>
     </apk-info>
diff --git a/instrumentation_tests/java_tests/Android.bp b/instrumentation_tests/java_tests/Android.bp
new file mode 100644
index 0000000..b6d5510
--- /dev/null
+++ b/instrumentation_tests/java_tests/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2018 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.
+
+cc_test_library {
+    name: "libgamecore_java_tests_jni",
+    sdk_version: "26", // Oreo
+    srcs: ["src/cpp/surface_flinger_test_activity.cpp"],
+    stl: "c++_shared",
+    shared_libs: [
+        "liblog",
+        "libandroid",
+        "libEGL",
+        "libGLESv2",
+        "libgamecore_sample",
+    ],
+    gtest: false,
+}
diff --git a/instrumentation_tests/java_tests/Android.mk b/instrumentation_tests/java_tests/Android.mk
index b72bbc0..11ce4f2 100644
--- a/instrumentation_tests/java_tests/Android.mk
+++ b/instrumentation_tests/java_tests/Android.mk
@@ -18,10 +18,18 @@
 
 LOCAL_PACKAGE_NAME := GameQualificationJavaTestCases
 LOCAL_SDK_VERSION := 26  # Oreo
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules repackaged.android.test.base
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx.test.rules \
+    repackaged.android.test.base \
+    truth-prebuilt
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libgamecore_sample \
+    libgamecore_java_tests_jni \
+    libagq
 LOCAL_MODULE_TAGS := tests
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_NDK_STL_VARIANT := c++_shared
 
 include $(BUILD_PACKAGE)
 
diff --git a/instrumentation_tests/java_tests/AndroidManifest.xml b/instrumentation_tests/java_tests/AndroidManifest.xml
index 750694c..5c4c47d 100644
--- a/instrumentation_tests/java_tests/AndroidManifest.xml
+++ b/instrumentation_tests/java_tests/AndroidManifest.xml
@@ -11,5 +11,8 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
+        <activity android:name="com.android.game.qualification.tests.SurfaceFlingerTestActivity"
+            android:label="SurfaceFlingerTestActivity">
+        </activity>
     </application>
 </manifest>
diff --git a/instrumentation_tests/java_tests/src/com/android/game/qualification/tests/SurfaceFlingerTest.java b/instrumentation_tests/java_tests/src/com/android/game/qualification/tests/SurfaceFlingerTest.java
new file mode 100644
index 0000000..dcbdb17
--- /dev/null
+++ b/instrumentation_tests/java_tests/src/com/android/game/qualification/tests/SurfaceFlingerTest.java
@@ -0,0 +1,57 @@
+package com.android.game.qualification.tests;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests related to the surface flinger.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SurfaceFlingerTest {
+
+    @Rule
+    public final ActivityTestRule<SurfaceFlingerTestActivity> mActivityRule =
+            new ActivityTestRule<>(SurfaceFlingerTestActivity.class);
+
+    /**
+     * Check surface flinger does not latch a frame before the GPU work is completed.
+     *
+     * Latching a frame before GPU work is completed can lead to unpredictable delays in the HWC
+     * that is impossible to detect in the app.
+     */
+    @Test
+    public void latchAfterReady() throws InterruptedException {
+        // Let the activity run for a few seconds.
+        Thread.sleep(3000);
+        SurfaceFlingerTestActivity activity = mActivityRule.getActivity();
+        mActivityRule.finishActivity();
+
+        Long[] readyTimes = activity.getReadyTimes().toArray(new Long[0]);
+        Long[] latchTimes = activity.getLatchTimes().toArray(new Long[0]);
+
+        assertWithMessage("Unable to retrieve frame ready time.")
+                .that(readyTimes)
+                .isNotEmpty();
+
+        assertWithMessage("Unable to retrieve buffer latch time.")
+                .that(latchTimes)
+                .isNotEmpty();
+
+        for (int i = 0; i < readyTimes.length; i++) {
+            // Check all frame ready time is before latch time.  Add a slight (0.1ms) tolerance
+            // because the latch time is slightly before the actual condition check in the
+            // surface flinger.
+            assertWithMessage(
+                    "SurfaceFlinger must latch after GPU work is completed for the frame")
+                    .that(latchTimes[i])
+                    .named("latch time")
+                    .isGreaterThan(readyTimes[i] - 100_000);
+        }
+    }
+}
diff --git a/instrumentation_tests/java_tests/src/com/android/game/qualification/tests/SurfaceFlingerTestActivity.java b/instrumentation_tests/java_tests/src/com/android/game/qualification/tests/SurfaceFlingerTestActivity.java
new file mode 100644
index 0000000..b11a970
--- /dev/null
+++ b/instrumentation_tests/java_tests/src/com/android/game/qualification/tests/SurfaceFlingerTestActivity.java
@@ -0,0 +1,127 @@
+package com.android.game.qualification.tests;
+
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+
+/**
+ * Test activity to retrieve frame ready time and buffer latch time.
+ */
+public class SurfaceFlingerTestActivity extends Activity {
+    private final String LOG_TAG = "SurfaceFlingerTestActivity";
+
+    private TestView mView;
+
+    private Queue<Long> mReadyTimes = new ArrayDeque<>();
+    private Queue<Long> mLatchTimes = new ArrayDeque<>();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mView = new TestView(this);
+        mView.setSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    public Queue<Long> getReadyTimes() {
+        return mReadyTimes;
+    }
+
+    public Queue<Long> getLatchTimes() {
+        return mLatchTimes;
+    }
+
+    private class TestView extends SurfaceView implements Runnable {
+        private volatile boolean mRunning = true;
+        private Thread mThread;
+        public TestView(Context context) {
+            super(context);
+
+            getHolder().addCallback(new SurfaceHolder.Callback() {
+                @Override
+                public void surfaceCreated(SurfaceHolder holder) {
+                    mRunning = true;
+                    mThread = new Thread(TestView.this);
+                    mThread.start();
+                }
+
+                @Override
+                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+                }
+
+                @Override
+                public void surfaceDestroyed(SurfaceHolder holder) {
+                    try {
+                        mRunning = false;
+                        mThread.join();
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+        }
+
+        public TestView(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
+            super(context, attrs, defStyleAttr);
+        }
+
+        public TestView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+            super(context, attrs, defStyleAttr, defStyleRes);
+        }
+
+        @Override
+        public void run() {
+            if (getHolder().getSurface().isValid()) {
+                initDisplay(getHolder().getSurface());
+
+                while (mRunning) {
+                    drawFrame();
+                    long[] frameData = getFrameData();
+                    while (frameData != null) {
+                        // Limit the number of frames to 240 to avoid infinitely growing buffer.
+                        int MAX_FRAMES = 240;
+                        if (getReadyTimes().size() == MAX_FRAMES) {
+                            getReadyTimes().poll();
+                            getLatchTimes().poll();
+                        }
+                        getReadyTimes().offer(frameData[0]);
+                        getLatchTimes().offer(frameData[1]);
+                        frameData = getFrameData();
+                    }
+                }
+            }
+        }
+    }
+
+    static {
+        System.loadLibrary("gamecore_java_tests_jni");
+    }
+
+    public native void initDisplay(Object surface);
+    public native void drawFrame();
+    public native long[] getFrameData();
+}
diff --git a/instrumentation_tests/java_tests/src/cpp/surface_flinger_test_activity.cpp b/instrumentation_tests/java_tests/src/cpp/surface_flinger_test_activity.cpp
new file mode 100644
index 0000000..638ca1a
--- /dev/null
+++ b/instrumentation_tests/java_tests/src/cpp/surface_flinger_test_activity.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#include <renderer.h>
+#include <common.h>
+#include <jni.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android/native_window_jni.h>
+
+#include <vector>
+#include <deque>
+
+namespace {
+
+using eglGetNextFrameIdANDROID_type = EGLBoolean (*)(EGLDisplay, EGLSurface, EGLuint64KHR *);
+using eglGetFrameTimestampsANDROID_type = EGLBoolean (*)(EGLDisplay, EGLSurface, EGLuint64KHR, EGLint, const EGLint *, EGLnsecsANDROID *);
+
+eglGetNextFrameIdANDROID_type eglGetNextFrameIdANDROID = nullptr;
+eglGetFrameTimestampsANDROID_type eglGetFrameTimestampsANDROID = nullptr;
+
+const int MAX_FRAMES = 240;
+android::gamecore::Renderer* renderer = nullptr;
+std::deque<EGLnsecsANDROID> frameReadyTime;
+std::deque<EGLnsecsANDROID> latchTime;
+std::deque<EGLuint64KHR> frameIds;
+
+} // end of anonymous namespace
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_initDisplay(JNIEnv* env, jobject, jobject surface) {
+    eglGetNextFrameIdANDROID =
+            reinterpret_cast<eglGetNextFrameIdANDROID_type>(
+                    eglGetProcAddress("eglGetNextFrameIdANDROID"));
+    if (eglGetNextFrameIdANDROID == nullptr) {
+        LOGE("Failed to load eglGetNextFrameIdANDROID");
+        return;
+    }
+    eglGetFrameTimestampsANDROID =
+            reinterpret_cast<eglGetFrameTimestampsANDROID_type>(
+                    eglGetProcAddress("eglGetFrameTimestampsANDROID"));
+    if (eglGetNextFrameIdANDROID == nullptr) {
+        LOGE("Failed to load eglGetFrameTimestampsANDROID");
+        return;
+    }
+
+    if (renderer == nullptr) {
+        // Create a renderer that draws many circles to overload the GPU.
+        renderer = new android::gamecore::Renderer(1500);
+    }
+    ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
+    renderer->initDisplay(window);
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_drawFrame(JNIEnv*, jobject) {
+    if (eglGetNextFrameIdANDROID == nullptr || eglGetFrameTimestampsANDROID == nullptr) {
+        return;
+    }
+    EGLuint64KHR frameId;
+    EGLBoolean rc = eglGetNextFrameIdANDROID(renderer->egl.display, renderer->egl.surface, &frameId);
+    if (rc == EGL_TRUE) {
+        frameIds.push_back(frameId);
+    }
+    renderer->update();
+    renderer->draw();
+
+    static const EGLint timestamps[] = {
+            EGL_RENDERING_COMPLETE_TIME_ANDROID,
+            EGL_COMPOSITION_LATCH_TIME_ANDROID};
+
+    while(!frameIds.empty()) {
+        EGLuint64KHR fid = *frameIds.begin();
+        EGLnsecsANDROID values[2];
+        rc = eglGetFrameTimestampsANDROID(
+                renderer->egl.display, renderer->egl.surface, fid, 2, timestamps, values);
+
+        // Timestamps pending, will try again next frame.
+        if (values[0] == EGL_TIMESTAMP_PENDING_ANDROID
+                || values[1] == EGL_TIMESTAMP_PENDING_ANDROID) {
+            break;
+        }
+        frameIds.pop_front();
+        if (rc == EGL_TRUE) {
+            if (frameReadyTime.size() == MAX_FRAMES) {
+                frameReadyTime.pop_front();
+                latchTime.pop_front();
+            }
+            frameReadyTime.push_back(values[0]);
+            latchTime.push_back(values[1]);
+        } else {
+            LOGE("Unable to retrieve frame data for frame %" PRIu64, fid);
+        }
+    }
+}
+
+
+/**
+ * Return the oldest available frame data.
+ */
+extern "C"
+JNIEXPORT jlongArray JNICALL
+Java_com_android_game_qualification_tests_SurfaceFlingerTestActivity_getFrameData(JNIEnv* env, jobject) {
+    if (frameReadyTime.empty()) {
+        return nullptr;
+    }
+    jlong buffer[] = { *frameReadyTime.begin(), *latchTime.begin() };
+    jlongArray result = env->NewLongArray(2);
+    env->SetLongArrayRegion(result, 0, 2, buffer);
+    frameReadyTime.pop_front();
+    latchTime.pop_front();
+    return result;
+}
+
+
diff --git a/sample_app/Android.bp b/sample_app/Android.bp
new file mode 100644
index 0000000..5f79451
--- /dev/null
+++ b/sample_app/Android.bp
@@ -0,0 +1,39 @@
+// Copyright 2018, 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.
+
+cc_library_shared {
+    name: "libgamecore_sample",
+    sdk_version: "26",
+    srcs: [
+        "src/cpp/circle.cpp",
+        "src/cpp/common.cpp",
+        "src/cpp/shader.cpp",
+        "src/cpp/vecmath.cpp",
+        "src/cpp/renderer.cpp",
+        "src/cpp/main.cpp",
+    ],
+    stl: "libc++",
+    shared_libs: [
+        "libagq",
+        "liblog",
+        "libandroid",
+        "libEGL",
+        "libGLESv2",
+    ],
+    static_libs: ["android_native_app_glue"],
+    ldflags: [
+        "-uANativeActivity_onCreate",
+    ],
+    export_include_dirs: ["src/cpp"],
+}
diff --git a/sample_app/Android.mk b/sample_app/Android.mk
index 7930ab5..6d89f99 100644
--- a/sample_app/Android.mk
+++ b/sample_app/Android.mk
@@ -17,21 +17,11 @@
 include $(CLEAR_VARS)
 
 LOCAL_SDK_VERSION := 26  # Oreo
-LOCAL_MODULE := libsample
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := src/cpp/sample_activity.cpp
-LOCAL_SHARED_LIBRARIES := libagq
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_SDK_VERSION := 26  # Oreo
 LOCAL_PACKAGE_NAME := GameQualificationSampleApp
 LOCAL_MODULE_TAGS := tests
-LOCAL_JNI_SHARED_LIBRARIES := libagq libsample
+LOCAL_JNI_SHARED_LIBRARIES := libagq libgamecore_sample
 LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src/java)
+LOCAL_NDK_STL_VARIANT := c++_shared
 
 include $(BUILD_PACKAGE)
 
diff --git a/sample_app/AndroidManifest.xml b/sample_app/AndroidManifest.xml
index a2c4a43..c4b9299 100644
--- a/sample_app/AndroidManifest.xml
+++ b/sample_app/AndroidManifest.xml
@@ -1,16 +1,23 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.game.qualification.example">
+    package="com.android.game.qualification.example">
 
-  <application
-      android:allowBackup="false"
-      android:label="SampleQualificationApp">
-    <activity android:name="com.android.game.qualification.example.SampleActivity">
-      <intent-filter>
-        <action android:name="android.intent.action.MAIN"/>
+    <application
+        android:allowBackup="false"
+        android:hasCode="false"
+        android:label="SampleQualificationApp">
 
-        <category android:name="android.intent.category.LAUNCHER"/>
-      </intent-filter>
-    </activity>
-  </application>
+        <!-- Our activity is the built-in NativeActivity framework class.
+         This will take care of integrating with our NDK code. -->
+        <activity android:name="android.app.NativeActivity"
+            android:configChanges="orientation|keyboardHidden">
+            <!-- Tell NativeActivity the name of our .so -->
+            <meta-data android:name="android.app.lib_name"
+                android:value="gamecore_sample" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
 </manifest>
diff --git a/sample_app/src/cpp/circle.cpp b/sample_app/src/cpp/circle.cpp
new file mode 100644
index 0000000..c2b72d9
--- /dev/null
+++ b/sample_app/src/cpp/circle.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#include "circle.h"
+#include "common.h"
+#include "shader.h"
+
+#include <cstdlib>
+#include <math.h>
+
+namespace android {
+namespace gamecore {
+
+namespace {
+
+GLfloat* initializeVertices(int numSegments) {
+    GLfloat* vertices = new GLfloat[2 * (numSegments + 2)];
+    vertices[0] = 0.0f;
+    vertices[1] = 0.0f;
+
+    float dTheta = static_cast<float>(2 * M_PI / numSegments);
+    for (int i = 0; i < numSegments + 1; i++) {
+        vertices[(i + 1) * 2] = cos(dTheta * i);
+        vertices[(i + 1) * 2 + 1] = sin(dTheta * i);
+    }
+    return vertices;
+}
+
+int const NUM_SEGMENTS = 36;
+GLfloat* gVertices = initializeVertices(NUM_SEGMENTS);
+
+auto const gVertexShader =
+        "uniform highp float uRadius;\n"
+        "uniform highp mat4 uMvpMatrix;\n"
+        "attribute vec4 vPosition;\n"
+        "void main() {\n"
+        "  gl_Position = uMvpMatrix * (vPosition * vec4(vec3(uRadius), 1.0));"
+        "}\n";
+
+auto const gFragmentShader =
+        "uniform lowp vec3 uColor;\n"
+        "void main() {\n"
+        "  gl_FragColor = vec4(uColor, 1.0);\n"
+        "}\n";
+
+} // end of anonymous namespace
+
+Circle::Circle(float radius) {
+    mRadius = radius;
+    mProgram = createProgram(gVertexShader, gFragmentShader);
+    mMvpMatrixHandle = glGetUniformLocation(mProgram, "uMvpMatrix");
+    mRadiusHandle = glGetUniformLocation(mProgram, "uRadius");
+    mVPositionHandle = glGetAttribLocation(mProgram, "vPosition");
+    mColorHandle = glGetUniformLocation(mProgram, "uColor");
+    checkGlError("glGetAttribLocation");
+    LOGI("glGetAttribLocation(\"vPosition\") = %d\n",
+         mVPositionHandle);
+
+    setColor(0.0f, 1.0f, 0.0f);
+}
+
+void Circle::draw() const {
+    Mat4 mvpMatrix = mViewProjectionMatrix * Mat4::Translation(mPosition);
+    glUseProgram(mProgram);
+    checkGlError("glUseProgram");
+
+    glUniform3fv(mColorHandle, 1, mColor);
+    checkGlError("glUniform3fv");
+    glUniform1f(mRadiusHandle, mRadius);
+    checkGlError("glUniform1f");
+    glVertexAttribPointer(mVPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gVertices);
+    checkGlError("glVertexAttribPointer");
+    glUniformMatrix4fv(mMvpMatrixHandle, 1, GL_FALSE, mvpMatrix.Ptr());
+    checkGlError("glUniformMatrix4fv");
+    glEnableVertexAttribArray(mVPositionHandle);
+    checkGlError("glEnableVertexAttribArray");
+    glDrawArrays(GL_TRIANGLE_FAN, 0, NUM_SEGMENTS + 2);
+    checkGlError("glDrawArrays");
+}
+
+void Circle::setColor(float r, float g, float b) {
+    mColor[0] = r;
+    mColor[1] = g;
+    mColor[2] = b;
+}
+
+const Vec3& Circle::getPosition() const {
+    return mPosition;
+}
+
+void Circle::setPosition(const Vec3& position) {
+    mPosition = position;
+}
+
+void Circle::updateViewProjection(const Mat4& vpMatrix) {
+    mViewProjectionMatrix = vpMatrix;
+}
+
+} // end of namespace gamecore
+} // end of namespace android
+
diff --git a/sample_app/src/cpp/circle.h b/sample_app/src/cpp/circle.h
new file mode 100644
index 0000000..da02d75
--- /dev/null
+++ b/sample_app/src/cpp/circle.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#ifndef ANDROID_GAMECORE_CIRCLE_H
+#define ANDROID_GAMECORE_CIRCLE_H
+
+#include "vecmath.h"
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace gamecore {
+
+class Circle {
+private:
+    float mRadius;
+    GLuint mProgram;
+
+    GLuint mVPositionHandle;
+    GLuint mMvpMatrixHandle;
+    GLuint mRadiusHandle;
+    GLuint mColorHandle;
+    GLfloat mColor[3];
+    Vec3 mPosition;
+    Mat4 mViewProjectionMatrix;
+
+public:
+    Circle(float radius);
+
+    void setColor(float r, float g, float b);
+
+    const Vec3& getPosition() const;
+    void setPosition(const Vec3& position);
+
+    void updateViewProjection(const Mat4& vpMatrix);
+    void draw() const;
+
+};
+
+} // end of namespace gamecore
+} // end of namespace android
+
+#endif // ANDROID_GAMECORE_CIRCLE_H
diff --git a/sample_app/src/cpp/common.cpp b/sample_app/src/cpp/common.cpp
new file mode 100644
index 0000000..682d98d
--- /dev/null
+++ b/sample_app/src/cpp/common.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#include "common.h"
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace gamecore {
+
+void checkGlError(const char* op) {
+    for (GLint error = glGetError(); error; error = glGetError()) {
+        LOGI("after %s() glError (0x%x)\n", op, error);
+    }
+}
+
+} // end of namespace gamecore
+} // end of namespace android
diff --git a/sample_app/src/cpp/common.h b/sample_app/src/cpp/common.h
new file mode 100644
index 0000000..32b62a5
--- /dev/null
+++ b/sample_app/src/cpp/common.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#ifndef ANDROID_GAMECORE_COMMON_H
+#define ANDROID_GAMECORE_COMMON_H
+
+#include <android/log.h>
+
+#define LOG_TAG "gamecore-sample"
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+
+namespace android {
+namespace gamecore {
+
+void checkGlError(const char* op);
+
+} // end of namespace gamecore
+} // end of namespace android
+
+#endif //ANDROID_GAMECORE_COMMON_H
diff --git a/sample_app/src/cpp/main.cpp b/sample_app/src/cpp/main.cpp
new file mode 100644
index 0000000..36be21d
--- /dev/null
+++ b/sample_app/src/cpp/main.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2010 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.
+ *
+ */
+
+#include "agq.h"
+#include "circle.h"
+#include "common.h"
+#include "renderer.h"
+#include "vecmath.h"
+
+#include <cassert>
+#include <chrono>
+#include <cinttypes>
+#include <cstdlib>
+#include <cstring>
+#include <errno.h>
+#include <initializer_list>
+#include <jni.h>
+#include <memory>
+#include <sys/time.h>
+#include <vector>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <android/sensor.h>
+#include <android/log.h>
+#include <android_native_app_glue.h>
+
+using namespace std::chrono_literals;
+using namespace android::gamecore;
+
+namespace {
+
+int animating = 0;
+
+/**
+ * Process the next input event.
+ */
+int32_t engine_handle_input(struct android_app*, AInputEvent* event) {
+    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
+        animating = 1;
+        return 1;
+    }
+    return 0;
+}
+
+/**
+ * Process the next main command.
+ */
+void engine_handle_cmd(struct android_app* app, int32_t cmd) {
+    //struct engine* engine = (struct engine*)app->userData;
+    Renderer* renderer = reinterpret_cast<Renderer*>(app->userData);
+
+    switch (cmd) {
+        case APP_CMD_SAVE_STATE:
+            // We are not saving the state.
+            break;
+        case APP_CMD_INIT_WINDOW:
+            // The window is being shown, get it ready.
+            if (app->window != NULL) {
+                renderer->initDisplay(app->window);
+                renderer->draw();
+                animating = 1;
+            }
+            break;
+        case APP_CMD_TERM_WINDOW:
+            // The window is being hidden or closed, clean it up.
+            //engine_term_display(engine);
+            renderer->terminateDisplay();
+            animating = 0;
+            break;
+        case APP_CMD_LOST_FOCUS:
+            // Also stop animating.
+            animating = 0;
+            renderer->draw();
+            break;
+        default:
+            break;
+    }
+}
+
+} // end of anonymous namespace
+
+/**
+ * This is the main entry point of a native application that is using
+ * android_native_app_glue.  It runs in its own thread, with its own
+ * event loop for receiving input events and doing other things.
+ */
+void android_main(struct android_app* state) {
+    std::srand(0);
+
+    LOGI("Running with SDK %d", state->activity->sdkVersion);
+
+    std::unique_ptr<Renderer> renderer(new Renderer(1));
+    state->userData = renderer.get();
+    state->onAppCmd = engine_handle_cmd;
+    state->onInputEvent = engine_handle_input;
+
+    // loop waiting for stuff to do.
+    while (1) {
+        // Read all pending events.
+        int events;
+        struct android_poll_source* source;
+
+        // If not animating, we will block forever waiting for events.
+        // If animating, we loop until all events are read, then continue
+        // to draw the next frame of animation.
+        while (ALooper_pollAll(animating ? 0 : -1, NULL, &events, (void**)&source) >= 0) {
+
+            // Process this event.
+            if (source != NULL) {
+                source->process(state, source);
+            }
+
+            // Check if we are exiting.
+            if (state->destroyRequested != 0) {
+                renderer->terminateDisplay();
+                return;
+            }
+        }
+
+        if (animating) {
+            renderer->update();
+
+            // Drawing is throttled to the screen update rate, so there
+            // is no need to do timing here.
+            renderer->draw();
+
+            // Broadcast intent every 5 seconds.
+            static auto last_timestamp = std::chrono::steady_clock::now();
+            auto now = std::chrono::steady_clock::now();
+            if (now - last_timestamp >= std::chrono::seconds(5)) {
+                last_timestamp = now;
+                android::GameQualification qualification;
+                qualification.startLoop(state->activity);
+            }
+        }
+    }
+}
diff --git a/sample_app/src/cpp/renderer.cpp b/sample_app/src/cpp/renderer.cpp
new file mode 100644
index 0000000..b478f2f
--- /dev/null
+++ b/sample_app/src/cpp/renderer.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#include "renderer.h"
+
+#include <vector>
+#include <memory>
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+
+namespace android {
+namespace gamecore {
+
+namespace {
+
+const float RADIUS = 0.1f;
+
+void printGLString(const char *name, GLenum s) {
+    const char *v = (const char *) glGetString(s);
+    LOGI("GL %s = %s\n", name, v);
+}
+
+} // end of anonymous namespace
+
+Renderer::Renderer(int numCircles) {
+    memset(&egl, 0, sizeof(egl));
+    state.numCircles = numCircles;
+}
+
+int Renderer::initDisplay(NativeWindowType window) {
+    egl.window = window;
+
+    // initialize OpenGL ES and EGL
+
+    /*
+     * Here specify the attributes of the desired configuration.
+     * Below, we select an EGLConfig with at least 8 bits per color
+     * component compatible with on-screen windows
+     */
+    const EGLint attribs[] = {
+            EGL_RENDERABLE_TYPE,
+            EGL_OPENGL_ES2_BIT,
+            EGL_BLUE_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_RED_SIZE, 8,
+            EGL_NONE
+    };
+    EGLint w;
+    EGLint h;
+    EGLint format;
+    EGLint numConfigs;
+    EGLConfig config;
+    EGLSurface surface;
+    EGLContext context;
+
+    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    eglInitialize(display, 0, 0);
+
+    /* Here, the application chooses the configuration it desires.
+     * find the best match if possible, otherwise use the very first one
+     */
+    eglChooseConfig(display, attribs, nullptr,0, &numConfigs);
+    std::unique_ptr<EGLConfig[]> supportedConfigs(new EGLConfig[numConfigs]);
+    assert(supportedConfigs);
+    eglChooseConfig(display, attribs, supportedConfigs.get(), numConfigs, &numConfigs);
+    assert(numConfigs);
+    auto i = 0;
+    for (; i < numConfigs; i++) {
+        auto& cfg = supportedConfigs[i];
+        EGLint r, g, b, d;
+        if (eglGetConfigAttrib(display, cfg, EGL_RED_SIZE, &r)   &&
+            eglGetConfigAttrib(display, cfg, EGL_GREEN_SIZE, &g) &&
+            eglGetConfigAttrib(display, cfg, EGL_BLUE_SIZE, &b)  &&
+            eglGetConfigAttrib(display, cfg, EGL_DEPTH_SIZE, &d) &&
+            r == 8 && g == 8 && b == 8 && d == 0 ) {
+
+            config = supportedConfigs[i];
+            break;
+        }
+    }
+    if (i == numConfigs) {
+        config = supportedConfigs[0];
+    }
+
+    EGLint attrib_list[] = {
+            EGL_CONTEXT_CLIENT_VERSION, 2,
+            EGL_NONE
+    };
+
+    /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
+     * guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
+     * As soon as we picked a EGLConfig, we can safely reconfigure the
+     * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
+    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
+    surface = eglCreateWindowSurface(display, config, window, NULL);
+    context = eglCreateContext(display, config, NULL, attrib_list);
+
+    // Enable Android timing.
+    eglSurfaceAttrib(display, surface, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);
+
+    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
+        LOGW("Unable to eglMakeCurrent");
+        return -1;
+    }
+
+    eglQuerySurface(display, surface, EGL_WIDTH, &w);
+    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
+
+    egl.display = display;
+    egl.context = context;
+    egl.surface = surface;
+    egl.width = w;
+    egl.height = h;
+    float ratio = float(w) / h;
+    egl.left = -ratio;
+    egl.right = ratio;
+    egl.top = 1.0f;
+    egl.bottom = -1.0f;
+
+    // Check openGL on the system
+    auto opengl_info = {GL_VENDOR, GL_RENDERER, GL_VERSION, GL_EXTENSIONS};
+    for (auto name : opengl_info) {
+        auto info = glGetString(name);
+        LOGI("OpenGL Info: %s", info);
+    }
+    printGLString("Version", GL_VERSION);
+    printGLString("Vendor", GL_VENDOR);
+    printGLString("Renderer", GL_RENDERER);
+    printGLString("Extensions", GL_EXTENSIONS);
+
+    // Initialize GL state.
+    glEnable(GL_CULL_FACE);
+    glDisable(GL_DEPTH_TEST);
+
+    // Initialize world state.
+    state.circles.resize(state.numCircles, Circle(RADIUS));
+    state.velocities.resize(state.numCircles);
+    for (auto& v : state.velocities) {
+        v = Vec2(
+                0.05f * (float(rand()) / RAND_MAX - 0.5f),
+                0.05f * (float(rand()) / RAND_MAX - 0.5f));
+    }
+
+    return 0;
+}
+
+void Renderer::terminateDisplay() {
+    if (egl.display != EGL_NO_DISPLAY) {
+        eglMakeCurrent(egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        if (egl.context != EGL_NO_CONTEXT) {
+            eglDestroyContext(egl.display, egl.context);
+        }
+        if (egl.surface != EGL_NO_SURFACE) {
+            eglDestroySurface(egl.display, egl.surface);
+        }
+        eglTerminate(egl.display);
+    }
+    egl.display = EGL_NO_DISPLAY;
+    egl.context = EGL_NO_CONTEXT;
+    egl.surface = EGL_NO_SURFACE;
+}
+
+void Renderer::update() {
+    // Done with events; draw next animation frame.
+    for (int i = 0; i < state.circles.size(); ++i) {
+        auto& circle = state.circles[i];
+        Vec2& v = state.velocities[i];
+        circle.setPosition(circle.getPosition() + Vec3(v, 0.0f));
+        Vec3 position = circle.getPosition();
+
+        float x;
+        float y;
+        float z;
+        position.Value(x, y, z);
+        float vx;
+        float vy;
+        v.Value(vx, vy);
+        if (x + RADIUS >= egl.right || x - RADIUS <= egl.left) {
+            vx = -vx;
+        }
+        if (y + RADIUS >= egl.top || y - RADIUS <= egl.bottom) {
+            vy = -vy;
+        }
+        v = Vec2(vx, vy);
+    }
+}
+
+void Renderer::draw() {
+    if (egl.display == NULL) {
+        // No display.
+        return;
+    }
+
+    // Just fill the screen with a color.
+    glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
+    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+    float ratio = float(egl.width) / egl.height;
+    Mat4 projectionMatrix = Mat4::Ortho2D(-ratio, 1.0f, ratio, -1.0f);
+    Mat4 viewMatrix =
+            Mat4::LookAt(
+                Vec3(0.0f, 0.0f, -1.0f),
+                Vec3(0.0f, 0.0f, 1.0f),
+                Vec3(0.0f, 1.0f, 0.0f));
+
+    for (int i = 0; i < state.circles.size(); ++i) {
+        auto& circle = state.circles[i];
+        circle.updateViewProjection(projectionMatrix * viewMatrix);
+        circle.draw();
+    }
+    eglSwapBuffers(egl.display, egl.surface);
+}
+
+} // end of namespace gamecore
+} // end of namespace android
+
+
diff --git a/sample_app/src/cpp/renderer.h b/sample_app/src/cpp/renderer.h
new file mode 100644
index 0000000..157bf91
--- /dev/null
+++ b/sample_app/src/cpp/renderer.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#ifndef ANDROID_GAMECORE_RENDERER_H
+#define ANDROID_GAMECORE_RENDERER_H
+
+#include "circle.h"
+
+#include <EGL/egl.h>
+#include <vector>
+
+namespace android {
+namespace gamecore {
+
+class Renderer {
+public:
+    struct Egl {
+        //struct android_app* app;
+        NativeWindowType window;
+        EGLDisplay display;
+        EGLSurface surface;
+        EGLContext context;
+        int32_t width;
+        int32_t height;
+        float left;
+        float right;
+        float top;
+        float bottom;
+    };
+
+    struct State {
+        int numCircles;
+        std::vector<Circle> circles;
+        std::vector<Vec2> velocities;
+    };
+
+    struct Egl egl;
+    struct State state;
+
+    Renderer(int numCircles);
+    int initDisplay(NativeWindowType window);
+    void terminateDisplay();
+    void update();
+    void draw();
+};
+
+} // end of namespace gamecore
+} // end of namespace android
+
+#endif // ANDROID_GAMECORE_RENDERER_H
diff --git a/sample_app/src/cpp/shader.cpp b/sample_app/src/cpp/shader.cpp
new file mode 100644
index 0000000..5512d55
--- /dev/null
+++ b/sample_app/src/cpp/shader.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#include "shader.h"
+#include "common.h"
+#include <cstdlib>
+#include <android/log.h>
+
+namespace android {
+namespace gamecore {
+
+namespace {
+
+GLuint loadShader(GLenum shaderType, const char* pSource) {
+
+    GLuint shader = glCreateShader(shaderType);
+    if (shader) {
+        glShaderSource(shader, 1, &pSource, NULL);
+        glCompileShader(shader);
+        GLint compiled = 0;
+        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+        if (!compiled) {
+            GLint infoLen = 0;
+            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+            if (infoLen) {
+                char* buf = (char*) malloc(infoLen);
+                if (buf) {
+                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
+                    LOGE("Could not compile shader %d:\n%s\n",
+                         shaderType, buf);
+                    free(buf);
+                }
+                glDeleteShader(shader);
+                shader = 0;
+            }
+        }
+    }
+    return shader;
+}
+
+} // end of anonymous namespace
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
+    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+    if (!vertexShader) {
+        return 0;
+    }
+
+    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+    if (!pixelShader) {
+        return 0;
+    }
+
+    GLuint program = glCreateProgram();
+    if (program) {
+        glAttachShader(program, vertexShader);
+        checkGlError("glAttachShader");
+        glAttachShader(program, pixelShader);
+        checkGlError("glAttachShader");
+        glLinkProgram(program);
+        GLint linkStatus = GL_FALSE;
+        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+        if (linkStatus != GL_TRUE) {
+            GLint bufLength = 0;
+            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+            if (bufLength) {
+                char* buf = (char*) malloc(bufLength);
+                if (buf) {
+                    glGetProgramInfoLog(program, bufLength, NULL, buf);
+                    LOGE("Could not link program:\n%s\n", buf);
+                    free(buf);
+                }
+            }
+            glDeleteProgram(program);
+            program = 0;
+        }
+    }
+    return program;
+}
+
+} // end of namespace gamecore
+} // end of namespace android
diff --git a/sample_app/src/cpp/shader.h b/sample_app/src/cpp/shader.h
new file mode 100644
index 0000000..978b685
--- /dev/null
+++ b/sample_app/src/cpp/shader.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.
+ *
+ */
+
+#ifndef ANDROID_GAMECORE_SHADER_H
+#define ANDROID_GAMECORE_SHADER_H
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace gamecore {
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource);
+
+} // end of namespace gamecore
+} // end of namespace android
+
+#endif // ANDROID_GAMECORE_SHADER_H
diff --git a/sample_app/src/cpp/vecmath.cpp b/sample_app/src/cpp/vecmath.cpp
new file mode 100644
index 0000000..55e50de
--- /dev/null
+++ b/sample_app/src/cpp/vecmath.cpp
@@ -0,0 +1,411 @@
+/*
+ * Copy_right 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * y_ou may_ not use this file ex_cept 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 ex_press or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Modified from
+// https://github.com/googlesamples/android-ndk/blob/master/teapots/common/ndk_helper/vecmath.cpp
+
+//--------------------------------------------------------------------------------
+// vecmath.cpp
+//--------------------------------------------------------------------------------
+#include "vecmath.h"
+
+namespace android {
+namespace gamecore {
+
+//--------------------------------------------------------------------------------
+// vec3
+//--------------------------------------------------------------------------------
+Vec3::Vec3(const Vec4& vec) {
+  x_ = vec.x_;
+  y_ = vec.y_;
+  z_ = vec.z_;
+}
+
+//--------------------------------------------------------------------------------
+// vec4
+//--------------------------------------------------------------------------------
+Vec4 Vec4::operator*(const Mat4& rhs) const {
+  Vec4 out;
+  out.x_ = x_ * rhs.f_[0] + y_ * rhs.f_[1] + z_ * rhs.f_[2] + w_ * rhs.f_[3];
+  out.y_ = x_ * rhs.f_[4] + y_ * rhs.f_[5] + z_ * rhs.f_[6] + w_ * rhs.f_[7];
+  out.z_ = x_ * rhs.f_[8] + y_ * rhs.f_[9] + z_ * rhs.f_[10] + w_ * rhs.f_[11];
+  out.w_ =
+      x_ * rhs.f_[12] + y_ * rhs.f_[13] + z_ * rhs.f_[14] + w_ * rhs.f_[15];
+  return out;
+}
+
+//--------------------------------------------------------------------------------
+// mat4
+//--------------------------------------------------------------------------------
+Mat4::Mat4() {
+  for (int32_t i = 0; i < 16; ++i) f_[i] = 0.f;
+  // column major identity matrix
+  f_[0] = f_[5] = f_[10] = f_[15] = 1.0f;
+}
+
+Mat4::Mat4(const float* mIn) {
+  for (int32_t i = 0; i < 16; ++i) f_[i] = mIn[i];
+}
+
+Mat4 Mat4::operator*(const Mat4& rhs) const {
+  Mat4 ret;
+  ret.f_[0] = f_[0] * rhs.f_[0] + f_[4] * rhs.f_[1] + f_[8] * rhs.f_[2] +
+              f_[12] * rhs.f_[3];
+  ret.f_[1] = f_[1] * rhs.f_[0] + f_[5] * rhs.f_[1] + f_[9] * rhs.f_[2] +
+              f_[13] * rhs.f_[3];
+  ret.f_[2] = f_[2] * rhs.f_[0] + f_[6] * rhs.f_[1] + f_[10] * rhs.f_[2] +
+              f_[14] * rhs.f_[3];
+  ret.f_[3] = f_[3] * rhs.f_[0] + f_[7] * rhs.f_[1] + f_[11] * rhs.f_[2] +
+              f_[15] * rhs.f_[3];
+
+  ret.f_[4] = f_[0] * rhs.f_[4] + f_[4] * rhs.f_[5] + f_[8] * rhs.f_[6] +
+              f_[12] * rhs.f_[7];
+  ret.f_[5] = f_[1] * rhs.f_[4] + f_[5] * rhs.f_[5] + f_[9] * rhs.f_[6] +
+              f_[13] * rhs.f_[7];
+  ret.f_[6] = f_[2] * rhs.f_[4] + f_[6] * rhs.f_[5] + f_[10] * rhs.f_[6] +
+              f_[14] * rhs.f_[7];
+  ret.f_[7] = f_[3] * rhs.f_[4] + f_[7] * rhs.f_[5] + f_[11] * rhs.f_[6] +
+              f_[15] * rhs.f_[7];
+
+  ret.f_[8] = f_[0] * rhs.f_[8] + f_[4] * rhs.f_[9] + f_[8] * rhs.f_[10] +
+              f_[12] * rhs.f_[11];
+  ret.f_[9] = f_[1] * rhs.f_[8] + f_[5] * rhs.f_[9] + f_[9] * rhs.f_[10] +
+              f_[13] * rhs.f_[11];
+  ret.f_[10] = f_[2] * rhs.f_[8] + f_[6] * rhs.f_[9] + f_[10] * rhs.f_[10] +
+               f_[14] * rhs.f_[11];
+  ret.f_[11] = f_[3] * rhs.f_[8] + f_[7] * rhs.f_[9] + f_[11] * rhs.f_[10] +
+               f_[15] * rhs.f_[11];
+
+  ret.f_[12] = f_[0] * rhs.f_[12] + f_[4] * rhs.f_[13] + f_[8] * rhs.f_[14] +
+               f_[12] * rhs.f_[15];
+  ret.f_[13] = f_[1] * rhs.f_[12] + f_[5] * rhs.f_[13] + f_[9] * rhs.f_[14] +
+               f_[13] * rhs.f_[15];
+  ret.f_[14] = f_[2] * rhs.f_[12] + f_[6] * rhs.f_[13] + f_[10] * rhs.f_[14] +
+               f_[14] * rhs.f_[15];
+  ret.f_[15] = f_[3] * rhs.f_[12] + f_[7] * rhs.f_[13] + f_[11] * rhs.f_[14] +
+               f_[15] * rhs.f_[15];
+
+  return ret;
+}
+
+Vec4 Mat4::operator*(const Vec4& rhs) const {
+  Vec4 ret;
+  ret.x_ = rhs.x_ * f_[0] + rhs.y_ * f_[4] + rhs.z_ * f_[8] + rhs.w_ * f_[12];
+  ret.y_ = rhs.x_ * f_[1] + rhs.y_ * f_[5] + rhs.z_ * f_[9] + rhs.w_ * f_[13];
+  ret.z_ = rhs.x_ * f_[2] + rhs.y_ * f_[6] + rhs.z_ * f_[10] + rhs.w_ * f_[14];
+  ret.w_ = rhs.x_ * f_[3] + rhs.y_ * f_[7] + rhs.z_ * f_[11] + rhs.w_ * f_[15];
+  return ret;
+}
+
+Mat4 Mat4::Inverse() {
+  Mat4 ret;
+  float det_1;
+  float pos = 0;
+  float neg = 0;
+  float temp;
+
+  temp = f_[0] * f_[5] * f_[10];
+  if (temp >= 0)
+    pos += temp;
+  else
+    neg += temp;
+  temp = f_[4] * f_[9] * f_[2];
+  if (temp >= 0)
+    pos += temp;
+  else
+    neg += temp;
+  temp = f_[8] * f_[1] * f_[6];
+  if (temp >= 0)
+    pos += temp;
+  else
+    neg += temp;
+  temp = -f_[8] * f_[5] * f_[2];
+  if (temp >= 0)
+    pos += temp;
+  else
+    neg += temp;
+  temp = -f_[4] * f_[1] * f_[10];
+  if (temp >= 0)
+    pos += temp;
+  else
+    neg += temp;
+  temp = -f_[0] * f_[9] * f_[6];
+  if (temp >= 0)
+    pos += temp;
+  else
+    neg += temp;
+  det_1 = pos + neg;
+
+  if (det_1 == 0.0) {
+    // Error
+  } else {
+    det_1 = 1.0f / det_1;
+    ret.f_[0] = (f_[5] * f_[10] - f_[9] * f_[6]) * det_1;
+    ret.f_[1] = -(f_[1] * f_[10] - f_[9] * f_[2]) * det_1;
+    ret.f_[2] = (f_[1] * f_[6] - f_[5] * f_[2]) * det_1;
+    ret.f_[4] = -(f_[4] * f_[10] - f_[8] * f_[6]) * det_1;
+    ret.f_[5] = (f_[0] * f_[10] - f_[8] * f_[2]) * det_1;
+    ret.f_[6] = -(f_[0] * f_[6] - f_[4] * f_[2]) * det_1;
+    ret.f_[8] = (f_[4] * f_[9] - f_[8] * f_[5]) * det_1;
+    ret.f_[9] = -(f_[0] * f_[9] - f_[8] * f_[1]) * det_1;
+    ret.f_[10] = (f_[0] * f_[5] - f_[4] * f_[1]) * det_1;
+
+    /* Calculate -C * inverse(A) */
+    ret.f_[12] =
+        -(f_[12] * ret.f_[0] + f_[13] * ret.f_[4] + f_[14] * ret.f_[8]);
+    ret.f_[13] =
+        -(f_[12] * ret.f_[1] + f_[13] * ret.f_[5] + f_[14] * ret.f_[9]);
+    ret.f_[14] =
+        -(f_[12] * ret.f_[2] + f_[13] * ret.f_[6] + f_[14] * ret.f_[10]);
+
+    ret.f_[3] = 0.0f;
+    ret.f_[7] = 0.0f;
+    ret.f_[11] = 0.0f;
+    ret.f_[15] = 1.0f;
+  }
+
+  *this = ret;
+  return *this;
+}
+
+//--------------------------------------------------------------------------------
+// Misc
+//--------------------------------------------------------------------------------
+Mat4 Mat4::RotationX(const float fAngle) {
+  Mat4 ret;
+  float fCosine, fSine;
+
+  fCosine = cosf(fAngle);
+  fSine = sinf(fAngle);
+
+  ret.f_[0] = 1.0f;
+  ret.f_[4] = 0.0f;
+  ret.f_[8] = 0.0f;
+  ret.f_[12] = 0.0f;
+  ret.f_[1] = 0.0f;
+  ret.f_[5] = fCosine;
+  ret.f_[9] = fSine;
+  ret.f_[13] = 0.0f;
+  ret.f_[2] = 0.0f;
+  ret.f_[6] = -fSine;
+  ret.f_[10] = fCosine;
+  ret.f_[14] = 0.0f;
+  ret.f_[3] = 0.0f;
+  ret.f_[7] = 0.0f;
+  ret.f_[11] = 0.0f;
+  ret.f_[15] = 1.0f;
+  return ret;
+}
+
+Mat4 Mat4::RotationY(const float fAngle) {
+  Mat4 ret;
+  float fCosine, fSine;
+
+  fCosine = cosf(fAngle);
+  fSine = sinf(fAngle);
+
+  ret.f_[0] = fCosine;
+  ret.f_[4] = 0.0f;
+  ret.f_[8] = -fSine;
+  ret.f_[12] = 0.0f;
+  ret.f_[1] = 0.0f;
+  ret.f_[5] = 1.0f;
+  ret.f_[9] = 0.0f;
+  ret.f_[13] = 0.0f;
+  ret.f_[2] = fSine;
+  ret.f_[6] = 0.0f;
+  ret.f_[10] = fCosine;
+  ret.f_[14] = 0.0f;
+  ret.f_[3] = 0.0f;
+  ret.f_[7] = 0.0f;
+  ret.f_[11] = 0.0f;
+  ret.f_[15] = 1.0f;
+  return ret;
+}
+
+Mat4 Mat4::RotationZ(const float fAngle) {
+  Mat4 ret;
+  float fCosine, fSine;
+
+  fCosine = cosf(fAngle);
+  fSine = sinf(fAngle);
+
+  ret.f_[0] = fCosine;
+  ret.f_[4] = fSine;
+  ret.f_[8] = 0.0f;
+  ret.f_[12] = 0.0f;
+  ret.f_[1] = -fSine;
+  ret.f_[5] = fCosine;
+  ret.f_[9] = 0.0f;
+  ret.f_[13] = 0.0f;
+  ret.f_[2] = 0.0f;
+  ret.f_[6] = 0.0f;
+  ret.f_[10] = 1.0f;
+  ret.f_[14] = 0.0f;
+  ret.f_[3] = 0.0f;
+  ret.f_[7] = 0.0f;
+  ret.f_[11] = 0.0f;
+  ret.f_[15] = 1.0f;
+  return ret;
+}
+
+Mat4 Mat4::Scale(const float scaleX, const float scaleY, const float scaleZ) {
+  Mat4 ret;
+  ret.f_[0] = scaleX;
+  ret.f_[5] = scaleY;
+  ret.f_[10] = scaleZ;
+  ret.f_[1] = ret.f_[2] = ret.f_[3] = ret.f_[4] = ret.f_[6] = ret.f_[7] =
+      ret.f_[8] = ret.f_[9] = ret.f_[11] = ret.f_[12] = ret.f_[13] =
+          ret.f_[14] = 0.f;
+  ret.f_[15] = 1.0f;
+  return ret;
+}
+
+Mat4 Mat4::Translation(const float fX, const float fY, const float fZ) {
+  Mat4 ret;
+  ret.f_[0] = 1.0f;
+  ret.f_[4] = 0.0f;
+  ret.f_[8] = 0.0f;
+  ret.f_[12] = fX;
+  ret.f_[1] = 0.0f;
+  ret.f_[5] = 1.0f;
+  ret.f_[9] = 0.0f;
+  ret.f_[13] = fY;
+  ret.f_[2] = 0.0f;
+  ret.f_[6] = 0.0f;
+  ret.f_[10] = 1.0f;
+  ret.f_[14] = fZ;
+  ret.f_[3] = 0.0f;
+  ret.f_[7] = 0.0f;
+  ret.f_[11] = 0.0f;
+  ret.f_[15] = 1.0f;
+  return ret;
+}
+
+Mat4 Mat4::Translation(const Vec3 vec) {
+  Mat4 ret;
+  ret.f_[0] = 1.0f;
+  ret.f_[4] = 0.0f;
+  ret.f_[8] = 0.0f;
+  ret.f_[12] = vec.x_;
+  ret.f_[1] = 0.0f;
+  ret.f_[5] = 1.0f;
+  ret.f_[9] = 0.0f;
+  ret.f_[13] = vec.y_;
+  ret.f_[2] = 0.0f;
+  ret.f_[6] = 0.0f;
+  ret.f_[10] = 1.0f;
+  ret.f_[14] = vec.z_;
+  ret.f_[3] = 0.0f;
+  ret.f_[7] = 0.0f;
+  ret.f_[11] = 0.0f;
+  ret.f_[15] = 1.0f;
+  return ret;
+}
+
+Mat4 Mat4::Perspective(float width, float height, float nearPlane,
+                       float farPlane) {
+  float n2 = 2.0f * nearPlane;
+  float rcpnmf = 1.f / (nearPlane - farPlane);
+
+  Mat4 result;
+  result.f_[0] = n2 / width;
+  result.f_[4] = 0;
+  result.f_[8] = 0;
+  result.f_[12] = 0;
+  result.f_[1] = 0;
+  result.f_[5] = n2 / height;
+  result.f_[9] = 0;
+  result.f_[13] = 0;
+  result.f_[2] = 0;
+  result.f_[6] = 0;
+  result.f_[10] = (farPlane + nearPlane) * rcpnmf;
+  result.f_[14] = farPlane * rcpnmf * n2;
+  result.f_[3] = 0;
+  result.f_[7] = 0;
+  result.f_[11] = -1.0;
+  result.f_[15] = 0;
+
+  return result;
+}
+
+Mat4 Mat4::Ortho2D(float left, float top, float right, float bottom) {
+  const float zNear = -1.0f;
+  const float zFar = 1.0f;
+  const float inv_z = 1.0f / (zFar - zNear);
+  const float inv_y = 1.0f / (-top + bottom);
+  const float inv_x = 1.0f / (right - left);
+
+  Mat4 result;
+  result.f_[0] = 2.0f * inv_x;
+  result.f_[1] = 0.0f;
+  result.f_[2] = 0.0f;
+  result.f_[3] = 0.0f;
+
+  result.f_[4] = 0.0f;
+  result.f_[5] = 2.0 * inv_y;
+  result.f_[6] = 0.0f;
+  result.f_[7] = 0.0f;
+
+  result.f_[8] = 0.0f;
+  result.f_[9] = 0.0f;
+  result.f_[10] = -2.0f * inv_z;
+  result.f_[11] = 0.0f;
+
+  result.f_[12] = -(right + left) * inv_x;
+  result.f_[13] = (top + bottom) * inv_y;
+  result.f_[14] = -(zFar + zNear) * inv_z;
+  result.f_[15] = 1.0f;
+  return result;
+}
+
+Mat4 Mat4::LookAt(const Vec3& vec_eye, const Vec3& vec_at, const Vec3& vec_up) {
+  Vec3 vec_forward, vec_up_norm, vec_side;
+  Mat4 result;
+
+  vec_forward.x_ = vec_eye.x_ - vec_at.x_;
+  vec_forward.y_ = vec_eye.y_ - vec_at.y_;
+  vec_forward.z_ = vec_eye.z_ - vec_at.z_;
+
+  vec_forward.Normalize();
+  vec_up_norm = vec_up;
+  vec_up_norm.Normalize();
+  vec_side = vec_up_norm.Cross(vec_forward);
+  vec_up_norm = vec_forward.Cross(vec_side);
+
+  result.f_[0] = vec_side.x_;
+  result.f_[4] = vec_side.y_;
+  result.f_[8] = vec_side.z_;
+  result.f_[12] = 0;
+  result.f_[1] = vec_up_norm.x_;
+  result.f_[5] = vec_up_norm.y_;
+  result.f_[9] = vec_up_norm.z_;
+  result.f_[13] = 0;
+  result.f_[2] = vec_forward.x_;
+  result.f_[6] = vec_forward.y_;
+  result.f_[10] = vec_forward.z_;
+  result.f_[14] = 0;
+  result.f_[3] = 0;
+  result.f_[7] = 0;
+  result.f_[11] = 0;
+  result.f_[15] = 1.0;
+
+  result.PostTranslate(-vec_eye.x_, -vec_eye.y_, -vec_eye.z_);
+  return result;
+}
+
+}  // namespace gamecore
+}  // namespace android
diff --git a/sample_app/src/cpp/vecmath.h b/sample_app/src/cpp/vecmath.h
new file mode 100644
index 0000000..8909d4e
--- /dev/null
+++ b/sample_app/src/cpp/vecmath.h
@@ -0,0 +1,972 @@
+/*
+ * Copyright 2013 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.
+ */
+
+// Modified from
+// https://github.com/googlesamples/android-ndk/blob/master/teapots/common/ndk_helper/vecmath.h
+
+#ifndef ANDROID_GAMECORE_VECMATH_H
+#define ANDROID_GAMECORE_VECMATH_H
+
+#include "common.h"
+#include <cmath>
+#include <cstdint>
+
+namespace android {
+namespace gamecore {
+
+/******************************************************************
+ * Helper class for vector math operations
+ * Currently all implementations are in pure C++.
+ * Each class is an opaque class so caller does not have a direct access
+ * to each element. This is for an ease of future optimization to use vector
+ *operations.
+ *
+ */
+
+class Vec2;
+class Vec3;
+class Vec4;
+class Mat4;
+
+/******************************************************************
+ * 2 elements vector class
+ *
+ */
+class Vec2 {
+ private:
+  float x_;
+  float y_;
+
+ public:
+  friend class Vec3;
+  friend class Vec4;
+  friend class Mat4;
+  friend class Quaternion;
+
+  Vec2() { x_ = y_ = 0.f; }
+
+  Vec2(const float fX, const float fY) {
+    x_ = fX;
+    y_ = fY;
+  }
+
+  Vec2(const Vec2& vec) {
+    x_ = vec.x_;
+    y_ = vec.y_;
+  }
+
+  Vec2(const float* pVec) {
+    x_ = (*pVec++);
+    y_ = (*pVec++);
+  }
+
+  // Operators
+  Vec2 operator*(const Vec2& rhs) const {
+    Vec2 ret;
+    ret.x_ = x_ * rhs.x_;
+    ret.y_ = y_ * rhs.y_;
+    return ret;
+  }
+
+  Vec2 operator/(const Vec2& rhs) const {
+    Vec2 ret;
+    ret.x_ = x_ / rhs.x_;
+    ret.y_ = y_ / rhs.y_;
+    return ret;
+  }
+
+  Vec2 operator+(const Vec2& rhs) const {
+    Vec2 ret;
+    ret.x_ = x_ + rhs.x_;
+    ret.y_ = y_ + rhs.y_;
+    return ret;
+  }
+
+  Vec2 operator-(const Vec2& rhs) const {
+    Vec2 ret;
+    ret.x_ = x_ - rhs.x_;
+    ret.y_ = y_ - rhs.y_;
+    return ret;
+  }
+
+  Vec2& operator+=(const Vec2& rhs) {
+    x_ += rhs.x_;
+    y_ += rhs.y_;
+    return *this;
+  }
+
+  Vec2& operator-=(const Vec2& rhs) {
+    x_ -= rhs.x_;
+    y_ -= rhs.y_;
+    return *this;
+  }
+
+  Vec2& operator*=(const Vec2& rhs) {
+    x_ *= rhs.x_;
+    y_ *= rhs.y_;
+    return *this;
+  }
+
+  Vec2& operator/=(const Vec2& rhs) {
+    x_ /= rhs.x_;
+    y_ /= rhs.y_;
+    return *this;
+  }
+
+  // External operators
+  friend Vec2 operator-(const Vec2& rhs) { return Vec2(rhs) *= -1; }
+
+  friend Vec2 operator*(const float lhs, const Vec2& rhs) {
+    Vec2 ret;
+    ret.x_ = lhs * rhs.x_;
+    ret.y_ = lhs * rhs.y_;
+    return ret;
+  }
+
+  friend Vec2 operator/(const float lhs, const Vec2& rhs) {
+    Vec2 ret;
+    ret.x_ = lhs / rhs.x_;
+    ret.y_ = lhs / rhs.y_;
+    return ret;
+  }
+
+  // Operators with float
+  Vec2 operator*(const float& rhs) const {
+    Vec2 ret;
+    ret.x_ = x_ * rhs;
+    ret.y_ = y_ * rhs;
+    return ret;
+  }
+
+  Vec2& operator*=(const float& rhs) {
+    x_ = x_ * rhs;
+    y_ = y_ * rhs;
+    return *this;
+  }
+
+  Vec2 operator/(const float& rhs) const {
+    Vec2 ret;
+    ret.x_ = x_ / rhs;
+    ret.y_ = y_ / rhs;
+    return ret;
+  }
+
+  Vec2& operator/=(const float& rhs) {
+    x_ = x_ / rhs;
+    y_ = y_ / rhs;
+    return *this;
+  }
+
+  // Compare
+  bool operator==(const Vec2& rhs) const {
+    if (x_ != rhs.x_ || y_ != rhs.y_) return false;
+    return true;
+  }
+
+  bool operator!=(const Vec2& rhs) const {
+    if (x_ == rhs.x_) return false;
+
+    return true;
+  }
+
+  float Length() const { return sqrtf(x_ * x_ + y_ * y_); }
+
+  Vec2 Normalize() {
+    float len = Length();
+    x_ = x_ / len;
+    y_ = y_ / len;
+    return *this;
+  }
+
+  float Dot(const Vec2& rhs) { return x_ * rhs.x_ + y_ * rhs.y_; }
+
+  bool Validate() {
+    if (std::isnan(x_) || std::isnan(y_)) return false;
+    return true;
+  }
+
+  void Value(float& fX, float& fY) {
+    fX = x_;
+    fY = y_;
+  }
+
+  void Dump() { LOGI("Vec2 %f %f", x_, y_); }
+};
+
+/******************************************************************
+ * 3 elements vector class
+ *
+ */
+class Vec3 {
+ private:
+  float x_, y_, z_;
+
+ public:
+  friend class Vec4;
+  friend class Mat4;
+  friend class Quaternion;
+
+  Vec3() { x_ = y_ = z_ = 0.f; }
+
+  Vec3(const float fX, const float fY, const float fZ) {
+    x_ = fX;
+    y_ = fY;
+    z_ = fZ;
+  }
+
+  Vec3(const Vec3& vec) {
+    x_ = vec.x_;
+    y_ = vec.y_;
+    z_ = vec.z_;
+  }
+
+  Vec3(const float* pVec) {
+    x_ = (*pVec++);
+    y_ = (*pVec++);
+    z_ = *pVec;
+  }
+
+  Vec3(const Vec2& vec, float f) {
+    x_ = vec.x_;
+    y_ = vec.y_;
+    z_ = f;
+  }
+
+  Vec3(const Vec4& vec);
+
+  // Operators
+  Vec3 operator*(const Vec3& rhs) const {
+    Vec3 ret;
+    ret.x_ = x_ * rhs.x_;
+    ret.y_ = y_ * rhs.y_;
+    ret.z_ = z_ * rhs.z_;
+    return ret;
+  }
+
+  Vec3 operator/(const Vec3& rhs) const {
+    Vec3 ret;
+    ret.x_ = x_ / rhs.x_;
+    ret.y_ = y_ / rhs.y_;
+    ret.z_ = z_ / rhs.z_;
+    return ret;
+  }
+
+  Vec3 operator+(const Vec3& rhs) const {
+    Vec3 ret;
+    ret.x_ = x_ + rhs.x_;
+    ret.y_ = y_ + rhs.y_;
+    ret.z_ = z_ + rhs.z_;
+    return ret;
+  }
+
+  Vec3 operator-(const Vec3& rhs) const {
+    Vec3 ret;
+    ret.x_ = x_ - rhs.x_;
+    ret.y_ = y_ - rhs.y_;
+    ret.z_ = z_ - rhs.z_;
+    return ret;
+  }
+
+  Vec3& operator+=(const Vec3& rhs) {
+    x_ += rhs.x_;
+    y_ += rhs.y_;
+    z_ += rhs.z_;
+    return *this;
+  }
+
+  Vec3& operator-=(const Vec3& rhs) {
+    x_ -= rhs.x_;
+    y_ -= rhs.y_;
+    z_ -= rhs.z_;
+    return *this;
+  }
+
+  Vec3& operator*=(const Vec3& rhs) {
+    x_ *= rhs.x_;
+    y_ *= rhs.y_;
+    z_ *= rhs.z_;
+    return *this;
+  }
+
+  Vec3& operator/=(const Vec3& rhs) {
+    x_ /= rhs.x_;
+    y_ /= rhs.y_;
+    z_ /= rhs.z_;
+    return *this;
+  }
+
+  // External operators
+  friend Vec3 operator-(const Vec3& rhs) { return Vec3(rhs) *= -1; }
+
+  friend Vec3 operator*(const float lhs, const Vec3& rhs) {
+    Vec3 ret;
+    ret.x_ = lhs * rhs.x_;
+    ret.y_ = lhs * rhs.y_;
+    ret.z_ = lhs * rhs.z_;
+    return ret;
+  }
+
+  friend Vec3 operator/(const float lhs, const Vec3& rhs) {
+    Vec3 ret;
+    ret.x_ = lhs / rhs.x_;
+    ret.y_ = lhs / rhs.y_;
+    ret.z_ = lhs / rhs.z_;
+    return ret;
+  }
+
+  // Operators with float
+  Vec3 operator*(const float& rhs) const {
+    Vec3 ret;
+    ret.x_ = x_ * rhs;
+    ret.y_ = y_ * rhs;
+    ret.z_ = z_ * rhs;
+    return ret;
+  }
+
+  Vec3& operator*=(const float& rhs) {
+    x_ = x_ * rhs;
+    y_ = y_ * rhs;
+    z_ = z_ * rhs;
+    return *this;
+  }
+
+  Vec3 operator/(const float& rhs) const {
+    Vec3 ret;
+    ret.x_ = x_ / rhs;
+    ret.y_ = y_ / rhs;
+    ret.z_ = z_ / rhs;
+    return ret;
+  }
+
+  Vec3& operator/=(const float& rhs) {
+    x_ = x_ / rhs;
+    y_ = y_ / rhs;
+    z_ = z_ / rhs;
+    return *this;
+  }
+
+  // Compare
+  bool operator==(const Vec3& rhs) const {
+    if (x_ != rhs.x_ || y_ != rhs.y_ || z_ != rhs.z_) return false;
+    return true;
+  }
+
+  bool operator!=(const Vec3& rhs) const {
+    if (x_ == rhs.x_) return false;
+
+    return true;
+  }
+
+  float Length() const { return sqrtf(x_ * x_ + y_ * y_ + z_ * z_); }
+
+  Vec3 Normalize() {
+    float len = Length();
+    x_ = x_ / len;
+    y_ = y_ / len;
+    z_ = z_ / len;
+    return *this;
+  }
+
+  float Dot(const Vec3& rhs) { return x_ * rhs.x_ + y_ * rhs.y_ + z_ * rhs.z_; }
+
+  Vec3 Cross(const Vec3& rhs) {
+    Vec3 ret;
+    ret.x_ = y_ * rhs.z_ - z_ * rhs.y_;
+    ret.y_ = z_ * rhs.x_ - x_ * rhs.z_;
+    ret.z_ = x_ * rhs.y_ - y_ * rhs.x_;
+    return ret;
+  }
+
+  bool Validate() {
+    if (std::isnan(x_) || std::isnan(y_) || std::isnan(z_)) return false;
+    return true;
+  }
+
+  void Value(float& fX, float& fY, float& fZ) {
+    fX = x_;
+    fY = y_;
+    fZ = z_;
+  }
+
+  void Dump() { LOGI("Vec3 %f %f %f", x_, y_, z_); }
+};
+
+/******************************************************************
+ * 4 elements vector class
+ *
+ */
+class Vec4 {
+ private:
+  float x_, y_, z_, w_;
+
+ public:
+  friend class Vec3;
+  friend class Mat4;
+  friend class Quaternion;
+
+  Vec4() { x_ = y_ = z_ = w_ = 0.f; }
+
+  Vec4(const float fX, const float fY, const float fZ, const float fW) {
+    x_ = fX;
+    y_ = fY;
+    z_ = fZ;
+    w_ = fW;
+  }
+
+  Vec4(const Vec4& vec) {
+    x_ = vec.x_;
+    y_ = vec.y_;
+    z_ = vec.z_;
+    w_ = vec.w_;
+  }
+
+  Vec4(const Vec3& vec, const float fW) {
+    x_ = vec.x_;
+    y_ = vec.y_;
+    z_ = vec.z_;
+    w_ = fW;
+  }
+
+  Vec4(const float* pVec) {
+    x_ = (*pVec++);
+    y_ = (*pVec++);
+    z_ = *pVec;
+    w_ = *pVec;
+  }
+
+  // Operators
+  Vec4 operator*(const Vec4& rhs) const {
+    Vec4 ret;
+    ret.x_ = x_ * rhs.x_;
+    ret.y_ = y_ * rhs.y_;
+    ret.z_ = z_ * rhs.z_;
+    ret.w_ = z_ * rhs.w_;
+    return ret;
+  }
+
+  Vec4 operator/(const Vec4& rhs) const {
+    Vec4 ret;
+    ret.x_ = x_ / rhs.x_;
+    ret.y_ = y_ / rhs.y_;
+    ret.z_ = z_ / rhs.z_;
+    ret.w_ = z_ / rhs.w_;
+    return ret;
+  }
+
+  Vec4 operator+(const Vec4& rhs) const {
+    Vec4 ret;
+    ret.x_ = x_ + rhs.x_;
+    ret.y_ = y_ + rhs.y_;
+    ret.z_ = z_ + rhs.z_;
+    ret.w_ = z_ + rhs.w_;
+    return ret;
+  }
+
+  Vec4 operator-(const Vec4& rhs) const {
+    Vec4 ret;
+    ret.x_ = x_ - rhs.x_;
+    ret.y_ = y_ - rhs.y_;
+    ret.z_ = z_ - rhs.z_;
+    ret.w_ = z_ - rhs.w_;
+    return ret;
+  }
+
+  Vec4& operator+=(const Vec4& rhs) {
+    x_ += rhs.x_;
+    y_ += rhs.y_;
+    z_ += rhs.z_;
+    w_ += rhs.w_;
+    return *this;
+  }
+
+  Vec4& operator-=(const Vec4& rhs) {
+    x_ -= rhs.x_;
+    y_ -= rhs.y_;
+    z_ -= rhs.z_;
+    w_ -= rhs.w_;
+    return *this;
+  }
+
+  Vec4& operator*=(const Vec4& rhs) {
+    x_ *= rhs.x_;
+    y_ *= rhs.y_;
+    z_ *= rhs.z_;
+    w_ *= rhs.w_;
+    return *this;
+  }
+
+  Vec4& operator/=(const Vec4& rhs) {
+    x_ /= rhs.x_;
+    y_ /= rhs.y_;
+    z_ /= rhs.z_;
+    w_ /= rhs.w_;
+    return *this;
+  }
+
+  // External operators
+  friend Vec4 operator-(const Vec4& rhs) { return Vec4(rhs) *= -1; }
+
+  friend Vec4 operator*(const float lhs, const Vec4& rhs) {
+    Vec4 ret;
+    ret.x_ = lhs * rhs.x_;
+    ret.y_ = lhs * rhs.y_;
+    ret.z_ = lhs * rhs.z_;
+    ret.w_ = lhs * rhs.w_;
+    return ret;
+  }
+
+  friend Vec4 operator/(const float lhs, const Vec4& rhs) {
+    Vec4 ret;
+    ret.x_ = lhs / rhs.x_;
+    ret.y_ = lhs / rhs.y_;
+    ret.z_ = lhs / rhs.z_;
+    ret.w_ = lhs / rhs.w_;
+    return ret;
+  }
+
+  // Operators with float
+  Vec4 operator*(const float& rhs) const {
+    Vec4 ret;
+    ret.x_ = x_ * rhs;
+    ret.y_ = y_ * rhs;
+    ret.z_ = z_ * rhs;
+    ret.w_ = w_ * rhs;
+    return ret;
+  }
+
+  Vec4& operator*=(const float& rhs) {
+    x_ = x_ * rhs;
+    y_ = y_ * rhs;
+    z_ = z_ * rhs;
+    w_ = w_ * rhs;
+    return *this;
+  }
+
+  Vec4 operator/(const float& rhs) const {
+    Vec4 ret;
+    ret.x_ = x_ / rhs;
+    ret.y_ = y_ / rhs;
+    ret.z_ = z_ / rhs;
+    ret.w_ = w_ / rhs;
+    return ret;
+  }
+
+  Vec4& operator/=(const float& rhs) {
+    x_ = x_ / rhs;
+    y_ = y_ / rhs;
+    z_ = z_ / rhs;
+    w_ = w_ / rhs;
+    return *this;
+  }
+
+  // Compare
+  bool operator==(const Vec4& rhs) const {
+    if (x_ != rhs.x_ || y_ != rhs.y_ || z_ != rhs.z_ || w_ != rhs.w_)
+      return false;
+    return true;
+  }
+
+  bool operator!=(const Vec4& rhs) const {
+    if (x_ == rhs.x_) return false;
+
+    return true;
+  }
+
+  Vec4 operator*(const Mat4& rhs) const;
+
+  float Length() const { return sqrtf(x_ * x_ + y_ * y_ + z_ * z_ + w_ * w_); }
+
+  Vec4 Normalize() {
+    float len = Length();
+    x_ = x_ / len;
+    y_ = y_ / len;
+    z_ = z_ / len;
+    w_ = w_ / len;
+    return *this;
+  }
+
+  float Dot(const Vec3& rhs) { return x_ * rhs.x_ + y_ * rhs.y_ + z_ * rhs.z_; }
+
+  Vec3 Cross(const Vec3& rhs) {
+    Vec3 ret;
+    ret.x_ = y_ * rhs.z_ - z_ * rhs.y_;
+    ret.y_ = z_ * rhs.x_ - x_ * rhs.z_;
+    ret.z_ = x_ * rhs.y_ - y_ * rhs.x_;
+    return ret;
+  }
+
+  bool Validate() {
+    if (std::isnan(x_) || std::isnan(y_) ||
+        std::isnan(z_) || std::isnan(w_))
+      return false;
+
+    return true;
+  }
+
+  void Value(float& fX, float& fY, float& fZ, float& fW) {
+    fX = x_;
+    fY = y_;
+    fZ = z_;
+    fW = w_;
+  }
+};
+
+/******************************************************************
+ * 4x4 matrix
+ *
+ */
+class Mat4 {
+ private:
+  float f_[16];
+
+ public:
+  friend class Vec3;
+  friend class Vec4;
+  friend class Quaternion;
+
+  Mat4();
+  Mat4(const float*);
+
+  Mat4 operator*(const Mat4& rhs) const;
+  Vec4 operator*(const Vec4& rhs) const;
+
+  Mat4 operator+(const Mat4& rhs) const {
+    Mat4 ret;
+    for (int32_t i = 0; i < 16; ++i) {
+      ret.f_[i] = f_[i] + rhs.f_[i];
+    }
+    return ret;
+  }
+
+  Mat4 operator-(const Mat4& rhs) const {
+    Mat4 ret;
+    for (int32_t i = 0; i < 16; ++i) {
+      ret.f_[i] = f_[i] - rhs.f_[i];
+    }
+    return ret;
+  }
+
+  Mat4& operator+=(const Mat4& rhs) {
+    for (int32_t i = 0; i < 16; ++i) {
+      f_[i] += rhs.f_[i];
+    }
+    return *this;
+  }
+
+  Mat4& operator-=(const Mat4& rhs) {
+    for (int32_t i = 0; i < 16; ++i) {
+      f_[i] -= rhs.f_[i];
+    }
+    return *this;
+  }
+
+  Mat4& operator*=(const Mat4& rhs) {
+    Mat4 ret;
+    ret.f_[0] = f_[0] * rhs.f_[0] + f_[4] * rhs.f_[1] + f_[8] * rhs.f_[2] +
+                f_[12] * rhs.f_[3];
+    ret.f_[1] = f_[1] * rhs.f_[0] + f_[5] * rhs.f_[1] + f_[9] * rhs.f_[2] +
+                f_[13] * rhs.f_[3];
+    ret.f_[2] = f_[2] * rhs.f_[0] + f_[6] * rhs.f_[1] + f_[10] * rhs.f_[2] +
+                f_[14] * rhs.f_[3];
+    ret.f_[3] = f_[3] * rhs.f_[0] + f_[7] * rhs.f_[1] + f_[11] * rhs.f_[2] +
+                f_[15] * rhs.f_[3];
+
+    ret.f_[4] = f_[0] * rhs.f_[4] + f_[4] * rhs.f_[5] + f_[8] * rhs.f_[6] +
+                f_[12] * rhs.f_[7];
+    ret.f_[5] = f_[1] * rhs.f_[4] + f_[5] * rhs.f_[5] + f_[9] * rhs.f_[6] +
+                f_[13] * rhs.f_[7];
+    ret.f_[6] = f_[2] * rhs.f_[4] + f_[6] * rhs.f_[5] + f_[10] * rhs.f_[6] +
+                f_[14] * rhs.f_[7];
+    ret.f_[7] = f_[3] * rhs.f_[4] + f_[7] * rhs.f_[5] + f_[11] * rhs.f_[6] +
+                f_[15] * rhs.f_[7];
+
+    ret.f_[8] = f_[0] * rhs.f_[8] + f_[4] * rhs.f_[9] + f_[8] * rhs.f_[10] +
+                f_[12] * rhs.f_[11];
+    ret.f_[9] = f_[1] * rhs.f_[8] + f_[5] * rhs.f_[9] + f_[9] * rhs.f_[10] +
+                f_[13] * rhs.f_[11];
+    ret.f_[10] = f_[2] * rhs.f_[8] + f_[6] * rhs.f_[9] + f_[10] * rhs.f_[10] +
+                 f_[14] * rhs.f_[11];
+    ret.f_[11] = f_[3] * rhs.f_[8] + f_[7] * rhs.f_[9] + f_[11] * rhs.f_[10] +
+                 f_[15] * rhs.f_[11];
+
+    ret.f_[12] = f_[0] * rhs.f_[12] + f_[4] * rhs.f_[13] + f_[8] * rhs.f_[14] +
+                 f_[12] * rhs.f_[15];
+    ret.f_[13] = f_[1] * rhs.f_[12] + f_[5] * rhs.f_[13] + f_[9] * rhs.f_[14] +
+                 f_[13] * rhs.f_[15];
+    ret.f_[14] = f_[2] * rhs.f_[12] + f_[6] * rhs.f_[13] + f_[10] * rhs.f_[14] +
+                 f_[14] * rhs.f_[15];
+    ret.f_[15] = f_[3] * rhs.f_[12] + f_[7] * rhs.f_[13] + f_[11] * rhs.f_[14] +
+                 f_[15] * rhs.f_[15];
+
+    *this = ret;
+    return *this;
+  }
+
+  Mat4 operator*(const float rhs) {
+    Mat4 ret;
+    for (int32_t i = 0; i < 16; ++i) {
+      ret.f_[i] = f_[i] * rhs;
+    }
+    return ret;
+  }
+
+  Mat4& operator*=(const float rhs) {
+    for (int32_t i = 0; i < 16; ++i) {
+      f_[i] *= rhs;
+    }
+    return *this;
+  }
+
+  Mat4& operator=(const Mat4& rhs) {
+    for (int32_t i = 0; i < 16; ++i) {
+      f_[i] = rhs.f_[i];
+    }
+    return *this;
+  }
+
+  Mat4 Inverse();
+
+  Mat4 Transpose() {
+    Mat4 ret;
+    ret.f_[0] = f_[0];
+    ret.f_[1] = f_[4];
+    ret.f_[2] = f_[8];
+    ret.f_[3] = f_[12];
+    ret.f_[4] = f_[1];
+    ret.f_[5] = f_[5];
+    ret.f_[6] = f_[9];
+    ret.f_[7] = f_[13];
+    ret.f_[8] = f_[2];
+    ret.f_[9] = f_[6];
+    ret.f_[10] = f_[10];
+    ret.f_[11] = f_[14];
+    ret.f_[12] = f_[3];
+    ret.f_[13] = f_[7];
+    ret.f_[14] = f_[11];
+    ret.f_[15] = f_[15];
+    *this = ret;
+    return *this;
+  }
+
+  Mat4& PostTranslate(float tx, float ty, float tz) {
+    f_[12] += (tx * f_[0]) + (ty * f_[4]) + (tz * f_[8]);
+    f_[13] += (tx * f_[1]) + (ty * f_[5]) + (tz * f_[9]);
+    f_[14] += (tx * f_[2]) + (ty * f_[6]) + (tz * f_[10]);
+    f_[15] += (tx * f_[3]) + (ty * f_[7]) + (tz * f_[11]);
+    return *this;
+  }
+
+  float* Ptr() { return f_; }
+
+  //--------------------------------------------------------------------------------
+  // Misc
+  //--------------------------------------------------------------------------------
+  static Mat4 Perspective(float width, float height, float nearPlane,
+                          float farPlane);
+  static Mat4 Ortho2D(float left, float top, float right, float bottom);
+
+  static Mat4 LookAt(const Vec3& vEye, const Vec3& vAt, const Vec3& vUp);
+
+  static Mat4 Translation(const float fX, const float fY, const float fZ);
+  static Mat4 Translation(const Vec3 vec);
+
+  static Mat4 RotationX(const float angle);
+
+  static Mat4 RotationY(const float angle);
+
+  static Mat4 RotationZ(const float angle);
+
+  static Mat4 Scale(const float scaleX, const float scaleY, const float scaleZ);
+
+  static Mat4 Identity() {
+    Mat4 ret;
+    ret.f_[0] = 1.f;
+    ret.f_[1] = 0;
+    ret.f_[2] = 0;
+    ret.f_[3] = 0;
+    ret.f_[4] = 0;
+    ret.f_[5] = 1.f;
+    ret.f_[6] = 0;
+    ret.f_[7] = 0;
+    ret.f_[8] = 0;
+    ret.f_[9] = 0;
+    ret.f_[10] = 1.f;
+    ret.f_[11] = 0;
+    ret.f_[12] = 0;
+    ret.f_[13] = 0;
+    ret.f_[14] = 0;
+    ret.f_[15] = 1.f;
+    return ret;
+  }
+
+  void Dump() {
+    LOGI("%f %f %f %f", f_[0], f_[1], f_[2], f_[3]);
+    LOGI("%f %f %f %f", f_[4], f_[5], f_[6], f_[7]);
+    LOGI("%f %f %f %f", f_[8], f_[9], f_[10], f_[11]);
+    LOGI("%f %f %f %f", f_[12], f_[13], f_[14], f_[15]);
+  }
+};
+
+/******************************************************************
+ * Quaternion class
+ *
+ */
+class Quaternion {
+ private:
+  float x_, y_, z_, w_;
+
+ public:
+  friend class Vec3;
+  friend class Vec4;
+  friend class Mat4;
+
+  Quaternion() {
+    x_ = 0.f;
+    y_ = 0.f;
+    z_ = 0.f;
+    w_ = 1.f;
+  }
+
+  Quaternion(const float fX, const float fY, const float fZ, const float fW) {
+    x_ = fX;
+    y_ = fY;
+    z_ = fZ;
+    w_ = fW;
+  }
+
+  Quaternion(const Vec3 vec, const float fW) {
+    x_ = vec.x_;
+    y_ = vec.y_;
+    z_ = vec.z_;
+    w_ = fW;
+  }
+
+  Quaternion(const float* p) {
+    x_ = *p++;
+    y_ = *p++;
+    z_ = *p++;
+    w_ = *p++;
+  }
+
+  Quaternion operator*(const Quaternion rhs) {
+    Quaternion ret;
+    ret.x_ = x_ * rhs.w_ + y_ * rhs.z_ - z_ * rhs.y_ + w_ * rhs.x_;
+    ret.y_ = -x_ * rhs.z_ + y_ * rhs.w_ + z_ * rhs.x_ + w_ * rhs.y_;
+    ret.z_ = x_ * rhs.y_ - y_ * rhs.x_ + z_ * rhs.w_ + w_ * rhs.z_;
+    ret.w_ = -x_ * rhs.x_ - y_ * rhs.y_ - z_ * rhs.z_ + w_ * rhs.w_;
+    return ret;
+  }
+
+  Quaternion& operator*=(const Quaternion rhs) {
+    Quaternion ret;
+    ret.x_ = x_ * rhs.w_ + y_ * rhs.z_ - z_ * rhs.y_ + w_ * rhs.x_;
+    ret.y_ = -x_ * rhs.z_ + y_ * rhs.w_ + z_ * rhs.x_ + w_ * rhs.y_;
+    ret.z_ = x_ * rhs.y_ - y_ * rhs.x_ + z_ * rhs.w_ + w_ * rhs.z_;
+    ret.w_ = -x_ * rhs.x_ - y_ * rhs.y_ - z_ * rhs.z_ + w_ * rhs.w_;
+    *this = ret;
+    return *this;
+  }
+
+  Quaternion Conjugate() {
+    x_ = -x_;
+    y_ = -y_;
+    z_ = -z_;
+    return *this;
+  }
+
+  // Non destuctive version
+  Quaternion Conjugated() {
+    Quaternion ret;
+    ret.x_ = -x_;
+    ret.y_ = -y_;
+    ret.z_ = -z_;
+    ret.w_ = w_;
+    return ret;
+  }
+
+  void ToMatrix(Mat4& mat) {
+    float x2 = x_ * x_ * 2.0f;
+    float y2 = y_ * y_ * 2.0f;
+    float z2 = z_ * z_ * 2.0f;
+    float xy = x_ * y_ * 2.0f;
+    float yz = y_ * z_ * 2.0f;
+    float zx = z_ * x_ * 2.0f;
+    float xw = x_ * w_ * 2.0f;
+    float yw = y_ * w_ * 2.0f;
+    float zw = z_ * w_ * 2.0f;
+
+    mat.f_[0] = 1.0f - y2 - z2;
+    mat.f_[1] = xy + zw;
+    mat.f_[2] = zx - yw;
+    mat.f_[4] = xy - zw;
+    mat.f_[5] = 1.0f - z2 - x2;
+    mat.f_[6] = yz + xw;
+    mat.f_[8] = zx + yw;
+    mat.f_[9] = yz - xw;
+    mat.f_[10] = 1.0f - x2 - y2;
+
+    mat.f_[3] = mat.f_[7] = mat.f_[11] = mat.f_[12] = mat.f_[13] = mat.f_[14] =
+        0.0f;
+    mat.f_[15] = 1.0f;
+  }
+
+  void ToMatrixPreserveTranslate(Mat4& mat) {
+    float x2 = x_ * x_ * 2.0f;
+    float y2 = y_ * y_ * 2.0f;
+    float z2 = z_ * z_ * 2.0f;
+    float xy = x_ * y_ * 2.0f;
+    float yz = y_ * z_ * 2.0f;
+    float zx = z_ * x_ * 2.0f;
+    float xw = x_ * w_ * 2.0f;
+    float yw = y_ * w_ * 2.0f;
+    float zw = z_ * w_ * 2.0f;
+
+    mat.f_[0] = 1.0f - y2 - z2;
+    mat.f_[1] = xy + zw;
+    mat.f_[2] = zx - yw;
+    mat.f_[4] = xy - zw;
+    mat.f_[5] = 1.0f - z2 - x2;
+    mat.f_[6] = yz + xw;
+    mat.f_[8] = zx + yw;
+    mat.f_[9] = yz - xw;
+    mat.f_[10] = 1.0f - x2 - y2;
+
+    mat.f_[3] = mat.f_[7] = mat.f_[11] = 0.0f;
+    mat.f_[15] = 1.0f;
+  }
+
+  static Quaternion RotationAxis(const Vec3 axis, const float angle) {
+    Quaternion ret;
+    float s = sinf(angle / 2);
+    ret.x_ = s * axis.x_;
+    ret.y_ = s * axis.y_;
+    ret.z_ = s * axis.z_;
+    ret.w_ = cosf(angle / 2);
+    return ret;
+  }
+
+  void Value(float& fX, float& fY, float& fZ, float& fW) {
+    fX = x_;
+    fY = y_;
+    fZ = z_;
+    fW = w_;
+  }
+};
+
+}  // namespace gamecore
+}  // namespace android
+
+#endif /* ANDROID_GAMECORE_VECMATH_H */
diff --git a/sample_app/src/java/com/android/game/qualification/example/MyGLRenderer.java b/sample_app/src/java/com/android/game/qualification/example/MyGLRenderer.java
deleted file mode 100644
index b1a786b..0000000
--- a/sample_app/src/java/com/android/game/qualification/example/MyGLRenderer.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2018 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.game.qualification.example;
-
-import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
-import android.opengl.Matrix;
-import android.util.Log;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-import java.util.Random;
-
-public class MyGLRenderer implements GLSurfaceView.Renderer {
-
-    private static final String TAG = "MyGLRenderer";
-    private Sphere[] mSphere;
-    private int numSpheres = 1;
-    // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
-    private final float[] mMVPMatrix = new float[16];
-    private final float[] mProjectionMatrix = new float[16];
-    private final float[] mViewMatrix = new float[16];
-
-    private float[][] mPosition;
-    private float[][] mVelocity;
-    private float mHeight;
-    private float mWidth;
-
-
-    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
-        // Set the background frame color
-        GLES20.glClearColor(0.0f, 0.5f, 0.5f, 1.0f);
-
-        Random rand = new Random(5);
-
-        mSphere = new Sphere[numSpheres];
-        mVelocity = new float[numSpheres][3];
-        mPosition = new float[numSpheres][3];
-
-        for (int i = 0; i < mSphere.length; ++i) {
-            mSphere[i] = new Sphere(0.05f, 75);
-
-            mVelocity[i][0] = (rand.nextFloat() * 2 - 1) * 0.02f;
-            mVelocity[i][1] = (rand.nextFloat() * 2 - 1) * 0.02f;
-            mVelocity[i][2] = 0.0f;
-        }
-    }
-
-    public void onDrawFrame(GL10 unused) {
-        float[] scratch = new float[16];
-
-        // Redraw background color
-        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
-
-        // Set the camera position (View matrix)
-        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 10, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
-        // Calculate the projection and view transformation
-        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
-
-        for (int i = 0; i < mSphere.length; i++) {
-            // Update position;
-            mPosition[i][0] += mVelocity[i][0];
-            mPosition[i][1] += mVelocity[i][1];
-            mPosition[i][2] += mVelocity[i][2];
-            if (mPosition[i][0] - mSphere[i].getRadius() < 0.0f
-                    || mPosition[i][0] + mSphere[i].getRadius() > mWidth) {
-                mVelocity[i][0] = -mVelocity[i][0];
-            }
-            if (mPosition[i][1] - mSphere[i].getRadius() < 0.0f
-                    || mPosition[i][1] + mSphere[i].getRadius() > mHeight) {
-                mVelocity[i][1] = -mVelocity[i][1];
-            }
-
-            Matrix.translateM(scratch, 0, mMVPMatrix, 0, mPosition[i][0], mPosition[i][1], mPosition[i][2]);
-
-            mSphere[i].draw(scratch);
-        }
-    }
-
-    public void onSurfaceChanged(GL10 unused, int width, int height) {
-        GLES20.glViewport(0, 0, width, height);
-
-        float ratio = (float) width / height;
-        mHeight = 1;
-        mWidth = ratio;
-        for (int i = 0; i < mSphere.length; i++) {
-            mPosition[i][0] = mWidth / 2.0f;
-            mPosition[i][1] = mHeight / 2.0f;
-        }
-
-        // This projection matrix is applied to object coordinates in the onDrawFrame() method.
-        // Using orthographic projection to make it easier to determine edge of screen.
-        Matrix.orthoM(mProjectionMatrix, 0, 0, ratio, 0, 1, 3, 20);
-    }
-        /**
-     * Utility method for compiling a OpenGL shader.
-     *
-     * <p><strong>Note:</strong> When developing shaders, use the checkGlError()
-     * method to debug shader coding errors.</p>
-     *
-     * @param type - Vertex or fragment shader type.
-     * @param shaderCode - String containing the shader code.
-     * @return - Returns an id for the shader.
-     */
-    public static int loadShader(int type, String shaderCode){
-        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
-        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
-        int shader = GLES20.glCreateShader(type);
-        // add the source code to the shader and compile it
-        GLES20.glShaderSource(shader, shaderCode);
-        GLES20.glCompileShader(shader);
-        return shader;
-    }
-    /**
-    * Utility method for debugging OpenGL calls. Provide the name of the call
-    * just after making it:
-    *
-    * <pre>
-    * mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
-    * MyGLRenderer.checkGlError("glGetUniformLocation");</pre>
-    *
-    * If the operation is not successful, the check throws an error.
-    *
-    * @param glOperation - Name of the OpenGL call to check.
-    */
-    public static void checkGlError(String glOperation) {
-        int error;
-        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
-            Log.e(TAG, glOperation + ": glError " + error);
-            throw new RuntimeException(glOperation + ": glError " + error);
-        }
-    }
-
-}
diff --git a/sample_app/src/java/com/android/game/qualification/example/MyGLSurfaceView.java b/sample_app/src/java/com/android/game/qualification/example/MyGLSurfaceView.java
deleted file mode 100644
index 1b7f3de..0000000
--- a/sample_app/src/java/com/android/game/qualification/example/MyGLSurfaceView.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2018 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.game.qualification.example;
-
-import android.content.Context;
-import android.opengl.GLSurfaceView;
-
-class MyGLSurfaceView extends GLSurfaceView {
-
-    private final MyGLRenderer mRenderer;
-
-    public MyGLSurfaceView(Context context){
-        super(context);
-
-        // Create an OpenGL ES 2.0 context
-        setEGLContextClientVersion(2);
-
-        mRenderer = new MyGLRenderer();
-
-        // Set the Renderer for drawing on the GLSurfaceView
-        setRenderer(mRenderer);
-    }
-}
diff --git a/sample_app/src/java/com/android/game/qualification/example/SampleActivity.java b/sample_app/src/java/com/android/game/qualification/example/SampleActivity.java
deleted file mode 100644
index 87a48af..0000000
--- a/sample_app/src/java/com/android/game/qualification/example/SampleActivity.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 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.game.qualification.example;
-
-import android.app.Activity;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-import android.os.Handler;
-
-public class SampleActivity extends Activity {
-    private static final long LOOP_PERIOD_MS = 5 * 1000L;
-
-    private GLSurfaceView mGLView;
-    private Handler mHandler;
-    private Runnable mTask;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // Create a GLSurfaceView instance and set it
-        // as the ContentView for this Activity.
-        mGLView = new MyGLSurfaceView(this);
-        setContentView(mGLView);
-
-        // Loop every 5s.
-        mHandler = new Handler();
-        mTask = new Runnable() {
-            @Override
-            public void run() {
-                broadcastIntent();
-                mHandler.postDelayed(this, LOOP_PERIOD_MS);
-            }
-        };
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mHandler.postDelayed(mTask, LOOP_PERIOD_MS);
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mHandler.removeCallbacks(mTask);
-    }
-
-    public native void broadcastIntent();
-
-    static {
-        System.loadLibrary("sample");
-    }
-}
diff --git a/sample_app/src/java/com/android/game/qualification/example/Sphere.java b/sample_app/src/java/com/android/game/qualification/example/Sphere.java
deleted file mode 100644
index d2c0146..0000000
--- a/sample_app/src/java/com/android/game/qualification/example/Sphere.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2018 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.game.qualification.example;
-
-import android.opengl.GLES20;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-
-public class Sphere {
-    private static final String VERTEX_SHADER_CODE =
-            // This matrix member variable provides a hook to manipulate
-            // the coordinates of the objects that use this vertex shader
-            "uniform mat4 uMVPMatrix;" +
-                    "attribute vec4 vPosition;" +
-                    "void main() {" +
-                    // the matrix must be included as a modifier of gl_Position
-                    // Note that the uMVPMatrix factor *must be first* in order
-                    // for the matrix multiplication product to be correct.
-                    "  gl_Position = uMVPMatrix * vPosition;" +
-                    "}";
-    private static final String FRAGMENT_SHADER_CODE =
-            "precision mediump float;" +
-                    "uniform vec4 vColor;" +
-                    "void main() {" +
-                    "  gl_FragColor = vColor;" +
-                    "}";
-    // number of coordinates per vertex in this array
-    private static final int COORDS_PER_VERTEX = 3;
-    private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
-
-    private FloatBuffer mSphereVertices;
-    private IntBuffer mIndices;
-    private final int mProgram;
-
-    private int mPositionHandle;
-    private int mColorHandle;
-    private int mMVPMatrixHandle;
-
-    private double mRadius;
-    private int mNumSegments;
-    private int mPoints;
-    private int mNumIndices;
-    private float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 0.0f };
-
-    public Sphere( float radius, int numSegments) {
-        this.mRadius = radius;
-        this.mNumSegments = numSegments;
-        mPoints = build();
-
-        // prepare shaders and OpenGL program
-        int vertexShader = MyGLRenderer.loadShader(
-                GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE);
-        int fragmentShader = MyGLRenderer.loadShader(
-                GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);
-        mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
-        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
-        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
-        GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables
-    }
-
-    public double getRadius() {
-        return mRadius;
-    }
-
-    public void draw(float[] mvpMatrix) {
-        // Add program to OpenGL environment
-        GLES20.glUseProgram(mProgram);
-        // get handle to vertex shader's vPosition member
-        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
-        // Enable a handle to the triangle vertices
-        GLES20.glEnableVertexAttribArray(mPositionHandle);
-        // Prepare the triangle coordinate data
-        GLES20.glVertexAttribPointer(
-                mPositionHandle,
-                COORDS_PER_VERTEX,
-                GLES20.GL_FLOAT,
-                false,
-                VERTEX_STRIDE,
-                mSphereVertices);
-        // get handle to fragment shader's vColor member
-        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
-        // Set color for drawing the triangle
-        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
-        // get handle to shape's transformation matrix
-        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
-        MyGLRenderer.checkGlError("glGetUniformLocation");
-        // Apply the projection and view transformation
-        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
-        MyGLRenderer.checkGlError("glUniformMatrix4fv");
-        // Draw the sphere.
-        GLES20.glDrawElements(
-                GLES20.GL_TRIANGLE_STRIP, mNumIndices, GLES20.GL_UNSIGNED_INT, mIndices);
-        // Disable vertex array
-        GLES20.glDisableVertexAttribArray(mPositionHandle);
-
-    }
-
-    private int build() {
-        // initialize vertex byte buffer for shape coordinates
-        mSphereVertices =
-                ByteBuffer.allocateDirect(
-                        mNumSegments * (mNumSegments + 1) * COORDS_PER_VERTEX * VERTEX_STRIDE * 2)
-                        .order(ByteOrder.nativeOrder())
-                        .asFloatBuffer();
-        mIndices =
-                ByteBuffer.allocateDirect(mNumSegments * (mNumSegments + 2) * 2 * 4)
-                        .order(ByteOrder.nativeOrder())
-                        .asIntBuffer();
-        /*
-         * x = r * sin(phi) * cos(theta)
-         * y = r * sin(phi) * sin(theta)
-         * z = r * cos(phi)
-         */
-        double dTheta = 2 * Math.PI / mNumSegments;
-        double dPhi = Math.PI / mNumSegments;
-        int points = 0;
-        boolean firstLoop = true;
-
-        double epsilon = 1e-10;
-
-        for(double phi = -(Math.PI); phi <= 0 + epsilon; phi += dPhi) {
-            //for each stage calculating the slices
-            for(double theta = 0.0; theta < (Math.PI * 2) - epsilon; theta+=dTheta) {
-                mSphereVertices.put((float) (mRadius * Math.sin(phi) * Math.cos(theta)) );
-                mSphereVertices.put((float) (mRadius * Math.sin(phi) * Math.sin(theta)) );
-                mSphereVertices.put((float) (mRadius * Math.cos(phi)) );
-
-                if (!firstLoop) {
-                    mIndices.put(points - mNumSegments);
-                    mIndices.put(points);
-                    mNumIndices += 2;
-                }
-                points++;
-
-            }
-            if (!firstLoop) {
-                // Finish off layer
-                mIndices.put(points - 2 * mNumSegments);
-                mIndices.put(points - mNumSegments);
-                mNumIndices += 2;
-            }
-            firstLoop = false;
-        }
-        mIndices.position(0);
-        mSphereVertices.position(0);
-
-        return points;
-    }
-}