diff --git a/build.gradle.kts b/build.gradle.kts index ade6f644a..da97c81e1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -211,37 +211,6 @@ tasks { // Forces the task to always run outputs.upToDateWhen { false } } - - // Visual debugger for OpenGL - register("renderDoc") { - // You need renderdoc installed on your system and available in your environment variables in order - // to use this task. - // You can download it from their official website at https://renderdoc.org/ - - val javaHome = Jvm.current().javaHome - val gradle = rootProject.tasks.wrapper.get().jarFile.absolutePath - - val seperator = - if (Os.isFamily(Os.FAMILY_WINDOWS)) ";" else ":" - - commandLine = listOf( - "renderdoccmd", "capture", "--opt-api-validation", "--opt-api-validation-unmute", "--opt-hook-children", "--wait-for-exit", "--working-dir", ".", - "$javaHome/bin/java", - //"-javaagent:${projectDir.resolve("lwjglx-debug-1.0.0.jar")}=t", - //"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", - "-Dorg.gradle.appname=gradlew", - "-Dorg.gradle.java.home=$javaHome", - "-Dorg.lwjgl.util.Debug=true", - "-Dorg.lwjgl.util.DebugLoader=true", - "-Dorg.lwjgl.util.DebugAllocator=true", - "-Dorg.lwjgl.util.DebugStack=true", - "-Dorg.lwjgl.util.DebugFunctions=true", - "-cp", listOf(projectDir.resolve("lwjgl.jar"), gradle, projectDir.resolve("lwjglx-debug-1.0.0.jar")) - .joinToString(seperator), - "org.gradle.wrapper.GradleWrapperMain", - "runClient", - ) - } } kotlin { diff --git a/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt b/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt index b72649506..a03f32e9e 100644 --- a/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt +++ b/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt @@ -17,104 +17,12 @@ package com.lambda.graphics.buffer -import org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER -import org.lwjgl.opengl.GL15.GL_DYNAMIC_COPY -import org.lwjgl.opengl.GL15.GL_DYNAMIC_DRAW -import org.lwjgl.opengl.GL15.GL_DYNAMIC_READ -import org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER -import org.lwjgl.opengl.GL15.GL_STATIC_COPY -import org.lwjgl.opengl.GL15.GL_STATIC_DRAW -import org.lwjgl.opengl.GL15.GL_STATIC_READ -import org.lwjgl.opengl.GL15.GL_STREAM_COPY -import org.lwjgl.opengl.GL15.GL_STREAM_DRAW -import org.lwjgl.opengl.GL15.GL_STREAM_READ -import org.lwjgl.opengl.GL15.glBufferSubData -import org.lwjgl.opengl.GL21.GL_PIXEL_PACK_BUFFER -import org.lwjgl.opengl.GL21.GL_PIXEL_UNPACK_BUFFER -import org.lwjgl.opengl.GL30.GL_MAP_FLUSH_EXPLICIT_BIT -import org.lwjgl.opengl.GL30.GL_MAP_INVALIDATE_BUFFER_BIT -import org.lwjgl.opengl.GL30.GL_MAP_INVALIDATE_RANGE_BIT -import org.lwjgl.opengl.GL30.GL_MAP_READ_BIT -import org.lwjgl.opengl.GL30.GL_MAP_UNSYNCHRONIZED_BIT -import org.lwjgl.opengl.GL30.GL_MAP_WRITE_BIT -import org.lwjgl.opengl.GL30.GL_TRANSFORM_FEEDBACK_BUFFER -import org.lwjgl.opengl.GL31.GL_COPY_READ_BUFFER -import org.lwjgl.opengl.GL31.GL_COPY_WRITE_BUFFER -import org.lwjgl.opengl.GL31.GL_TEXTURE_BUFFER -import org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER -import org.lwjgl.opengl.GL40.GL_DRAW_INDIRECT_BUFFER -import org.lwjgl.opengl.GL42.GL_ATOMIC_COUNTER_BUFFER -import org.lwjgl.opengl.GL43.GL_DISPATCH_INDIRECT_BUFFER -import org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER -import org.lwjgl.opengl.GL44.GL_CLIENT_STORAGE_BIT -import org.lwjgl.opengl.GL44.GL_DYNAMIC_STORAGE_BIT -import org.lwjgl.opengl.GL44.GL_MAP_COHERENT_BIT -import org.lwjgl.opengl.GL44.GL_MAP_PERSISTENT_BIT -import org.lwjgl.opengl.GL44.GL_QUERY_BUFFER import org.lwjgl.opengl.GL46.* import java.nio.ByteBuffer -abstract class Buffer( - /** - * Specifies how many buffer must be used - * - * | Number of Buffers | Purpose | - * |-------------------|---------------------------------------------------------------------------------------------------------------| - * | 1 Buffer | Simple operations like storing vertex data, reading from the framebuffer, etc. | - * | 2 Buffers | Increase throughput by not having to explicitly sync memory. | - * | 3 Buffers | If the driver run alongside the CPU and GPU, then each must have their own buffer to avoid stalling. | - */ - val buffers: Int = 1, - - /** - * Edge case to handle vertex arrays - */ - val isVertexArray: Boolean = false, - val validate: Boolean = true -) { - /** - * Specifies how the buffers are used - * - * | Buffer Usage | Description | - * |----------------------------------|-----------------------------------------------------------------| - * | [GL_STREAM_DRAW] | Data is set once and used a few times for drawing. | - * | [GL_STREAM_READ] | Data is set once and used a few times for reading. | - * | [GL_STREAM_COPY] | Data is set once and used a few times for copying. | - * | [GL_STATIC_DRAW] | Data is set once and used many times for drawing. | - * | [GL_STATIC_READ] | Data is set once and used many times for reading. | - * | [GL_STATIC_COPY] | Data is set once and used many times for copying. | - * | [GL_DYNAMIC_DRAW] | Data is modified repeatedly and used many times for drawing. | - * | [GL_DYNAMIC_READ] | Data is modified repeatedly and used many times for reading. | - * | [GL_DYNAMIC_COPY] | Data is modified repeatedly and used many times for copying. | - * - * @see Buffer object - */ - abstract val usage: Int - - /** - * Specifies the target to which the buffer object is bound which must be one - * of the following: - * - * | Buffer Binding Target | Purpose | - * |---------------------------------|--------------------------------------| - * | [GL_ARRAY_BUFFER] | Vertex attributes | - * | [GL_ATOMIC_COUNTER_BUFFER] | Atomic counter storage | - * | [GL_COPY_READ_BUFFER] | Buffer copy source | - * | [GL_COPY_WRITE_BUFFER] | Buffer copy destination | - * | [GL_DISPATCH_INDIRECT_BUFFER] | Indirect compute dispatch commands | - * | [GL_DRAW_INDIRECT_BUFFER] | Indirect command arguments | - * | [GL_ELEMENT_ARRAY_BUFFER] | Vertex array indices | - * | [GL_PIXEL_PACK_BUFFER] | Pixel read target | - * | [GL_PIXEL_UNPACK_BUFFER] | Texture data source | - * | [GL_QUERY_BUFFER] | Query result buffer | - * | [GL_SHADER_STORAGE_BUFFER] | Read-write storage for shaders | - * | [GL_TEXTURE_BUFFER] | Texture data buffer | - * | [GL_TRANSFORM_FEEDBACK_BUFFER] | Transform feedback buffer | - * | [GL_UNIFORM_BUFFER] | Uniform block storage | - * - * @see Buffer object - */ - abstract val target: Int +class Buffer private constructor( + val target: Int, + var buffer: Int, /** * Specifies a combination of access flags indicating the desired @@ -135,29 +43,10 @@ abstract class Buffer( * * @see Buffer object */ - abstract val access: Int - - /** - * Index of the current buffer. - */ - private var index: Int = 0; private set(value) { - if (field == value) return - field = value - id = bufferIds[value] - } - - /** - * ID of the current buffer. - */ - var id: Int = 0; get() { - if (field == 0) field = bufferIds[0] - return field - } private set - - /** - * List of all the buffers. - */ - private val bufferIds = IntArray(buffers) + val access: Int, +) { + fun bind() = glBindBuffer(target, buffer) + fun unbind() = glBindBuffer(target, 0) /** * Execute the [block] in a bound context @@ -165,92 +54,56 @@ abstract class Buffer( fun bind(block: Buffer.() -> Unit) { bind() block(this) - bind(0) + unbind() } /** - * Binds the buffer id to the [target]. - */ - open fun bind(id: Int) = glBindBuffer(target, id) - - /** - * Binds current the buffer [index] to the [target]. - */ - fun bind() = bind(id) - - /** - * Swaps the buffer [index] if [buffers] is greater than 1. - */ - fun swap() { index = (index + 1) % buffers } - - /** - * Update the current buffer without re-allocating. + * Allocates the buffer with the specified data. * - * @throws [IllegalArgumentException] if the target or usage is invalid + * @param data The data to put in the new allocated buffer * - * @see glBufferSubData + * @see glBufferData */ - open fun update(data: ByteBuffer, offset: Long) { - validate() - - bind() - glBufferSubData(target, offset, data) - bind(0) - } + fun allocate(data: ByteBuffer) = + bind { glBufferData(target, data, GL_DYNAMIC_DRAW) } /** - * Update the current buffer without re-allocating. + * Allocates the buffer with the specified size. * - * @throws [IllegalArgumentException] if the target or usage is invalid + * @param data The data to put in the new allocated buffer * - * @see glBufferSubData + * @see glBufferData */ - open fun update(offset: Long, size: Long, data: Long) { - validate() - - bind() - nglBufferSubData(target, offset, size, data) - bind(0) - } + fun allocate(size: Long) = + bind { glBufferData(target, size, GL_DYNAMIC_DRAW) } /** - * Allocates each backing buffer with the specified data. + * Update the current buffer without re-allocating. * - * @param data The data to put in the new allocated buffer * @throws [IllegalArgumentException] if the target or usage is invalid * - * @see glBufferData + * @see glBufferSubData */ - open fun allocate(data: ByteBuffer) { - validate() - - repeat(buffers) { - bind() - glBufferData(target, data, usage) - swap() - } + fun update(offset: Long, data: ByteBuffer) { + check(offset > -1) { "Cannot have negative buffer offsets" } + check(access and GL_DYNAMIC_STORAGE_BIT != 0) { "Buffer contents cannot be modified because the buffer was created without the GL_DYNAMIC_STORAGE_BIT set." } - bind(0) + bind { glBufferSubData(target, offset, data) } } /** - * Allocates memory for each backing buffer of specified size. + * Update the current buffer without re-allocating. * - * @param size The size of the new buffer * @throws [IllegalArgumentException] if the target or usage is invalid * - * @see glBufferData + * @see glBufferSubData */ - open fun allocate(size: Long) { - validate() - - repeat(buffers) { - bind() - glBufferData(target, size.coerceAtLeast(0), usage) - swap() - } + fun update(offset: Long, size: Long, data: Long) { + check(offset > -1) { "Cannot have negative buffer offsets" } + check(size > -1) { "Cannot have negative sized buffers" } + check(access and GL_DYNAMIC_STORAGE_BIT != 0) { "Buffer contents cannot be modified because the buffer was created without the GL_DYNAMIC_STORAGE_BIT set." } - bind(0) + bind { nglBufferSubData(target, offset, size, data) } } /** @@ -265,17 +118,7 @@ abstract class Buffer( * * @see glBufferStorage */ - open fun storage(data: ByteBuffer) { - validate() - - repeat(buffers) { - bind() - glBufferStorage(target, data, access) - swap() - } - - bind(0) - } + fun storage(data: ByteBuffer) = bind { glBufferStorage(target, data, access) } /** * Allocates storage for the buffer object. @@ -290,25 +133,16 @@ abstract class Buffer( * * @see glBufferStorage */ - open fun storage(size: Long) { - validate() - - repeat(buffers) { - bind() - glBufferStorage(target, size.coerceAtLeast(0), access) - swap() - } - - bind(0) + fun storage(size: Long) { + check(size > -1) { "Cannot have negative sized buffers" } + bind { glBufferStorage(target, size, access) } } - // TODO: - // GL_MAP_COHERENT_BIT makes it so changes in the mapped memory are automatically visible to the gpu, no memory barrier and syncing required, but a bit slower - // You still need to swap after each update or you will risk having conflicting reads and writes - // glFlushMappedBufferRange + fun storage(size: Int) = storage(size.toLong()) /** - * Maps a specified region of the buffer's data store into client memory, processes it using the provided lambda, and then unmaps the buffer. + * Maps a specified region of the buffer's data store into client memory, processes it using the provided lambda, and + * then unmaps the buffer. * * If [access] contains the `GL_MAP_PERSISTENT_BIT` flag, the buffer will not be unmapped. * @@ -318,15 +152,12 @@ abstract class Buffer( * * @see Direct memory access */ - open fun map(size: Long, offset: Long, block: (ByteBuffer) -> Unit = {}): ByteBuffer { - validate() - bind() - - check(offset >= 0 || size >= 0) - { "Invalid offset or size parameter offset: $offset size: $size." } + fun map(offset: Long, size: Long, block: (ByteBuffer) -> Unit = {}): ByteBuffer { + check(offset > -1) { "Cannot have negative buffer offsets" } + check(size > -1) { "Cannot have negative sized buffers" } check(offset + size <= glGetBufferParameteri(target, GL_BUFFER_SIZE)) - { "Out of bound (is the buffer initialized?) $size + $offset > ${glGetBufferParameteri(target, GL_BUFFER_SIZE)}." } + { "Segmentation fault Size $size + Offset $offset > Buffer ${glGetBufferParameteri(target, GL_BUFFER_SIZE)}." } check(glGetBufferParameteri(target, GL_BUFFER_MAPPED) == GL_FALSE) { "Buffer is already mapped." } @@ -339,43 +170,58 @@ abstract class Buffer( access and GL_MAP_INVALIDATE_BUFFER_BIT != 0 || access and GL_MAP_UNSYNCHRONIZED_BIT != 0)) || access and GL_MAP_WRITE_BIT != 0 - ) - { "GL_MAP_READ_BIT is set and any of GL_MAP_INVALIDATE_RANGE_BIT, GL_MAP_INVALIDATE_BUFFER_BIT or GL_MAP_UNSYNCHRONIZED_BIT is set." } + ) { "GL_MAP_READ_BIT is set and any of GL_MAP_INVALIDATE_RANGE_BIT, GL_MAP_INVALIDATE_BUFFER_BIT or GL_MAP_UNSYNCHRONIZED_BIT is set." } + + bind() val sharedRegion = glMapBufferRange(target, offset, size, access) - ?: throw IllegalStateException("Failed to map buffer.") + check(sharedRegion != null) { "Could not map the buffer" } block(sharedRegion) - if (access and GL_MAP_PERSISTENT_BIT == 0) { - if (!glUnmapBuffer(target)) - throw IllegalStateException("An unknown error occurred due to GPU memory availability of buffer corruption.") - } + if (access and GL_MAP_PERSISTENT_BIT == 0) + check(glUnmapBuffer(target)) { "An unknown error occurred due to GPU memory availability of buffer corruption." } - bind(0) + unbind() return sharedRegion } /** - * Uploads the specified data to the buffer starting at the given offset. - * - * This abstract function should be implemented to perform the actual data transfer into the buffer. - * - * @param data Data to set in memory - * @param offset The starting offset within the buffer of the range to be mapped + * Indicates modifications to a range of a mapped buffer. + */ + fun flushMappedRange(offset: Long, size: Long) = bind { glFlushMappedBufferRange(target, offset, size) } + + /** + * Copies all or part of one buffer object's data store to the data store of another buffer object. */ - abstract fun upload(data: ByteBuffer, offset: Long) + // fun copy(dst: Buffer, readOffset: Long, writeOffset: Long, size: Long) {} - private fun validate() { - if (!validate) return + /** + * Deletes the buffer object and creates a new one + */ + fun orphan() { + delete() + create() + } - check(usage in GL_STREAM_DRAW..GL_DYNAMIC_COPY) - { "Usage is invalid, refer to the documentation table." } + /** + * Deletes the buffer and mark all allocated data as free + */ + fun delete() { + glDeleteBuffers(buffer) + buffer = -69 + } - check(target in bindingCheckMappings) - { "Target is invalid, refer to the documentation table." } + /** + * Creates a new buffer + */ + fun create() { + check(buffer == -69) { "Cannot create a new buffer if the previous one is not deleted" } + buffer = glGenBuffers() + } + init { check(access and GL_MAP_COHERENT_BIT == 0 || access and GL_MAP_PERSISTENT_BIT != 0) { "GL_MAP_COHERENT_BIT requires GL_MAP_PERSISTENT_BIT flag." } @@ -383,52 +229,15 @@ abstract class Buffer( { "GL_MAP_PERSISTENT_BIT requires GL_MAP_READ_BIT or GL_MAP_WRITE_BIT." } } - init { - check(buffers > 0) { "Cannot generate less than one buffer" } - - if (isVertexArray) glGenVertexArrays(bufferIds) // If there are more than 1 buffer you should expect undefined behavior, this is not the way to do it - else glGenBuffers(bufferIds) - } - companion object { - val bindingCheckMappings = mapOf( - GL_ARRAY_BUFFER to GL_ARRAY_BUFFER_BINDING, - GL_ATOMIC_COUNTER_BUFFER to GL_ATOMIC_COUNTER_BUFFER_BINDING, - GL_COPY_READ_BUFFER_BINDING to GL_COPY_READ_BUFFER_BINDING, - GL_COPY_WRITE_BUFFER_BINDING to GL_COPY_WRITE_BUFFER_BINDING, - GL_DISPATCH_INDIRECT_BUFFER to GL_DISPATCH_INDIRECT_BUFFER_BINDING, - GL_DRAW_INDIRECT_BUFFER to GL_DRAW_INDIRECT_BUFFER_BINDING, - GL_ELEMENT_ARRAY_BUFFER to GL_ELEMENT_ARRAY_BUFFER_BINDING, - GL_PIXEL_PACK_BUFFER to GL_PIXEL_PACK_BUFFER_BINDING, - GL_PIXEL_UNPACK_BUFFER to GL_PIXEL_UNPACK_BUFFER_BINDING, - GL_QUERY_BUFFER to GL_QUERY_BUFFER_BINDING, - GL_SHADER_STORAGE_BUFFER to GL_SHADER_STORAGE_BUFFER_BINDING, - GL_TEXTURE_BUFFER to GL_TEXTURE_BUFFER_BINDING, - GL_TRANSFORM_FEEDBACK_BUFFER to GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, - GL_UNIFORM_BUFFER to GL_UNIFORM_BUFFER_BINDING, - ) - - @JvmField - var lastIbo = 0 - var prevIbo = 0 - - fun createPipelineBuffer(bufferTarget: Int) = object : Buffer(buffers = 1, validate = false) { - override val target: Int = bufferTarget - - override val usage: Int = GL_STATIC_DRAW - override val access: Int = GL_MAP_WRITE_BIT - - override fun bind(id: Int) { - if (bufferTarget != GL_ELEMENT_ARRAY_BUFFER) { - super.bind(id) - return - } - - if (id != 0) prevIbo = lastIbo - super.bind(if (id != 0) id else prevIbo) - } - - override fun upload(data: ByteBuffer, offset: Long) = throw UnsupportedOperationException() - } + /** + * Creates a buffer. + * + * If no [buffer] is provided, one will be created. + * + * @see [Buffer.access] + */ + fun create(target: Int, access: Int, buffer: Int = glGenBuffers(), block: Buffer.() -> Unit = {}) = + Buffer(target, buffer, access).apply { block() } } } diff --git a/src/main/kotlin/com/lambda/graphics/buffer/frame/CachedFrame.kt b/src/main/kotlin/com/lambda/graphics/buffer/frame/CachedFrame.kt deleted file mode 100644 index e7ad8030a..000000000 --- a/src/main/kotlin/com/lambda/graphics/buffer/frame/CachedFrame.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.buffer.frame - -import com.lambda.Lambda.mc -import com.lambda.graphics.RenderMain -import com.lambda.graphics.gl.Matrices -import org.joml.Matrix4f -import org.lwjgl.opengl.GL11C.glViewport - -/** - * A class that handles a cached frame, encapsulating a framebuffer with a specified width and height. - * It provides methods for binding the framebuffer texture and writing to the framebuffer with custom rendering operations. - * - * @param width The width of the framebuffer. - * @param height The height of the framebuffer. - */ -class CachedFrame(val width: Int, val height: Int) { - - // The framebuffer associated with this cached frame - private val frameBuffer = FrameBuffer(width, height) - - /** - * Binds the color texture of the framebuffer to a specified texture slot. - * - * @param slot The texture slot to bind the color texture to. Defaults to slot 0. - */ - fun bind(slot: Int = 0) = frameBuffer.bindColorTexture(slot) - - /** - * Executes custom drawing operations on the framebuffer. - * - * The method temporarily modifies the view and projection matrices, the viewport, - * and then restores them after the block is executed. - * - * @param block A block of code that performs custom drawing operations on the framebuffer. - */ - fun write(block: () -> Unit): CachedFrame { - frameBuffer.write { - // Save the current viewmodel matrix - Matrices.push() - // Set the viewmodel matrix to translate the scene away - Matrices.peek().set(Matrix4f().translate(0f, 0f, -3000f)) - - // Save the previous projection matrix and set a custom orthographic projection - val prevProj = Matrix4f(RenderMain.projectionMatrix) - RenderMain.projectionMatrix.setOrtho(0f, width.toFloat(), height.toFloat(), 0f, 1000f, 21000f) - - // Resize the viewport to match the framebuffer's dimensions - glViewport(0, 0, width, height) - - // Execute the drawing operations defined in the block - block() - - // Restore the previous viewport dimensions - glViewport(0, 0, mc.framebuffer.viewportWidth, mc.framebuffer.viewportHeight) - - // Restore the previous projection matrix - RenderMain.projectionMatrix.set(prevProj) - - // Restore the previous viewmodel matrix - Matrices.pop() - } - - return this - } -} diff --git a/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt b/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt deleted file mode 100644 index 02294dd53..000000000 --- a/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.buffer.frame - -import com.lambda.Lambda.mc -import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib -import com.lambda.graphics.buffer.vertex.attributes.VertexMode -import com.lambda.graphics.pipeline.VertexPipeline -import com.lambda.graphics.texture.TextureUtils -import com.mojang.blaze3d.systems.RenderSystem -import net.minecraft.client.gl.GlBackend -import net.minecraft.client.texture.GlTexture -import org.lwjgl.opengl.GL12C.GL_CLAMP_TO_EDGE -import org.lwjgl.opengl.GL30C.GL_COLOR_ATTACHMENT0 -import org.lwjgl.opengl.GL30C.GL_COLOR_BUFFER_BIT -import org.lwjgl.opengl.GL30C.GL_DEPTH_ATTACHMENT -import org.lwjgl.opengl.GL30C.GL_DEPTH_BUFFER_BIT -import org.lwjgl.opengl.GL30C.GL_DEPTH_COMPONENT -import org.lwjgl.opengl.GL30C.GL_DEPTH_COMPONENT32F -import org.lwjgl.opengl.GL30C.GL_FLOAT -import org.lwjgl.opengl.GL30C.GL_FRAMEBUFFER -import org.lwjgl.opengl.GL30C.GL_FRAMEBUFFER_COMPLETE -import org.lwjgl.opengl.GL30C.GL_LINEAR -import org.lwjgl.opengl.GL30C.GL_RGBA -import org.lwjgl.opengl.GL30C.GL_TEXTURE_2D -import org.lwjgl.opengl.GL30C.GL_TEXTURE_MAG_FILTER -import org.lwjgl.opengl.GL30C.GL_TEXTURE_MIN_FILTER -import org.lwjgl.opengl.GL30C.GL_TEXTURE_WRAP_S -import org.lwjgl.opengl.GL30C.GL_TEXTURE_WRAP_T -import org.lwjgl.opengl.GL30C.GL_UNSIGNED_BYTE -import org.lwjgl.opengl.GL30C.glBindFramebuffer -import org.lwjgl.opengl.GL30C.glCheckFramebufferStatus -import org.lwjgl.opengl.GL30C.glClear -import org.lwjgl.opengl.GL30C.glClearColor -import org.lwjgl.opengl.GL30C.glClearDepth -import org.lwjgl.opengl.GL30C.glFramebufferTexture2D -import org.lwjgl.opengl.GL30C.glGenFramebuffers -import org.lwjgl.opengl.GL30C.glGenTextures -import org.lwjgl.opengl.GL30C.glTexImage2D -import org.lwjgl.opengl.GL30C.glTexParameteri -import java.nio.IntBuffer - -open class FrameBuffer( - var width: Int = 1, - var height: Int = 1, - private val depth: Boolean = false -) { - val fbo = glGenFramebuffers() - - val colorAttachment = glGenTextures() - val depthAttachment by lazy(::glGenTextures) - - private val clearMask = if (!depth) GL_COLOR_BUFFER_BIT - else GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT - - private var lastWidth = -1 - private var lastHeight = -1 - - open fun write(block: () -> Unit): FrameBuffer { - glBindFramebuffer(GL_FRAMEBUFFER, fbo) - - update() - block() - - glBindFramebuffer(GL_FRAMEBUFFER, 0) - return this - } - - fun bind() { - glBindFramebuffer(GL_FRAMEBUFFER, fbo) - } - - fun updateScreenSized() { - width = mc.framebuffer.viewportWidth - height = mc.framebuffer.viewportHeight - update() - } - - fun update() { - if (width == lastWidth && height == lastHeight) { - glClear(clearMask) - return - } - - lastWidth = width - lastHeight = height - - setupBufferTexture(colorAttachment) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as IntBuffer?) - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorAttachment, 0) - - if (depth) { - setupBufferTexture(depthAttachment) - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null as IntBuffer?) - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthAttachment, 0) - } - - glClearColor(0f, 0f, 0f, 0f) - glClearDepth(1.0) - - glClear(clearMask) - - val fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER) - - check(fboStatus == GL_FRAMEBUFFER_COMPLETE) { "Framebuffer not complete: $fboStatus" } - } - - open fun bindColorTexture(slot: Int = 0): FrameBuffer { - TextureUtils.bindTexture(colorAttachment, slot) - return this - } - - open fun bindDepthTexture(slot: Int = 0): FrameBuffer { - check(depth) { - "Cannot bind depth texture of a non-depth framebuffer" - } - - TextureUtils.bindTexture(depthAttachment, slot) - return this - } - - companion object { - val pipeline = VertexPipeline(VertexMode.TRIANGLES, VertexAttrib.Group.POS_UV) - private var lastFrameBuffer: Int? = null - - private fun setupBufferTexture(id: Int) { - TextureUtils.bindTexture(id) - TextureUtils.setupTexture(GL_LINEAR, GL_LINEAR) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) - } - } -} diff --git a/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt b/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt deleted file mode 100644 index d7d383401..000000000 --- a/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.buffer.pixel - -import com.lambda.graphics.buffer.Buffer -import com.lambda.graphics.texture.Texture -import com.lambda.util.math.MathUtils.toInt -import org.lwjgl.opengl.GL45C.GL_ALPHA -import org.lwjgl.opengl.GL45C.GL_BGR -import org.lwjgl.opengl.GL45C.GL_BGRA -import org.lwjgl.opengl.GL45C.GL_BLUE -import org.lwjgl.opengl.GL45C.GL_DYNAMIC_STORAGE_BIT -import org.lwjgl.opengl.GL45C.GL_GREEN -import org.lwjgl.opengl.GL45C.GL_LINEAR -import org.lwjgl.opengl.GL45C.GL_MAP_COHERENT_BIT -import org.lwjgl.opengl.GL45C.GL_MAP_PERSISTENT_BIT -import org.lwjgl.opengl.GL45C.GL_MAP_WRITE_BIT -import org.lwjgl.opengl.GL45C.GL_PIXEL_UNPACK_BUFFER -import org.lwjgl.opengl.GL45C.GL_RED -import org.lwjgl.opengl.GL45C.GL_RG -import org.lwjgl.opengl.GL45C.GL_RGB -import org.lwjgl.opengl.GL45C.GL_RGBA -import org.lwjgl.opengl.GL45C.GL_STATIC_DRAW -import org.lwjgl.opengl.GL45C.GL_TEXTURE_2D -import org.lwjgl.opengl.GL45C.GL_TEXTURE_MAG_FILTER -import org.lwjgl.opengl.GL45C.GL_TEXTURE_MIN_FILTER -import org.lwjgl.opengl.GL45C.GL_UNSIGNED_BYTE -import org.lwjgl.opengl.GL45C.glBindTexture -import org.lwjgl.opengl.GL45C.glTexImage2D -import org.lwjgl.opengl.GL45C.glTexParameteri -import org.lwjgl.opengl.GL45C.glTexSubImage2D -import java.nio.ByteBuffer - -/** - * Represents a Pixel Buffer Object (PBO) that facilitates asynchronous data transfer to the GPU. - * - * Every function that performs a pixel transfer operation can use buffer objects instead of client memory. - * Functions that perform an upload operation, a pixel unpack, will use the buffer object bound to the target GL_PIXEL_UNPACK_BUFFER. - * If a buffer is bound, then the pointer value that those functions take is not a pointer, but an offset from the beginning of that buffer. - * - * Asynchronous should only be used for medium-size blocks of data being updated one time or less per frame - * Persistent should only be used for streaming operations with large blocks of data updated once or more per frame - * - * @property texture The [Texture] instance to use - * @property asynchronous Whether to use 2 buffers or not - * @property persistent Whether to map a block in memory to upload or not - * - * @see Pixel buffer object - */ -class PixelBuffer( - private val texture: Texture, - private val asynchronous: Boolean = false, - private val persistent: Boolean = false, -) : Buffer(buffers = asynchronous.toInt() + 1) { - override val usage = GL_STATIC_DRAW - override val target = GL_PIXEL_UNPACK_BUFFER - override val access = - if (persistent) GL_MAP_WRITE_BIT or GL_DYNAMIC_STORAGE_BIT or GL_MAP_PERSISTENT_BIT or GL_MAP_COHERENT_BIT - else GL_MAP_WRITE_BIT or GL_DYNAMIC_STORAGE_BIT - - private val channels = channelMapping[texture.format] - ?: throw IllegalArgumentException("Invalid image format, expected OpenGL format, got ${texture.format} instead") - private val size = texture.width * texture.height * channels * 1L - // private var sharedRegion: ByteBuffer? = null - - override fun upload(data: ByteBuffer, offset: Long) { - if (!asynchronous) { - glBindTexture(GL_TEXTURE_2D, texture.id) - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture.width, texture.height, texture.format, GL_UNSIGNED_BYTE, data) - glBindTexture(GL_TEXTURE_2D, 0) - return - } - - bind() - glBindTexture(GL_TEXTURE_2D, texture.id) - - // Copy pixels from PBO to texture object - // Use offset instead of pointer - glTexSubImage2D( - GL_TEXTURE_2D, // Target - 0, // Mipmap level - 0, 0, // x and y offset - texture.width, // Width of the texture - texture.height, // Height of the texture - texture.format, // Format of your texture (depends on your data) - GL_UNSIGNED_BYTE, // Type (depends on your data) - 0, // PBO offset (for asynchronous transfer) - ) - - swap() - bind() - - // if (persistent) data.putTo(sharedRegion) - // else update(data, offset) - update(data, offset) - - bind(0) - } - - init { - if (!texture.initialized) throw IllegalStateException("Cannot use uninitialized textures for pixel buffers") - - // We can't call the texture's bind method because the animated texture updates the - // data when binding the texture, causing a null pointer exception due to the animated - // texture object not being initialized - glBindTexture(GL_TEXTURE_2D, texture.id) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0, texture.format, GL_UNSIGNED_BYTE, 0) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) - - storage(size) - - // bind() - // sharedRegion = if (persistent) map(size, 0) else null - // bind(0) - } - - companion object { - /** - * Returns how many channels are used for each image format - */ - private val channelMapping = mapOf( - GL_RED to 1, - GL_GREEN to 1, - GL_BLUE to 1, - GL_ALPHA to 1, - GL_RG to 2, - GL_RGB to 3, - GL_BGR to 3, - GL_RGBA to 4, - GL_BGRA to 4, - ) - } -} diff --git a/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt b/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt index 518d4d8b3..a0a5db567 100644 --- a/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt +++ b/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt @@ -17,23 +17,19 @@ package com.lambda.graphics.buffer.vertex -import com.lambda.graphics.buffer.Buffer import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib import com.lambda.graphics.buffer.vertex.attributes.VertexMode import com.lambda.graphics.pipeline.PersistentBuffer import org.lwjgl.opengl.GL30C.GL_UNSIGNED_INT import org.lwjgl.opengl.GL30C.glBindVertexArray +import org.lwjgl.opengl.GL30C.glGenVertexArrays import org.lwjgl.opengl.GL32C.glDrawElementsBaseVertex -import java.nio.ByteBuffer class VertexArray( private val vertexMode: VertexMode, private val attributes: VertexAttrib.Group -) : Buffer(isVertexArray = true) { - override val usage: Int = -1 - override val target: Int = -1 - override val access: Int = -1 - +) { + private val vao = glGenVertexArrays() private var linkedVBO: PersistentBuffer? = null fun renderIndices( @@ -50,7 +46,8 @@ class VertexArray( indicesSize: Long, indicesPointer: Long, verticesOffset: Long - ) = bind { + ) { + glBindVertexArray(vao) glDrawElementsBaseVertex( vertexMode.mode, indicesSize.toInt() / Int.SIZE_BYTES, @@ -58,21 +55,14 @@ class VertexArray( indicesPointer, verticesOffset.toInt() / attributes.stride, ) + glBindVertexArray(0) } - fun linkVbo(vbo: PersistentBuffer, block: VertexArray.() -> Unit = { }) { + fun linkVbo(vbo: PersistentBuffer) { linkedVBO = vbo - bind { - vbo.use { - attributes.link() - block(this@VertexArray) - } - } + glBindVertexArray(vao) + vbo.buffer.bind { attributes.link() } + glBindVertexArray(0) } - - override fun map(size: Long, offset: Long, block: (ByteBuffer) -> Unit) = throw UnsupportedOperationException() - override fun upload(data: ByteBuffer, offset: Long) = throw UnsupportedOperationException() - - override fun bind(id: Int) = glBindVertexArray(id) } diff --git a/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt b/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt index d8ae2f4f6..e1bde0196 100644 --- a/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt +++ b/src/main/kotlin/com/lambda/graphics/buffer/vertex/attributes/VertexAttrib.kt @@ -64,23 +64,11 @@ sealed class VertexAttrib( @Suppress("ClassName") open class Group(vararg val attributes: VertexAttrib) { - object POS_UV : Group( - Vec2, Vec2 - ) - // GUI object FONT : Group( Vec3, Vec2, Color ) - object RECT : Group( - Vec3, Vec2, Color - ) - - object RECT_OUTLINE : Group( - Vec3, Vec2, Float, Color - ) - // WORLD object DYNAMIC_RENDERER : Group( Vec3, Vec3, Color @@ -94,9 +82,7 @@ sealed class VertexAttrib( Vec3, Vec2, Color ) - val stride = attributes.sumOf { attribute -> - attribute.size - } + val stride = attributes.sumOf { it.size } fun link() { attributes.foldIndexed(0L) { index, pointer, attrib -> diff --git a/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt b/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt index 44ce5014e..c221a9e8d 100644 --- a/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt +++ b/src/main/kotlin/com/lambda/graphics/pipeline/PersistentBuffer.kt @@ -17,9 +17,11 @@ package com.lambda.graphics.pipeline -import com.lambda.graphics.buffer.Buffer.Companion.createPipelineBuffer +import com.lambda.graphics.buffer.Buffer import com.lambda.graphics.buffer.DynamicByteBuffer.Companion.dynamicByteBuffer import com.lambda.graphics.gl.kibibyte +import org.lwjgl.opengl.GL30.GL_MAP_WRITE_BIT +import org.lwjgl.opengl.GL44.GL_DYNAMIC_STORAGE_BIT import org.lwjgl.system.MemoryUtil.memCopy /** @@ -33,16 +35,17 @@ class PersistentBuffer( */ val byteBuffer = dynamicByteBuffer(stride * initialSize) + /** + * Represents a OpenGl Object that store unformatted memory + */ + val buffer = Buffer.create(target, GL_MAP_WRITE_BIT or GL_DYNAMIC_STORAGE_BIT) { allocate(byteBuffer.capacity.toLong()) } + /** * Data that has passed through the buffer within previous frame */ private val snapshot = dynamicByteBuffer(1) private var snapshotData = 0L - /** - * Represents a gpu-side buffer - */ - val glBuffer = createPipelineBuffer(target) private var glSize = 0 var uploadOffset = 0L @@ -54,8 +57,7 @@ class PersistentBuffer( if (glSize != byteBuffer.capacity) { glSize = byteBuffer.capacity - - glBuffer.allocate(byteBuffer.data) + buffer.allocate(byteBuffer.data) snapshot.realloc(byteBuffer.capacity) snapshotData = 0 return @@ -65,7 +67,7 @@ class PersistentBuffer( if (snapshot.mismatch(byteBuffer) >= 0) return } - glBuffer.update(uploadOffset, dataCount, dataStart) + buffer.update(uploadOffset, dataCount, dataStart) } fun end() { @@ -86,6 +88,4 @@ class PersistentBuffer( uploadOffset = 0 snapshotData = 0 } - - fun use(block: () -> Unit) = glBuffer.bind { block() } } diff --git a/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index ee58ec7c5..2e284e8fc 100644 --- a/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -23,7 +23,6 @@ import com.lambda.event.events.WorldEvent import com.lambda.event.events.onStaticRender import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.SafeListener.Companion.listenConcurrently -import com.lambda.module.Module import com.lambda.module.modules.client.StyleEditor import com.lambda.threading.awaitMainThread import com.lambda.util.world.FastVector @@ -35,7 +34,7 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedDeque class ChunkedESP private constructor( - owner: Module, + owner: Any, private val update: ShapeBuilder.(World, FastVector) -> Unit ) { private val rendererMap = ConcurrentHashMap() @@ -54,18 +53,10 @@ class ChunkedESP private constructor( init { //listen { rebuildQueue.add(rendererMap[ChunkPos.toLong(it.pos)] ?: return@listen) } - listen { - if (!owner.isEnabled) return@listen - it.chunk.renderer.notifyChunks() - } - - listen { - if (!owner.isEnabled) return@listen - rendererMap.remove(it.chunk.pos.toLong())?.notifyChunks() - } + listen { it.chunk.renderer.notifyChunks() } + listen { rendererMap.remove(it.chunk.pos.toLong())?.notifyChunks() } listenConcurrently { - if (!owner.isEnabled) return@listenConcurrently if (++ticks % StyleEditor.updateFrequency == 0) { val polls = minOf(StyleEditor.rebuildsPerTick, rebuildQueue.size) @@ -76,19 +67,18 @@ class ChunkedESP private constructor( } owner.onStaticRender { - if (!owner.isEnabled || uploadQueue.isEmpty()) return@onStaticRender + if (uploadQueue.isEmpty()) return@onStaticRender + val polls = minOf(StyleEditor.uploadsPerTick, uploadQueue.size) + repeat(polls) { uploadQueue.poll()?.invoke() } } - owner.listen { - if (!owner.isEnabled) return@listen - rendererMap.values.forEach { it.renderer.render() } - } + owner.listen { rendererMap.values.forEach { it.renderer.render() } } } companion object { - fun Module.newChunkedESP( + fun Any.newChunkedESP( update: ShapeBuilder.(World, FastVector) -> Unit ) = ChunkedESP(this@newChunkedESP, update) } @@ -102,8 +92,8 @@ class ChunkedESP private constructor( .map { ChunkPos(chunk.pos.x + it.first, chunk.pos.z + it.second) } fun notifyChunks() { - neighbors.forEach { chunkPos -> - owner.rendererMap[chunkPos.toLong()]?.let { + neighbors.forEach { + owner.rendererMap[it.toLong()]?.let { if (!owner.rebuildQueue.contains(it)) owner.rebuildQueue.add(it) } diff --git a/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt b/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt deleted file mode 100644 index ecb6f262c..000000000 --- a/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.renderer.gui - -import com.lambda.graphics.RenderMain -import com.lambda.graphics.buffer.vertex.attributes.VertexAttrib -import com.lambda.graphics.buffer.vertex.attributes.VertexMode -import com.lambda.graphics.pipeline.VertexPipeline -import com.lambda.graphics.shader.Shader.Companion.shader -import com.lambda.graphics.texture.Texture -import com.lambda.gui.components.ClickGuiLayout -import com.lambda.util.math.Rect -import com.lambda.util.math.Vec2d -import org.lwjgl.glfw.GLFW.glfwGetTime - -object TextureRenderer { - private val pipeline = VertexPipeline(VertexMode.TRIANGLES, VertexAttrib.Group.POS_UV) - - private val mainShader = shader("pos_tex") - private val coloredShader = shader("pos_tex_shady") - - fun drawTexture(texture: Texture, rect: Rect) { - texture.bind() - mainShader.use() - - drawInternal(rect) - } - - fun drawTextureShaded(texture: Texture, rect: Rect) { - texture.bind() - coloredShader.use() - - coloredShader["u_Shade"] = 1.0 - coloredShader["u_ShadeTime"] = glfwGetTime() * ClickGuiLayout.colorSpeed * 5.0 - coloredShader["u_ShadeColor1"] = ClickGuiLayout.primaryColor - coloredShader["u_ShadeColor2"] = ClickGuiLayout.secondaryColor - - coloredShader["u_ShadeSize"] = RenderMain.screenSize / Vec2d(ClickGuiLayout.colorWidth, ClickGuiLayout.colorHeight) - - drawInternal(rect) - } - - fun drawInternal(rect: Rect) { - val pos1 = rect.leftTop - val pos2 = rect.rightBottom - - pipeline.immediate { - buildQuad( - vertex { - vec2(pos1.x, pos1.y) - vec2(0.0, 0.0) - }, - vertex { - vec2(pos1.x, pos2.y) - vec2(0.0, 1.0) - }, - vertex { - vec2(pos2.x, pos2.y) - vec2(1.0, 1.0) - }, - vertex { - vec2(pos2.x, pos1.y) - vec2(1.0, 0.0) - } - ) - } - } -} diff --git a/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaAtlas.kt b/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaAtlas.kt index 8093dc5a8..bc4004394 100644 --- a/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaAtlas.kt +++ b/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaAtlas.kt @@ -19,7 +19,7 @@ package com.lambda.graphics.renderer.gui.font.core import com.google.common.math.IntMath import com.lambda.core.Loadable -import com.lambda.graphics.texture.TextureOwner.uploadField +import com.lambda.graphics.texture.TextureOwner.upload import com.lambda.threading.runGameScheduled import com.lambda.util.math.Vec2d import com.lambda.util.stream @@ -206,7 +206,7 @@ object LambdaAtlas : Loadable { val str = "Loaded ${bufferPool.size} fonts" // avoid race condition runGameScheduled { - bufferPool.forEach { (owner, image) -> owner.uploadField(image) } + bufferPool.forEach { (owner, image) -> owner.upload(image) } bufferPool.clear() } diff --git a/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt b/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt deleted file mode 100644 index 65e78d2f1..000000000 --- a/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.graphics.renderer.gui.font.sdf - -import com.lambda.graphics.buffer.frame.CachedFrame -import com.lambda.graphics.buffer.frame.FrameBuffer -import com.lambda.graphics.shader.Shader.Companion.shader -import com.lambda.graphics.texture.Texture -import com.lambda.util.math.Vec2d -import java.awt.image.BufferedImage - -/** - * A class that represents a distance field texture, which is created by rendering a given texture - * onto a framebuffer with specific shader operations. - * - * The texture is used to create a signed distance field (SDF) for rendering operations. - * - * @param image Image data to upload - */ -class DistanceFieldTexture(image: BufferedImage) : Texture(image, levels = 0) { - private val shader = shader("post/sdf") - - private val frame = CachedFrame(width, height).write { - FrameBuffer.pipeline.immediate { - val (pos1, pos2) = Vec2d.ZERO to Vec2d(width, height) - - buildQuad( - vertex { - vec2(pos1.x, pos1.y).vec2(0.0, 1.0) - }, - vertex { - vec2(pos1.x, pos2.y).vec2(0.0, 0.0) - }, - vertex { - vec2(pos2.x, pos2.y).vec2(1.0, 0.0) - }, - vertex { - vec2(pos2.x, pos1.y).vec2(1.0, 1.0) - } - ) - - shader.use() - shader["u_TexelSize"] = Vec2d.ONE / pos2 - super.bind(0) - } - } - - override fun bind(slot: Int) { - frame.bind(slot) - } -} diff --git a/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt b/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt index 95e69350f..3590dff00 100644 --- a/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt +++ b/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt @@ -17,7 +17,6 @@ package com.lambda.graphics.texture -import com.lambda.graphics.buffer.pixel.PixelBuffer import com.lambda.util.LambdaResource import com.lambda.util.stream import org.lwjgl.BufferUtils @@ -26,7 +25,6 @@ import java.nio.ByteBuffer class AnimatedTexture(path: LambdaResource) : Texture(image = null) { - private val pbo: PixelBuffer private val gif: ByteBuffer // Do NOT free this pointer private val frameDurations: IntArray // Array of frame duration milliseconds as ints val channels: Int @@ -51,7 +49,7 @@ class AnimatedTexture(path: LambdaResource) : Texture(image = null) { .position(blockSize * currentFrame) .limit(blockSize * (currentFrame + 1)) - pbo.upload(slice, offset = 0) + update(slice, width, height) gif.clear() currentFrame = (currentFrame + 1) % frames @@ -85,7 +83,5 @@ class AnimatedTexture(path: LambdaResource) : Texture(image = null) { frameDurations = IntArray(frames) pDelays.getIntBuffer(frames).get(frameDurations) - - pbo = PixelBuffer(this@AnimatedTexture) } } diff --git a/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt b/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt index 75e9adbb6..b568a45f1 100644 --- a/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt +++ b/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt @@ -17,7 +17,6 @@ package com.lambda.graphics.texture -import com.lambda.graphics.renderer.gui.font.sdf.DistanceFieldTexture import com.lambda.util.readImage import java.awt.image.BufferedImage @@ -93,16 +92,6 @@ object TextureOwner { textureMap.computeIfAbsent(this@upload) { mutableListOf() }.add(it) } - /** - * Uploads a distance field texture from image data and associates it with the object - * Distance field textures are commonly used for rendering fonts. - * - * @param data The image data as a [BufferedImage] to create the distance field texture - * @return The created distance field texture object - */ - fun Any.uploadField(data: BufferedImage) = - DistanceFieldTexture(data).also { textureMap.computeIfAbsent(this@uploadField) { mutableListOf() }.add(it) } - /** * Uploads a GIF and associates it with the object as an animated texture * diff --git a/src/main/kotlin/com/lambda/gui/MenuBar.kt b/src/main/kotlin/com/lambda/gui/MenuBar.kt index d9013ca2b..28bd05b52 100644 --- a/src/main/kotlin/com/lambda/gui/MenuBar.kt +++ b/src/main/kotlin/com/lambda/gui/MenuBar.kt @@ -481,4 +481,4 @@ object MenuBar { } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index f5ec015b2..9151322ba 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -92,4 +92,4 @@ data class BreakContext( value("Sort Mode", sortMode) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 93a2bc185..61f11a486 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -96,4 +96,4 @@ data class PlaceContext( value("Current Dir Is Invalid", currentDirIsValid) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/lambda/network/CapeManager.kt b/src/main/kotlin/com/lambda/network/CapeManager.kt index 9f038aaa7..e12b9ef53 100644 --- a/src/main/kotlin/com/lambda/network/CapeManager.kt +++ b/src/main/kotlin/com/lambda/network/CapeManager.kt @@ -38,8 +38,10 @@ import com.lambda.util.FolderRegister.capes import com.lambda.util.StringUtils.asIdentifier import com.lambda.util.extension.resolveFile import kotlinx.coroutines.runBlocking +import net.minecraft.client.texture.NativeImage import net.minecraft.client.texture.NativeImage.read import net.minecraft.client.texture.NativeImageBackedTexture +import org.lwjgl.BufferUtils import java.util.* import java.util.concurrent.ConcurrentHashMap import kotlin.concurrent.fixedRateTimer @@ -99,7 +101,14 @@ object CapeManager : ConcurrentHashMap(), Loadable { .downloadIfNotPresent(cape.url).getOrNull() ?.readBytes() ?: return@runIO - mc.textureManager.registerTexture(cape.id.asIdentifier, NativeImageBackedTexture({ cape.id }, TextureUtils.readImage(bytes))) + val buffer = BufferUtils + .createByteBuffer(bytes.size) + .put(bytes) + .flip() + + val image = read(NativeImage.Format.RGBA, buffer) + + mc.textureManager.registerTexture(cape.id.asIdentifier, NativeImageBackedTexture({ cape.id }, image)) put(uuid, cape.id) }.invokeOnCompletion { block(it) }