add gl and example
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
parent
eff722ea05
commit
7fc4b44571
@ -1,5 +0,0 @@
|
||||
<graph>
|
||||
<box diameter="10" position="[33, 40, 3]">
|
||||
|
||||
</box>
|
||||
</graph>
|
@ -0,0 +1,67 @@
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
|
||||
id 'java'
|
||||
id 'application'
|
||||
}
|
||||
|
||||
group 'me.eater.threedom'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
switch (OperatingSystem.current()) {
|
||||
case OperatingSystem.LINUX:
|
||||
project.ext.lwjglNatives = "natives-linux"
|
||||
break
|
||||
case OperatingSystem.MAC_OS:
|
||||
project.ext.lwjglNatives = "natives-macos"
|
||||
break
|
||||
case OperatingSystem.WINDOWS:
|
||||
project.ext.lwjglNatives = "natives-windows"
|
||||
break
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation platform("org.lwjgl:lwjgl-bom:3.2.3")
|
||||
|
||||
implementation project(":threedom")
|
||||
implementation project(":threedom-gl")
|
||||
|
||||
implementation "org.lwjgl:lwjgl"
|
||||
implementation "org.lwjgl:lwjgl-opengl"
|
||||
implementation "org.lwjgl:lwjgl-glfw"
|
||||
|
||||
implementation 'org.joml:joml:1.9.24'
|
||||
|
||||
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = 'me.eater.threedom.example.MainKt'
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "12"
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "12"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_12
|
||||
targetCompatibility = JavaVersion.VERSION_12
|
||||
}
|
||||
|
||||
|
||||
run {
|
||||
|
||||
jvmArgs("-javaagent:$projectDir/../tools/lwjglx-debug-1.0.0.jar")
|
||||
}
|
@ -0,0 +1,251 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
package me.eater.threedom.example.shader
|
||||
|
||||
import me.eater.threedom.gl.Shader
|
||||
import me.eater.threedom.gl.ShaderPreProcessor
|
||||
import me.eater.threedom.gl.ShaderProgram
|
||||
|
||||
class Plain private constructor(vertexShader: Shader, fragmentShader: Shader) :
|
||||
ShaderProgram(vertexShader, fragmentShader) {
|
||||
|
||||
var objectColor by vec3()
|
||||
var lightColor by vec3()
|
||||
var lightPos by vec3()
|
||||
var viewPos by vec3()
|
||||
|
||||
companion object {
|
||||
fun load(preProcessor: ShaderPreProcessor): Plain {
|
||||
val vertex = preProcessor.compile(
|
||||
this::class.java.getResourceAsStream("/shaders/plain/vertex.glsl").readAllBytes()
|
||||
.toString(Charsets.UTF_8),
|
||||
Shader.ShaderType.Vertex
|
||||
)
|
||||
|
||||
val fragment = preProcessor.compile(
|
||||
this::class.java.getResourceAsStream("/shaders/plain/fragment.glsl").readAllBytes()
|
||||
.toString(Charsets.UTF_8),
|
||||
Shader.ShaderType.Fragment
|
||||
|
||||
)
|
||||
return Plain(
|
||||
vertex,
|
||||
fragment
|
||||
).also {
|
||||
vertex.delete()
|
||||
fragment.delete()
|
||||
}
|
||||
}
|
||||
|
||||
fun lamp(preProcessor: ShaderPreProcessor): Plain {
|
||||
val vertex = preProcessor.compile(
|
||||
this::class.java.getResourceAsStream("/shaders/plain/vertex.glsl").readAllBytes()
|
||||
.toString(Charsets.UTF_8),
|
||||
Shader.ShaderType.Vertex
|
||||
)
|
||||
|
||||
val fragment = preProcessor.compile(
|
||||
this::class.java.getResourceAsStream("/shaders/plain/fragment_lamp.glsl").readAllBytes()
|
||||
.toString(Charsets.UTF_8),
|
||||
Shader.ShaderType.Fragment
|
||||
|
||||
)
|
||||
return Plain(
|
||||
vertex,
|
||||
fragment
|
||||
).also {
|
||||
vertex.delete()
|
||||
fragment.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package me.eater.threedom.example.vertex
|
||||
|
||||
import me.eater.threedom.gl.vertex.VertexData
|
||||
|
||||
class Simple : VertexData() {
|
||||
var position by vec3()
|
||||
var normal by vec3()
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec3 FragPos;
|
||||
in vec3 Normal;
|
||||
in vec3 LightPos; // extra in variable, since we need the light position in view space we calculate this in the vertex shader
|
||||
|
||||
uniform vec3 lightColor;
|
||||
uniform vec3 objectColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
// ambient
|
||||
float ambientStrength = 0.1;
|
||||
vec3 ambient = ambientStrength * lightColor;
|
||||
|
||||
// diffuse
|
||||
vec3 norm = normalize(Normal);
|
||||
vec3 lightDir = normalize(LightPos - FragPos);
|
||||
float diff = max(dot(norm, lightDir), 0.0);
|
||||
vec3 diffuse = diff * lightColor;
|
||||
|
||||
// specular
|
||||
float specularStrength = 0.5;
|
||||
vec3 viewDir = normalize(-FragPos); // the viewer is always at (0,0,0) in view-space, so viewDir is (0,0,0) - Position => -Position
|
||||
vec3 reflectDir = reflect(-lightDir, norm);
|
||||
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
|
||||
vec3 specular = specularStrength * spec * lightColor;
|
||||
|
||||
vec3 result = (ambient + diffuse + specular) * objectColor;
|
||||
FragColor = vec4(result, 1.0);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
#version 330 core
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() {
|
||||
FragColor = vec4(1.0);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
|
||||
out vec3 FragPos;
|
||||
out vec3 Normal;
|
||||
out vec3 LightPos;
|
||||
|
||||
uniform vec3 lightPos; // we now define the uniform in the vertex shader and pass the 'view space' lightpos to the fragment shader. lightPos is currently in world space.
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * view * model * vec4(aPos, 1.0);
|
||||
FragPos = vec3(view * model * vec4(aPos, 1.0));
|
||||
Normal = mat3(transpose(inverse(view * model))) * aNormal;
|
||||
LightPos = vec3(view * vec4(lightPos, 1.0)); // Transform world-space light position to view-space light position
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 384 KiB |
@ -0,0 +1,37 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group 'me.eater.threedom'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation platform("org.lwjgl:lwjgl-bom:3.2.3")
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation project(":threedom")
|
||||
|
||||
implementation 'org.joml:joml:1.9.24'
|
||||
implementation "org.lwjgl:lwjgl"
|
||||
implementation "org.lwjgl:lwjgl-opengl"
|
||||
implementation "org.lwjgl:lwjgl-stb"
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "12"
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "12"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_12
|
||||
targetCompatibility = JavaVersion.VERSION_12
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package me.eater.threedom.gl
|
||||
|
||||
import me.eater.threedom.dom.render.IRenderTarget
|
||||
import me.eater.threedom.gl.dom.ICamera
|
||||
import org.lwjgl.system.MemoryStack
|
||||
import org.lwjgl.system.MemoryStack.stackPush
|
||||
|
||||
object GL : IRenderTarget<ICamera<*>> {
|
||||
override val type = "GL"
|
||||
val stack: MemoryStack = stackPush()
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package me.eater.threedom.gl
|
||||
|
||||
import me.eater.threedom.gl.GL.stack
|
||||
import me.eater.threedom.gl.exception.ShaderCompilationException
|
||||
import org.lwjgl.opengl.GL20.*
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
|
||||
data class Shader(val shaderId: Int, val shaderType: ShaderType) {
|
||||
enum class ShaderType(val glId: Int) {
|
||||
Fragment(GL_FRAGMENT_SHADER),
|
||||
Vertex(GL_VERTEX_SHADER);
|
||||
}
|
||||
|
||||
fun delete() {
|
||||
glDeleteShader(shaderId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(source: String, shaderType: ShaderType): Shader {
|
||||
val shaderId = glCreateShader(shaderType.glId)
|
||||
glShaderSource(shaderId, source)
|
||||
glCompileShader(shaderId)
|
||||
val success = stack.mallocInt(1)
|
||||
glGetShaderiv(shaderId, GL_COMPILE_STATUS, success)
|
||||
if (success.get(0) == 0) {
|
||||
throw ShaderCompilationException(glGetShaderInfoLog(shaderId))
|
||||
}
|
||||
|
||||
return Shader(shaderId, shaderType)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package me.eater.threedom.gl
|
||||
|
||||
class ShaderPreProcessor {
|
||||
private val regex = Regex("(?<module>[^:]+)::(?<call>[^:(]+)\\((?<args>.+)\\)")
|
||||
private val calls: MutableMap<Pair<String, String>, (List<String>) -> String> = mutableMapOf()
|
||||
|
||||
fun process(input: String): String =
|
||||
input.split("\n").joinToString("\n") {
|
||||
if (it.startsWith("#[") && it.endsWith("]")) {
|
||||
val macro = it.substring(2 until it.length - 1)
|
||||
val match = regex.find(macro) ?: return@joinToString it
|
||||
val module = match.groups["module"]!!.value
|
||||
val call = match.groups["call"]!!.value
|
||||
val args = match.groups["args"]!!.value.split(",").map { arg -> arg.trim() }.toList()
|
||||
|
||||
calls[module to call]?.invoke(args) ?: throw RuntimeException("Can't find macro $module::$call")
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
fun register(module: String, call: String, block: (List<String>) -> String) {
|
||||
calls[module to call] = block
|
||||
}
|
||||
|
||||
fun compile(source: String, type: Shader.ShaderType): Shader {
|
||||
return Shader.create(process(source), type)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createDefault() = ShaderPreProcessor().apply {
|
||||
register("3dom", "import") { it ->
|
||||
val knownUniforms = mapOf(
|
||||
"model" to "mat4",
|
||||
"view" to "mat4",
|
||||
"projection" to "mat4",
|
||||
"normalModel" to "mat3"
|
||||
)
|
||||
|
||||
it.joinToString("\n") { uni ->
|
||||
"uniform ${knownUniforms[uni]} $uni;"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,220 @@
|
||||
package me.eater.threedom.gl
|
||||
|
||||
import me.eater.threedom.gl.GL.stack
|
||||
import me.eater.threedom.gl.dom.ICamera
|
||||
import me.eater.threedom.gl.exception.ShaderProgramLinkingException
|
||||
import me.eater.threedom.gl.texture.Texture
|
||||
import me.eater.threedom.utils.joml.toFloat
|
||||
import org.joml.*
|
||||
import org.lwjgl.opengl.GL20.*
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
open class ShaderProgram(private val programId: Int = glCreateProgram()) {
|
||||
private val uniforms: MutableMap<String, Int> = mutableMapOf()
|
||||
val queuedUniformValues: MutableMap<Int, () -> Unit> = mutableMapOf()
|
||||
private val textures: MutableMap<Int, Int> = mutableMapOf()
|
||||
private var textureIndex: Int = 0
|
||||
|
||||
var model by mat4()
|
||||
var normalModel by mat3()
|
||||
var view by mat4()
|
||||
var projection by mat4()
|
||||
|
||||
private fun getUniformLocation(name: String) = uniforms.getOrPut(name) {
|
||||
glGetUniformLocation(programId, name)
|
||||
}
|
||||
|
||||
protected fun sampler2D(
|
||||
index: Int = textureIndex++,
|
||||
name: String? = null
|
||||
): ReadWriteProperty<ShaderProgram, Texture?> {
|
||||
var setUniform = false
|
||||
|
||||
return object : ReadWriteProperty<ShaderProgram, Texture?> {
|
||||
override fun getValue(thisRef: ShaderProgram, property: KProperty<*>): Texture? {
|
||||
return Texture.getTexture(textures[index] ?: return null)
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: ShaderProgram, property: KProperty<*>, value: Texture?) {
|
||||
textures[index] = value?.id ?: 0
|
||||
|
||||
if (setUniform) {
|
||||
return
|
||||
}
|
||||
|
||||
val loc = getUniformLocation(name ?: property.name)
|
||||
if (loc == -1) {
|
||||
return
|
||||
}
|
||||
|
||||
if (glGetInteger(GL_CURRENT_PROGRAM) == thisRef.programId) {
|
||||
glUniform1i(loc, index)
|
||||
} else {
|
||||
queuedUniformValues[loc] = {
|
||||
glUniform1i(loc, index)
|
||||
}
|
||||
}
|
||||
|
||||
setUniform = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun <T> uniform(
|
||||
name: String? = null,
|
||||
setter: (location: Int, value: T) -> Unit,
|
||||
getter: (programId: Int, location: Int) -> T?
|
||||
) = UniformProperty(name, setter, getter)
|
||||
|
||||
open class UniformProperty<T>(
|
||||
val name: String? = null,
|
||||
val setter: (location: Int, value: T) -> Unit,
|
||||
val getter: (programId: Int, location: Int) -> T?
|
||||
) {
|
||||
operator fun getValue(thisRef: ShaderProgram, property: KProperty<*>): T? {
|
||||
val loc = thisRef.getUniformLocation(name ?: property.name)
|
||||
if (loc == -1) {
|
||||
return null
|
||||
}
|
||||
|
||||
return getter(thisRef.programId, loc)
|
||||
}
|
||||
|
||||
operator fun setValue(thisRef: ShaderProgram, property: KProperty<*>, value: T?) {
|
||||
val valueNonNull = value ?: return
|
||||
|
||||
val loc = thisRef.getUniformLocation(name ?: property.name)
|
||||
if (loc == -1) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (glGetInteger(GL_CURRENT_PROGRAM) == thisRef.programId) {
|
||||
setter(loc, valueNonNull)
|
||||
} else {
|
||||
thisRef.queuedUniformValues[loc] = {
|
||||
setter(loc, valueNonNull)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun float(name: String? = null): UniformProperty<Float> =
|
||||
uniform(name, ::glUniform1f, ::glGetUniformf)
|
||||
|
||||
protected fun int(name: String? = null): UniformProperty<Int> =
|
||||
uniform(name, ::glUniform1i, ::glGetUniformi)
|
||||
|
||||
protected fun vec2(name: String? = null): UniformProperty<Vector2fc> =
|
||||
uniform(name, { loc, value ->
|
||||
val floatArray = MemoryUtil.memAllocFloat(2)
|
||||
value.get(floatArray)
|
||||
glUniform2fv(loc, floatArray)
|
||||
MemoryUtil.memFree(floatArray)
|
||||
}, { programId, location ->
|
||||
val buffer = FloatArray(2)
|
||||
glGetUniformfv(programId, location, buffer)
|
||||
Vector2f().set(buffer)
|
||||
})
|
||||
|
||||
protected fun vec3(name: String? = null): UniformProperty<Vector3fc> =
|
||||
uniform(name, { loc, value ->
|
||||
val floatArray = MemoryUtil.memAllocFloat(3)
|
||||
value.get(floatArray)
|
||||
glUniform3fv(loc, floatArray)
|
||||
MemoryUtil.memFree(floatArray)
|
||||
}, { programId, location ->
|
||||
val buffer = FloatArray(3)
|
||||
glGetUniformfv(programId, location, buffer)
|
||||
Vector3f().set(buffer)
|
||||
})
|
||||
|
||||
protected fun vec4(name: String? = null): UniformProperty<Vector4fc> =
|
||||
uniform(name, { loc, value ->
|
||||
val floatArray = MemoryUtil.memAllocFloat(4)
|
||||
value.get(floatArray)
|
||||
glUniform4fv(loc, floatArray)
|
||||
MemoryUtil.memFree(floatArray)
|
||||
}, { programId, location ->
|
||||
val buffer = FloatArray(4)
|
||||
glGetUniformfv(programId, location, buffer)
|
||||
Vector4f().set(buffer)
|
||||
})
|
||||
|
||||
protected fun mat3(name: String? = null): UniformProperty<Matrix3fc> =
|
||||
uniform(name, { loc, value ->
|
||||
val floatArray = FloatArray(3 * 3)
|
||||
value.get(floatArray)
|
||||
glUniformMatrix3fv(loc, false, floatArray)
|
||||
}, { programId, location ->
|
||||
val buffer = FloatArray(3 * 3)
|
||||
glGetUniformfv(programId, location, buffer)
|
||||
Matrix3f().set(buffer)
|
||||
})
|
||||
|
||||
protected fun bool(name: String? = null): UniformProperty<Boolean> =
|
||||
uniform(name, { loc, value ->
|
||||
glUniform1i(loc, if (value) 1 else 0)
|
||||
}, { programId, location ->
|
||||
glGetUniformi(programId, location) == 1
|
||||
})
|
||||
|
||||
protected fun mat4(name: String? = null): UniformProperty<Matrix4fc> =
|
||||
uniform(name, { loc, value ->
|
||||
val floatArray = FloatArray(4 * 4)
|
||||
value.get(floatArray)
|
||||
glUniformMatrix4fv(loc, false, floatArray)
|
||||
}, { programId, location ->
|
||||
val buffer = FloatArray(4 * 4)
|
||||
glGetUniformfv(programId, location, buffer)
|
||||
Matrix4f().set(buffer)
|
||||
})
|
||||
|
||||
fun enable() {
|
||||
glUseProgram(programId)
|
||||
|
||||
for ((_, queued) in queuedUniformValues) {
|
||||
queued()
|
||||
}
|
||||
|
||||
for ((index, texture) in textures) {
|
||||
glActiveTexture(GL_TEXTURE0 + index)
|
||||
glBindTexture(GL_TEXTURE_2D, texture)
|
||||
}
|
||||
|
||||
queuedUniformValues.clear()
|
||||
}
|
||||
|
||||
fun delete() {
|
||||
glDeleteProgram(programId)
|
||||
}
|
||||
|
||||
fun disable() {
|
||||
glUseProgram(0)
|
||||
}
|
||||
|
||||
fun uses(name: String): Boolean {
|
||||
return getUniformLocation(name) != -1
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun camera(renderer: ICamera<*>) {
|
||||
this.projection = renderer.projection.toFloat()
|
||||
this.view = renderer.view.toFloat()
|
||||
}
|
||||
|
||||
constructor(vertexShader: Shader, fragmentShader: Shader) : this() {
|
||||
glAttachShader(programId, vertexShader.shaderId)
|
||||
glAttachShader(programId, fragmentShader.shaderId)
|
||||
|
||||
glLinkProgram(programId)
|
||||
val success = stack.mallocInt(1)
|
||||
glGetProgramiv(programId, GL_LINK_STATUS, success)
|
||||
if (success.get(0) == 0) {
|
||||
throw ShaderProgramLinkingException(glGetProgramInfoLog(programId))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package me.eater.threedom.gl.dom
|
||||
|
||||
import me.eater.threedom.dom.render.IRenderNode
|
||||
import me.eater.threedom.gl.GL
|
||||
|
||||
interface GLRenderNode<T : IRenderNode<T, GL, ICamera<*>>> :
|
||||
IRenderNode<T, GL, ICamera<*>> {
|
||||
override val target: GL
|
||||
get() = GL
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package me.eater.threedom.gl.dom
|
||||
|
||||
import me.eater.threedom.dom.INode
|
||||
import org.joml.Matrix4dc
|
||||
|
||||
interface ICamera<T : INode<T>> : INode<T> {
|
||||
var width: Double
|
||||
var height: Double
|
||||
|
||||
val projection: Matrix4dc
|
||||
val view: Matrix4dc
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package me.eater.threedom.gl.dom
|
||||
|
||||
import me.eater.threedom.dom.IDocument
|
||||
import me.eater.threedom.dom.Node
|
||||
import org.joml.Matrix4d
|
||||
import org.joml.Matrix4dc
|
||||
|
||||
class PerspectiveCamera(document: IDocument?) : Node<PerspectiveCamera>(document), ICamera<PerspectiveCamera> {
|
||||
override fun cloneSelf(): PerspectiveCamera =
|
||||
PerspectiveCamera(document)
|
||||
|
||||
var fieldOfView: Double = 45.0
|
||||
set(value) {
|
||||
field = value
|
||||
updateProjection()
|
||||
}
|
||||
|
||||
var far: Double = 100.0
|
||||
set(value) {
|
||||
field = value
|
||||
updateProjection()
|
||||
}
|
||||
|
||||
var near: Double = 0.1
|
||||
set(value) {
|
||||
field = value
|
||||
updateProjection()
|
||||
}
|
||||
|
||||
override var width: Double = 800.0
|
||||
set(value) {
|
||||
field = value
|
||||
updateProjection()
|
||||
}
|
||||
|
||||
override var height: Double = 600.0
|
||||
set(value) {
|
||||
field = value
|
||||
updateProjection()
|
||||
}
|
||||
|
||||
private fun updateProjection() {
|
||||
projection = makeProjection()
|
||||
}
|
||||
|
||||
private fun makeProjection(): Matrix4dc = Matrix4d().perspective(fieldOfView, width / height, near, far)
|
||||
|
||||
override var projection: Matrix4dc = makeProjection()
|
||||
private set
|
||||
|
||||
override val view: Matrix4dc
|
||||
get() = absolute
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package me.eater.threedom.gl.dom
|
||||
|
||||
import me.eater.threedom.dom.IDocument
|
||||
import me.eater.threedom.dom.Node
|
||||
import me.eater.threedom.gl.ShaderProgram
|
||||
import me.eater.threedom.gl.vertex.VertexArrayObject
|
||||
import me.eater.threedom.utils.joml.toFloat
|
||||
import org.joml.Matrix3d
|
||||
import org.joml.Matrix4d
|
||||
import org.lwjgl.opengl.GL30.*
|
||||
|
||||
open class TriMesh(document: IDocument?) : Node<TriMesh>(document), GLRenderNode<TriMesh> {
|
||||
var mesh: VertexArrayObject? = null
|
||||
var program: ShaderProgram? = null
|
||||
private set
|
||||
var programConfig: (ShaderProgram.(TriMesh) -> Unit)? = null
|
||||
private set
|
||||
|
||||
fun <T : ShaderProgram> use(program: T, block: T.(TriMesh) -> Unit) {
|
||||
this.program = program
|
||||
@kotlin.Suppress("UNCHECKED_CAST")
|
||||
this.programConfig = block as ShaderProgram.(TriMesh) -> Unit
|
||||
}
|
||||
|
||||
fun <T : ShaderProgram> use(program: T) {
|
||||
this.program = program
|
||||
this.programConfig = null
|
||||
}
|
||||
|
||||
override fun cloneSelf(): TriMesh {
|
||||
return TriMesh(document).also {
|
||||
it.mesh = mesh
|
||||
it.program = program
|
||||
it.programConfig = programConfig
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(renderer: ICamera<*>) {
|
||||
val mesh = mesh ?: return
|
||||
val program = program ?: return
|
||||
program.enable()
|
||||
programConfig?.let { program.it(this) }
|
||||
program.camera(renderer)
|
||||
program.model = absolute.toFloat()
|
||||
program.normalModel = model.invert(Matrix4d()).transpose().get3x3(Matrix3d()).toFloat()
|
||||
glBindVertexArray(mesh.id)
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh.size)
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package me.eater.threedom.gl.exception
|
||||
|
||||
data class ShaderCompilationException(val infoLog: String) : RuntimeException("Shader failed compiling: $infoLog")
|
@ -0,0 +1,4 @@
|
||||
package me.eater.threedom.gl.exception
|
||||
|
||||
data class ShaderProgramLinkingException(val infoLog: String) :
|
||||
RuntimeException("Failed linking ShaderProgram: $infoLog")
|
@ -0,0 +1,8 @@
|
||||
package me.eater.threedom.gl.geometry
|
||||
|
||||
import me.eater.threedom.dom.IDocument
|
||||
import me.eater.threedom.dom.INode
|
||||
import me.eater.threedom.gl.dom.TriMesh
|
||||
|
||||
class Plane(document: IDocument?) : TriMesh(document) {
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package me.eater.threedom.gl.texture
|
||||
|
||||
import me.eater.threedom.gl.GL.stack
|
||||
import org.lwjgl.opengl.GL30.*
|
||||
import org.lwjgl.stb.STBImage.stbi_load_from_memory
|
||||
import org.lwjgl.stb.STBImage.stbi_set_flip_vertically_on_load
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
import java.lang.ref.WeakReference
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class Texture {
|
||||
var id: Int = run {
|
||||
val pTexture = stack.mallocInt(1)
|
||||
glGenTextures(pTexture)
|
||||
pTexture.get(0).also {
|
||||
textures[it] = WeakReference(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun load(image: ByteArray, flipped: Boolean = true, mipmapLevel: Int = 0) {
|
||||
val mem = MemoryUtil.memAlloc(image.size)
|
||||
mem.put(image)
|
||||
mem.rewind()
|
||||
load(mem, flipped, mipmapLevel)
|
||||
MemoryUtil.memFree(mem)
|
||||
}
|
||||
|
||||
fun load(image: ByteBuffer, flipped: Boolean = true, mipmapLevel: Int = 0) {
|
||||
stbi_set_flip_vertically_on_load(flipped)
|
||||
val pWidth = stack.mallocInt(1)
|
||||
val pHeight = stack.mallocInt(1)
|
||||
val pChannels = stack.mallocInt(1)
|
||||
val imagePixelData = stbi_load_from_memory(image, pWidth, pHeight, pChannels, 4)
|
||||
glBindTexture(GL_TEXTURE_2D, id)
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
mipmapLevel,
|
||||
GL_RGBA8,
|
||||
pWidth.get(0),
|
||||
pHeight.get(0),
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
imagePixelData
|
||||
)
|
||||
glGenerateMipmap(GL_TEXTURE_2D)
|
||||
glBindTexture(GL_TEXTURE_2D, 0)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val textures = mutableMapOf<Int, WeakReference<Texture>>()
|
||||
|
||||
fun getTexture(id: Int): Texture? = textures[id]?.get()
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package me.eater.threedom.gl.vertex
|
||||
|
||||
import me.eater.threedom.gl.GL.stack
|
||||
import org.lwjgl.opengl.GL30.*
|
||||
import kotlin.math.min
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
class VertexArrayObject() {
|
||||
var size: Int = 0
|
||||
|
||||
val id = run {
|
||||
val pId = stack.mallocInt(1)
|
||||
glGenVertexArrays(pId)
|
||||
pId.get(0)
|
||||
}
|
||||
|
||||
constructor(block: VertexArrayObject.() -> Unit) : this() {
|
||||
block(this)
|
||||
}
|
||||
|
||||
var pointerIndex: Int = 0
|
||||
|
||||
operator fun <T : VertexData> VertexBuffer<T>.invoke(prop: KProperty1<T, *>) =
|
||||
bind(this, prop)
|
||||
|
||||
fun <T : VertexData> bind(vertexBuffer: VertexBuffer<T>, prop: KProperty1<T, *>) {
|
||||
if (vertexBuffer.items.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
size = if (pointerIndex == 0) {
|
||||
vertexBuffer.items.size
|
||||
} else {
|
||||
min(size, vertexBuffer.items.size)
|
||||
}
|
||||
|
||||
val first = vertexBuffer.items.first()
|
||||
val item = first.getDataPoint(prop) ?: return
|
||||
var size = item.slots
|
||||
var length = item.length
|
||||
var repeat = 1
|
||||
|
||||
var i = 4
|
||||
while (size > 4) {
|
||||
if ((size % i) == 0) {
|
||||
repeat = size / i
|
||||
size = i
|
||||
length /= i
|
||||
break
|
||||
}
|
||||
|
||||
i--
|
||||
}
|
||||
|
||||
val stride = if (vertexBuffer.interleaved) {
|
||||
vertexBuffer.stride
|
||||
} else {
|
||||
item.length
|
||||
}
|
||||
|
||||
var offset = first.getOffset(prop)!!
|
||||
|
||||
if (!vertexBuffer.interleaved) {
|
||||
offset *= vertexBuffer.items.size
|
||||
}
|
||||
|
||||
glBindVertexArray(id)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.id)
|
||||
for (x in 0 until repeat) {
|
||||
val pointer = pointerIndex++
|
||||
glVertexAttribPointer(pointer, size, item.type, false, stride, offset + (x * length).toLong())
|
||||
glEnableVertexAttribArray(pointer)
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
glBindVertexArray(0)
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package me.eater.threedom.gl.vertex
|
||||
|
||||
import me.eater.threedom.gl.GL.stack
|
||||
import org.lwjgl.opengl.GL30.*
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
open class VertexBuffer<T : VertexData>(
|
||||
items: List<T> = listOf(),
|
||||
val interleaved: Boolean = false,
|
||||
val usage: Int = GL_STATIC_DRAW
|
||||
) {
|
||||
val id: Int = run {
|
||||
val pId = stack.mallocInt(1)
|
||||
glGenBuffers(pId)
|
||||
pId.get(0)
|
||||
}
|
||||
|
||||
@Suppress("CanBePrimaryConstructorProperty")
|
||||
open val items: List<T> = items
|
||||
var stride: Int
|
||||
private set
|
||||
|
||||
init {
|
||||
val size = items.size * (items.firstOrNull()?.length ?: 0).also { stride = it }
|
||||
val byteBuffer = MemoryUtil.memAlloc(size)
|
||||
|
||||
var offset = 0
|
||||
for (i in items.indices) {
|
||||
val item = items[i]
|
||||
|
||||
if (interleaved) {
|
||||
item.write(offset, byteBuffer)
|
||||
offset += item.length
|
||||
} else {
|
||||
item.writeNonInterleaved(i, items.size, byteBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, id)
|
||||
glBufferData(GL_ARRAY_BUFFER, byteBuffer, usage)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
MemoryUtil.memFree(byteBuffer)
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package me.eater.threedom.gl.vertex
|
||||
|
||||
class VertexBufferMutable<T: VertexData>(override val items: MutableList<T> = mutableListOf()) : VertexBuffer<T>()
|
@ -0,0 +1,56 @@
|
||||
package me.eater.threedom.gl.vertex
|
||||
|
||||
import me.eater.threedom.gl.vertex.VertexDataPoint.*
|
||||
import org.joml.*
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
abstract class VertexData() {
|
||||
private val items = linkedMapOf<String, Pair<Int, VertexDataPoint<*>>>()
|
||||
private var offset = 0
|
||||
|
||||
val length
|
||||
get() = items.map { (_, v) -> v.component2().length }.sum()
|
||||
|
||||
class VertexDataRegister<T>(private val default: T, val provider: (default: T) -> VertexDataPoint<T>) {
|
||||
operator fun provideDelegate(thisRef: VertexData, prop: KProperty<*>): VertexDataPoint<T> {
|
||||
val delegate = provider(default)
|
||||
thisRef.items[prop.name] = thisRef.offset to delegate
|
||||
thisRef.offset += delegate.length
|
||||
return delegate
|
||||
}
|
||||
}
|
||||
|
||||
fun int(value: Int = 0): VertexDataRegister<Int> = VertexDataRegister(value, ::SingleInt)
|
||||
fun double(value: Double = 0.0): VertexDataRegister<Double> = VertexDataRegister(value, ::SingleDouble)
|
||||
fun float(value: Float = 0f): VertexDataRegister<Float> = VertexDataRegister(value, ::SingleFloat)
|
||||
fun bool(value: Boolean = false): VertexDataRegister<Boolean> = VertexDataRegister(value, ::SingleBoolean)
|
||||
fun vec2(value: Vector2fc = Vector2f()): VertexDataRegister<Vector2fc> = VertexDataRegister(value, ::Vec2)
|
||||
fun vec3(value: Vector3fc = Vector3f()): VertexDataRegister<Vector3fc> = VertexDataRegister(value, ::Vec3)
|
||||
fun vec4(value: Vector4fc = Vector4f()): VertexDataRegister<Vector4fc> = VertexDataRegister(value, ::Vec4)
|
||||
fun mat3(value: Matrix3fc = Matrix3f()): VertexDataRegister<Matrix3fc> = VertexDataRegister(value, ::Mat3)
|
||||
fun mat4(value: Matrix4fc = Matrix4f()): VertexDataRegister<Matrix4fc> = VertexDataRegister(value, ::Mat4)
|
||||
|
||||
fun getDataPoint(prop: KProperty1<out VertexData, *>): VertexDataPoint<*>? = items[prop.name]?.component2()
|
||||
|
||||
fun getOffset(prop: KProperty1<out VertexData, *>): Int? = items[prop.name]?.component1()
|
||||
|
||||
fun writeNonInterleaved(position: Int, size: Int, byteBuffer: ByteBuffer) {
|
||||
var offset = 0;
|
||||
for ((_, pair) in items) {
|
||||
val (_, item) = pair
|
||||
item.write(offset + (position * item.length), byteBuffer)
|
||||
offset += item.length * size
|
||||
}
|
||||
}
|
||||
|
||||
fun write(position: Int, byteBuffer: ByteBuffer) {
|
||||
var pos = position
|
||||
for ((_, pair) in items) {
|
||||
val (_, item) = pair
|
||||
item.write(pos, byteBuffer)
|
||||
pos += item.length
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package me.eater.threedom.gl.vertex
|
||||
|
||||
import org.joml.*
|
||||
import org.lwjgl.opengl.GL30.*
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
abstract class VertexDataPoint<T>(val type: Int, var value: T, val length: Int, val slots: Int = 1) {
|
||||
abstract fun write(position: Int, buffer: ByteBuffer)
|
||||
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
return value
|
||||
}
|
||||
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
class SingleInt(value: Int) : VertexDataPoint<Int>(GL_INT, value, 4) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
buffer.putInt(position, value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SingleFloat(value: Float) : VertexDataPoint<Float>(GL_FLOAT, value, 4) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
buffer.putFloat(position, value)
|
||||
}
|
||||
}
|
||||
|
||||
class SingleDouble(value: Double) : VertexDataPoint<Double>(GL_DOUBLE, value, 8) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
buffer.putDouble(position, value)
|
||||
}
|
||||
}
|
||||
|
||||
class SingleBoolean(value: Boolean) : VertexDataPoint<Boolean>(GL_BOOL, value, 1) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
buffer.put(position, if (value) 1 else 2)
|
||||
}
|
||||
}
|
||||
|
||||
class Vec2(value: Vector2fc) : VertexDataPoint<Vector2fc>(GL_FLOAT, value, 8, 2) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
value.get(position, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
class Vec3(value: Vector3fc) : VertexDataPoint<Vector3fc>(GL_FLOAT, value, 12, 3) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
value.get(position, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
class Vec4(value: Vector4fc) : VertexDataPoint<Vector4fc>(GL_FLOAT, value, 16, 4) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
value.get(position, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
class Mat3(value: Matrix3fc) : VertexDataPoint<Matrix3fc>(GL_FLOAT, value, 3 * 3 * 4, 9) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
value.get(position, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
class Mat4(value: Matrix4fc) : VertexDataPoint<Matrix4fc>(GL_FLOAT, value, 4 * 4 * 4, 16) {
|
||||
override fun write(position: Int, buffer: ByteBuffer) {
|
||||
value.get(position, buffer)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package me.eater.threedom.dom
|
||||
|
||||
class PlainNode(document: IDocument?) : Node<PlainNode>(document) {
|
||||
override fun cloneSelf(): PlainNode {
|
||||
return PlainNode(document)
|
||||
}
|
||||
override fun cloneSelf(): PlainNode = PlainNode(document)
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package me.eater.threedom.dom.event
|
||||
|
||||
import me.eater.threedom.dom.INode
|
||||
import me.eater.threedom.kapt.EventName
|
||||
|
||||
@EventName("NodeLocationIndexStateChange")
|
||||
class NodeLocationIndexStateChange(val shouldIndex: Boolean, val node: INode<*>)
|
@ -0,0 +1,10 @@
|
||||
package me.eater.threedom.dom.render
|
||||
|
||||
import me.eater.threedom.dom.INode
|
||||
|
||||
interface IRenderNode<N : IRenderNode<N, T, C>, T : IRenderTarget<C>, C> :
|
||||
INode<N> {
|
||||
val target: T
|
||||
|
||||
fun render(renderer: C)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package me.eater.threedom.dom.render
|
||||
|
||||
interface IRenderTarget<C> {
|
||||
val type: String
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue