Kotlin/NativeのOpenGLサンプルを触ってみる

## こんにちは！
本記事は[コネヒト Advent Calendar 2017](https://qiita.com/advent-calendar/2017/connehito)の24日目の記事になります。

こんにちは！Androidエンジニアの[富田](https://twitter.com/tommykw)です。今回は[Kotlin/Native](https://github.com/JetBrains/kotlin-native)のOpenGLのサンプルコードを触ってみましたので紹介したいと思います。

## Kotlin/Nativeとは？
LLVMを用いてネイティブコードを生成してくれるコンパイラで、各プラットフォームのコンパイルを可能にするように設計されています。先日[v0.5](https://blog.jetbrains.com/kotlin/2017/12/kotlinnative-v0-5-released-calling-kotlin-from-swift-and-c-llvm-5-and-more/)がリリースされてSwiftからKotlinコードを利用できたり、Python Extensionサンプルが追加されました。

![スクリーンショット 2017-12-22 8.58.32.png](https://qiita-image-store.s3.amazonaws.com/0/23391/16bd8c01-7605-c2af-7eb8-11e7a5c4d36a.png)

Kotlin/Nativeについては以下の記事でも紹介されておりますので、興味のある方はご覧ください。
[Kotlin/Nativeを使ってiOSアプリを作ってみる
](https://qiita.com/noripi/items/4ee969c48b3da5ca6fbd)[Kotlin/Nativeを使ってXcode + CLionでiOSアプリを書く](https://qiita.com/oboenikui/items/07481c6f44fe3e2c4440)

## Kotlin/Nativeのサンプルについて
Kotlin/Nativeのリポジトリには、たくさんの[サンプルコード](https://github.com/JetBrains/kotlin-native/tree/master/samples
)がありますので、いくつか紹介します。

|サンプル|内容|
|---|---|
|[csvparser](https://github.com/JetBrains/kotlin-native/tree/master/samples/csvparser)|csvのファイルをparseする|
|[gitchurn](https://github.com/JetBrains/kotlin-native/tree/master/samples/gitchurn)|gitリポジトリの統計情報を表示する|
|[libcurl](https://github.com/JetBrains/kotlin-native/tree/master/samples/libcurl)|HTTPクライアント|
|[nonBlockingEchoServer](https://github.com/JetBrains/kotlin-native/tree/master/samples/nonBlockingEchoServer)|コルーチンを利用したマルチクライアント|
|[opengl](https://github.com/JetBrains/kotlin-native/tree/master/samples/opengl)|OpenGLのティーポットサンプル|
|[python_extension](https://github.com/JetBrains/kotlin-native/tree/master/samples/python_extension)|Python Extension|
|[tensorflow](https://github.com/JetBrains/kotlin-native/tree/master/samples/tensorflow)|TensorFlowのデモ|
|[socket](https://github.com/JetBrains/kotlin-native/tree/master/samples/socket)|Socketサーバ|
|[tetris](https://github.com/JetBrains/kotlin-native/tree/master/samples/tetris)|テトリスアプリケーション|
|[videoplayer](https://github.com/JetBrains/kotlin-native/tree/master/samples/videoplayer)|ビデオプレイヤー|
|[win32](https://github.com/JetBrains/kotlin-native/tree/master/samples/win32)|Win32のHello World|

## プラットフォームライブラリ
各プラットフォーム毎にネイティブコードを利用できるようにするため、ビルド時にライブラリが含まれるようになっています。このライブラリをプラットフォームライブラリと呼ばれています。

実際にビルドをしてもらうと、`dist/klib/platform`以下にプラットフォームライブラリが含まれているのがわかります。さらに`dist/klib/platform/macbook`以下を見ると、以下のネイティブライブラリがあることがわかりました。今回はOpenGLを使いたいので、Kotlin側で`import platform.OpenGL`と記述すると利用できます。

|ライブラリ|パッケージ|
|---|---|
|AppKit|platform.ApplicationServices|
|CFNetwork|platform.CFNetwork|
|CoreData|platform.CoreData|
|CoreFoundation|platform.CoreFoundation|
|CoreGraphics|platform.CoreGraphics|
|CoreImage|platform.CoreImage|
|CoreServices|platform.CoreServices|
|CoreText|platform.CoreText|
|CoreVideo|platform.CoreVideo|
|DiskArbitration|platform.DiskArbitration|
|Foundation|platform.Foundation|
|CoreVideo|platform.CoreVideo|
|GLUT|platform.GLUT|
|IOKit|platform.IOKit|
|IOSurface|platform.IOSurface|
|ImageIO|platform.ImageIO|
|Metal|platform.Metal|
|OpenGL|platform.OpenGL|
|OpenGL3|platform.OpenGL3|
|OpenGLCommon|platform.OpenGLCommon|
|QuartzCore|platform.QuartzCore|
|Security|platform.Security|
|libkern|platform.libkern|
|objc|platform.objc|
|osx|platform.osx|
|posix|platform.posix|
|zlib|platform.zlib|

## OpenGLのサンプルを書いてみる
[こちら](http://openglsamples.sourceforge.net/projects/index.php/blog/index/)のOpenGL GLUT TriangleサンプルをKotlin/Nativeを用いてKotlinで書いてみたいと思います。

<img width="1102" alt="スクリーンショット 2017-12-23 14.31.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/23391/46e075b9-0cf4-a59f-f6c7-72d1d35aa26c.png">

提供されているcppのサンプルコードは以下の通りです。

```cpp:Triangle.cpp
#include <stdio.h>
#include <windows.h>
#include <GL/gl.h>
#include <GL/glut.h>

typedef struct {
    int width;
    int height;
    char* title;
    float field_of_view_angle;
    float z_near;
    float z_far;
} glutWindow;

glutWindow win;

void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(0.0f,0.0f,-3.0f);			
 
    glBegin(GL_TRIANGLES);
    glColor3f(0.0f,0.0f,1.0f);			
    glVertex3f( 0.0f, 1.0f, 0.0f);		
    glColor3f(0.0f,1.0f,0.0f);			
    glVertex3f(-1.0f,-1.0f, 0.0f);		
    glColor3f(1.0f,0.0f,0.0f);			
    glVertex3f( 1.0f,-1.0f, 0.0f);		
    glEnd();				

    glutSwapBuffers();
}


void initialize () 
{
    glMatrixMode(GL_PROJECTION);
    glViewport(0, 0, win.width, win.height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    GLfloat aspect = (GLfloat) win.width / win.height;
    gluPerspective(win.field_of_view_angle, aspect, win.z_near, win.z_far);	
    glMatrixMode(GL_MODELVIEW);
    glShadeModel( GL_SMOOTH );
    glClearDepth( 1.0f );
    glEnable( GL_DEPTH_TEST );
    glDepthFunc( GL_LEQUAL );
    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
    glClearColor(0.0, 0.0, 0.0, 1.0);							
}

int main(int argc, char **argv) 
{
    win.width = 640;
    win.height = 480;
    win.title = "OpenGL/GLUT Example. Visit http://openglsamples.sf.net ";
    win.field_of_view_angle = 45;
    win.z_near = 1.0f;
    win.z_far = 500.0f;
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );
    glutInitWindowSize(win.width,win.height);
    glutCreateWindow(win.title);
    glutDisplayFunc(display);
    glutIdleFunc(display);
    initialize();
    glutMainLoop();
    return 0;
}
```
こちらをKotlinで書いてみると次のようになります。

```kotlin:Triangle.kt
import kotlinx.cinterop.*
import platform.GLUT.*
import platform.OpenGL.*
import platform.OpenGLCommon.*

private val windowWidth = 640
private val windowHeight = 480

private fun display() {
    glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslatef(0.0f, 0.0f, -3.0f)

    glBegin(GL_TRIANGLES);
    glColor3f(0.0f,　0.0f,　1.0f)
    glVertex3f(0.0f, 1.0f,　0.0f)
    glColor3f(0.0f,　1.0f,　0.0f)
    glVertex3f(-1.0f,　-1.0f,　0.0f)
    glColor3f(1.0f,　0.0f,　0.0f)
    glVertex3f(1.0f,　-1.0f,　0.0f)
    glEnd()

    glutSwapBuffers()
}

private fun initialize() {
    glMatrixMode(GL_PROJECTION)
    glViewport(0, 0, windowWidth, windowHeight)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    val aspect = windowWidth.toDouble() / windowHeight
    gluPerspective(45.0, aspect, 1.0, 500.0)
    glMatrixMode(GL_MODELVIEW)
    glShadeModel(GL_SMOOTH)
    glClearDepth(1.0)
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
}

fun main(args: Array<String>) {
    memScoped {
        val argc = alloc<IntVar>().apply { value = 0 }
        glutInit(argc.ptr, null)
    }

    glutInitDisplayMode(GLUT_RGB or GLUT_DOUBLE or GLUT_DEPTH)
    glutInitWindowSize(windowWidth, windowHeight)
    glutCreateWindow("Triangle")
    glutDisplayFunc(staticCFunction(::display))
    glutIdleFunc(staticCFunction(::display))

    initialize()
    glutMainLoop()
}
```

アプリケーションの実行結果は以下となります。ビルドや実行方法に関しては、[こちら](https://github.com/JetBrains/kotlin-native/tree/master/samples/opengl)のOpenGLサンプルをご確認ください。

<img width="400" alt="スクリーンショット 2017-12-24 10.36.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/23391/b7fa3b31-c284-e851-fed7-42dce8258c58.png">

## 最後に
### まとめ
Kotlin/Nativeを用いて、OpenGLのネイティブライブラリをKotlinから呼び出してみました。Kotlin/Nativeリポジトリにはtensorflow、python_extentionなど面白そうなサンプルがたくさんありますので、興味のある方は触ってみると良いかもしれません。個人的な目標として、来年はKotlin/Nativeのサンプルを送ってみたいと思います！

### 明日の予告
[コネヒト Advent Calendar 2017](https://qiita.com/advent-calendar/2017/connehito)の最終日は[itosho](https://twitter.com/itosho?lang=ja)さんの記事です！！メリークリスマス！！！