All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.ru.casperix.opengl.renderer.OpenGlRenderer2d.kt Maven / Gradle / Ivy

package ru.casperix.opengl.renderer

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.Renderer2D
import ru.casperix.renderer.vector.VectorGraphic

class OpenGlRenderer2d : Renderer2D {
    override val environment = OpenGlRendererEnvironment(::flush)

    val statistic = OpenGlRendererStatistic()
    var config = OpenGlRendererConfig()

    private var lastRenderState: RenderState? = null

    private val stateController = StateController()
    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()

        glFrontFace(GL_CCW)

        if (config.geometryCullFace) {
            glEnable(GL_CULL_FACE)
            glCullFace(GL_BACK)
        } else {
            glDisable(GL_CULL_FACE)
        }

        setBlend()
    }

    private fun setBlend() {
        glEnable(GL_BLEND)
        if (config.alphaPremultiplied) {
            if (config.alphaSeparate) {
                glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
                glBlendFuncSeparate(1, GL_ONE_MINUS_SRC_ALPHA, 1, GL_ONE_MINUS_SRC_ALPHA);
            } else {
                glBlendEquation(GL_FUNC_ADD)
                glBlendFunc(1, GL_ONE_MINUS_SRC_ALPHA)
            }
        } else {
            if (config.alphaSeparate) {
                glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
                glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, GL_ONE_MINUS_SRC_ALPHA);
            } else {
                glBlendEquation(GL_FUNC_ADD)
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
            }
        }
    }

    private fun unsetBlend() {
        glDisable(GL_BLEND)
    }

    fun end() {
        flush()

        unsetBlend()

        stateController.setShader(null)
        stateController.setGeometry(null)

        glActiveTexture(GL_TEXTURE0)
        glDisable(GL_CULL_FACE)
    }

    override fun flush() {
        lastRenderState?.let {
            drawBuffer(it)
        }
        lastRenderState = null
    }

    override fun clear() {
        flush()
        environment.clearColor.apply {
            glClearColor(red, green, blue, alpha)
        }
        glClear(GL_COLOR_BUFFER_BIT)
    }

    override fun drawGraphic(graphic: VectorGraphic, transform: Matrix3f) {
        graphic.shapes.forEach { shape ->
            if (shape.vertexData.vertices.size == 0) return@forEach
            val data = graphicProvider.get(stateController, shape, statistic.frameIndex) ?: return@forEach
            pushToBuffer(shape.transform * transform, data)
        }
    }

    private fun pushToBuffer(nextTransform: Matrix3f, nextData: DeviceShapeData) {
        val nextGraphic = nextData.graphic
        val nextGeometry = nextGraphic.vertexData
        val nextMaterial = nextGraphic.material
        val nextBuffer = nextData.buffer
        val nextShader = nextData.shader

        val nextRenderState = RenderState(
            nextTransform,
            nextTransform * environment.viewMatrix * environment.projectionMatrix,
            -Vector3f.Z,
            nextShader,
            nextMaterial,
            nextBuffer
        )

        val useBatch = config.cacheAccumulateBatches
        val last = lastRenderState
        val isStateChanged = lastRenderState != nextRenderState
        val isStaticLastOrNext = last?.buffer?.isStatic == true || nextBuffer.isStatic
        val isBufferLimitReached =
            nextGeometry.indices.size + nextBuffer.data.indicesAmount > config.cacheMaxDrawBufferIndices


        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.setViewDirection(renderState.viewDirection)

        environment.lights.forEachIndexed { index, light ->
            stateController.setLight(index, light)
        }

        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, environment.viewPort.height - y, z)
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy