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

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

package casperix.opengl.renderer

import casperix.math.axis_aligned.int32.Box2i
import casperix.math.axis_aligned.int32.Dimension2i
import casperix.math.geometry.Quad2f
import casperix.math.geometry.Triangle2f
import casperix.math.quad_matrix.float32.Matrix3f
import casperix.math.vector.float32.Vector3f
import casperix.opengl.*
import casperix.opengl.misc.GLErrorPrinter
import casperix.opengl.renderer.impl.DeviceGraphicData
import casperix.opengl.renderer.impl.GraphicDataProvider
import casperix.opengl.renderer.impl.RenderState
import casperix.opengl.renderer.shader.ShaderController
import casperix.renderer.Environment
import casperix.renderer.Renderer2D
import casperix.renderer.material.Material
import casperix.renderer.vector.VectorGraphic
import 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 shaderProvider = ShaderProvider()
    private val stateController = StateController(config.textureConfig)
    private val graphicProvider = GraphicDataProvider(stateController, config.cacheConfig.staticConfig)

    fun begin() {
        validate()
        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)
        }

        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        validate()
    }

    fun end() {
        validate()
        flush()

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

        glDisable(GL_BLEND)
        glActiveTexture(GL_TEXTURE0)
        glDisable(GL_CULL_FACE)

        validate()
    }

   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.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.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) {
        if (graphic.geometry.vertices.size == 0) {
            return
        }

        val data = graphicProvider.get(stateController, graphic, statistic.frameIndex) ?: return
        val controller = shaderProvider.getOrCreate(data.attributes) ?: return

        pushToBuffer(modelTransform, controller, data)
    }


    private fun pushToBuffer(nextTransform: Matrix3f, nextController: ShaderController, nextData: DeviceGraphicData) {
        val nextGraphic = nextData.graphic
        val nextGeometry = nextGraphic.geometry
        val nextMaterial = nextGraphic.material
        val nextBuffer = nextData.buffer
        val nextRenderState = RenderState(nextTransform, nextTransform * environment.viewMatrix * environment.projectionMatrix, nextController, 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)

        buffer.draw()

        statistic.current.triangles += buffer.data.indicesAmount / 3
        statistic.current.batches++

        if (!buffer.isStatic) {
            buffer.reset()
        }
        validate()
    }

    private fun Vector3f.flipY():Vector3f {
        return Vector3f(x, viewPort.height - y, z)
    }

    private fun validate() {
        GLErrorPrinter.throwIfExist()
    }


}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy