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

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

There is a newer version: 1.2.1
Show newest version
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