Move everything under kotlin-native folder

I was forced to manually do update the following files, because otherwise
they would be ignored according .gitignore settings. Probably they
should be deleted from repo.

Interop/.idea/compiler.xml
Interop/.idea/gradle.xml
Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_3.xml
Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_3.xml
Interop/.idea/modules.xml
Interop/.idea/modules/Indexer/Indexer.iml
Interop/.idea/modules/Runtime/Runtime.iml
Interop/.idea/modules/StubGenerator/StubGenerator.iml
backend.native/backend.native.iml
backend.native/bc.frontend/bc.frontend.iml
backend.native/cli.bc/cli.bc.iml
backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt
backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt
backend.native/tests/link/lib/foo.kt
backend.native/tests/link/lib/foo2.kt
backend.native/tests/teamcity-test.property
This commit is contained in:
Stanislav Erokhin
2020-10-27 21:00:28 +03:00
parent 91e4162dad
commit f624800b84
2830 changed files with 0 additions and 0 deletions
@@ -0,0 +1,16 @@
package = sample.androidnative.bmpformat
---
#include <stdint.h>
typedef struct __attribute__((packed)) {
uint16_t magic;
uint32_t size;
uint32_t zero;
uint8_t padding1[8];
int32_t width;
int32_t height;
uint8_t padding2[2];
uint16_t bits;
uint8_t padding3[24];
} BMPHeader;
@@ -0,0 +1,38 @@
<?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">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<!-- 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,7 @@
package sample.androidnative
import kotlinx.cinterop.*
import sample.androidnative.bmpformat.BMPHeader
val BMPHeader.data
get() = (ptr.reinterpret<ByteVar>() + sizeOf<BMPHeader>()) as CArrayPointer<ByteVar>
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package sample.androidnative
import kotlinx.cinterop.*
/**
* Disposable class manages and owns all its native resources. It allocates some resources
* during construction and may allocate some additional resources during operation.
* It must free all its resource once [dispose] is invoked. "Disposed" is a final state of the disposable
* class. It is not supposed to be used after being disposed.
*/
interface Disposable {
/**
* Disposes all native resources owned by this class. This function must be invoked
* exactly once as the last operation on the corresponding class.
*/
fun dispose()
}
/**
* Helper class to implement [Disposable] interface. It contains an [arena] for native
* memory allocations and a number of helper methods to simplify management of other
* kinds of native resources.
*
* It is important to wrap all potentially exception-throwing code in the class constructor
* into [tryConstruct] invocation, because, when object construction fails with exception,
* it ensures that all the resource that were allocated so far will get freed.
*/
abstract class DisposableContainer : Disposable {
val arena = Arena()
override fun dispose() {
arena.clear()
}
inline fun <T> tryConstruct(init: () -> T): T =
try { init() }
catch (e: Throwable) {
dispose()
throw e
}
inline fun <T> disposable(
message: String = "disposable",
create: () -> T?,
crossinline dispose: (T) -> Unit
): T =
tryConstruct {
create()?.also {
arena.defer { dispose(it) }
} ?: throw Error(message)
}
inline fun <T : Disposable> disposable(create: () -> T): T =
disposable(
create = create,
dispose = { it.dispose() })
}
@@ -0,0 +1,226 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package sample.androidnative
import kotlinx.cinterop.*
import platform.android.*
import platform.posix.*
import platform.gles3.*
import platform.linux.*
fun logError(message: String) {
__android_log_write(ANDROID_LOG_ERROR.convert(), "KonanActivity", message)
}
fun logInfo(message: String) {
__android_log_write(ANDROID_LOG_INFO.convert(), "KonanActivity", message)
}
private fun getUnixError() = strerror(posix_errno())!!.toKString()
const val LOOPER_ID_INPUT = 2
inline fun <reified T : CPointed> CPointer<*>?.dereferenceAs(): T = this!!.reinterpret<T>().pointed
class Engine(val state: NativeActivityState) : DisposableContainer() {
private val renderer = Renderer(this, state.activity!!.pointed, state.savedState)
private var queue: CPointer<AInputQueue>? = null
private var rendererState: COpaquePointer? = 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
private val pointerSize = CPointerVar.size
private val now = arena.alloc<timespec>()
private val eventPointer = arena.alloc<COpaquePointerVar>()
private val inputEvent = arena.alloc<CPointerVar<AInputEvent>>()
fun mainLoop() {
callToManagedAPI()
val fd = arena.alloc<IntVar>()
while (true) {
// Process events.
eventLoop@ while (true) {
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()
else -> logError("Unprocessed event: $id")
}
}
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()
}
}
}
override fun dispose() {
renderer.destroy()
super.dispose()
}
private val jniBrigde = JniBridge(state.activity!!.pointed.vm!!)
private fun callToManagedAPI() = jniBrigde.withLocalFrame {
// Actually, this is a context pointer.
val context = JniObject(state.activity!!.pointed.clazz!!)
val contextClass = FindClass("android/content/Context")
val getSystemServiceMethod = GetMethodID(
contextClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;")!!
val vibrator = CallObjectMethod(context, getSystemServiceMethod, "vibrator")
if (vibrator != null) {
val vibratorClass = FindClass("android/os/Vibrator")
val vibrateMethod = GetMethodID(vibratorClass, "vibrate", "(J)V")!!
CallVoidMethod(vibrator, vibrateMethod, 500L)
}
}
private fun processSysEvent(fd: IntVar): Boolean {
val readBytes = read(fd.value, eventPointer.ptr, pointerSize.convert()).toLong()
if (readBytes != pointerSize.toLong()) {
logError("Failure reading event, $readBytes read: ${getUnixError()}")
return true
}
try {
val event = eventPointer.value.dereferenceAs<NativeActivityEvent>()
println("got ${event.eventKind}")
when (event.eventKind) {
NativeActivityEventKind.START -> {
logInfo("Activity started")
renderer.start()
}
NativeActivityEventKind.STOP -> {
renderer.stop()
}
NativeActivityEventKind.DESTROY -> {
rendererState?.let {
free(it)
rendererState = null
}
return false
}
NativeActivityEventKind.NATIVE_WINDOW_CREATED -> {
val windowEvent = eventPointer.value.dereferenceAs<NativeActivityWindowEvent>()
if (!renderer.initialize(windowEvent.window!!))
return false
logInfo("Renderer initialized")
renderer.draw()
}
NativeActivityEventKind.INPUT_QUEUE_CREATED -> {
val queueEvent = eventPointer.value.dereferenceAs<NativeActivityQueueEvent>()
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 = eventPointer.value.dereferenceAs<NativeActivityQueueEvent>()
AInputQueue_detachLooper(queueEvent.queue)
}
NativeActivityEventKind.NATIVE_WINDOW_DESTROYED -> {
renderer.destroy()
}
NativeActivityEventKind.SAVE_INSTANCE_STATE -> {
val saveStateEvent = eventPointer.value.dereferenceAs<NativeActivitySaveStateEvent>()
val state = renderer.getState()
val dataSize = state.second
rendererState = realloc(rendererState, dataSize.convert())
memcpy(rendererState, state.first, dataSize.convert())
logInfo("Saving instance state to $rendererState: $dataSize bytes")
saveStateEvent.savedState = rendererState
saveStateEvent.savedStateSize = dataSize.convert()
}
}
} finally {
notifySysEventProcessed()
}
return true
}
private fun getTime(): Float {
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.convert()), AMotionEvent_getRawY(event, i.convert()))
private fun getEventTime(event: CPointer<AInputEvent>?) =
AMotionEvent_getEventTime(event) / 1_000_000_000.0f
private fun processUserInput(): Unit {
if (AInputQueue_getEvent(queue, inputEvent.ptr) < 0) {
logError("Failure reading input event")
return
}
val event = inputEvent.value
val eventType = AInputEvent_getType(event)
if (eventType.toUInt() == AINPUT_EVENT_TYPE_MOTION) {
val action = AKeyEvent_getAction(event).toUInt() and AMOTION_EVENT_ACTION_MASK
when (action) {
AMOTION_EVENT_ACTION_DOWN -> {
animating = false
currentPoint = getEventPoint(event, 0)
startTime = getEventTime(event)
startPoint = currentPoint
}
AMOTION_EVENT_ACTION_UP -> {
val endPoint = getEventPoint(event, 0)
val endTime = getEventTime(event)
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).toInt()
for (i in 0 until numberOfPointers)
move(getEventPoint(event, i))
}
}
}
AInputQueue_finishEvent(queue, event, 1)
}
private fun move(newPoint: Vector2) {
renderer.rotateBy(newPoint - currentPoint)
currentPoint = newPoint
}
}
@@ -0,0 +1,110 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package sample.androidnative
import kotlinx.cinterop.*
import platform.android.*
data class JniClass(val jclass: jclass)
data class JniObject(val jobject: jobject)
data class JniMethod(val jmethod: jmethodID)
fun asJniClass(jclass: jclass?) =
if (jclass != null) JniClass(jclass) else null
fun asJniObject(jobject: jobject?) =
if (jobject != null) JniObject(jobject) else null
fun asJniMethod(jmethodID: jmethodID?) =
if (jmethodID != null) JniMethod(jmethodID) else null
class JniBridge(val vm: CPointer<JavaVMVar>) {
private val vmFunctions: JNIInvokeInterface = vm.pointed.pointed!!
val jniEnv = memScoped {
val envStorage = alloc<CPointerVar<JNIEnvVar>>()
if (vmFunctions.AttachCurrentThreadAsDaemon!!(vm, envStorage.ptr, null) != 0)
throw Error("Cannot attach thread to the VM")
envStorage.value!!
}
private val envFunctions: JNINativeInterface = jniEnv.pointed.pointed!!
// JNI operations.
private val fNewStringUTF = envFunctions.NewStringUTF!!
private val fFindClass = envFunctions.FindClass!!
private val fGetMethodID = envFunctions.GetMethodID!!
private val fCallVoidMethodA = envFunctions.CallVoidMethodA!!
private val fCallObjectMethodA = envFunctions.CallObjectMethodA!!
private val fExceptionCheck = envFunctions.ExceptionCheck!!
private val fExceptionDescribe = envFunctions.ExceptionDescribe!!
private val fExceptionClear = envFunctions.ExceptionClear!!
val fPushLocalFrame = envFunctions.PushLocalFrame!!
val fPopLocalFrame = envFunctions.PopLocalFrame!!
private fun check() {
if (fExceptionCheck(jniEnv) != 0.toUByte()) {
fExceptionDescribe(jniEnv)
fExceptionClear(jniEnv)
throw Error("JVM exception thrown")
}
}
fun toJString(string: String) = memScoped {
val result = asJniObject(fNewStringUTF(jniEnv, string.cstr.ptr))
check()
result
}
fun toJValues(arguments: Array<out Any?>, scope: MemScope): CPointer<jvalue>? {
val result = scope.allocArray<jvalue>(arguments.size)
arguments.mapIndexed { index, it ->
when (it) {
null -> result[index].l = null
is JniObject -> result[index].l = it.jobject
is String -> result[index].l = toJString(it)?.jobject
is Int -> result[index].i = it
is Long -> result[index].j = it
else -> throw Error("Unsupported conversion for ${it::class.simpleName}")
}
}
return result
}
fun FindClass(name: String) = memScoped {
val result = asJniClass(fFindClass(jniEnv, name.cstr.ptr))
check()
result
}
fun GetMethodID(clazz: JniClass?, name: String, signature: String) = memScoped {
val result = asJniMethod(fGetMethodID(jniEnv, clazz?.jclass, name.cstr.ptr, signature.cstr.ptr))
check()
result
}
fun CallVoidMethod(receiver: JniObject?, method: JniMethod, vararg arguments: Any?) = memScoped {
fCallVoidMethodA(jniEnv, receiver?.jobject, method.jmethod,
toJValues(arguments, this@memScoped))
check()
}
fun CallObjectMethod(receiver: JniObject?, method: JniMethod, vararg arguments: Any?) = memScoped {
val result = asJniObject(fCallObjectMethodA(jniEnv, receiver?.jobject, method.jmethod,
toJValues(arguments, this@memScoped)))
check()
result
}
// Usually, use this
inline fun <T> withLocalFrame(block: JniBridge.() -> T): T {
if (fPushLocalFrame(jniEnv, 0) < 0)
throw Error("Cannot push new local frame")
try {
return block()
} finally {
fPopLocalFrame(jniEnv, null)
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package sample.androidnative
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,302 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package sample.androidnative
import kotlinx.cinterop.*
import platform.android.*
import platform.egl.*
import platform.posix.*
import platform.gles.*
import sample.androidnative.bmpformat.BMPHeader
class Renderer(val container: DisposableContainer,
val nativeActivity: ANativeActivity,
val savedMatrix: COpaquePointer?) {
private var display: EGLDisplay? = null
private var surface: EGLSurface? = null
private var context: EGLContext? = null
private var initialized = false
var screen = Vector2.Zero
private val matrix = container.arena.allocArray<FloatVar>(16)
init {
if (savedMatrix != null) {
memcpy(matrix, savedMatrix, 16 * 4)
} else {
for (i in 0..3)
for (j in 0..3)
matrix[i * 4 + j] = if (i == j) 1.0f else 0.0f
}
}
fun initialize(window: CPointer<ANativeWindow>): Boolean {
with(container.arena) {
logInfo("Initializing context..")
display = eglGetDisplay(null)
if (display == null) {
logError("eglGetDisplay() returned error ${eglGetError()}")
return false
}
if (eglInitialize(display, null, null) == 0u) {
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>()
if (eglChooseConfig(display, attribs, null, 0, numConfigs.ptr) == 0u) {
throw Error("eglChooseConfig()#1 returned error ${eglGetError()}")
}
val supportedConfigs = allocArray<EGLConfigVar>(numConfigs.value)
if (eglChooseConfig(display, attribs, supportedConfigs, numConfigs.value, numConfigs.ptr) == 0u) {
throw Error("eglChooseConfig()#2 returned error ${eglGetError()}")
}
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) != 0u &&
eglGetConfigAttrib (display, supportedConfigs[configIndex], EGL_GREEN_SIZE, g.ptr) != 0u &&
eglGetConfigAttrib(display, supportedConfigs[configIndex], EGL_BLUE_SIZE, b.ptr) != 0u &&
eglGetConfigAttrib(display, supportedConfigs[configIndex], EGL_DEPTH_SIZE, d.ptr) != 0u &&
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) {
throw Error("eglCreateWindowSurface() returned error ${eglGetError()}")
}
context = eglCreateContext(display, supportedConfigs[configIndex], null, null)
if (context == null) {
throw Error("eglCreateContext() returned error ${eglGetError()}")
}
if (eglMakeCurrent(display, surface, surface, context) == 0u) {
throw Error("eglMakeCurrent() returned error ${eglGetError()}")
}
val width = alloc<EGLintVar>()
val height = alloc<EGLintVar>()
if (eglQuerySurface(display, surface, EGL_WIDTH, width.ptr) == 0u
|| eglQuerySurface (display, surface, EGL_HEIGHT, height.ptr) == 0u) {
throw Error("eglQuerySurface() returned error ${eglGetError()}")
}
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)
checkErrors()
glLoadIdentity()
glFrustumf(-ratio, ratio, -1.0f, 1.0f, 1.0f, 10.0f)
glMatrixMode(GL_MODELVIEW)
checkErrors()
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_AND_BACK, GL_DIFFUSE, cValuesOf(0.0f, 1.0f, 1.0f, 1.0f))
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, cValuesOf(0.3f, 0.3f, 0.3f, 1.0f))
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 30.0f)
loadTexture("kotlin_logo.bmp")
initialized = true
return true
}
}
fun checkErrors() {
val error = glGetError()
if (error.toInt() != GL_NO_ERROR)
throw Error("OpenGL error 0x${error.toInt().toString(16)}")
}
fun getState() = matrix to 16 * 4
fun rotateBy(vector: Vector2) {
if (!initialized) return
val length = vector.length
if (length < 1e-9f) return
val angle = 180 * length / screen.length
val x = -vector.y / length
val y = -vector.x / length
glPushMatrix()
glMatrixMode(GL_MODELVIEW)
checkErrors()
glLoadIdentity()
glRotatef(angle, x, y, 0.0f)
glMultMatrixf(matrix)
glGetFloatv(GL_MODELVIEW_MATRIX, matrix)
glPopMatrix()
}
private fun loadTexture(assetName: String): Unit = memScoped {
val asset = AAssetManager_open(nativeActivity.assetManager, assetName, AASSET_MODE_BUFFER.convert())
?: throw Error("Error opening asset $assetName")
println("loading texture $assetName")
try {
val length = AAsset_getLength(asset)
val buffer = allocArray<ByteVar>(length)
if (AAsset_read(asset, buffer, length.convert()) != length.toInt()) {
throw Error("Error reading asset")
}
with(buffer.reinterpret<BMPHeader>().pointed) {
if (magic != 0x4d42.toUShort() || zero != 0u || size != length.toUInt() || bits != 24.toUShort()) {
throw Error("Error parsing texture file")
}
val numberOfBytes = width * height * 3
// Swap BGR in bitmap to RGB.
for (i in 0 until numberOfBytes step 3) {
val t = data[i]
data[i] = data[i + 2]
data[i + 2] = t
}
println("loaded texture ${width}x${height}")
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)
}
} finally {
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(): Unit {
if (!initialized) return
glPushMatrix()
glMatrixMode(GL_MODELVIEW)
checkErrors()
glMultMatrixf(matrix)
glClear((GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT).toUInt())
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
val polygon = RegularPolyhedra.Dodecahedron
val vertices = mutableListOf<Float>()
val texCoords = mutableListOf<Float>()
val triangles = mutableListOf<Byte>()
val normals = mutableListOf<Float>()
for (face in polygon.faces) {
val u = polygon.vertices[face[2].toInt()] - polygon.vertices[face[1].toInt()]
val v = polygon.vertices[face[0].toInt()] - polygon.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()
polygon.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])
}
}
memScoped {
glFrontFace(GL_CW)
glVertexPointer(4, GL_FLOAT, 0, vertices.toFloatArray().toCValues().ptr)
glTexCoordPointer(2, GL_FLOAT, 0, texCoords.toFloatArray().toCValues().ptr)
glNormalPointer(GL_FLOAT, 0, normals.toFloatArray().toCValues().ptr)
glDrawElements(GL_TRIANGLES, triangles.size, GL_UNSIGNED_BYTE, triangles.toByteArray().toCValues().ptr)
}
glPopMatrix()
if (eglSwapBuffers(display, surface) == 0u) {
val error = eglGetError()
if (error != EGL_BAD_SURFACE)
throw Error("eglSwapBuffers() returned error $error")
else {
if (eglMakeCurrent(display, surface, surface, context) == 0u) {
throw Error("Reinit eglMakeCurrent() returned error ${eglGetError()}")
}
if (eglSwapBuffers(display, surface) == 0u)
throw Error("Bad eglSwapBuffers() after surface reinit: ${eglGetError()}")
}
}
}
fun start() {
logInfo("Starting renderer.")
if (initialized) {
if (eglMakeCurrent(display, surface, surface, context) == 0u) {
throw Error("eglMakeCurrent() returned error ${eglGetError()}")
}
}
}
fun stop() {
logInfo("Stopping renderer..")
eglMakeCurrent(display, null, null, null)
}
fun destroy() {
if (!initialized) return
logInfo("Destroying renderer..")
eglMakeCurrent(display, null, null, null)
context?.let { eglDestroyContext(display, it) }
surface?.let { eglDestroySurface(display, it) }
display?.let { eglTerminate(display) }
display = null
surface = null
context = null
initialized = false
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package sample.androidnative
import kotlinx.cinterop.*
import platform.android.*
import platform.posix.*
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)
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package sample.androidnative
import kotlinx.cinterop.*
import platform.android.*
fun main() {
logInfo("Entering main().")
memScoped {
val state = alloc<NativeActivityState>()
getNativeActivityState(state.ptr)
val engine = Engine(state)
try {
engine.mainLoop()
} finally {
engine.dispose()
}
}
kotlin.system.exitProcess(0)
}
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

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>