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;
- }
-}