commonMain.ru.casperix.opengl.renderer.OpenGlRenderer2d.kt Maven / Gradle / Ivy
package ru.casperix.opengl.renderer
import ru.casperix.math.axis_aligned.int32.Box2i
import ru.casperix.math.axis_aligned.int32.Dimension2i
import ru.casperix.math.geometry.Quad2f
import ru.casperix.math.geometry.Triangle2f
import ru.casperix.math.quad_matrix.float32.Matrix3f
import ru.casperix.math.vector.float32.Vector3f
import ru.casperix.opengl.core.*
import ru.casperix.opengl.renderer.impl.DeviceShapeData
import ru.casperix.opengl.renderer.impl.GraphicDataProvider
import ru.casperix.opengl.renderer.impl.RenderState
import ru.casperix.renderer.Environment
import ru.casperix.renderer.Renderer2D
import ru.casperix.renderer.material.Material
import ru.casperix.renderer.material.SimpleMaterial
import ru.casperix.renderer.vector.VectorGraphic
import ru.casperix.renderer.vector.builder.VectorBuilder
@ExperimentalUnsignedTypes
class OpenGlRenderer2d : Renderer2D {
override var environment = Environment()
set(value) {
flush()
field = value
}
override var viewPort: Dimension2i = Dimension2i.ZERO
set(view) {
flush()
glViewport(0, 0, view.width, view.height)
field = view
}
val statistic = OpenGlRendererStatistic()
val config = OpenGlRendererConfig()
private var lastRenderState: RenderState? = null
private var scissorArea: Box2i? = null
private val stateController = StateController(config.textureConfig)
private val graphicProvider = GraphicDataProvider(stateController, config)
init {
begin()
}
/**
* Need only if you call gl command outside
*/
fun begin() {
statistic.current.dynamicBufferAmount = graphicProvider.getDynamicBufferAmount()
statistic.current.staticBufferAmount = graphicProvider.getStaticBufferAmount()
statistic.nextFrame()
glEnable(GL_BLEND)
glFrontFace(GL_CCW)
if (config.cullFace) {
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
} else {
glDisable(GL_CULL_FACE)
}
if (config.premultipliedAlpha) {
glBlendEquation(GL_FUNC_ADD)
glBlendFunc(1, GL_ONE_MINUS_SRC_ALPHA)
} else {
glBlendEquation (GL_FUNC_ADD)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
}
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0)
}
fun end() {
flush()
stateController.setShader(null)
stateController.setGeometry(null)
glDisable(GL_BLEND)
glActiveTexture(GL_TEXTURE0)
glDisable(GL_CULL_FACE)
}
override fun flush() {
lastRenderState?.let {
drawBuffer(it)
}
lastRenderState = null
}
override fun clear() {
flush()
environment.backgroundColor.toColor4f().apply {
glClearColor(red, green, blue, alpha)
}
glClear(GL_COLOR_BUFFER_BIT)
}
override fun getScissor(): Box2i? {
return scissorArea
}
override fun setScissor(area: Box2i?) {
if (this.scissorArea == area) return
flush()
if (area == null) {
scissorArea = null
glDisable(GL_SCISSOR_TEST)
} else {
val clamped = area//Box2i(area.min.clamp(Vector2i.ZERO, viewportMax), area.max.lower(viewportMax))
glScissor(clamped.min.x, viewPort.height - clamped.max.y - 1, clamped.dimension.x, clamped.dimension.y)
glEnable(GL_SCISSOR_TEST)
scissorArea = clamped
}
}
override fun drawQuad(material: Material, quad: Quad2f) {
val geometry = if ((material as? SimpleMaterial)?.albedoMap != null) {
VectorBuilder(hasPosition2 = true, hasTextureCoord = true).buildGeometry {
addQuad(quad, quad)
}
} else {
VectorBuilder(hasPosition2 = true).buildGeometry {
addQuad(quad)
}
}
drawGraphic(VectorGraphic(material, geometry))
}
override fun drawTriangle(material: Material, triangle: Triangle2f) {
val geometry = if ((material as? SimpleMaterial)?.albedoMap != null) {
VectorBuilder(hasPosition2 = true, hasTextureCoord = true).buildGeometry {
addTriangle(triangle, triangle)
}
} else {
VectorBuilder(hasPosition2 = true).buildGeometry {
addTriangle(triangle)
}
}
drawGraphic(VectorGraphic(material, geometry))
}
override fun drawGraphic(graphic: VectorGraphic, modelTransform: Matrix3f) {
graphic.shapes.forEach { shape ->
if (shape.geometry.vertices.size == 0) return@forEach
val data = graphicProvider.get(stateController, shape, statistic.frameIndex) ?: return@forEach
pushToBuffer(modelTransform, data)
}
}
private fun pushToBuffer(nextTransform: Matrix3f, nextData: DeviceShapeData) {
val nextGraphic = nextData.graphic
val nextGeometry = nextGraphic.geometry
val nextMaterial = nextGraphic.material
val nextBuffer = nextData.buffer
val nextShader = nextData.shader
val nextRenderState = RenderState(
nextTransform,
nextTransform * environment.viewMatrix * environment.projectionMatrix,
nextShader,
nextMaterial,
nextBuffer
)
val useBatch = config.cacheConfig.accumulateBatches
val last = lastRenderState
val isStateChanged = lastRenderState != nextRenderState
val isStaticLastOrNext = last?.buffer?.isStatic == true || nextBuffer.isStatic
val isBufferLimitReached =
nextGeometry.indices.size + nextBuffer.data.indicesAmount > config.cacheConfig.maxDrawBufferIndices
if (isStateChanged || isStaticLastOrNext || isBufferLimitReached) {
if (last != null) {
drawBuffer(last)
}
}
if (!nextBuffer.isStatic) {
nextBuffer.appendData(nextGeometry.vertices.data, nextGeometry.indices)
}
if (isStateChanged || !useBatch) {
statistic.current.states++
lastRenderState = nextRenderState
if (!useBatch) {
drawBuffer(nextRenderState)
}
}
}
private fun drawBuffer(renderState: RenderState) {
val buffer = renderState.buffer
if (buffer.isStatic) {
statistic.current.static++
} else {
buffer.uploadData(stateController)
}
stateController.setShader(renderState.controller)
stateController.setMaterial(renderState.material)
stateController.setGeometry(renderState.buffer.data)
stateController.setTransform(renderState.model, renderState.projectionViewModel)
stateController.setLightPos(environment.lightPosition)
stateController.setAmbientColor(environment.ambientColor)
buffer.draw()
statistic.current.triangles += buffer.data.indicesAmount / 3
statistic.current.batches++
if (!buffer.isStatic) {
buffer.reset()
}
}
private fun Vector3f.flipY(): Vector3f {
return Vector3f(x, viewPort.height - y, z)
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy