-
Notifications
You must be signed in to change notification settings - Fork 7
Quick Start. TeaVM
Create new project in IDEA: File > New > Project
Choose Gradle
Choose Kotlin/JVM as framework
Write project name
Add jcenter to repositories:
repositories {
jcenter()
}
To create web-application, add thelema-teavm to dependecies:
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.ksdfv.thelema:thelema-teavm:0.3.0"
}
In kotlin directory src/main/kotlin
create package and name it for somthing like "com.example"
In this package create object Main. Type main, and IDEA will suggest you create main function, accept it. Code will looks like this:
package com.example
object Main {
@JvmStatic
fun main(args: Array<String>) {
}
}
To initialize engine, write:
val app = TeaVMApp()
We will create a basic application using built-in tools, but we will still create the shader manually. Write in main function:
@Language("GLSL")
val shader = Shader(
vertCode = """
attribute vec3 aPosition;
varying vec3 vPosition;
uniform mat4 viewProj;
void main() {
vPosition = aPosition;
gl_Position = viewProj * vec4(aPosition, 1.0);
}""",
fragCode = """
varying vec3 vPosition;
void main() {
gl_FragColor = vec4(vPosition, 1.0);
}"""
)
In the shader, we only use one aPosition attribute. The name of this attribute used by default in Thelema.
The value of this attribute passed to the fragment shader via varying variable.
We also pass the camera matrix and multiply by the position, so that the object positioned relative to the camera and its vertex correctly projected onto the screen.
Now we need to create meshes, and a function that will be called in the render loop.
val box = BoxMeshBuilder().build()
val control = OrbitCameraControl()
control.listenToMouse()
GL.glClearColor(0f, 0f, 0f, 1f)
GL.isDepthTestEnabled = true
We use the built-in tool BoxMeshBuilder
to generate a mesh box.
Create a camera controller OrbitCameraControl
and bind it to mouse events,
so that we can rotate and move the camera.
Set background color and enable depth test.
Now let's define how rendering must work.
GL.render {
GL.glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
control.update(APP.deltaTime)
ActiveCamera.update()
shader.bind()
shader["viewProj"] = ActiveCamera.viewProjectionMatrix
box.render(shader)
}
Сlear the screen with a given color.
Here we have to update the state of the camera controller and correspondingly update the camera matrix (the camera updated separately from the controller).
We transfer necessary data to the shader, in particular the camera matrix.
And finally, render our mesh with shader.
Start render loop:
app.startLoop()
Camera controls:
- rotate by holding down the Middle mouse button
- move while holding Shift + Middle mouse button
We also need a web page for running our application.
Create index.html file in directory
src/main/resources
and add the following code there:
<!DOCTYPE html>
<html>
<head>
<title>TeaVM example</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script type="text/javascript" charset="utf-8" src="classes.js"></script>
</head>
<body onload="main()">
<canvas id="canvas" width="1280" height="720"></canvas>
</body>
</html>
To compile code to javascript, we will use the TeaVM CLI tool.
TeaVM CLI created by TeaVM developer. It is one of main TeaVM tools. You can download the latest version of this tool via Maven Repository
Using this tool allows you to configure compilation directly without any Gradle or Maven plugins.
We will need to configure the compilation via command line in Gradle. To find out what command parameters can be used, run the tool without parameters, and it will show description of all parameters:
java -jar teavm.jar
To compile through TeaVM, we need to specify the following parameters:
- classpath. Directories that must contain JVM class files .class, including classes from dependencies.
- targetdir. Result directory for compiled files by TeaVM
- At the end of all parameters, specify the class to that containing the main function. Class name must be fully qualified class name, for example com.example.Main.
Before we create a command execution task in Gradle, we need to create auxiliary tasks.
static Boolean checkDependency(File file) {
def name = file.name
if (name.contains("thelema")) return true
return !(name.contains("teavm") ||
name.contains("gson") ||
name.contains("jzlib") ||
name.contains("joda-time") ||
name.contains("annotations")
)
}
task copyRuntimeClasses(type: Copy) {
dependsOn(build)
configurations.runtimeClasspath.collect {
if (checkDependency(it)) {
from(zipTree(it).matching {
include("**/*.class")
})
}
true
}
includeEmptyDirs = false
into("$buildDir/classes/dependencies")
}
task copyRuntimeResources(type: Copy) {
from("$buildDir/resources/main")
into("$buildDir/teavm")
}
We have created two tasks copyRuntimeClasses
and copyRuntimeResources
.
copyRuntimeClasses will unpack all jar dependencies and copy from them
classes to the directory build/classes/dependencies
.
Later, we will pass this directory path to parameters for TeaVM.
We only take files that with .class extension.
Also, using the checkDependency
function, we exclude all unnecessary
dependencies, in particular, these are the teavm dependencies.
With copyRuntimeResources
we copy the resource files from
src/main/resources
to build/teavm
.
Now we can create a task to compile through TeaVM.
task teavmCompile(type: Exec) {
dependsOn(copyRuntimeClasses, copyRuntimeResources)
commandLine([
"java",
"-jar",
"teavm.jar",
"--classpath",
"$buildDir/classes/dependencies",
"$buildDir/classes/kotlin/main",
"--targetdir",
"build/teavm",
"--debug",
"--sourcemaps",
"com.example.Main"
])
}
type: Exec
indicates that in this task Gradle should invoke the command
via commandLine
.
In classpath parameter, we pass the directory with dependency classes
build/classes/dependencies
and directory with generated classes
of our application build/classes/kotlin/main
.
In this example, we will enable debug options debug
and sourcemaps
.
If you want to see what dependencies will be added to compilation, you can write this code in the teavmCompile task.
println("=== dependencies ===")
configurations.runtimeClasspath.collect {
if (checkDependency(it)) println(it.name)
true
}
Now we can run teavmCompile task via IntelliJ in Gradle panel on the right in the Tasks -> Other.
As a result, TeaVM should generate several files including classes.js.
These files should be located in build/teavm
. index.html
will be placed in the same directory, which we can open in the browser
to see the result.
See build.gradle
-
Quick Start
-
3D graphics
-
Math