You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
7.1 KiB
Kotlin
252 lines
7.1 KiB
Kotlin
package me.eater.threedom.example
|
|
|
|
import me.eater.threedom.dom.Document
|
|
import me.eater.threedom.dom.IDocument
|
|
import me.eater.threedom.dom.PlainNode
|
|
import me.eater.threedom.dom.createNode
|
|
import me.eater.threedom.example.shader.Plain
|
|
import me.eater.threedom.example.vertex.Simple
|
|
import me.eater.threedom.gl.GL
|
|
import me.eater.threedom.gl.ShaderPreProcessor
|
|
import me.eater.threedom.gl.dom.PerspectiveCamera
|
|
import me.eater.threedom.gl.dom.TriMesh
|
|
import me.eater.threedom.gl.texture.Texture
|
|
import me.eater.threedom.gl.vertex.VertexArrayObject
|
|
import me.eater.threedom.gl.vertex.VertexBuffer
|
|
import me.eater.threedom.utils.joml.toFloat
|
|
import me.eater.threedom.utils.joml.translation
|
|
import me.eater.threedom.utils.joml.vec3
|
|
import org.joml.Vector2f
|
|
import org.joml.Vector3f
|
|
import org.lwjgl.glfw.GLFW.*
|
|
import org.lwjgl.glfw.GLFWErrorCallback
|
|
import org.lwjgl.opengl.GL.createCapabilities
|
|
import org.lwjgl.opengl.GL11.*
|
|
import org.lwjgl.system.MemoryStack.stackPush
|
|
import org.lwjgl.system.MemoryUtil.NULL
|
|
import java.time.Instant
|
|
import kotlin.math.PI
|
|
import kotlin.math.cos
|
|
import kotlin.math.sin
|
|
|
|
fun main() {
|
|
GLFWErrorCallback.createPrint(System.err).set()
|
|
|
|
if (!glfwInit()) {
|
|
println("Unable to initialize GLFW")
|
|
return
|
|
}
|
|
|
|
glfwDefaultWindowHints()
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4)
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4)
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
|
|
|
|
val window = glfwCreateWindow(300, 300, "Hello world", NULL, NULL)
|
|
if (window == NULL) {
|
|
println("Failed creating window")
|
|
return
|
|
}
|
|
|
|
val stack = stackPush()
|
|
val pWidth = stack.mallocInt(1)
|
|
val pHeight = stack.mallocInt(1)
|
|
|
|
// Get the window size passed to glfwCreateWindow
|
|
glfwGetWindowSize(window, pWidth, pHeight)
|
|
|
|
// Get the resolution of the primary monitor
|
|
val vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()) ?: return
|
|
|
|
// Center the window
|
|
glfwSetWindowPos(
|
|
window,
|
|
(vidmode.width() - pWidth.get(0)) / 2,
|
|
(vidmode.height() - pHeight.get(0)) / 2
|
|
)
|
|
|
|
|
|
// Make the OpenGL context current
|
|
glfwMakeContextCurrent(window)
|
|
|
|
var width = pWidth.get(0)
|
|
var height = pHeight.get(0)
|
|
|
|
// Enable v-sync
|
|
glfwSwapInterval(1)
|
|
glfwShowWindow(window)
|
|
createCapabilities()
|
|
|
|
val doc: IDocument = Document()
|
|
|
|
val triMesh = doc.createNode<TriMesh>()
|
|
|
|
val preProcessor = ShaderPreProcessor.createDefault()
|
|
|
|
val mesh = listOf(
|
|
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
|
|
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
|
|
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
|
|
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
|
|
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
|
|
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
|
|
|
|
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
|
|
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
|
|
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
|
|
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
|
|
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
|
|
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
|
|
|
|
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
|
|
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
|
|
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
|
|
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
|
|
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
|
|
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
|
|
|
|
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
|
|
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
|
|
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
|
|
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
|
|
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
|
|
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
|
|
|
|
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
|
|
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
|
|
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
|
|
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
|
|
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
|
|
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
|
|
|
|
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
|
|
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
|
|
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
|
|
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
|
|
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
|
|
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f
|
|
)
|
|
|
|
val buffer = mesh
|
|
.chunked(6)
|
|
.map {
|
|
Simple().apply {
|
|
position = vec3(it[0], it[1], it[2])
|
|
normal = vec3(it[3], it[4], it[5])
|
|
}
|
|
}
|
|
.let {
|
|
VertexBuffer(it)
|
|
}
|
|
|
|
val VAO = VertexArrayObject {
|
|
buffer(Simple::position)
|
|
buffer(Simple::normal)
|
|
}
|
|
|
|
triMesh.mesh = VAO
|
|
println("Loading mesh")
|
|
println("Loading shader program")
|
|
val plainShader = Plain.load(preProcessor)
|
|
val lampShader = Plain.lamp(preProcessor)
|
|
|
|
val texture = Texture()
|
|
texture.load(Simple::class.java.getResourceAsStream("/textures/fujiwara.jpg").readAllBytes())
|
|
plainShader.apply {
|
|
objectColor = vec3(1.0, 0.5, 0.0)
|
|
lightColor = vec3(1.0, 1.0, 1.0)
|
|
}
|
|
|
|
triMesh.use(lampShader)
|
|
|
|
val empty = doc.createNode<PlainNode>()
|
|
|
|
doc.addNode(empty)
|
|
empty.addNode(triMesh)
|
|
|
|
val around = mutableListOf<TriMesh>()
|
|
|
|
val amount = 3
|
|
val deg = (PI * 2) / amount
|
|
for (i in 0 until amount) {
|
|
val trimeshDup = triMesh.clone()
|
|
trimeshDup.use(plainShader)
|
|
around.add(trimeshDup)
|
|
triMesh.addNode(trimeshDup)
|
|
|
|
trimeshDup.model {
|
|
translate(2 * sin(deg * i), 0.0, 2 * cos(deg * i))
|
|
}
|
|
}
|
|
|
|
val camera = doc.createNode<PerspectiveCamera>()
|
|
doc.addNode(camera)
|
|
|
|
glfwSetFramebufferSizeCallback(window) { _, newWidth, newHeight ->
|
|
glViewport(0, 0, newWidth, newHeight)
|
|
|
|
camera.width = newWidth.toDouble()
|
|
camera.height = newHeight.toDouble()
|
|
}
|
|
|
|
glfwSetKeyCallback(window) { _, key, _, action, _ ->
|
|
if ((key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) && action == GLFW_RELEASE)
|
|
glfwSetWindowShouldClose(window, true)
|
|
|
|
|
|
var z = 0.0
|
|
var x = 0.0
|
|
val speed = 0.1
|
|
if (key == GLFW_KEY_UP && action != GLFW_RELEASE) {
|
|
z += speed
|
|
}
|
|
|
|
if (key == GLFW_KEY_DOWN && action != GLFW_RELEASE) {
|
|
z -= speed
|
|
}
|
|
|
|
if (key == GLFW_KEY_LEFT && action != GLFW_RELEASE) {
|
|
x += speed
|
|
}
|
|
|
|
if (key == GLFW_KEY_RIGHT && action != GLFW_RELEASE) {
|
|
x -= speed
|
|
}
|
|
|
|
if (x != 0.0 || z != 0.0) {
|
|
camera.model {
|
|
translateLocal(x, 0.0, z)
|
|
}
|
|
}
|
|
}
|
|
|
|
println("Starting render loop")
|
|
glClearColor(0.3f, 0.2f, 0.2f, 1.0f)
|
|
glEnable(GL_DEPTH_TEST)
|
|
while (!glfwWindowShouldClose(window)) {
|
|
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
|
|
|
|
empty.model {
|
|
setTranslation(0.0, 0.0, sin(Instant.now().toEpochMilli().toDouble() / 1000) - 3)
|
|
}
|
|
|
|
triMesh.model {
|
|
rotateY(0.005)
|
|
}
|
|
|
|
around.forEach {
|
|
it.model {
|
|
rotateY(-0.005)
|
|
}
|
|
}
|
|
|
|
plainShader.lightPos = triMesh.absolute.translation.toFloat()
|
|
plainShader.viewPos = camera.absolute.translation.toFloat()
|
|
doc.render(GL, camera)
|
|
|
|
glfwSwapBuffers(window)
|
|
glfwPollEvents()
|
|
}
|
|
}
|
|
|