Kotlin

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

こんにちは!

本記事はコネヒト Advent Calendar 2017の24日目の記事になります。

こんにちは!Androidエンジニアの富田です。今回はKotlin/NativeのOpenGLのサンプルコードを触ってみましたので紹介したいと思います。

Kotlin/Nativeとは?

LLVMを用いてネイティブコードを生成してくれるコンパイラで、各プラットフォームのコンパイルを可能にするように設計されています。先日v0.5がリリースされてSwiftからKotlinコードを利用できたり、Python Extensionサンプルが追加されました。

スクリーンショット 2017-12-22 8.58.32.png

Kotlin/Nativeについては以下の記事でも紹介されておりますので、興味のある方はご覧ください。
Kotlin/Nativeを使ってiOSアプリを作ってみる
Kotlin/Nativeを使ってXcode + CLionでiOSアプリを書く

Kotlin/Nativeのサンプルについて

Kotlin/Nativeのリポジトリには、たくさんのサンプルコードがありますので、いくつか紹介します。

サンプル 内容
csvparser csvのファイルをparseする
gitchurn gitリポジトリの統計情報を表示する
libcurl HTTPクライアント
nonBlockingEchoServer コルーチンを利用したマルチクライアント
opengl OpenGLのティーポットサンプル
python_extension Python Extension
tensorflow TensorFlowのデモ
socket Socketサーバ
tetris テトリスアプリケーション
videoplayer ビデオプレイヤー
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のサンプルを書いてみる

こちらのOpenGL GLUT TriangleサンプルをKotlin/Nativeを用いてKotlinで書いてみたいと思います。

スクリーンショット 2017-12-23 14.31.42.png

提供されている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で書いてみると次のようになります。

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()
}

アプリケーションの実行結果は以下となります。ビルドや実行方法に関しては、こちらのOpenGLサンプルをご確認ください。

スクリーンショット 2017-12-24 10.36.39.png

最後に

まとめ

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

明日の予告

コネヒト Advent Calendar 2017の最終日はitoshoさんの記事です!!メリークリスマス!!!