129 lines
3.5 KiB
Kotlin
129 lines
3.5 KiB
Kotlin
package ru.dbotthepony.kstarbound.client.render
|
|
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap
|
|
import org.lwjgl.opengl.GL15.GL_STREAM_DRAW
|
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
|
import java.util.function.Function
|
|
import java.util.stream.Stream
|
|
|
|
interface IGeometryLayer {
|
|
fun add(renderer: () -> Unit)
|
|
fun getBuilder(config: RenderConfig<*>): VertexBuilder
|
|
fun render()
|
|
fun clear()
|
|
fun bakeIntoMeshes(): Stream<Pair<ConfiguredMesh<*>, RenderLayer.Point>>
|
|
}
|
|
|
|
object OneShotGeometryLayer : IGeometryLayer {
|
|
override fun add(renderer: () -> Unit) {
|
|
TODO("Not yet implemented")
|
|
}
|
|
|
|
override fun getBuilder(config: RenderConfig<*>): VertexBuilder {
|
|
TODO("Not yet implemented")
|
|
}
|
|
|
|
override fun render() {
|
|
TODO("Not yet implemented")
|
|
}
|
|
|
|
override fun clear() {
|
|
TODO("Not yet implemented")
|
|
}
|
|
|
|
override fun bakeIntoMeshes(): Stream<Pair<ConfiguredMesh<*>, RenderLayer.Point>> {
|
|
TODO("Not yet implemented")
|
|
}
|
|
}
|
|
|
|
class LayeredRenderer {
|
|
class Layer(val layer: RenderLayer.Point) : IGeometryLayer {
|
|
private data class Tracker(val builder: StreamVertexBuilder, var emptyFrames: Int = 0)
|
|
private val meshes = ArrayList<ConfiguredMesh<*>>()
|
|
private val callbacks = ArrayList<() -> Unit>()
|
|
private val builders = Reference2ObjectLinkedOpenHashMap<RenderConfig<*>, Tracker>()
|
|
|
|
override fun add(renderer: () -> Unit) {
|
|
callbacks.add(renderer)
|
|
}
|
|
|
|
override fun getBuilder(config: RenderConfig<*>): VertexBuilder {
|
|
return builders.computeIfAbsent(config, Function {
|
|
Tracker(StreamVertexBuilder(config.program.attributes, initialCapacity = config.initialBuilderCapacity))
|
|
}).builder.builder
|
|
}
|
|
|
|
override fun render() {
|
|
val iterator = builders.entries.iterator()
|
|
|
|
for ((k, v) in iterator) {
|
|
if (v.builder.builder.isNotEmpty()) {
|
|
v.emptyFrames = 0
|
|
k.setup()
|
|
v.builder.upload(GL_STREAM_DRAW)
|
|
v.builder.draw()
|
|
k.uninstall()
|
|
} else {
|
|
v.emptyFrames++
|
|
|
|
if (v.emptyFrames >= 600) {
|
|
// ~10 seconds of inactivity, free resources
|
|
iterator.remove()
|
|
}
|
|
}
|
|
}
|
|
|
|
meshes.forEach {
|
|
it.render()
|
|
}
|
|
|
|
callbacks.forEach {
|
|
it.invoke()
|
|
}
|
|
}
|
|
|
|
override fun clear() {
|
|
builders.values.forEach { it.builder.builder.begin() }
|
|
meshes.clear()
|
|
callbacks.clear()
|
|
}
|
|
|
|
override fun bakeIntoMeshes(): Stream<Pair<ConfiguredMesh<*>, RenderLayer.Point>> {
|
|
if (meshes.isNotEmpty() || callbacks.isNotEmpty())
|
|
throw IllegalStateException("This layer (index $layer) contains preconfigured meshes and/or callbacks")
|
|
|
|
return builders.entries.stream()
|
|
.filter { it.value.builder.builder.isNotEmpty() }
|
|
.map { ConfiguredMesh(it.key, Mesh(it.value.builder.builder)) to layer }
|
|
}
|
|
}
|
|
|
|
private val layers = Object2ObjectAVLTreeMap<RenderLayer.Point, Layer>()
|
|
|
|
fun getBuilder(layer: RenderLayer.Point, config: RenderConfig<*>): VertexBuilder {
|
|
return getLayer(layer).getBuilder(config)
|
|
}
|
|
|
|
fun bakeIntoMeshes(): Stream<Pair<ConfiguredMesh<*>, RenderLayer.Point>> {
|
|
return layers.values.stream().flatMap { it.bakeIntoMeshes() }
|
|
}
|
|
|
|
fun add(layer: RenderLayer.Point, renderer: () -> Unit) {
|
|
getLayer(layer).add(renderer)
|
|
}
|
|
|
|
fun getLayer(layer: RenderLayer.Point): Layer {
|
|
return layers.computeIfAbsent(layer, Function { Layer(it) })
|
|
}
|
|
|
|
fun render() {
|
|
layers.values.forEach { it.render() }
|
|
}
|
|
|
|
fun clear() {
|
|
layers.values.forEach { it.clear() }
|
|
}
|
|
}
|