Sample: textured dodecahedron rotated by fingers
This commit is contained in:
+2
-2
@@ -138,14 +138,14 @@ void setupCompilationFlags() {
|
|||||||
"android_arm32":
|
"android_arm32":
|
||||||
["-target", getTargetArg("android_arm32"),
|
["-target", getTargetArg("android_arm32"),
|
||||||
"--sysroot=$android_arm32SysrootDir",
|
"--sysroot=$android_arm32SysrootDir",
|
||||||
"-D__ANDROID__", "-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=32", "-DANDROID",
|
"-D__ANDROID__", "-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=32", "-DKONAN_ANDROID",
|
||||||
// HACKS!
|
// HACKS!
|
||||||
"-I$android_arm32SysrootDir/usr/include/c++/4.9.x",
|
"-I$android_arm32SysrootDir/usr/include/c++/4.9.x",
|
||||||
"-I$android_arm32SysrootDir/usr/include/c++/4.9.x/arm-linux-androideabi"],
|
"-I$android_arm32SysrootDir/usr/include/c++/4.9.x/arm-linux-androideabi"],
|
||||||
"android_arm64":
|
"android_arm64":
|
||||||
["-target", getTargetArg("android_arm64"),
|
["-target", getTargetArg("android_arm64"),
|
||||||
"--sysroot=$android_arm64SysrootDir",
|
"--sysroot=$android_arm64SysrootDir",
|
||||||
"-D__ANDROID__", "-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=64", "-DANDROID",
|
"-D__ANDROID__", "-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=64", "-DKONAN_ANDROID",
|
||||||
// HACKS!
|
// HACKS!
|
||||||
"-I$android_arm64SysrootDir/usr/include/c++/4.9.x",
|
"-I$android_arm64SysrootDir/usr/include/c++/4.9.x",
|
||||||
"-I$android_arm64SysrootDir/usr/include/c++/4.9.x/aarch64-linux-android"]
|
"-I$android_arm64SysrootDir/usr/include/c++/4.9.x/aarch64-linux-android"]
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
#include "KString.h"
|
#include "KString.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef KONAN_ANDROID
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -43,9 +44,11 @@
|
|||||||
//--- main --------------------------------------------------------------------//
|
//--- main --------------------------------------------------------------------//
|
||||||
extern "C" KInt Konan_start(const ObjHeader*);
|
extern "C" KInt Konan_start(const ObjHeader*);
|
||||||
|
|
||||||
static int pipeC, pipeKonan;
|
namespace {
|
||||||
|
int pipeC, pipeKonan;
|
||||||
|
|
||||||
static NativeActivityState nativeActivityState;
|
NativeActivityState nativeActivityState;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void getNativeActivityState(NativeActivityState* state) {
|
extern "C" void getNativeActivityState(NativeActivityState* state) {
|
||||||
state->activity = nativeActivityState.activity;
|
state->activity = nativeActivityState.activity;
|
||||||
@@ -54,13 +57,13 @@ extern "C" void getNativeActivityState(NativeActivityState* state) {
|
|||||||
state->looper = nativeActivityState.looper;
|
state->looper = nativeActivityState.looper;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void notifySysEventProcessed(int fd) {
|
extern "C" void notifySysEventProcessed() {
|
||||||
if (fd != pipeKonan) return;
|
|
||||||
int8_t message;
|
int8_t message;
|
||||||
write(pipeKonan, &message, sizeof(message));
|
write(pipeKonan, &message, sizeof(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* entry(void* param) {
|
namespace {
|
||||||
|
void* entry(void* param) {
|
||||||
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
|
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
|
||||||
ALooper_addFd(looper, pipeKonan, LOOPER_ID_SYS, ALOOPER_EVENT_INPUT, NULL, NULL);
|
ALooper_addFd(looper, pipeKonan, LOOPER_ID_SYS, ALOOPER_EVENT_INPUT, NULL, NULL);
|
||||||
nativeActivityState.looper = looper;
|
nativeActivityState.looper = looper;
|
||||||
@@ -83,7 +86,7 @@ static void* entry(void* param) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void runKonan_start() {
|
void runKonan_start() {
|
||||||
int pipes[2];
|
int pipes[2];
|
||||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes)) {
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes)) {
|
||||||
LOGE("Could not create pipe: %s", strerror(errno));
|
LOGE("Could not create pipe: %s", strerror(errno));
|
||||||
@@ -99,7 +102,7 @@ static void runKonan_start() {
|
|||||||
pthread_create(&thread, &attr, entry, nullptr);
|
pthread_create(&thread, &attr, entry, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void putEventSynchronously(void* event) {
|
void putEventSynchronously(void* event) {
|
||||||
uint64_t value = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(event));
|
uint64_t value = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(event));
|
||||||
if (write(pipeC, &value, sizeof(value)) != sizeof(value)) {
|
if (write(pipeC, &value, sizeof(value)) != sizeof(value)) {
|
||||||
LOGE("Failure writing event: %s\n", strerror(errno));
|
LOGE("Failure writing event: %s\n", strerror(errno));
|
||||||
@@ -110,25 +113,25 @@ static void putEventSynchronously(void* event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onDestroy(ANativeActivity* activity) {
|
void onDestroy(ANativeActivity* activity) {
|
||||||
LOGV("onDestroy called");
|
LOGV("onDestroy called");
|
||||||
NativeActivityEvent event = { DESTROY };
|
NativeActivityEvent event = { DESTROY };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onStart(ANativeActivity* activity) {
|
void onStart(ANativeActivity* activity) {
|
||||||
LOGV("onStart called");
|
LOGV("onStart called");
|
||||||
NativeActivityEvent event = { START };
|
NativeActivityEvent event = { START };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onResume(ANativeActivity* activity) {
|
void onResume(ANativeActivity* activity) {
|
||||||
LOGV("onResume called");
|
LOGV("onResume called");
|
||||||
NativeActivitySaveStateEvent event = { RESUME };
|
NativeActivitySaveStateEvent event = { RESUME };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
|
void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
|
||||||
LOGV("onSaveInstanceState called");
|
LOGV("onSaveInstanceState called");
|
||||||
NativeActivitySaveStateEvent event = { SAVE_INSTANCE_STATE };
|
NativeActivitySaveStateEvent event = { SAVE_INSTANCE_STATE };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
@@ -136,61 +139,61 @@ static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
|
|||||||
return event.savedState;
|
return event.savedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onPause(ANativeActivity* activity) {
|
void onPause(ANativeActivity* activity) {
|
||||||
LOGV("onPause called");
|
LOGV("onPause called");
|
||||||
NativeActivityEvent event = { PAUSE };
|
NativeActivityEvent event = { PAUSE };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onStop(ANativeActivity* activity) {
|
void onStop(ANativeActivity* activity) {
|
||||||
LOGV("onStop called");
|
LOGV("onStop called");
|
||||||
NativeActivityEvent event = { STOP };
|
NativeActivityEvent event = { STOP };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onConfigurationChanged(ANativeActivity* activity) {
|
void onConfigurationChanged(ANativeActivity* activity) {
|
||||||
LOGV("onConfigurationChanged called");
|
LOGV("onConfigurationChanged called");
|
||||||
NativeActivityEvent event = { CONFIGURATION_CHANGED };
|
NativeActivityEvent event = { CONFIGURATION_CHANGED };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onLowMemory(ANativeActivity* activity) {
|
void onLowMemory(ANativeActivity* activity) {
|
||||||
LOGV("onLowMemory called");
|
LOGV("onLowMemory called");
|
||||||
NativeActivityEvent event = { LOW_MEMORY };
|
NativeActivityEvent event = { LOW_MEMORY };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
|
void onWindowFocusChanged(ANativeActivity* activity, int focused) {
|
||||||
LOGV("onWindowFocusChanged called");
|
LOGV("onWindowFocusChanged called");
|
||||||
NativeActivityEvent event = { focused ? WINDOW_GAINED_FOCUS : WINDOW_LOST_FOCUS };
|
NativeActivityEvent event = { focused ? WINDOW_GAINED_FOCUS : WINDOW_LOST_FOCUS };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
|
void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
|
||||||
LOGV("onNativeWindowCreated called");
|
LOGV("onNativeWindowCreated called");
|
||||||
NativeActivityWindowEvent event = { NATIVE_WINDOW_CREATED, window };
|
NativeActivityWindowEvent event = { NATIVE_WINDOW_CREATED, window };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
|
void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
|
||||||
LOGV("onNativeWindowDestroyed called");
|
LOGV("onNativeWindowDestroyed called");
|
||||||
NativeActivityWindowEvent event = { NATIVE_WINDOW_DESTROYED, window };
|
NativeActivityWindowEvent event = { NATIVE_WINDOW_DESTROYED, window };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
|
void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
|
||||||
LOGV("onInputQueueCreated called");
|
LOGV("onInputQueueCreated called");
|
||||||
NativeActivityQueueEvent event = { INPUT_QUEUE_CREATED, queue };
|
NativeActivityQueueEvent event = { INPUT_QUEUE_CREATED, queue };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
|
void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
|
||||||
LOGV("onInputQueueDestroyed called");
|
LOGV("onInputQueueDestroyed called");
|
||||||
NativeActivityQueueEvent event = { INPUT_QUEUE_DESTROYED, queue };
|
NativeActivityQueueEvent event = { INPUT_QUEUE_DESTROYED, queue };
|
||||||
putEventSynchronously(&event);
|
putEventSynchronously(&event);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ANativeActivity_onCreate.
|
|
||||||
extern "C" void Konan_main(ANativeActivity* activity, void* savedState, size_t savedStateSize) {
|
extern "C" void Konan_main(ANativeActivity* activity, void* savedState, size_t savedStateSize) {
|
||||||
nativeActivityState = {activity, savedState, savedStateSize};
|
nativeActivityState = {activity, savedState, savedStateSize};
|
||||||
|
|
||||||
@@ -211,4 +214,4 @@ extern "C" void Konan_main(ANativeActivity* activity, void* savedState, size_t s
|
|||||||
runKonan_start();
|
runKonan_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // KONAN_ANDROID
|
||||||
@@ -36,7 +36,7 @@ struct NativeActivityState {
|
|||||||
|
|
||||||
void getNativeActivityState(struct NativeActivityState* state);
|
void getNativeActivityState(struct NativeActivityState* state);
|
||||||
|
|
||||||
void notifySysEventProcessed(int fd);
|
void notifySysEventProcessed();
|
||||||
|
|
||||||
#define LOOPER_ID_SYS 1
|
#define LOOPER_ID_SYS 1
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#include "KString.h"
|
#include "KString.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
#ifndef ANDROID
|
#ifndef KONAN_ANDROID
|
||||||
|
|
||||||
//--- Setup args --------------------------------------------------------------//
|
//--- Setup args --------------------------------------------------------------//
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef KONAN_ANDROID
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1133,7 +1133,7 @@ void Kotlin_io_Console_print(KString message) {
|
|||||||
const KChar* utf16 = CharArrayAddressOfElementAt(message, 0);
|
const KChar* utf16 = CharArrayAddressOfElementAt(message, 0);
|
||||||
std::string utf8;
|
std::string utf8;
|
||||||
utf8::utf16to8(utf16, utf16 + message->count_, back_inserter(utf8));
|
utf8::utf16to8(utf16, utf16 + message->count_, back_inserter(utf8));
|
||||||
#ifdef ANDROID
|
#ifdef KONAN_ANDROID
|
||||||
__android_log_print(ANDROID_LOG_INFO, "Konan_main", "%s", utf8.c_str());
|
__android_log_print(ANDROID_LOG_INFO, "Konan_main", "%s", utf8.c_str());
|
||||||
#else
|
#else
|
||||||
write(STDOUT_FILENO, utf8.c_str(), utf8.size());
|
write(STDOUT_FILENO, utf8.c_str(), utf8.size());
|
||||||
@@ -1146,7 +1146,7 @@ void Kotlin_io_Console_println(KString message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Kotlin_io_Console_println0() {
|
void Kotlin_io_Console_println0() {
|
||||||
#ifdef ANDROID
|
#ifdef KONAN_ANDROID
|
||||||
__android_log_print(ANDROID_LOG_INFO, "Konan_main", "\n");
|
__android_log_print(ANDROID_LOG_INFO, "Konan_main", "\n");
|
||||||
#else
|
#else
|
||||||
write(STDOUT_FILENO, "\n", 1);
|
write(STDOUT_FILENO, "\n", 1);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Android Native Activity
|
||||||
|
This example shows how to build an Android Native Activity.
|
||||||
|
|
||||||
|
The example will render a textured dodecahedron using OpenGL ES library. It can be rotated with fingers.
|
||||||
|
Please make sure that Android SDK version 25 is installed, using Android SDK manager.
|
||||||
|
|
||||||
|
To build use `ANDROID_HOME=<your path to android sdk> ../gradlew buildApk`.
|
||||||
|
|
||||||
|
Run `adb install build/outputs/apk/androidNativeActivity-arm-debug.apk` to deploy the apk on Android device or emulator
|
||||||
|
(note that only ARM-based devices are currently supported).
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
headers = stdio.h stdlib.h string.h math.h time.h android/log.h androidLauncher.h EGL/egl.h GLES/gl.h
|
||||||
|
excludeDependentModules.osx = true
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
static int interop_errno() {
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle-experimental:0.9.2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: "konan"
|
||||||
|
|
||||||
|
konanInterop {
|
||||||
|
arm32 {
|
||||||
|
defFile "android.def"
|
||||||
|
pkg "android"
|
||||||
|
includeDirs "../../runtime/src/launcher/cpp"
|
||||||
|
target "android_arm32"
|
||||||
|
}
|
||||||
|
arm64 {
|
||||||
|
defFile "android.def"
|
||||||
|
pkg "android"
|
||||||
|
includeDirs "../../runtime/src/launcher/cpp"
|
||||||
|
target "android_arm64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
konanArtifacts {
|
||||||
|
PolyhedronArm32 {
|
||||||
|
useInterop "arm32"
|
||||||
|
target "android_arm32"
|
||||||
|
}
|
||||||
|
PolyhedronArm64 {
|
||||||
|
useInterop "arm64"
|
||||||
|
target "android_arm64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def platforms = ["armeabi-v7a", "arm64-v8a"]
|
||||||
|
def artifacts = ["PolyhedronArm32", "PolyhedronArm64"]
|
||||||
|
|
||||||
|
apply plugin: "com.android.model.application"
|
||||||
|
|
||||||
|
model {
|
||||||
|
android {
|
||||||
|
compileSdkVersion = 25
|
||||||
|
buildToolsVersion = '25.0.2'
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = 'com.android.konan_activity'
|
||||||
|
minSdkVersion.apiLevel 9
|
||||||
|
targetSdkVersion.apiLevel 25
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk {
|
||||||
|
moduleName = "polyhedron"
|
||||||
|
}
|
||||||
|
|
||||||
|
productFlavors {
|
||||||
|
create("arm") {
|
||||||
|
ndk {
|
||||||
|
abiFilters.addAll(platforms)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
libs(PrebuiltLibraries) {
|
||||||
|
libpoly {
|
||||||
|
binaries.withType(SharedLibraryBinary) {
|
||||||
|
def name = targetPlatform.getName()
|
||||||
|
def index = platforms.indexOf(name)
|
||||||
|
if (index >= 0)
|
||||||
|
sharedLibraryFile = file("build/konan/bin/${artifacts[index]}/libpoly.so")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
android.sources {
|
||||||
|
main {
|
||||||
|
jniLibs {
|
||||||
|
dependencies {
|
||||||
|
library "libpoly"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task renameArtifacts << {
|
||||||
|
for (artifact in artifacts) {
|
||||||
|
copy {
|
||||||
|
from "build/konan/bin/${artifact}"
|
||||||
|
into "build/konan/bin/${artifact}"
|
||||||
|
rename "${artifact}.kexe", "libpoly.so"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renameArtifacts.dependsOn "compileKonanPolyhedronArm32"
|
||||||
|
renameArtifacts.dependsOn "compileKonanPolyhedronArm64"
|
||||||
|
|
||||||
|
tasks.matching { it.name == 'preBuild' }.all {
|
||||||
|
it.dependsOn 'renameArtifacts'
|
||||||
|
}
|
||||||
|
|
||||||
|
task buildApk(type: DefaultTask) {
|
||||||
|
dependsOn "compileKonanPolyhedronArm32"
|
||||||
|
dependsOn "compileKonanPolyhedronArm64"
|
||||||
|
dependsOn "renameArtifacts"
|
||||||
|
dependsOn "assembleDebug"
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- BEGIN_INCLUDE(manifest) -->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="polyhedron"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
|
||||||
|
<!-- This .apk has no Java code itself, so set hasCode to false. -->
|
||||||
|
<application
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:fullBackupContent="false"
|
||||||
|
android:icon="@mipmap/konan_activity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:hasCode="false">
|
||||||
|
|
||||||
|
<!-- 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:label="@string/app_name"
|
||||||
|
android:configChanges="orientation|keyboardHidden">
|
||||||
|
<!-- Tell NativeActivity the name of our .so -->
|
||||||
|
<meta-data android:name="android.app.lib_name"
|
||||||
|
android:value="poly" />
|
||||||
|
<meta-data android:name="android.app.func_name"
|
||||||
|
android:value="Konan_main" />
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
<!-- END_INCLUDE(manifest) -->
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import android.*
|
||||||
|
|
||||||
|
private fun logError(message: String) {
|
||||||
|
__android_log_write(ANDROID_LOG_ERROR, "KonanActivity", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun logInfo(message: String) {
|
||||||
|
__android_log_write(ANDROID_LOG_INFO, "KonanActivity", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
val errno: Int
|
||||||
|
get() = interop_errno()
|
||||||
|
|
||||||
|
fun getUnixError() = strerror(errno)!!.toKString()
|
||||||
|
|
||||||
|
const val LOOPER_ID_INPUT = 2
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
logInfo("Hello world!")
|
||||||
|
memScoped {
|
||||||
|
val state = alloc<NativeActivityState>()
|
||||||
|
getNativeActivityState(state.ptr)
|
||||||
|
val engine = Engine(this, state)
|
||||||
|
engine.mainLoop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Engine(val arena: NativePlacement, val state: NativeActivityState) {
|
||||||
|
private val renderer = Renderer(arena, state.activity!!.pointed)
|
||||||
|
private var queue: CPointer<AInputQueue>? = null
|
||||||
|
|
||||||
|
private var currentPoint = Vector2.Zero
|
||||||
|
private var startPoint = Vector2.Zero
|
||||||
|
private var startTime = 0.0f
|
||||||
|
private var animationEndTime = 0.0f
|
||||||
|
private var velocity = Vector2.Zero
|
||||||
|
private var acceleration = Vector2.Zero
|
||||||
|
|
||||||
|
private var needRedraw = true
|
||||||
|
private var animating = false
|
||||||
|
|
||||||
|
fun mainLoop() {
|
||||||
|
while (true) {
|
||||||
|
// Process events.
|
||||||
|
memScoped {
|
||||||
|
eventLoop@while (true) {
|
||||||
|
val fd = alloc<IntVar>()
|
||||||
|
val id = ALooper_pollAll(if (needRedraw || animating) 0 else -1, fd.ptr, null, null)
|
||||||
|
if (id < 0) break@eventLoop
|
||||||
|
when (id) {
|
||||||
|
LOOPER_ID_SYS -> {
|
||||||
|
if (!processSysEvent(fd))
|
||||||
|
return // An error occured.
|
||||||
|
}
|
||||||
|
|
||||||
|
LOOPER_ID_INPUT -> processUserInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
when {
|
||||||
|
animating -> {
|
||||||
|
val elapsed = getTime() - startTime
|
||||||
|
if (elapsed >= animationEndTime) {
|
||||||
|
animating = false
|
||||||
|
} else {
|
||||||
|
move(startPoint + velocity * elapsed + acceleration * (elapsed * elapsed * 0.5f))
|
||||||
|
renderer.draw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
needRedraw -> renderer.draw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processSysEvent(fd: IntVar): Boolean = memScoped {
|
||||||
|
val ptr = allocArray<LongVar>(1)
|
||||||
|
val readBytes = read(fd.value, ptr, 8).toLong()
|
||||||
|
if (readBytes != 8L) {
|
||||||
|
logError("Failure reading event, $readBytes read: ${getUnixError()}")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val event = ptr[0].toCPointer<NativeActivityEvent>()!!.pointed
|
||||||
|
when (event.eventKind) {
|
||||||
|
NativeActivityEventKind.START -> logInfo("START event received")
|
||||||
|
|
||||||
|
NativeActivityEventKind.DESTROY -> return false
|
||||||
|
|
||||||
|
NativeActivityEventKind.NATIVE_WINDOW_CREATED -> {
|
||||||
|
val windowEvent = ptr[0].toCPointer<NativeActivityWindowEvent>()!!.pointed
|
||||||
|
if (!renderer.initialize(windowEvent.window!!))
|
||||||
|
return false
|
||||||
|
logInfo("Renderer initialized")
|
||||||
|
renderer.draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeActivityEventKind.INPUT_QUEUE_CREATED -> {
|
||||||
|
val queueEvent = ptr[0].toCPointer<NativeActivityQueueEvent>()!!.pointed
|
||||||
|
if (queue != null)
|
||||||
|
AInputQueue_detachLooper(queue)
|
||||||
|
queue = queueEvent.queue
|
||||||
|
AInputQueue_attachLooper(queue, state.looper, LOOPER_ID_INPUT, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeActivityEventKind.INPUT_QUEUE_DESTROYED -> {
|
||||||
|
val queueEvent = ptr[0].toCPointer<NativeActivityQueueEvent>()!!.pointed
|
||||||
|
AInputQueue_detachLooper(queueEvent.queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeActivityEventKind.NATIVE_WINDOW_DESTROYED -> {
|
||||||
|
renderer.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
notifySysEventProcessed()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTime(): Float {
|
||||||
|
memScoped {
|
||||||
|
val now = alloc<timespec>()
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, now.ptr)
|
||||||
|
return now.tv_sec + now.tv_nsec / 1_000_000_000.0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getEventPoint(event: CPointer<AInputEvent>?, i: Int) =
|
||||||
|
Vector2(AMotionEvent_getRawX(event, i.signExtend<size_t>()), AMotionEvent_getRawY(event, i.signExtend<size_t>()))
|
||||||
|
|
||||||
|
private fun getEventTime(event: CPointer<AInputEvent>?) =
|
||||||
|
AMotionEvent_getEventTime(event) / 1_000_000_000.0f
|
||||||
|
|
||||||
|
private fun processUserInput(): Unit = memScoped {
|
||||||
|
logInfo("Processing user input")
|
||||||
|
val event = alloc<CPointerVar<AInputEvent>>()
|
||||||
|
if (AInputQueue_getEvent(queue, event.ptr) < 0) {
|
||||||
|
logError("Failure reading input event")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val eventType = AInputEvent_getType(event.value)
|
||||||
|
if (eventType == AINPUT_EVENT_TYPE_MOTION) {
|
||||||
|
val action = AKeyEvent_getAction(event.value) and AMOTION_EVENT_ACTION_MASK
|
||||||
|
when (action) {
|
||||||
|
AMOTION_EVENT_ACTION_DOWN -> {
|
||||||
|
animating = false
|
||||||
|
currentPoint = getEventPoint(event.value, 0)
|
||||||
|
startTime = getEventTime(event.value)
|
||||||
|
startPoint = currentPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
AMOTION_EVENT_ACTION_UP -> {
|
||||||
|
val endPoint = getEventPoint(event.value, 0)
|
||||||
|
val endTime = getEventTime(event.value)
|
||||||
|
animating = true
|
||||||
|
velocity = (endPoint - startPoint) / (endTime - startTime + 1e-9f)
|
||||||
|
if (velocity.length > renderer.screen.length)
|
||||||
|
velocity = velocity * (renderer.screen.length / velocity.length)
|
||||||
|
acceleration = velocity.normalized() * (-renderer.screen.length * 0.5f)
|
||||||
|
animationEndTime = velocity.length / acceleration.length
|
||||||
|
startPoint = endPoint
|
||||||
|
startTime = endTime
|
||||||
|
move(endPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
AMOTION_EVENT_ACTION_MOVE -> {
|
||||||
|
val numberOfPointers = AMotionEvent_getPointerCount(event.value).toInt()
|
||||||
|
for (i in 0 until numberOfPointers)
|
||||||
|
move(getEventPoint(event.value, i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AInputQueue_finishEvent(queue, event.value, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun move(newPoint: Vector2) {
|
||||||
|
renderer.rotateBy(newPoint - currentPoint)
|
||||||
|
currentPoint = newPoint
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const val Zero = 0.0f
|
||||||
|
const val DodeA = 0.93417235896f // (Sqrt(5) + 1) / (2 * Sqrt(3))
|
||||||
|
const val DodeB = 0.35682208977f // (Sqrt(5) - 1) / (2 * Sqrt(3))
|
||||||
|
const val DodeC = 0.57735026919f // 1 / Sqrt(3)
|
||||||
|
const val IcosA = 0.52573111212f // Sqrt(5 - Sqrt(5)) / Sqrt(10)
|
||||||
|
const val IcosB = 0.85065080835f // Sqrt(5 + Sqrt(5)) / Sqrt(10)
|
||||||
|
|
||||||
|
enum class RegularPolyhedra(val vertices: Array<Vector3>, val faces: Array<ByteArray>) {
|
||||||
|
Dodecahedron(
|
||||||
|
arrayOf(
|
||||||
|
Vector3(-DodeA, Zero, DodeB), Vector3(-DodeA, Zero, -DodeB), Vector3(DodeA, Zero, -DodeB),
|
||||||
|
Vector3(DodeA, Zero, DodeB), Vector3(DodeB, -DodeA, Zero), Vector3(-DodeB, -DodeA, Zero),
|
||||||
|
Vector3(-DodeB, DodeA, Zero), Vector3(DodeB, DodeA, Zero), Vector3(Zero, DodeB, -DodeA),
|
||||||
|
Vector3(Zero, -DodeB, -DodeA), Vector3(Zero, -DodeB, DodeA), Vector3(Zero, DodeB, DodeA),
|
||||||
|
Vector3(-DodeC, -DodeC, DodeC), Vector3(-DodeC, -DodeC, -DodeC), Vector3(DodeC, -DodeC, -DodeC),
|
||||||
|
Vector3(DodeC, -DodeC, DodeC), Vector3(-DodeC, DodeC, DodeC), Vector3(-DodeC, DodeC, -DodeC),
|
||||||
|
Vector3(DodeC, DodeC, -DodeC), Vector3(DodeC, DodeC, DodeC)
|
||||||
|
),
|
||||||
|
arrayOf(
|
||||||
|
byteArrayOf(0, 12, 10, 11, 16), byteArrayOf(1, 17, 8, 9, 13), byteArrayOf(2, 14, 9, 8, 18),
|
||||||
|
byteArrayOf(3, 19, 11, 10, 15), byteArrayOf(4, 14, 2, 3, 15), byteArrayOf(5, 12, 0, 1, 13),
|
||||||
|
byteArrayOf(6, 17, 1, 0, 16), byteArrayOf(7, 19, 3, 2, 18), byteArrayOf(8, 17, 6, 7, 18),
|
||||||
|
byteArrayOf(9, 14, 4, 5, 13), byteArrayOf(10, 12, 5, 4, 15), byteArrayOf(11, 19, 7, 6, 16)
|
||||||
|
)),
|
||||||
|
Icosahedron(
|
||||||
|
arrayOf(
|
||||||
|
Vector3(-IcosA, Zero, IcosB), Vector3(IcosA, Zero, IcosB), Vector3(-IcosA, Zero, -IcosB),
|
||||||
|
Vector3(IcosA, Zero, -IcosB), Vector3(Zero, IcosB, IcosA), Vector3(Zero, IcosB, -IcosA),
|
||||||
|
Vector3(Zero, -IcosB, IcosA), Vector3(Zero, -IcosB, -IcosA), Vector3(IcosB, IcosA, Zero),
|
||||||
|
Vector3(-IcosB, IcosA, Zero), Vector3(IcosB, -IcosA, Zero), Vector3(-IcosB, -IcosA, Zero)
|
||||||
|
),
|
||||||
|
arrayOf(
|
||||||
|
byteArrayOf(1, 4, 0), byteArrayOf(4, 9, 0), byteArrayOf(4, 5, 9), byteArrayOf(8, 5, 4),
|
||||||
|
byteArrayOf(1, 8, 4), byteArrayOf(1, 10, 8), byteArrayOf(10, 3, 8), byteArrayOf(8, 3, 5),
|
||||||
|
byteArrayOf(3, 2, 5), byteArrayOf(3, 7, 2), byteArrayOf(3, 10, 7), byteArrayOf(10, 6, 7),
|
||||||
|
byteArrayOf(6, 11, 7), byteArrayOf(6, 0, 11), byteArrayOf(6, 1, 0), byteArrayOf(10, 1, 6),
|
||||||
|
byteArrayOf(11, 0, 9), byteArrayOf(2, 11, 9), byteArrayOf(5, 2, 9), byteArrayOf(11, 2, 7)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
val verticesPerFace get() = faces[0].size
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import android.*
|
||||||
|
|
||||||
|
private fun logError(message: String) {
|
||||||
|
__android_log_write(ANDROID_LOG_ERROR, "KonanActivity", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun logInfo(message: String) {
|
||||||
|
__android_log_write(ANDROID_LOG_INFO, "KonanActivity", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Renderer(val arena: NativePlacement, val nativeActivity: ANativeActivity) {
|
||||||
|
|
||||||
|
private var display: EGLDisplay? = null
|
||||||
|
private var surface: EGLSurface? = null
|
||||||
|
private var context: EGLContext? = null
|
||||||
|
private var initialized = false
|
||||||
|
|
||||||
|
var screen = Vector2.Zero
|
||||||
|
|
||||||
|
fun initialize(window: CPointer<ANativeWindow>): Boolean {
|
||||||
|
with(arena) {
|
||||||
|
logInfo("Initializing context..")
|
||||||
|
display = eglGetDisplay(null)
|
||||||
|
if (display == null) {
|
||||||
|
logError("eglGetDisplay() returned error ${eglGetError()}")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (eglInitialize(display, null, null) == 0) {
|
||||||
|
logError("eglInitialize() returned error ${eglGetError()}")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val attribs = cValuesOf(
|
||||||
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||||
|
EGL_BLUE_SIZE, 8,
|
||||||
|
EGL_GREEN_SIZE, 8,
|
||||||
|
EGL_RED_SIZE, 8,
|
||||||
|
EGL_NONE
|
||||||
|
)
|
||||||
|
val numConfigs = alloc<EGLintVar>()
|
||||||
|
|
||||||
|
val supportedConfigs = allocArray<EGLConfigVar>(numConfigs.value)
|
||||||
|
if (eglChooseConfig(display, attribs, null, 0, numConfigs.ptr) == 0
|
||||||
|
|| eglChooseConfig(display, attribs, supportedConfigs, numConfigs.value, numConfigs.ptr) == 0) {
|
||||||
|
logError("eglChooseConfig() returned error ${eglGetError()}")
|
||||||
|
destroy()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var configIndex = 0
|
||||||
|
while (configIndex < numConfigs.value) {
|
||||||
|
val r = alloc<EGLintVar>()
|
||||||
|
val g = alloc<EGLintVar>()
|
||||||
|
val b = alloc<EGLintVar>()
|
||||||
|
val d = alloc<EGLintVar>()
|
||||||
|
if (eglGetConfigAttrib(display, supportedConfigs[configIndex], EGL_RED_SIZE, r.ptr) != 0 &&
|
||||||
|
eglGetConfigAttrib(display, supportedConfigs[configIndex], EGL_GREEN_SIZE, g.ptr) != 0 &&
|
||||||
|
eglGetConfigAttrib(display, supportedConfigs[configIndex], EGL_BLUE_SIZE, b.ptr) != 0 &&
|
||||||
|
eglGetConfigAttrib(display, supportedConfigs[configIndex], EGL_DEPTH_SIZE, d.ptr) != 0 &&
|
||||||
|
r.value == 8 && g.value == 8 && b.value == 8 && d.value == 0 ) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
++configIndex
|
||||||
|
}
|
||||||
|
if (configIndex >= numConfigs.value)
|
||||||
|
configIndex = 0
|
||||||
|
|
||||||
|
surface = eglCreateWindowSurface(display, supportedConfigs[configIndex], window, null)
|
||||||
|
if (surface == null) {
|
||||||
|
logError("eglCreateWindowSurface() returned error ${eglGetError()}")
|
||||||
|
destroy()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
context = eglCreateContext(display, supportedConfigs[configIndex], null, null)
|
||||||
|
if (context == null) {
|
||||||
|
logError("eglCreateContext() returned error ${eglGetError()}")
|
||||||
|
destroy()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eglMakeCurrent(display, surface, surface, context) == 0) {
|
||||||
|
logError("eglMakeCurrent() returned error ${eglGetError()}")
|
||||||
|
destroy()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val width = alloc<EGLintVar>()
|
||||||
|
val height = alloc<EGLintVar>()
|
||||||
|
if (eglQuerySurface(display, surface, EGL_WIDTH, width.ptr) == 0
|
||||||
|
|| eglQuerySurface(display, surface, EGL_HEIGHT, height.ptr) == 0) {
|
||||||
|
logError("eglQuerySurface() returned error ${eglGetError()}")
|
||||||
|
destroy()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
this@Renderer.screen = Vector2(width.value.toFloat(), height.value.toFloat())
|
||||||
|
|
||||||
|
glDisable(GL_DITHER)
|
||||||
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST)
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f)
|
||||||
|
glEnable(GL_CULL_FACE)
|
||||||
|
glShadeModel(GL_SMOOTH)
|
||||||
|
glEnable(GL_DEPTH_TEST)
|
||||||
|
|
||||||
|
glViewport(0, 0, width.value, height.value)
|
||||||
|
|
||||||
|
val ratio = width.value.toFloat() / height.value
|
||||||
|
glMatrixMode(GL_PROJECTION)
|
||||||
|
glLoadIdentity()
|
||||||
|
glFrustumf(-ratio, ratio, -1.0f, 1.0f, 1.0f, 10.0f)
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW)
|
||||||
|
glTranslatef(0.0f, 0.0f, -2.0f)
|
||||||
|
glLightfv(GL_LIGHT0, GL_POSITION, cValuesOf(1.25f, 1.25f, -2.0f, 0.0f))
|
||||||
|
glEnable(GL_LIGHTING)
|
||||||
|
glEnable(GL_LIGHT0)
|
||||||
|
glEnable(GL_TEXTURE_2D)
|
||||||
|
glMaterialfv(GL_FRONT, GL_DIFFUSE, cValuesOf(0.0f, 1.0f, 1.0f, 1.0f))
|
||||||
|
glMaterialfv(GL_FRONT, GL_SPECULAR, cValuesOf(0.3f, 0.3f, 0.3f, 1.0f))
|
||||||
|
glMaterialf(GL_FRONT, GL_SHININESS, 30.0f)
|
||||||
|
|
||||||
|
loadTexture("kotlin_logo.bmp")
|
||||||
|
|
||||||
|
glPushMatrix()
|
||||||
|
glLoadIdentity()
|
||||||
|
glGetFloatv(GL_MODELVIEW_MATRIX, matrix)
|
||||||
|
glPopMatrix()
|
||||||
|
|
||||||
|
initialized = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val matrix = arena.allocArray<FloatVar>(16)
|
||||||
|
|
||||||
|
fun rotateBy(vec: Vector2) {
|
||||||
|
val len = vec.length
|
||||||
|
if (len < 1e-9f) return
|
||||||
|
val angle = 180 * len / screen.length
|
||||||
|
val x = - vec.y / len
|
||||||
|
val y = - vec.x / len
|
||||||
|
|
||||||
|
glPushMatrix()
|
||||||
|
glMatrixMode(GL_MODELVIEW)
|
||||||
|
glLoadIdentity()
|
||||||
|
glRotatef(angle, x, y, 0.0f)
|
||||||
|
glMultMatrixf(matrix)
|
||||||
|
glGetFloatv(GL_MODELVIEW_MATRIX, matrix)
|
||||||
|
glPopMatrix()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BMPHeader(val rawPtr: NativePtr) {
|
||||||
|
inline fun <reified T : CPointed> memberAt(offset: Long): T {
|
||||||
|
return interpretPointed<T>(this.rawPtr + offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
val magic get() = memberAt<ShortVar>(0).value.toInt()
|
||||||
|
val size get() = memberAt<IntVar>(2).value
|
||||||
|
val zero get() = memberAt<IntVar>(6).value
|
||||||
|
val width get() = memberAt<IntVar>(18).value
|
||||||
|
val height get() = memberAt<IntVar>(22).value
|
||||||
|
val bits get() = memberAt<ShortVar>(28).value.toInt()
|
||||||
|
val data get() = interpretCPointer<ByteVar>(rawPtr + 54) as CArrayPointer<ByteVar>
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadTexture(assetName: String): Unit = with(arena) {
|
||||||
|
val asset = AAssetManager_open(nativeActivity.assetManager, assetName, AASSET_MODE_BUFFER)
|
||||||
|
if (asset == null) {
|
||||||
|
logError("Error opening asset")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val length = AAsset_getLength(asset)
|
||||||
|
val buf = allocArray<ByteVar>(length)
|
||||||
|
if (AAsset_read(asset, buf, length) != length.toInt()) {
|
||||||
|
logError("Error reading asset")
|
||||||
|
AAsset_close(asset)
|
||||||
|
}
|
||||||
|
with (BMPHeader(buf.rawValue)) {
|
||||||
|
if (magic != 0x4d42 || zero != 0 || size != length.toInt() || bits != 24) {
|
||||||
|
logError("Error parsing texture file")
|
||||||
|
AAsset_close(asset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val numberOfBytes = width * height * 3
|
||||||
|
for (i in 0 until numberOfBytes step 3) {
|
||||||
|
val t = data[i]
|
||||||
|
data[i] = data[i + 2]
|
||||||
|
data[i + 2] = t
|
||||||
|
}
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 1)
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
||||||
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND.toFloat())
|
||||||
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, cValuesOf(1.0f, 1.0f, 1.0f, 1.0f))
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data)
|
||||||
|
AAsset_close(asset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val texturePoints = arrayOf(
|
||||||
|
Vector2(0.0f, 0.2f), Vector2(0.0f, 0.8f), Vector2(0.6f, 1.0f), Vector2(1.0f, 0.5f), Vector2(0.8f, 0.0f)
|
||||||
|
)
|
||||||
|
|
||||||
|
private val scale = 1.25f
|
||||||
|
|
||||||
|
fun draw() = with (arena) {
|
||||||
|
if (!initialized) return
|
||||||
|
|
||||||
|
glPushMatrix()
|
||||||
|
glMatrixMode(GL_MODELVIEW)
|
||||||
|
|
||||||
|
glMultMatrixf(matrix)
|
||||||
|
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
|
||||||
|
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY)
|
||||||
|
glEnableClientState(GL_NORMAL_ARRAY)
|
||||||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
|
||||||
|
|
||||||
|
val poly = RegularPolyhedra.Dodecahedron
|
||||||
|
val vertices = mutableListOf<Float>()
|
||||||
|
val texCoords = mutableListOf<Float>()
|
||||||
|
val triangles = mutableListOf<Byte>()
|
||||||
|
val normals = mutableListOf<Float>()
|
||||||
|
for (face in poly.faces) {
|
||||||
|
val u = poly.vertices[face[2].toInt()] - poly.vertices[face[1].toInt()]
|
||||||
|
val v = poly.vertices[face[0].toInt()] - poly.vertices[face[1].toInt()]
|
||||||
|
val normal = u.crossProduct(v).normalized()
|
||||||
|
|
||||||
|
val copiedFace = ByteArray(face.size)
|
||||||
|
for (j in face.indices) {
|
||||||
|
copiedFace[j] = (vertices.size / 4).toByte()
|
||||||
|
poly.vertices[face[j].toInt()].copyCoordinatesTo(vertices)
|
||||||
|
vertices.add(scale)
|
||||||
|
normal.copyCoordinatesTo(normals)
|
||||||
|
texturePoints[j].copyCoordinatesTo(texCoords)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j in 1..face.size-2) {
|
||||||
|
triangles.add(copiedFace[0])
|
||||||
|
triangles.add(copiedFace[j])
|
||||||
|
triangles.add(copiedFace[j + 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glFrontFace(GL_CW)
|
||||||
|
glVertexPointer(4, GL_FLOAT, 0, vertices.toFloatArray().toCValues().getPointer(this))
|
||||||
|
glTexCoordPointer(2, GL_FLOAT, 0, texCoords.toFloatArray().toCValues().getPointer(this))
|
||||||
|
glNormalPointer(GL_FLOAT, 0, normals.toFloatArray().toCValues().getPointer(this))
|
||||||
|
glDrawElements(GL_TRIANGLES, triangles.size, GL_UNSIGNED_BYTE, triangles.toByteArray().toCValues().getPointer(this))
|
||||||
|
|
||||||
|
glPopMatrix()
|
||||||
|
|
||||||
|
if (eglSwapBuffers(display, surface) == 0) {
|
||||||
|
logError("eglSwapBuffers() returned error ${eglGetError()}")
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun destroy() {
|
||||||
|
logInfo("Destroying context..")
|
||||||
|
|
||||||
|
eglMakeCurrent(display, null, null, null)
|
||||||
|
context?.let { eglDestroyContext(display, it) }
|
||||||
|
surface?.let { eglDestroySurface(display, it) }
|
||||||
|
eglTerminate(display)
|
||||||
|
|
||||||
|
display = null
|
||||||
|
surface = null
|
||||||
|
context = null
|
||||||
|
initialized = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import android.*
|
||||||
|
|
||||||
|
class Vector2(val x: Float, val y: Float) {
|
||||||
|
val length by lazy { sqrtf(x * x + y * y) }
|
||||||
|
|
||||||
|
fun normalized(): Vector2 {
|
||||||
|
val len = length
|
||||||
|
return Vector2(x / len, y / len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyCoordinatesTo(arr: MutableList<Float>) {
|
||||||
|
arr.add(x)
|
||||||
|
arr.add(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun minus(other: Vector2) = Vector2(x - other.x, y - other.y)
|
||||||
|
operator fun plus(other: Vector2) = Vector2(x + other.x, y + other.y)
|
||||||
|
operator fun times(other: Float) = Vector2(x * other, y * other)
|
||||||
|
operator fun div(other: Float) = Vector2(x / other, y / other)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val Zero = Vector2(0.0f, 0.0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vector3(val x: Float, val y: Float, val z: Float) {
|
||||||
|
val length by lazy { sqrtf(x * x + y * y + z * z) }
|
||||||
|
|
||||||
|
fun crossProduct(other: Vector3): Vector3 =
|
||||||
|
Vector3(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x)
|
||||||
|
|
||||||
|
fun normalized(): Vector3 {
|
||||||
|
val len = length
|
||||||
|
return Vector3(x / len, y / len, z / len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyCoordinatesTo(arr: MutableList<Float>) {
|
||||||
|
arr.add(x)
|
||||||
|
arr.add(y)
|
||||||
|
arr.add(z)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun minus(other: Vector3) = Vector3(x - other.x, y - other.y, z - other.z)
|
||||||
|
operator fun plus(other: Vector3) = Vector3(x + other.x, y + other.y, z + other.z)
|
||||||
|
}
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">KonanActivity</string>
|
||||||
|
</resources>
|
||||||
@@ -8,4 +8,5 @@ include ':socket'
|
|||||||
include ':tetris'
|
include ':tetris'
|
||||||
include ':tensorflow'
|
include ':tensorflow'
|
||||||
include ':concurrent'
|
include ':concurrent'
|
||||||
|
include ':androidNativeActivity'
|
||||||
includeBuild '../'
|
includeBuild '../'
|
||||||
|
|||||||
Reference in New Issue
Block a user