diff --git a/common/src/main/java/com/lambda/mixin/render/ChatHudMixin.java b/common/src/main/java/com/lambda/mixin/render/ChatHudMixin.java index a1e8dd4b5..66109cce0 100644 --- a/common/src/main/java/com/lambda/mixin/render/ChatHudMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/ChatHudMixin.java @@ -28,6 +28,20 @@ @Mixin(ChatHud.class) public class ChatHudMixin { + /** + * Redirects the chat HUD text rendering to apply custom text parsing and color adjustments. + * + *

This method intercepts calls to {@code DrawContext#drawTextWithShadow} during chat rendering. + * It processes the text using {@code LambdaMoji.INSTANCE.parse} to incorporate custom formatting (e.g., emoji support), + * overrides the x-coordinate by rendering at x = 0, and adjusts the text color by combining a white base (0xFFFFFF) + * with an alpha value derived from the provided {@code color} parameter. + * + * @param text the text to be rendered, processed for custom formatting + * @param x the original x-coordinate (ignored as rendering occurs at x = 0) + * @param y the y-coordinate for rendering the text + * @param color the original text color used to compute the alpha channel for the final rendered color + * @return the result of the underlying text rendering call + */ @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/OrderedText;III)I")) int redirectRenderCall(DrawContext instance, TextRenderer textRenderer, OrderedText text, int x, int y, int color) { return instance.drawTextWithShadow(textRenderer, LambdaMoji.INSTANCE.parse(text, x, y, color), 0, y, 16777215 + (color << 24)); diff --git a/common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java b/common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java index 170b025e1..eafc58bf2 100644 --- a/common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/ChatInputSuggestorMixin.java @@ -55,19 +55,56 @@ public abstract class ChatInputSuggestorMixin { @Shadow private @Nullable CompletableFuture pendingSuggestions; + /** + * Displays the current suggestion list. + * + *

This shadowed method should be overridden to update the suggestion display. If + * {@code narrateFirstSuggestion} is {@code true}, the method should also provide narration + * for the first suggestion to enhance accessibility. + * + * @param narrateFirstSuggestion if {@code true}, the first suggestion is narrated; otherwise, it is not + */ @Shadow public abstract void show(boolean narrateFirstSuggestion); + /** + * Determines if the current chat input text should be treated as a command. + * + *

This method overrides the provided showCompletions flag during the refresh + * process by evaluating the text currently entered in the chat input field. It returns + * true if the input is recognized as a command, and false otherwise.

+ * + * @param showCompletions the initial flag indicating whether completions should be shown (its value is ignored) + * @return true if the chat input text is recognized as a command, false otherwise + */ @ModifyVariable(method = "refresh", at = @At(value = "STORE"), index = 3) private boolean refreshModify(boolean showCompletions) { return CommandManager.INSTANCE.isCommand(textField.getText()); } + /** + * Redirects the command dispatcher retrieval to use a custom dispatcher based on the current chat input. + *

+ * Instead of invoking the default dispatcher from the network handler during a refresh, this method + * fetches a dispatcher tailored to the chat input text from the CommandManager. + * + * @return a command dispatcher that reflects the current chat input context + */ @Redirect(method = "refresh", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;getCommandDispatcher()Lcom/mojang/brigadier/CommandDispatcher;")) private CommandDispatcher refreshRedirect(ClientPlayNetworkHandler instance) { return CommandManager.INSTANCE.currentDispatcher(textField.getText()); } + /** + * Injects emoji suggestion logic at the end of the chat refresh process. + * + *

This method verifies that emoji suggestions are enabled and that the current input is not a command. + * It extracts text up to the cursor, identifies the last colon to determine the start of the emoji query, + * and filters available emojis based on the subsequent substring. The resulting suggestions are then + * prepared and, once completed, are displayed. + * + * @param ci the callback information for the injection point + */ @Inject(method = "refresh", at = @At("TAIL")) private void refreshEmojiSuggestion(CallbackInfo ci) { if (!LambdaMoji.INSTANCE.isEnabled() || @@ -105,6 +142,16 @@ private void refreshEmojiSuggestion(CallbackInfo ci) { @Unique private static final Pattern COLON_PATTERN = Pattern.compile("(:[a-zA-Z0-9_]+)"); + /** + * Returns the index of the last occurrence of a colon that precedes valid emoji key characters. + * + *

The method uses a regular expression to identify sequences starting with a colon + * followed by alphanumeric characters or underscores. If the input is null, empty, or no + * matching sequence is found, it returns -1.

+ * + * @param input the string to be searched for a colon pattern + * @return the index of the last colon matching the pattern, or -1 if none is found + */ @Unique private int neoLambda$getLastColon(String input) { if (Strings.isNullOrEmpty(input)) return -1; diff --git a/common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java b/common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java index cc6b0edb8..5187060f4 100644 --- a/common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java @@ -26,6 +26,16 @@ @Mixin(ChatScreen.class) public abstract class ChatScreenMixin { + /** + * Intercepts the chat message sending process to execute Lambda commands. + * + *

If the chat text is recognized as a Lambda command, the command is executed via the CommandManager, + * and the default message sending is canceled by setting the callback's return value to true.

+ * + * @param chatText the text of the chat message being processed + * @param addToHistory flag indicating whether the message should be added to the chat history (not used for Lambda commands) + * @param cir callback used to override the normal sending behavior of the chat message + */ @Inject(method = "sendMessage", at = @At("HEAD"), cancellable = true) void sendMessageInject(String chatText, boolean addToHistory, CallbackInfoReturnable cir) { if (!CommandManager.INSTANCE.isLambdaCommand(chatText)) return; diff --git a/common/src/main/java/com/lambda/mixin/render/DrawContextMixin.java b/common/src/main/java/com/lambda/mixin/render/DrawContextMixin.java index d71452858..e0c887514 100644 --- a/common/src/main/java/com/lambda/mixin/render/DrawContextMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/DrawContextMixin.java @@ -45,8 +45,35 @@ @Mixin(DrawContext.class) public abstract class DrawContextMixin { - @Shadow protected abstract void drawTooltip(TextRenderer textRenderer, List components, int x, int y, TooltipPositioner positioner); + /** + * Renders a tooltip using the provided text renderer, tooltip components, and positioning strategy. + * + *

This shadowed method is implemented by the target class to draw tooltips at the specified coordinates. + * The tooltip components determine the content displayed, while the positioner defines how the tooltip is positioned on the screen.

+ * + * @param textRenderer the renderer used for drawing the tooltip's text + * @param components the list of components forming the tooltip's content + * @param x the x-coordinate for the tooltip's starting position + * @param y the y-coordinate for the tooltip's starting position + * @param positioner the strategy that determines the tooltip's placement + */ +@Shadow protected abstract void drawTooltip(TextRenderer textRenderer, List components, int x, int y, TooltipPositioner positioner); + /** + * Overrides the default tooltip rendering to include additional components. + * + *

This injected method converts a list of text components into tooltip components. If optional tooltip data + * is present, it is inserted into the list, and if the currently focused item is a filled map, a map preview + * component is added. It then calls the shadowed tooltip drawing method with the modified components and cancels + * further execution of the original method.

+ * + * @param textRenderer the renderer used to draw text + * @param text the list of text elements to be converted into tooltip components + * @param data an optional tooltip data element to be incorporated into the tooltip + * @param x the x-coordinate for tooltip positioning + * @param y the y-coordinate for tooltip positioning + * @param ci the callback information used to cancel the original tooltip rendering + */ @Inject(method = "drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V", at = @At("HEAD"), cancellable = true) void drawItemTooltip(TextRenderer textRenderer, List text, Optional data, int x, int y, CallbackInfo ci) { List list = text.stream().map(Text::asOrderedText).map(TooltipComponent::of).collect(Collectors.toList()); diff --git a/common/src/main/java/com/lambda/mixin/render/SplashOverlayMixin.java b/common/src/main/java/com/lambda/mixin/render/SplashOverlayMixin.java index 3787b762b..5478ca4f1 100644 --- a/common/src/main/java/com/lambda/mixin/render/SplashOverlayMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/SplashOverlayMixin.java @@ -60,6 +60,15 @@ public LogoTextureMixin(Identifier location) { super(location); } + /** + * Redirects the default texture loading to supply a custom banner texture. + * + *

This method intercepts the call to load texture data from the default resource pack and returns + * an InputSupplier that provides an InputStream for the custom texture located at "textures/lambda_banner.png". + * The provided resource pack, resource type, and identifier are ignored.

+ * + * @return an InputSupplier that supplies the custom banner texture's input stream + */ @Redirect(method = "loadTextureData", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/DefaultResourcePack;open(Lnet/minecraft/resource/ResourceType;Lnet/minecraft/util/Identifier;)Lnet/minecraft/resource/InputSupplier;")) InputSupplier loadTextureData(DefaultResourcePack instance, ResourceType type, Identifier id) { return () -> LambdaResourceKt.getStream("textures/lambda_banner.png"); diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index e448cd798..c8163f0fa 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -36,6 +36,13 @@ object RenderMain { var screenSize = Vec2d.ZERO var scaleFactor = 1.0 + /** + * Renders the 2D interface elements. + * + * This function resets the transformation matrices with a translation along the z-axis to position the 2D + * layer appropriately, then sets up the OpenGL state and posts rendering events for fixed GUI elements, + * the HUD, and scaled elements using the default and current GUI scaling factors. + */ @JvmStatic fun render2D() { resetMatrices(Matrix4f().translate(0f, 0f, -3000f)) @@ -50,6 +57,12 @@ object RenderMain { } } + /** + * Prepares the 3D rendering context by resetting matrices with the given transformation, + * updating the projection matrix, and posting a world render event. + * + * @param matrix the transformation matrix used to reset the rendering matrices. + */ @JvmStatic fun render3D(matrix: Matrix4f) { resetMatrices(matrix) @@ -60,6 +73,16 @@ object RenderMain { } } + /** + * Recalculates the screen dimensions and updates the orthographic projection matrix based on a scaling factor. + * + * This function retrieves the current framebuffer dimensions, computes the scaled width and height + * by dividing them by the provided factor, and then updates the global screen size and scale factor. + * It sets the projection matrix to an orthographic projection using the calculated dimensions with a + * near plane of 1000f and a far plane of 21000f. + * + * @param factor the scale factor used to adjust the framebuffer dimensions. + */ private fun rescale(factor: Double) { val width = mc.window.framebufferWidth.toFloat() val height = mc.window.framebufferHeight.toFloat() diff --git a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt index 286740dfb..b13edf513 100644 --- a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt +++ b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt @@ -27,11 +27,43 @@ class Animation(initialValue: Double, val update: (Double) -> Double) { private var prevValue = initialValue private var currValue = initialValue - operator fun getValue(thisRef: Any?, property: KProperty<*>) = value() - operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: Double) = setValue(valueIn) + /** + * Retrieves the current interpolated animation value. + * + * This operator function enables property delegation by returning the animation's + * current value as computed by the [value] method, which interpolates between the previous + * and current states. + */ +operator fun getValue(thisRef: Any?, property: KProperty<*>) = value() + /** + * Delegated setter that updates the animation's value. + * + * This operator function is triggered when a delegated property is assigned a new value. It sets both the previous + * and current values of the animation to the provided [valueIn]. + * + * @param thisRef The object owning the delegated property (unused). + * @param property Metadata for the property being assigned (unused). + * @param valueIn The new value to set for the animation. + */ +operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: Double) = setValue(valueIn) - fun value(): Double = lerp(mc.partialTicks, prevValue, currValue) + /** + * Computes and returns the current interpolated animation value. + * + * This function uses linear interpolation between the previous and current animation values, + * leveraging the partial tick value from the rendering context (mc.partialTicks) to ensure smooth transitions. + * + * @return the interpolated animation value. + */ +fun value(): Double = lerp(mc.partialTicks, prevValue, currValue) + /** + * Resets the animation state by updating both the previous and current values. + * + * This ensures that the animation starts from a consistent value without any interpolation. + * + * @param valueIn The new value to set as both the previous and current animation state. + */ fun setValue(valueIn: Double) { prevValue = valueIn currValue = valueIn diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt index 0025bfdff..7375c3f21 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt @@ -114,18 +114,29 @@ abstract class Buffer( private val bufferIds = IntArray(buffers) /** - * Binds the buffer id to the [target] - */ + * Binds the specified buffer id to this buffer's target. + * + * This operation makes the buffer identified by [id] the active buffer for subsequent OpenGL operations + * on the target defined by this buffer. + * + * @param id the unique identifier of the buffer to bind. + */ open fun bind(id: Int) = glBindBuffer(target, id) /** - * Binds current the buffer [index] to the [target] - */ + * Binds the active buffer to its target. + * + * Retrieves the buffer ID associated with the current [index] using [bufferAt] and binds it + * by calling the overloaded [bind] method. + */ fun bind() = bind(bufferAt(index)) /** - * Returns the id of the buffer based on the index - */ + * Retrieves the buffer identifier at the specified index. + * + * @param index The 0-based index of the buffer ID. + * @return The buffer identifier corresponding to the given index. + */ fun bufferAt(index: Int) = bufferIds[index] /** @@ -136,8 +147,17 @@ abstract class Buffer( } /** - * Update the current buffer without re-allocating - * Alternative to [map] + * Updates the current buffer's data at the given offset without reallocating its memory. + * + * This method uploads the provided data to the buffer using a sub-data update mechanism. + * It verifies that the buffer's target is valid and bound before performing the update; + * if either check fails, an IllegalArgumentException is returned. + * + * @param data the new data to upload into the buffer. + * @param offset the byte offset within the buffer at which the update should start. + * @return an IllegalArgumentException if the buffer is not valid or bound; otherwise, null. + * + * @see map */ open fun update( data: ByteBuffer, @@ -155,10 +175,14 @@ abstract class Buffer( } /** - * Allocates a region of memory for the buffer - * This function handles the buffer binding + * Allocates and initializes buffer storage using the provided data. + * + * This function validates the buffer's target and usage before allocation. If the validation fails, it returns an + * IllegalArgumentException. Otherwise, it binds and allocates storage for each buffer in sequence, updating the current + * buffer index by swapping after each allocation, and finally resets the binding. * - * @param data The data to put in the new allocated buffer + * @param data The ByteBuffer containing the initial data for the buffer. + * @return An IllegalArgumentException if validation fails; null if allocation succeeds. */ open fun allocate(data: ByteBuffer): Throwable? { if (!bufferValid(target, access)) @@ -179,10 +203,14 @@ abstract class Buffer( } /** - * Grows the backing buffers - * This function handles the buffer binding + * Allocates memory for each backing buffer using the specified size. + * + * This method first validates that the buffer's target and usage are valid. If either check fails, it returns an + * [IllegalArgumentException] with a descriptive message. Otherwise, it binds each backing buffer in turn, allocates its + * memory storage, and updates to the next buffer before finally unbinding. * - * @param size The size of the new buffer + * @param size The desired size for each buffer allocation. + * @return An [IllegalArgumentException] if validation fails, or null if the allocation succeeds. */ open fun allocate(size: Long): Throwable? { if (!bufferValid(target, access)) @@ -203,9 +231,14 @@ abstract class Buffer( } /** - * Create a new buffer storage - * This function cannot be called twice for the same buffer - * This function handles the buffer binding + * Allocates new storage for the OpenGL buffer using the provided data. + * + * This function validates the target and usage before creating storage for each buffer in a multi-buffer + * configuration. It handles the binding of each buffer and automatically swaps buffers during storage allocation. + * Note that this operation should only be performed once per buffer. + * + * @param data the ByteBuffer containing the data to initialize the buffer storage. + * @return an IllegalArgumentException for an invalid target or usage, or null if storage allocation is successful. */ open fun storage(data: ByteBuffer): Throwable? { if (!bufferValid(target, access)) @@ -226,11 +259,14 @@ abstract class Buffer( } /** - * Create a new buffer storage - * This function cannot be called twice for the same buffer - * This function handles the buffer binding + * Allocates storage for the buffer object. + * + * This function initializes storage for each allocated buffer using the specified size, + * automatically handling binding and unbinding. It validates the buffer's target, access flags, + * and usage before allocation. Note that it should only be called once per buffer. * - * @param size The size of the storage buffer + * @param size the desired storage size in bytes (negative values are coerced to zero) + * @return an IllegalArgumentException if the target or usage is invalid; null if storage allocation succeeds */ open fun storage(size: Long): Throwable? { if (!bufferValid(target, access)) @@ -251,12 +287,16 @@ abstract class Buffer( } /** - * Maps all or part of a buffer object's data store into the client's address space + * Maps a specified region of the buffer's data store into client memory, processes it using the provided lambda, and then unmaps the buffer. * - * @param size Specifies the length of the range to be mapped. - * @param offset Specifies the starting offset within the buffer of the range to be mapped. - * @param block Lambda scope with the mapped buffer passed in - * @return Error encountered during the mapping process + * The function validates the offset, size, and mapping access flags before mapping the buffer. It then passes the mapped ByteBuffer to the lambda, + * allowing for direct data manipulation. After the lambda completes, the buffer is unmapped. If any validation or operation fails, a Throwable + * describing the error is returned; otherwise, null is returned to indicate success. + * + * @param size the length of the memory region to map. + * @param offset the starting offset within the buffer for mapping. + * @param block a lambda function that receives the mapped ByteBuffer for data manipulation. + * @return null if the mapping and unmapping succeed; otherwise, a Throwable with the error details. */ open fun map( size: Long, @@ -311,12 +351,14 @@ abstract class Buffer( } /** - * Sets the given data into the client mapped memory and executes the provided processing function to manage data transfer. - * - * @param data Data to set in memory - * @param offset The starting offset within the buffer of the range to be mapped - * @return Error encountered during the mapping process - */ + * 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 The ByteBuffer containing the data to be uploaded. + * @param offset The offset within the buffer at which to begin the upload. + * @return A Throwable if an error occurs during the upload process, or null if the upload is successful. + */ abstract fun upload(data: ByteBuffer, offset: Long): Throwable? init { diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt index 3bb15197e..109132933 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt @@ -36,16 +36,41 @@ interface IRenderContext { fun render() fun upload() - fun clear() + /** + * Clears the current rendering context. + * + * This resets the context by removing any queued or rendered data, + * preparing it for new drawing commands. + */ +fun clear() + /** + * Executes an immediate draw by sequentially calling upload, render, and clear. + * + * This method provides a single entry point for performing the complete draw cycle without requiring separate calls for + * uploading data, rendering the content, and clearing the context. + */ fun immediateDraw() { upload() render() clear() } - fun grow(amount: Int) + /** + * Increases the capacity of the rendering context by the specified amount. + * + * @param amount The additional capacity units to add. + */ +fun grow(amount: Int) + /** + * Executes the given block within the current rendering context. + * + * This method allows for scoping multiple rendering operations or configurations within + * a single lambda, using the current context as the receiver. + * + * @param block a lambda with receiver that encapsulates rendering commands. + */ fun use(block: IRenderContext.() -> Unit) { block() } diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/VertexPipeline.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/VertexPipeline.kt index 808b690ee..ef3aa4e4a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/VertexPipeline.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/VertexPipeline.kt @@ -177,6 +177,13 @@ class VertexPipeline( uploadedIndices = indicesCount } + /** + * Resets the vertex position and index counters to their initial state. + * + * This method sets the vertex position pointer back to its original location and resets + * the vertex index, indices count, and uploaded indices counters to zero, preparing the + * pipeline for a new rendering sequence. + */ override fun clear() { verticesPosition = verticesPointer vertexIndex = 0 @@ -184,6 +191,11 @@ class VertexPipeline( uploadedIndices = 0 } + /** + * Finalizes the vertex pipeline. + * + * Currently, this method is a placeholder reserved for future cleanup or resource finalization logic. It does not perform any actions. + */ fun finalize() { } diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/CachedFrame.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/CachedFrame.kt index 9126de77e..46caf44a8 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/CachedFrame.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/CachedFrame.kt @@ -43,12 +43,14 @@ class CachedFrame(val width: Int, val height: Int) { fun bind(slot: Int = 0) = frameBuffer.bindColorTexture(slot) /** - * Executes custom drawing operations on the framebuffer. + * Executes custom drawing operations within the cached framebuffer. * - * The method temporarily modifies the view and projection matrices, the viewport, - * and then restores them after the block is executed. + * This function temporarily adjusts the OpenGL state by saving the current view and projection matrices, + * as well as the viewport dimensions, then sets up a translation and an orthographic projection matching + * the framebuffer's size. After executing the provided drawing block, it restores all previous settings. * - * @param block A block of code that performs custom drawing operations on the framebuffer. + * @param block A lambda containing the drawing operations to be executed on the framebuffer. + * @return The current CachedFrame instance. */ fun write(block: () -> Unit): CachedFrame { frameBuffer.write { diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt index d340b524c..957f66b00 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/FrameBuffer.kt @@ -45,6 +45,15 @@ open class FrameBuffer( private var lastWidth = -1 private var lastHeight = -1 + /** + * Binds this framebuffer, updates its configuration, executes the given rendering block, and restores the previous framebuffer binding. + * + * Temporarily replaces the currently bound framebuffer with this instance, ensuring it is properly configured by invoking an update, + * and then executes the provided block of rendering commands. After the block completes, it restores the prior framebuffer context. + * + * @param block the lambda containing rendering operations to execute while this framebuffer is active. + * @return this framebuffer instance. + */ open fun write(block: () -> Unit): FrameBuffer { val prev = lastFrameBuffer ?: mc.framebuffer.fbo @@ -60,6 +69,16 @@ open class FrameBuffer( return this } + /** + * Updates the framebuffer attachments to match the current dimensions. + * + * If the current width and height match the previously recorded dimensions, the function + * simply clears the framebuffer using the configured clear mask. Otherwise, it updates the + * color texture attachment and, if enabled, the depth texture attachment by reconfiguring + * texture parameters and reallocating storage to the new dimensions. After resetting the clear + * color and depth values and clearing the framebuffer, it verifies that the framebuffer is complete, + * throwing an error if it is not. + */ private fun update() { if (width == lastWidth && height == lastWidth) { glClear(clearMask) @@ -91,11 +110,30 @@ open class FrameBuffer( } } + /** + * Binds the framebuffer's color attachment texture to the specified texture slot. + * + * This method makes the framebuffer's color texture available for use in subsequent rendering operations. + * The current instance is returned to facilitate method chaining. + * + * @param slot The texture slot index to which the color texture is bound. Defaults to 0. + * @return The current FrameBuffer instance. + */ open fun bindColorTexture(slot: Int = 0): FrameBuffer { TextureUtils.bindTexture(colorAttachment, slot) return this } + /** + * Binds the framebuffer's depth texture to the specified texture slot. + * + * This method binds the depth texture to the given texture unit, enabling it for subsequent rendering operations. + * If the framebuffer was created without a depth attachment, an [IllegalStateException] is thrown. + * + * @param slot the texture slot to bind the depth texture to (default is 0). + * @return this [FrameBuffer] instance. + * @throws IllegalStateException if the framebuffer does not have a depth attachment. + */ open fun bindDepthTexture(slot: Int = 0): FrameBuffer { check(depth) { "Cannot bind depth texture of a non-depth framebuffer" @@ -109,6 +147,14 @@ open class FrameBuffer( val pipeline = VertexPipeline(VertexMode.TRIANGLES, VertexAttrib.Group.POS_UV) private var lastFrameBuffer: Int? = null + /** + * Configures an OpenGL texture object with linear filtering and clamp-to-edge wrapping. + * + * This function binds the texture identified by [id], sets both its minification and magnification filters to + * linear filtering, and applies clamp-to-edge wrapping for its S and T coordinates. + * + * @param id the identifier of the texture to configure. + */ private fun setupBufferTexture(id: Int) { TextureUtils.bindTexture(id) TextureUtils.setupTexture(GL_LINEAR, GL_LINEAR) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/ScreenFrameBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/ScreenFrameBuffer.kt index 466473a81..3cd4f3474 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/frame/ScreenFrameBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/frame/ScreenFrameBuffer.kt @@ -25,6 +25,21 @@ import com.lambda.util.math.Vec2d import org.lwjgl.opengl.GL11C.* class ScreenFrameBuffer(depth: Boolean = false) : FrameBuffer(depth = depth) { + /** + * Renders the frame buffer content onto a quad using the specified shader. + * + * This function binds the frame buffer's color texture and activates the provided shader, + * allowing for further customization via the optional [shaderBlock]. It then computes normalized + * UV coordinates based on the given screen positions ([pos1] and [pos2]) relative to the current + * screen dimensions, and defines a quadrilateral for rendering. Finally, it applies a blending mode, + * uploads, renders, and clears the internal pipeline. + * + * @param shader The shader used for rendering the frame buffer content. + * @param pos1 The starting screen position (in pixels) for the rendering region. Defaults to [Vec2d.ZERO]. + * @param pos2 The ending screen position (in pixels) for the rendering region. Defaults to [RenderMain.screenSize]. + * @param shaderBlock Optional lambda for additional shader configuration. + * @return The current instance of [ScreenFrameBuffer] to support method chaining. + */ fun read( shader: Shader, pos1: Vec2d = Vec2d.ZERO, @@ -59,6 +74,15 @@ class ScreenFrameBuffer(depth: Boolean = false) : FrameBuffer(depth = depth) { return this } + /** + * Updates the frame buffer's dimensions to match the current window size and executes rendering instructions with a specific blend configuration. + * + * This method sets the frame buffer's width and height based on the current Minecraft window dimensions, then calls the superclass + * write method while wrapping the provided rendering block within a blend function using source alpha and one minus source alpha factors. + * + * @param block the lambda containing rendering instructions to be executed with the configured blending. + * @return the current instance of ScreenFrameBuffer. + */ override fun write(block: () -> Unit): ScreenFrameBuffer { width = mc.window.framebufferWidth height = mc.window.framebufferHeight @@ -68,9 +92,23 @@ class ScreenFrameBuffer(depth: Boolean = false) : FrameBuffer(depth = depth) { } as ScreenFrameBuffer } - override fun bindColorTexture(slot: Int) = + /** + * Binds the color texture to the specified slot and returns the current instance as a ScreenFrameBuffer. + * + * This method delegates to the superclass implementation and casts its result to ensure type consistency. + * + * @param slot The texture binding slot. + */ + override fun bindColorTexture(slot: Int) = super.bindColorTexture(slot) as ScreenFrameBuffer - override fun bindDepthTexture(slot: Int) = + /** + * Binds the depth texture to the specified slot by invoking the superclass method and casting the result + * to a ScreenFrameBuffer. + * + * @param slot the texture slot to which the depth texture is bound. + * @return the current ScreenFrameBuffer instance. + */ + override fun bindDepthTexture(slot: Int) = super.bindDepthTexture(slot) as ScreenFrameBuffer } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt index c8e590125..d0a4ca677 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt @@ -49,6 +49,19 @@ class PixelBuffer( 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 + /** + * Uploads pixel data from the provided buffer to the associated texture via the Pixel Buffer Object. + * + * This method binds the pixel buffer and texture, then transfers pixel data using glTexSubImage2D, + * covering the texture's entire dimensions and using an offset for asynchronous transfers. Depending + * on the bufferMapping flag, it either maps the buffer for a memory-based update or performs a direct update. + * After swapping internal buffers and unbinding, it returns any error encountered during the upload, + * or null if the upload was successful. + * + * @param data The ByteBuffer containing the pixel data to upload. + * @param offset The offset within the pixel buffer from which to start the data transfer. + * @return A Throwable if an error occurred during the upload, or null on success. + */ override fun upload( data: ByteBuffer, offset: Long, diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt index 700ff8a8f..567528c14 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt @@ -30,6 +30,15 @@ class ElementBuffer(mode: VertexMode) : override val target: Int = GL_ELEMENT_ARRAY_BUFFER override val access: Int = GL_MAP_WRITE_BIT + /** + * Uploads data to the buffer. + * + * Delegates to the allocation routine to upload the provided data and returns any error encountered during this process. + * + * @param data the ByteBuffer containing the data to be uploaded. + * @param offset the offset within the data (currently not used). + * @return a Throwable if an error occurs during allocation, or null if the upload succeeds. + */ override fun upload( data: ByteBuffer, offset: Long, diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt index 5362c6ad4..f2b2e4937 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt @@ -27,19 +27,50 @@ class VertexArray : Buffer(isVertexArray = true) { override val target: Int = -1 override val access: Int = -1 + /** + * Throws an UnsupportedOperationException to indicate that memory mapping is not supported for vertex array objects. + * + * @param size the intended size of the memory mapping. + * @param offset the intended offset in the buffer. + * @param block the callback to process the mapped ByteBuffer (unused). + * @throws UnsupportedOperationException always thrown to indicate that mapping is not supported. + */ override fun map( size: Long, offset: Long, block: (ByteBuffer) -> Unit ): Throwable = throw UnsupportedOperationException("Cannot map a vertex array object to memory") + /** + * Attempts to upload data to the vertex array object. + * + * This operation is not supported for vertex array objects and always throws an + * UnsupportedOperationException. + * + * @param data the buffer containing the data to upload (unused) + * @param offset the offset at which data would have been uploaded (unused) + * @throws UnsupportedOperationException always thrown to indicate that data uploads are not supported for vertex array objects. + */ override fun upload( data: ByteBuffer, offset: Long, ): Throwable = throw UnsupportedOperationException("Data cannot be uploaded to a vertex array object") - override fun allocate(size: Long) = throw UnsupportedOperationException("Cannot grow a vertex array object") + /** + * Throws an UnsupportedOperationException because vertex array objects cannot be resized. + * + * @param size The requested allocation size, which is ignored because growing a vertex array is unsupported. + * @throws UnsupportedOperationException always thrown to indicate the operation is not supported. + */ +override fun allocate(size: Long) = throw UnsupportedOperationException("Cannot grow a vertex array object") + /** + * Binds the vertex array object using the specified identifier. + * + * This method calls `glBindVertexArray` to bind the vertex array and resets the current vertex buffer binding. + * + * @param id the OpenGL identifier of the vertex array. + */ override fun bind(id: Int) { glBindVertexArray(id); BufferRenderer.currentVertexBuffer = null } diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt index 1d9ff8185..c1b9ff16d 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt @@ -32,6 +32,16 @@ class VertexBuffer( override val target: Int = GL_ARRAY_BUFFER override val access: Int = GL_MAP_WRITE_BIT + /** + * Uploads vertex data to the vertex buffer. + * + * This method allocates memory for the vertex buffer using the provided data. + * The offset parameter is not used in the current implementation. + * + * @param data the ByteBuffer containing the vertex data. + * @param offset the starting offset for the upload operation (currently ignored). + * @return a Throwable if an error occurs during allocation, otherwise null. + */ override fun upload( data: ByteBuffer, offset: Long, diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt index ddcefd46e..b5f4c2495 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt @@ -24,6 +24,15 @@ object GlStateUtils { private var blendState = false private var cullState = true + /** + * Temporarily configures OpenGL states for rendering, executes the provided block, and then restores the previous state. + * + * This function saves the current depth testing, blending, and face culling states. It then sets up OpenGL by disabling depth testing, + * enabling blending, disabling face culling, setting the depth mask to false, and enabling line smoothing before executing the provided block. + * After the block completes, it restores the original depth testing, blending, face culling, depth mask, and line smoothing settings. + * + * @param block OpenGL operations to perform under the temporary state configuration. + */ fun setupGL(block: () -> Unit) { val savedDepthTest = depthTestState val savedBlend = blendState @@ -46,6 +55,15 @@ object GlStateUtils { cull(savedCull) } + /** + * Enables depth testing and optionally configures the depth mask during the execution of a code block. + * + * When invoked, depth testing is enabled. If [maskWrite] is true, the depth buffer is made writable before the + * block is executed and set back to non-writable afterward. Finally, depth testing is disabled once the block finishes. + * + * @param maskWrite if true, enables writing to the depth buffer for the duration of the block. + * @param block the code to execute with the modified depth state. + */ fun withDepth(maskWrite: Boolean = false, block: () -> Unit) { depthTest(true) if (maskWrite) glDepthMask(true) @@ -54,6 +72,14 @@ object GlStateUtils { depthTest(false) } + /** + * Executes the provided block with face culling enabled. + * + * This function turns on face culling, executes the given block of code, and then disables face culling, + * ensuring that the OpenGL state is restored after the block execution. + * + * @param block the code to run with face culling enabled. + */ fun withFaceCulling(block: () -> Unit) { cull(true) block() diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt index 71189e7e5..95126a355 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt @@ -89,11 +89,13 @@ object Matrices { } /** - * Translates the current matrix by the given x, y, and z values. + * Applies a translation to the top matrix on the transformation stack. * - * @param x The translation amount along the X axis. - * @param y The translation amount along the Y axis. - * @param z The translation amount along the Z axis. Defaults to `0f`. + * The given offsets are used to modify the current transformation matrix in-place. + * + * @param x the translation offset along the X axis. + * @param y the translation offset along the Y axis. + * @param z the translation offset along the Z axis (default is 0f). */ fun translate(x: Float, y: Float, z: Float = 0f) { stack.last().translate(x, y, z) @@ -152,12 +154,13 @@ object Matrices { } /** - * Temporarily sets a vertex offset vector for the duration of a block. + * Applies a temporary vertex offset to mitigate precision issues in matrix operations on large coordinates. * - * Use this to avoid precision loss when using matrices while being on huge coordinates. + * The provided `offset` is set prior to executing the block and then reset to null, ensuring that the transformation + * is only applied during the block's execution. * - * @param offset The transformation offset to apply to vertices. - * @param block The block of code to execute with the transformation applied. + * @param offset the offset to apply to vertices for reducing precision loss. + * @param block the block of code within which the vertex offset is active. */ fun withVertexOffset(offset: Vec3d, block: () -> Unit) { vertexOffset = offset diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt index c99fe6d68..2e75cba3f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt @@ -113,4 +113,11 @@ val Long.kibibyte get() = this * 1024 val Long.mebibyte get() = this * 1024 * 1024 val Long.gibibyte get() = this * 1024 * 1024 * 1024 +/** + * Copies the contents of this ByteBuffer into the specified destination ByteBuffer. + * + * Data is transferred starting at the current positions of both buffers, and their positions are advanced by the number of bytes copied. + * + * @param dst the destination ByteBuffer to receive the copied bytes. + */ fun ByteBuffer.putTo(dst: ByteBuffer) { dst.put(this) } diff --git a/common/src/main/kotlin/com/lambda/graphics/pipeline/ScissorAdapter.kt b/common/src/main/kotlin/com/lambda/graphics/pipeline/ScissorAdapter.kt index be66c63a7..30c710d08 100644 --- a/common/src/main/kotlin/com/lambda/graphics/pipeline/ScissorAdapter.kt +++ b/common/src/main/kotlin/com/lambda/graphics/pipeline/ScissorAdapter.kt @@ -28,6 +28,17 @@ import com.mojang.blaze3d.systems.RenderSystem.enableScissor object ScissorAdapter { private var stack = ArrayDeque() + /** + * Restricts rendering operations to the specified scissor rectangle. + * + * This function clamps the provided rectangle to the boundaries of the current scissor area (if one exists), + * pushes the resulting rectangle onto an internal stack, and sets the scissor test to that area. It then + * executes the given lambda block within this constrained context. After the block completes, the function + * restores the previous scissor state or disables scissor testing if no prior rectangle exists. + * + * @param rect The area to which rendering should be confined. + * @param block A lambda with rendering instructions to execute within the scissor area. + */ fun scissor(rect: Rect, block: () -> Unit) { val processed = stack.lastOrNull()?.let(rect::clamp) ?: rect @@ -40,6 +51,15 @@ object ScissorAdapter { stack.lastOrNull()?.let { scissorRect(it) } ?: disableScissor() } + /** + * Configures the scissor test region using the provided rectangle. + * + * The function scales the rectangle by the current rendering scale factor, computes the screen-space coordinates, + * and adjusts the y-coordinate relative to the framebuffer height. It then enables the scissor test with the computed + * integer bounds. + * + * @param rect The rectangle defining the area to restrict rendering. + */ private fun scissorRect(rect: Rect) { val pos1 = rect.leftTop * RenderMain.scaleFactor val pos2 = rect.rightBottom * RenderMain.scaleFactor diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt index fb5c0d201..6087aa279 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ChunkedESP.kt @@ -117,6 +117,13 @@ class ChunkedESP private constructor( } } + /** + * Asynchronously rebuilds the ESP renderer for the current chunk. + * + * A new [StaticESPRenderer] instance is created on the main thread with a specific configuration, then updated using + * the owner's update function for every coordinate within the chunk. Finally, an upload task is queued that uploads + * the new renderer and sets it as the active renderer for the chunk. + */ suspend fun rebuild() { val newRenderer = awaitMainThread { StaticESPRenderer(false) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt index 3087e16c0..861cfc21d 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/ESPRenderer.kt @@ -45,6 +45,12 @@ open class ESPRenderer(tickedMode: Boolean) { outlines.upload() } + /** + * Renders the ESP effect. + * + * Activates the shader and updates its uniforms with the current tick delta and camera position, + * then applies face culling and a specific line width to render the faces and outlines. + */ fun render() { shader.use() shader["u_TickDelta"] = Lambda.mc.partialTicks @@ -54,6 +60,9 @@ open class ESPRenderer(tickedMode: Boolean) { GlStateUtils.withLineWidth(RenderSettings.outlineWidth, outlines::render) } + /** + * Clears the vertex data from both face and outline pipelines. + */ open fun clear() { faces.clear() outlines.clear() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGUIRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGUIRenderer.kt index 3bd9a33c0..219560650 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGUIRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGUIRenderer.kt @@ -35,6 +35,17 @@ abstract class AbstractGUIRenderer( ) { private val pipeline = VertexPipeline(VertexMode.TRIANGLES, attribGroup) + /** + * Renders GUI elements using the vertex pipeline and shader. + * + * This method clears the current vertex pipeline, activates the shader, and then executes the + * provided lambda [block] to customize vertex data. If [shade] is true, additional shader uniforms + * are configured for dynamic shading effects—such as time, colors, and size adjustments—for rendering. + * Finally, the pipeline data is uploaded to the GPU and rendered. + * + * @param shade Whether to apply shading effects (default is false). + * @param block A lambda that operates on the vertex pipeline to customize the rendering. + */ protected fun render( shade: Boolean = false, block: VertexPipeline.() -> Unit diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt index b8a736f64..5388fd4ac 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/TextureRenderer.kt @@ -34,6 +34,15 @@ object TextureRenderer { private val mainShader = shader("renderer/pos_tex") private val coloredShader = shader("renderer/pos_tex_shady") + /** + * Draws the specified texture within the given rectangular area. + * + * This function binds the provided texture and activates the main shader before rendering + * a quad that corresponds to the area defined by the rectangle. + * + * @param texture the texture to render. + * @param rect the area within which the texture should be drawn. + */ fun drawTexture(texture: Texture, rect: Rect) { texture.bind() mainShader.use() @@ -41,6 +50,17 @@ object TextureRenderer { drawInternal(rect) } + /** + * Renders a texture with a dynamic shading effect. + * + * Binds the given texture and activates a specialized shader that applies animated color shading. + * The shading parameters are determined using the current time (via glfwGetTime()) and the settings + * from GuiSettings, including color speed, primary and secondary colors, and a size factor calculated + * from the screen dimensions. The final rendering is delegated to the internal drawing routine. + * + * @param texture the texture to render with shading. + * @param rect the rectangular area defining where the texture should be drawn. + */ fun drawTextureShaded(texture: Texture, rect: Rect) { texture.bind() coloredShader.use() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index a3774186c..495ef53ce 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -46,14 +46,17 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/ private val gap get() = RenderSettings.gap * 0.5f - 0.8f /** - * Builds the vertex array for rendering the provided text string at a specified position. + * Renders a text string at a specified position with configurable color, scale, shadow, and emoji parsing. * - * @param text The text to render. - * @param position The position to render the text. - * @param color The color of the text. - * @param scale The scale factor of the text. - * @param shadow Whether to render a shadow for the text. - * @param parseEmoji Whether to parse and render emojis in the text. + * This function sets up shader parameters for font and emoji textures, binds the current font assets, + * and processes the text to generate glyph vertices for rendering. + * + * @param text the text string to render. + * @param position the position at which the text is drawn. + * @param color the color to use for the text. + * @param scale the scale factor for the text size. + * @param shadow if true, renders a shadow effect along with the text. + * @param parseEmoji if true, parses and renders emoji characters in the text. */ fun drawString( text: String, @@ -75,6 +78,18 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/ } } + /** + * Renders a single glyph at the specified position with the given scale and color. + * + * This function sets up shader parameters and binds the current font and emoji textures before + * computing the effective scale and adjusted positions based on the glyph’s dimensions and the current + * baseline offset. It then builds and renders the glyph’s quad. + * + * @param glyph the glyph information containing size and texture coordinates. + * @param position the rendering position where the glyph will be drawn. + * @param color the color applied to the glyph (default is [Color.WHITE]). + * @param scale the scale factor for the glyph (default is 1.0). + */ fun drawGlyph( glyph: GlyphInfo, position: Vec2d, @@ -99,13 +114,17 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/ } /** - * Renders a single glyph at a given position. + * Constructs and adds a quad for the specified glyph to the vertex pipeline. * - * @param glyph The glyph information to render. - * @param origin The position to start from - * @param pos1 The starting position of the glyph. - * @param pos2 The end position of the glyph - * @param color The color of the glyph. + * The quad's vertices are computed by offsetting the provided boundary positions (`pos1` and `pos2`) + * with the given `origin`. Each vertex is assigned texture coordinates derived from the glyph data and + * tinted with the specified color. + * + * @param glyph The glyph providing texture mapping coordinates. + * @param origin The positional offset applied to the glyph's vertices. + * @param pos1 One corner of the glyph's bounding rectangle. + * @param pos2 The diagonally opposite corner of the glyph's bounding rectangle. + * @param color The color to apply to the glyph. */ private fun VertexPipeline.buildGlyph( glyph: GlyphInfo, @@ -150,22 +169,35 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/ } /** - * Calculates the height of the text based on the specified scale. - * - * @param scale The scale factor for the height calculation. - * @return The height of the text at the specified scale. - */ + * Computes the effective height of the rendered text. + * + * The height is derived from the current font's base height, adjusted by a scaling factor + * that ensures consistent visual proportions. + * + * @param scale the scaling factor to apply (default is 1.0) + * @return the effective height of the text for the provided scale + */ fun getHeight(scale: Double = 1.0) = chars.height * getScaleFactor(scale) * 0.7 /** - * Iterates over each character and emoji in the text and applies a block operation. + * Processes a text string by iterating over its characters and emojis, computing rendering positions, and invoking a block for each glyph. * - * @param text The text to iterate over. - * @param color The color of the text. - * @param scale The scale of the text. - * @param shadow Whether to render a shadow. - * @param parseEmoji Whether to parse and include emojis. - * @param block The function to apply to each character or emoji glyph. + * The function calculates an adjusted scale factor and applies a gap between glyphs as well as a baseline offset for proper + * vertical alignment. For every glyph, if shadow rendering is enabled, it first invokes the block for a shadow glyph (using an offset) + * followed by the main glyph. It handles control characters (such as newlines) to adjust positioning, and when emoji parsing is enabled, + * it recursively splits the text to separately process emoji sequences and regular characters. + * + * @param text the string to process. + * @param color the base color used for rendering the glyphs. + * @param scale the scale factor applied to the text size. + * @param shadow if true, renders a shadow glyph before the main glyph. + * @param parseEmoji if true, detects and processes emoji sequences in the text. + * @param block a function that is invoked for each glyph. It receives: + * - GlyphInfo: the glyph information. + * - Vec2d: the starting position of the glyph. + * - Vec2d: the ending position of the glyph (computed from its size). + * - Color: the color to render the glyph. + * - Boolean: a flag indicating whether the glyph represents a shadow. */ private fun processText( text: String, @@ -184,6 +216,18 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/ var posX = 0.0 var posY = getHeight(scale) * -0.5 + baselineOffset * actualScale + /** + * Renders a glyph with the provided information and styling. + * + * If the glyph information is null, no rendering occurs. The function calculates the glyph's scaled size + * and computes its drawing coordinates based on the current position and scale factor. A non-zero offset + * indicates that the glyph is rendered as a shadow; in this case, the horizontal drawing position remains + * unchanged. Otherwise, the drawing position is advanced based on the glyph's width and a predefined gap. + * + * @param info The glyph information containing its size and other rendering details; if null, the glyph is not drawn. + * @param color The color applied to the glyph. + * @param offset An optional offset for positioning; a non-zero value flags the glyph as a shadow. + */ fun drawGlyph(info: GlyphInfo?, color: Color, offset: Double = 0.0) { if (info == null) return val isShadow = offset != 0.0 @@ -198,6 +242,16 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/ val parsed = if (parseEmoji) emojis.parse(text) else mutableListOf() + /** + * Processes a segment of text for rendering, handling regular characters and emojis. + * + * The function iterates over the given text section, drawing each glyph while managing control characters + * such as newlines and carriage returns to update the rendering position. When emojis are enabled and + * detected, it splits the text to render emoji characters separately, ensuring proper text layout. + * + * @param section The portion of text to be processed. + * @param hasEmojis Indicates whether the section may contain emojis that require special handling. + */ fun processTextSection(section: String, hasEmojis: Boolean) { if (section.isEmpty()) return if (!parseEmoji || parsed.isEmpty() || !hasEmojis) { @@ -243,11 +297,14 @@ object FontRenderer : AbstractGUIRenderer(VertexAttrib.Group.FONT, shader("font/ } /** - * Calculates the scale factor for the text based on the provided scale. - * - * @param scale The base scale factor. - * @return The adjusted scale factor. - */ + * Computes an adjusted scale factor for text rendering. + * + * This method applies a constant multiplier (8.5) to the base scale and normalizes it + * by the current text font's height, ensuring consistent text sizing across different fonts. + * + * @param scale the input base scale factor. + * @return the resulting adjusted scale factor. + */ fun getScaleFactor(scale: Double): Double = scale * 8.5 / chars.height /** diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaAtlas.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaAtlas.kt index 2eaf82985..732937e28 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaAtlas.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaAtlas.kt @@ -72,8 +72,26 @@ object LambdaAtlas : Loadable { private val metricCache = mutableMapOf() private val heightCache = Object2DoubleArrayMap() - operator fun LambdaFont.get(char: Char): GlyphInfo? = fontMap.getValue(this)[char] - operator fun LambdaEmoji.get(string: String): GlyphInfo? = emojiMap.getValue(this)[string.removeSurrounding(":")] + /** + * Retrieves the glyph information for the specified character for this font. + * + * Returns the [GlyphInfo] from the font's internal glyph map corresponding to the provided character, + * or null if no glyph information is available. + * + * @param char The character to look up. + * @return The glyph information associated with the character, or null if it is not found. + */ +operator fun LambdaFont.get(char: Char): GlyphInfo? = fontMap.getValue(this)[char] + /** + * Retrieves the glyph information for an emoji based on the provided identifier. + * + * The input string is expected to be wrapped in colons (e.g., ":smile:"); the surrounding colons + * are removed before looking up the glyph in the emoji map. + * + * @param string the emoji identifier, potentially enclosed in colons. + * @return the corresponding glyph information, or null if it is not found. + */ +operator fun LambdaEmoji.get(string: String): GlyphInfo? = emojiMap.getValue(this)[string.removeSurrounding(":")] val LambdaFont.height: Double get() = heightCache.getDouble(fontCache[this@height]) @@ -147,6 +165,19 @@ object LambdaAtlas : Loadable { bufferPool[this@buildBuffer] = image } + /** + * Builds a texture atlas by rendering multiple glyphs from the font. + * + * The function loads the font (or retrieves it from a cache), calculates a texture image based on the given number + * of characters, and draws each glyph onto the image. It computes the UV coordinates for each character and stores + * the associated glyph metadata, while also updating a shared buffer pool with the generated image. An exception is + * thrown if the texture atlas is too small to accommodate all glyphs. + * + * @param characters The total number of characters to render into the atlas (default is 2048). This value also influences + * the calculated texture size. + * + * @throws IllegalStateException if the texture atlas is not large enough to fit the glyphs. + */ fun LambdaFont.buildBuffer( characters: Int = 2048 // How many characters from that font should be used for the generation ) { @@ -198,7 +229,15 @@ object LambdaAtlas : Loadable { bufferPool[this@buildBuffer] = image } - // TODO: Change this when we've refactored the loadables + /** + * Loads all font and emoji entries, schedules texture uploads, and returns a summary message. + * + * Iterates over all registered fonts and emojis to initialize their underlying resources, prepares a summary count based on + * the current buffer pool size, and schedules the upload of buffered images to their respective owners. The buffer pool is cleared + * after the upload to prevent race conditions. + * + * @return A summary string indicating the number of fonts loaded. + */ override fun load(): String { LambdaFont.entries.forEach(LambdaFont::load) LambdaEmoji.entries.forEach(LambdaEmoji::load) @@ -213,6 +252,17 @@ object LambdaAtlas : Loadable { return str } + /** + * Renders the specified character as a buffered image using the provided font. + * + * If the font cannot display the character, the function returns null. Otherwise, it retrieves or + * computes the font metrics to determine the image dimensions, creates a buffered image, applies + * anti-aliasing and default rendering hints, and draws the character in white. + * + * @param font the font used for rendering the character. + * @param codePoint the character to render. + * @return a BufferedImage with the rendered character, or null if the font cannot display it. + */ private fun getCharImage(font: Font, codePoint: Char): BufferedImage? { if (!font.canDisplay(codePoint)) return null diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaEmoji.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaEmoji.kt index 0175f2156..a450e028a 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaEmoji.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaEmoji.kt @@ -25,15 +25,25 @@ enum class LambdaEmoji(val url: String) { private val emojiRegex = Regex(":[a-zA-Z0-9_]+:") /** - * Parses the emojis in the given text. - * - * @param text The text to parse. - * - * @return A list of parsed strings that does not contain the colons - */ + * Extracts emoji names from the provided text. + * + * The function scans the input text for patterns matching emojis in the `:name:` format and returns a mutable list + * of the emoji names with the surrounding colons removed. + * + * @param text the input text containing potential emoji patterns + * @return a mutable list of extracted emoji names + */ fun parse(text: String): MutableList = emojiRegex.findAll(text).map { it.value }.toMutableList() + /** + * Triggers the build of buffers for all available emoji sets. + * + * Iterates through each entry in the enum, calling its buffer-building function, and returns + * a message indicating the total number of emoji sets processed. + * + * @return a string reporting the number of loaded emoji sets. + */ fun load(): String { entries.forEach { it.buildBuffer() } return "Loaded ${entries.size} emoji sets" diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaFont.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaFont.kt index f6d3ed01f..387ae4343 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaFont.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/core/LambdaFont.kt @@ -23,6 +23,14 @@ enum class LambdaFont(val fontName: String) { FiraSansRegular("FiraSans-Regular"), FiraSansBold("FiraSans-Bold"); + /** + * Loads all font entries by triggering their buffer-building routines and returns a summary message. + * + * Iterates through each font in the enumeration, invoking its [buildBuffer] method, and returns a string + * indicating the number of fonts loaded, formatted as "Loaded X fonts" where X is the total number. + * + * @return a string summarizing the count of loaded fonts. + */ fun load(): String { entries.forEach { it.buildBuffer() } return "Loaded ${entries.size} fonts" diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt index 1f8eaf5e0..85b59ec5f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/sdf/DistanceFieldTexture.kt @@ -56,6 +56,13 @@ class DistanceFieldTexture(image: BufferedImage) : Texture(image, levels = 0) { } } + /** + * Binds the SDF texture's underlying frame to the specified texture slot. + * + * This ensures that the distance field texture is activated in the given slot for rendering. + * + * @param slot the texture slot index to bind the frame to. + */ override fun bind(slot: Int) { frame.bind(slot) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt index a4178b69b..013820cd2 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/FilledRectRenderer.kt @@ -30,6 +30,17 @@ object FilledRectRenderer : AbstractGUIRenderer( private const val MIN_SIZE = 0.5 private const val MIN_ALPHA = 3 + /** + * Renders a filled rectangle with uniformly rounded corners and a single fill color. + * + * This overload delegates to the more detailed version by applying the same color to all corners and using + * a uniform rounding radius. When shading is enabled, an additional shading effect is applied. + * + * @param rect the rectangle defining the position and dimensions. + * @param roundRadius the uniform radius for rounding each corner; defaults to 0.0. + * @param color the fill color for the rectangle, applied to all corners; defaults to Color.WHITE. + * @param shade if true, applies a shading effect; defaults to false. + */ fun filledRect( rect: Rect, roundRadius: Double = 0.0, @@ -37,6 +48,20 @@ object FilledRectRenderer : AbstractGUIRenderer( shade: Boolean = false, ) = filledRect(rect, roundRadius, color, color, color, color, shade) + /** + * Renders a filled rectangle with rounded corners and individual corner colors. + * + * This overload applies a uniform rounding radius to all corners and delegates to the + * implementation that supports distinct radii for each corner. + * + * @param rect the boundaries of the rectangle. + * @param roundRadius the uniform rounding radius applied to each corner. + * @param leftTop the color at the top-left corner. + * @param rightTop the color at the top-right corner. + * @param rightBottom the color at the bottom-right corner. + * @param leftBottom the color at the bottom-left corner. + * @param shade if true, shading is applied to the rectangle. + */ fun filledRect( rect: Rect, roundRadius: Double = 0.0, @@ -52,6 +77,20 @@ object FilledRectRenderer : AbstractGUIRenderer( shade ) + /** + * Renders a filled rectangle with customizable corner rounding and a uniform fill color. + * + * This overload simplifies rendering when the same color is applied uniformly to all corners. + * It delegates to the more detailed version that accepts separate colors for each corner. + * + * @param rect The rectangle defining the position and dimensions. + * @param leftTopRadius The rounding radius of the top-left corner. + * @param rightTopRadius The rounding radius of the top-right corner. + * @param rightBottomRadius The rounding radius of the bottom-right corner. + * @param leftBottomRadius The rounding radius of the bottom-left corner. + * @param color The color used to fill the rectangle. + * @param shade If true, enables shading during rendering. + */ fun filledRect( rect: Rect, leftTopRadius: Double = 0.0, @@ -67,6 +106,25 @@ object FilledRectRenderer : AbstractGUIRenderer( shade ) + /** + * Renders a filled rectangle with customizable corner radii and individual corner colors. + * + * The function calculates the rectangle's size from its top-left and bottom-right coordinates, + * and performs early exits if the rectangle is too small or if all corner colors are nearly transparent. + * Each provided corner radius is clamped to ensure it does not exceed half the rectangle's dimensions. + * If shading is enabled, a shading effect is applied during rendering. + * + * @param rect The rectangle defining the position and dimensions. + * @param leftTopRadius The rounding radius for the top-left corner. + * @param rightTopRadius The rounding radius for the top-right corner. + * @param rightBottomRadius The rounding radius for the bottom-right corner. + * @param leftBottomRadius The rounding radius for the bottom-left corner. + * @param leftTop The color for the top-left corner. + * @param rightTop The color for the top-right corner. + * @param rightBottom The color for the bottom-right corner. + * @param leftBottom The color for the bottom-left corner. + * @param shade If true, applies a shading effect to the rendered rectangle. + */ fun filledRect( rect: Rect, leftTopRadius: Double = 0.0, diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt index 127cacc98..9aad270ce 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/OutlineRectRenderer.kt @@ -40,6 +40,17 @@ object OutlineRectRenderer : AbstractGUIRenderer( private const val QUALITY = 8 private const val VERTICES_COUNT = QUALITY * 4 + /** + * Renders an outlined rectangle with optional rounded corners and glow effect using a uniform color. + * + * This overload applies the same color to every corner by delegating to the detailed outlineRect function. + * + * @param rect the rectangle defining the area for the outline. + * @param roundRadius the radius used to round the rectangle's corners (defaults to 0.0 for sharp corners). + * @param glowRadius the radius defining the glow effect; if below 1, rendering is skipped. + * @param color the color applied uniformly to all corners. + * @param shade if true, applies a shading effect during rendering. + */ fun outlineRect( rect: Rect, roundRadius: Double = 0.0, @@ -48,6 +59,22 @@ object OutlineRectRenderer : AbstractGUIRenderer( shade: Boolean = false, ) = outlineRect(rect, roundRadius, glowRadius, color, color, color, color, shade) + /** + * Renders an outlined rectangle with optional rounded corners and glow. + * + * The function computes the vertex data for the rectangle's outline—applying rounded + * corners and a glow effect if specified—and renders quads to form both the outline and + * its glow. Rendering is skipped entirely if the glow radius is below 1. + * + * @param rect The base rectangle for the outline. + * @param roundRadius The radius for rounded corners; if zero, the corners remain sharp. + * @param glowRadius The thickness of the glow effect; values below 1 disable rendering. + * @param leftTop Color for the top-left corner. + * @param rightTop Color for the top-right corner. + * @param rightBottom Color for the bottom-right corner. + * @param leftBottom Color for the bottom-left corner. + * @param shade When true, applies a shading effect during rendering. + */ fun outlineRect( rect: Rect, roundRadius: Double = 0.0, diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt index c5b718a4d..17a2b07a8 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt @@ -37,12 +37,28 @@ class Shader private constructor(fragmentPath: String, vertexPath: String) { loadShader(ShaderType.FRAGMENT_SHADER, "shaders/fragment/$fragmentPath.frag") ) + /** + * Activates the shader program and updates the "u_ProjModel" uniform. + * + * This function sets the active OpenGL program to this shader and ensures that the + * current projection model matrix from RenderMain is applied by updating the corresponding uniform. + */ fun use() { glUseProgram(id) set("u_ProjModel", RenderMain.projModel) } - private fun loc(name: String) = + /** + * Retrieves the location of the specified uniform variable. + * + * This function checks a cache for the uniform location. If the uniform location is already cached, + * it returns the cached value. Otherwise, it queries OpenGL for the location using `glGetUniformLocation`, + * caches the result, and then returns it. + * + * @param name the name of the uniform variable. + * @return the location of the uniform variable. + */ + private fun loc(name: String) = if (uniformCache.containsKey(name)) uniformCache.getInt(name) else @@ -75,16 +91,43 @@ class Shader private constructor(fragmentPath: String, vertexPath: String) { color.alpha / 255f ) - operator fun set(name: String, mat: Matrix4f) = + /** + * Assigns a 4x4 matrix value to a uniform variable in the shader program. + * + * This operator overload uses the uniform variable name to locate its position + * within the shader and updates it with the provided matrix data. + * + * @param name the name of the uniform variable. + * @param mat the 4x4 matrix to assign to the uniform. + */ + operator fun set(name: String, mat: Matrix4f) = uniformMatrix(loc(name), mat) companion object { private val shaderCache = hashMapOf, Shader>() - fun shader(path: String) = + /** + * Retrieves or creates a shader instance using a single shader path. + * + * This function returns a [Shader] by applying the provided file path for both the vertex and fragment shader sources. + * + * @param path the file path for the shader sources used for both vertex and fragment shaders. + * @return the corresponding [Shader] instance. + */ + fun shader(path: String) = shader(path, path) - fun shader(fragmentPath: String, vertexPath: String) = + /** + * Retrieves a Shader instance for the specified fragment and vertex shader paths. + * + * This function checks the cache for an existing Shader corresponding to the given paths. If none is found, + * it creates a new Shader instance, caches it, and returns the instance. + * + * @param fragmentPath The file path to the fragment shader. + * @param vertexPath The file path to the vertex shader. + * @return The Shader instance associated with the provided shader paths. + */ + fun shader(fragmentPath: String, vertexPath: String) = shaderCache.getOrPut(fragmentPath to vertexPath) { Shader(fragmentPath, vertexPath) } diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt index 6cfda3c32..edbbad3fc 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt @@ -30,6 +30,18 @@ object ShaderUtils { private val matrixBuffer = BufferUtils.createFloatBuffer(4 * 4) private const val shaderInfoLogLength = 512 + /** + * Loads and compiles an OpenGL shader using the source from the specified resource. + * + * This function creates a shader object based on the provided shader type, reads the shader source code from + * the given LambdaResource using UTF-8 encoding, and attaches and compiles the shader source. If compilation fails, + * a RuntimeException is thrown with detailed error information. + * + * @param type The shader type defining the OpenGL shader to create. + * @param resource The LambdaResource containing the shader source code. + * @return The OpenGL handle for the successfully compiled shader. + * @throws RuntimeException If the shader compilation fails. + */ fun loadShader(type: ShaderType, resource: LambdaResource): Int { // Create new shader object val shader = glCreateShader(type.gl) @@ -53,6 +65,18 @@ object ShaderUtils { return shader } + /** + * Creates a new OpenGL shader program by linking the provided shader objects. + * + * This function creates a new shader program, attaches all supplied shader IDs, + * and attempts to link them together. If linking fails, it throws a RuntimeException + * containing detailed error information. On successful linking, all shader objects + * are deleted. + * + * @param shaders one or more shader IDs to be linked into the program. + * @return the ID of the linked shader program. + * @throws RuntimeException if the shader program fails to link. + */ fun createShaderProgram(vararg shaders: Int): Int { // Create new shader program val program = glCreateProgram() @@ -73,6 +97,16 @@ object ShaderUtils { return program } + /** + * Compiles the specified OpenGL shader. + * + * This function compiles the shader identified by the given ID and checks its compilation status. + * It returns null when compilation is successful; if the compilation fails, it returns the shader's + * error log (limited to a predefined length). + * + * @param shader the ID of the shader to compile. + * @return null if the shader compiles successfully, or a string containing the error log if compilation fails. + */ private fun compileShader(shader: Int): String? { glCompileShader(shader) val status = glGetShaderi(shader, GL_COMPILE_STATUS) @@ -81,6 +115,17 @@ object ShaderUtils { else glGetShaderInfoLog(shader, shaderInfoLogLength) } + /** + * Attaches the specified shaders to the shader program and links it. + * + * This function iterates over the provided shader IDs and attaches each to the given program. + * It then links the program and checks the linking status. If linking is successful, it returns null; + * otherwise, it returns the error log from the linking process. + * + * @param program the OpenGL shader program identifier. + * @param shaders an array of shader identifiers to attach and link. + * @return null if linking succeeds; otherwise, the linking error log. + */ private fun linkProgram(program: Int, shaders: IntArray): String? { shaders.forEach { glAttachShader(program, it) diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt b/common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt index 6229c2c17..938194f08 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt @@ -39,11 +39,26 @@ class AnimatedTexture(path: LambdaResource) : Texture(image = null) { private var currentFrame = 0 private var lastUpload = 0L + /** + * Binds the animated texture to the specified texture slot. + * + * This method updates the texture's current frame via [update] before binding it, + * ensuring that the most recent frame is rendered. + * + * @param slot the texture unit to which the texture is bound. + */ override fun bind(slot: Int) { update() super.bind(slot) } + /** + * Updates the animated texture by advancing to the next frame if its display duration has elapsed. + * + * This function checks if the elapsed time since the last frame upload meets or exceeds the current frame's duration. + * When the condition is met, it slices the GIF data to obtain the current frame's byte block, uploads it to the pixel buffer, + * resets the buffer, advances the frame counter (wrapping around at the end), and updates the timestamp for the last upload. + */ fun update() { if (System.currentTimeMillis() - lastUpload >= frameDurations[currentFrame]) { // This is cool because instead of having a buffer for each frame we can diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt index 5ac12882a..081c92c07 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt @@ -75,25 +75,35 @@ open class Texture { var height = -1; protected set /** - * Binds the texture to a specific slot in the graphics pipeline. + * Binds this texture to the specified slot in the graphics pipeline. + * + * @param slot The slot to bind the texture to. Defaults to 0. */ open fun bind(slot: Int = 0) { bindTexture(id, slot) } /** - * Unbinds the currently bound texture + * Unbinds any texture from the specified slot. + * + * This method removes the current texture binding on the given slot by binding a texture ID of 0. + * If no slot is explicitly provided, the operation defaults to slot 0. + * + * @param slot The slot index from which to unbind the texture. */ open fun unbind(slot: Int = 0) { bindTexture(0, slot) } /** - * Uploads an image to the texture and generates mipmaps for the texture if applicable - * This function does not bind the texture + * Uploads the given image data to the texture at the specified mipmap level. + * + * This function updates the texture's dimensions to match the image, marks it as initialized, and + * configures level-of-detail parameters before setting texture filtering options. If mipmapping is enabled, + * mipmaps are automatically generated. Note that the texture must be bound before calling this function. * - * @param image The image to upload to the texture - * @param offset The mipmap level to upload the image to + * @param image the BufferedImage containing the texture data. + * @param offset the mipmap level where the image data is applied (typically 0 for the base level). */ fun upload(image: BufferedImage, offset: Int = 0) { // Store level_base +1 through `level` images and generate @@ -111,13 +121,17 @@ open class Texture { } /** - * Uploads an image to the texture and generates mipmaps for the texture if applicable - * This function does not bind the texture + * Uploads image data from a ByteBuffer to the texture. * - * @param buffer The image buffer to upload to the texture - * @param width The width of the texture - * @param height The height of the texture - * @param offset The mipmap level to upload the image to + * This method updates the texture's dimensions, configures Level of Detail (LOD) parameters, + * and marks the texture as initialized. The image data is uploaded at the specified mipmap level, + * and if mipmap generation is enabled (i.e. when more than zero levels are configured), it triggers + * the creation of the full mipmap chain. Note that the texture is assumed to be bound before calling this method. + * + * @param buffer The image data to be uploaded. + * @param width The width of the texture image. + * @param height The height of the texture image. + * @param offset The mipmap level where the image data will be applied, typically 0 for the base level. */ fun upload(buffer: ByteBuffer, width: Int, height: Int, offset: Int = 0) { // Store level_base +1 through `level` images and generate @@ -135,13 +149,18 @@ open class Texture { } /** - * Updates the data of a texture - * This function does not bind the texture + * Updates the content of an existing texture with new image data. + * + * If the texture is not yet initialized, this method delegates to [upload] to set up + * the texture data. When the texture is already initialized, it updates the texture + * at the specified mipmap level. Note that this method does not bind the texture; + * it assumes the texture is already bound. * - * @param image The image to upload to the texture - * @param offset The mipmap level to upload the image to + * @param image The BufferedImage containing the new texture data. + * @param offset The mipmap level to update (default is 0). * - * @throws IllegalStateException If the texture has the consistency flag and is already initialized + * @throws IllegalStateException if the provided image data's dimensions are incompatible + * with the texture's existing dimensions. */ fun update(image: BufferedImage, offset: Int = 0) { if (!initialized) return upload(image, offset) @@ -151,15 +170,18 @@ open class Texture { } /** - * Updates the data of a texture - * This function does not bind the texture + * Updates the texture content with new image data from the provided ByteBuffer. + * + * If the texture is not yet initialized, this function delegates to the texture upload routine. + * For an already initialized texture, it checks that the new dimensions do not exceed the original ones + * and updates the specified mipmap level without binding the texture. * - * @param buffer The image buffer to upload to the texture - * @param width The width of the texture - * @param height The height of the texture - * @param offset The mipmap level to upload the image to + * @param buffer The ByteBuffer containing the new texture data. + * @param width The width of the new image data. + * @param height The height of the new image data. + * @param offset The mipmap level at which to update the texture. * - * @throws IllegalStateException If the texture has the consistency flag and is already initialized + * @throws IllegalStateException if the provided dimensions exceed those of the initialized texture. */ fun update(buffer: ByteBuffer, width: Int, height: Int, offset: Int = 0) { if (!initialized) return upload(buffer, width, height, offset) @@ -168,6 +190,17 @@ open class Texture { glTexSubImage2D(GL_TEXTURE_2D, offset, 0, 0, width, height, format, GL_UNSIGNED_BYTE, buffer) } + /** + * Configures the level-of-detail (LOD) parameters for the texture. + * + * This method sets the minimum and maximum LOD values, as well as the base and maximum mipmap levels, + * using the specified total number of mipmap levels. The [levels] parameter includes the base level (level 0), + * so the highest valid mipmap level is [levels] - 1. + * + * Note: This configuration may not work correctly with textures using immutable storage. + * + * @param levels The total number of mipmap levels, including the base level. + */ private fun setupLOD(levels: Int) { // When you call glTextureStorage, you're specifying the total number of levels, including level 0 // This is a 0-based index system, which means that the maximum mipmap level is n-1 @@ -180,7 +213,18 @@ open class Texture { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels) } - private fun checkDimensions(width: Int, height: Int) = + /** + * Validates that the dimensions of the new texture data do not exceed the allowed total dimensions. + * + * This method checks that the sum of the provided width and height does not exceed the sum of the texture's + * current width and height, and ensures that the texture has been initialized. If the condition fails, it + * throws an IllegalStateException with details about the expected and received total dimensions. + * + * @param width The width of the new texture data. + * @param height The height of the new texture data. + * @throws IllegalStateException if the texture is uninitialized or if the new data's dimensions exceed the allowed limits. + */ + private fun checkDimensions(width: Int, height: Int) = check(width + height <= this.width + this.height && initialized) { "Client tried to update a texture with more data than allowed\n" + "Expected ${this.width + this.height} bytes but got ${width + height}" diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt b/common/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt index 5ace20405..677012bab 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/TextureOwner.kt @@ -34,11 +34,13 @@ object TextureOwner { get() = textureMap.getValue(this@texture)[0] /** - * Retrieves a specific texture owned by the object by its index - * - * @param index The index of the texture to retrieve - * @return The texture [T] at the given index - */ + * Retrieves the texture associated with the receiver object at the specified index. + * + * The returned texture is cast to the provided generic type [T], allowing for type-safe access. + * + * @param index the position of the texture in the object's associated texture list. + * @return the texture at the specified index, cast as type [T]. + */ @Suppress("unchecked_cast") fun Any.texture(index: Int) = textureMap.getValue(this@texture)[index] as T @@ -57,11 +59,12 @@ object TextureOwner { } /** - * Binds a list of textures to texture slots, ensuring no more than 32 textures - * are bound at once (to fit within the typical GPU limitations) + * Binds the provided textures to consecutive GPU slots starting at slot 0. + * + * Ensures that no more than 32 textures are bound at once to comply with GPU limitations. * - * @param textures The list of textures to be bound - * @throws IllegalArgumentException If more than 32 textures are provided + * @param textures the textures to be bound + * @throws IllegalArgumentException if more than 32 textures are provided */ fun bind(vararg textures: Texture) { check(textures.size < 33) { "Texture slot overflow, expected to use less than 33 slots, got ${textures.size} slots" } @@ -92,21 +95,23 @@ object TextureOwner { Texture(path.readImage(), levels = mipmaps).also { 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 - */ + * Uploads a distance field texture from the provided image data and associates it with the calling object. + * + * Distance field textures are typically used for rendering high-quality fonts. The created texture is + * added to the object's texture list maintained in the texture map. + * + * @param data The image data used to create the distance field texture. + * @return The newly created distance field texture. + */ 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 - * - * @param path The resource path to the GIF file - * @return The created animated texture object - */ + * Uploads an animated GIF as a texture and associates it with the calling object's texture list. + * + * @param path The resource path of the GIF file. + * @return The animated texture instance that was created. + */ fun Any.uploadGif(path: String) = AnimatedTexture(path).also { textureMap.computeIfAbsent(this@uploadGif) { mutableListOf() }.add(it) } } diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt index fada950a8..32c0daf9d 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt @@ -33,11 +33,29 @@ object TextureUtils { .withCompressionLevel(COMPRESSION_LEVEL) .withMultiThreadedCompressionEnabled(THREADED_COMPRESSION) + /** + * Binds a texture to a specified texture slot. + * + * Activates the texture unit corresponding to GL_TEXTURE0 plus the slot offset, then binds the texture identified by the provided texture ID. + * + * @param id the identifier of the texture to bind. + * @param slot the texture slot index (default is 0). + */ fun bindTexture(id: Int, slot: Int = 0) { RenderSystem.activeTexture(GL_TEXTURE0 + slot) RenderSystem.bindTexture(id) } + /** + * Configures the active texture's sampling and pixel storage parameters. + * + * This function sets the texture's minification and magnification filters using the provided values, + * clamps the S and T texture coordinates to the edge, and resets the pixel unpacking parameters to their + * default state. This ensures proper texture sampling and data alignment when uploading image data. + * + * @param minFilter the OpenGL filter to apply for texture minification. + * @param magFilter the OpenGL filter to apply for texture magnification. + */ fun setupTexture(minFilter: Int, magFilter: Int) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter) @@ -50,6 +68,16 @@ object TextureUtils { glPixelStorei(GL_UNPACK_ALIGNMENT, 4) } + /** + * Converts a BufferedImage to a native image pointer. + * + * This function encodes the given [bufferedImage] using a PNG encoder preset, transfers the encoded data into a ByteBuffer, + * and then delegates to the ByteBuffer-based image reader with the specified [format]. It returns a pointer to the native image. + * + * @param bufferedImage the image to be converted. + * @param format the target format for the native image. + * @return a pointer to the native image. + */ fun readImage( bufferedImage: BufferedImage, format: NativeImage.Format, @@ -66,6 +94,16 @@ object TextureUtils { return readImage(buffer, format) } + /** + * Reads image data from a ByteBuffer using a specified format and returns a pointer to the resulting native image. + * + * This function interprets the raw image data in the provided ByteBuffer according to the given format, + * converts it into a NativeImage, and then returns the pointer to the native image data. + * + * @param image A ByteBuffer containing raw image data. + * @param format The format used to decode the image data. + * @return A pointer to the native image as a Long. + */ fun readImage( image: ByteBuffer, format: NativeImage.Format, diff --git a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt index fc5feea18..e15bb1727 100644 --- a/common/src/main/kotlin/com/lambda/gui/GuiManager.kt +++ b/common/src/main/kotlin/com/lambda/gui/GuiManager.kt @@ -27,10 +27,28 @@ import kotlin.reflect.KClass object GuiManager : Loadable { val typeMap = mutableMapOf, (owner: Layout, converted: Any) -> Layout>() + /** + * Registers a layout conversion adapter for the specified type T. + * + * The provided lambda is stored in a mapping with the key set as the reified type's KClass, + * enabling type-specific conversions of a Layout. The lambda casts the given instance to T + * and applies the conversion. + * + * @param block A conversion lambda that accepts a Layout and an instance of type T, returning a modified Layout. + */ private inline fun typeAdapter(noinline block: (Layout, T) -> Layout) { typeMap[T::class] = { owner, converted -> block(owner, converted as T) } } + /** + * Loads and registers built-in GUI type adapters. + * + * This method overrides the load function from the Loadable interface to register a BooleanSetting + * adapter. The adapter maps a Layout to a conversion function that applies the booleanSetting method. + * It returns a message indicating the total number of loaded GUI type adapters. + * + * @return a status message with the count of loaded GUI type adapters. + */ override fun load(): String { typeAdapter { owner, ref -> owner.booleanSetting(ref) @@ -40,8 +58,16 @@ object GuiManager : Loadable { } /** - * Attempts to convert the given [reference] to the [Layout] - */ + * Converts the provided [reference] into a [Layout] using a registered type adapter. + * + * This extension function checks the runtime type of [reference] against a registry of layout converters. + * If an appropriate adapter is found, it is invoked with the current layout and [reference], followed by + * applying the optional [block] for further configuration. + * + * @param reference the object used to determine the conversion adapter based on its runtime type. + * @param block an optional lambda for further configuring the converted [Layout]. + * @return the converted [Layout] if an adapter for the reference's type exists; otherwise, `null`. + */ @UIBuilder inline fun Layout.layoutOf( reference: Any, diff --git a/common/src/main/kotlin/com/lambda/gui/LambdaScreen.kt b/common/src/main/kotlin/com/lambda/gui/LambdaScreen.kt index cf9a58d83..f4376f63a 100644 --- a/common/src/main/kotlin/com/lambda/gui/LambdaScreen.kt +++ b/common/src/main/kotlin/com/lambda/gui/LambdaScreen.kt @@ -57,6 +57,11 @@ class LambdaScreen( } } + /** + * Activates this screen. + * + * Closes any currently open screen and schedules a render call to set this instance as the active GUI. + */ fun show() { mc.currentScreen?.close() @@ -65,20 +70,55 @@ class LambdaScreen( } } + /** + * Called when the screen becomes visible. + * + * Triggers the layout's show event to notify that the GUI has been displayed. + */ override fun onDisplayed() { layout.onEvent(GuiEvent.Show) } + /** + * Signals that the screen is being removed. + * + * Triggers a [GuiEvent.Hide] event in the layout system to handle any necessary cleanup or state updates when the screen is dismissed. + */ override fun removed() { layout.onEvent(GuiEvent.Hide) } - override fun shouldPause() = false - + /** + * Determines whether the game should pause when this screen is active. + * + * Always returns false so that gameplay continues uninterrupted. + * + * @return false + */ +override fun shouldPause() = false + + /** + * Renders the screen without applying the default background tint. + * + * This override removes the background tint to ensure the custom layout is displayed as intended. + * + * @param context the drawing context used for rendering. + * @param mouseX the current mouse x-coordinate. + * @param mouseY the current mouse y-coordinate. + * @param delta the time elapsed since the last frame. + */ override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, delta: Float) { // Let's remove background tint } + /** + * Processes key press events by mapping the input key codes and dispatching them to the GUI layout. + * + * This method translates the provided keyCode and scanCode into a virtual key code using a US keyboard mapping, + * then triggers a corresponding key press event on the layout. When the escape key is pressed, it closes the screen. + * + * @return true, indicating the event was handled. + */ override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { val translated = KeyCode.virtualMapUS(keyCode, scanCode) layout.onEvent(GuiEvent.KeyPress(translated)) @@ -90,25 +130,66 @@ class LambdaScreen( return true } + /** + * Processes a character typing event by dispatching it to the layout. + * + * @param chr the character that was typed. + * @param modifiers modifier keys active during the event; currently not used. + * @return always true, indicating the event was handled. + */ override fun charTyped(chr: Char, modifiers: Int): Boolean { layout.onEvent(GuiEvent.CharTyped(chr)) return true } + /** + * Handles mouse click events by converting the click coordinates to the screen's coordinate system and dispatching a click event to the layout. + * + * @return true, indicating that the event was handled. + */ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { layout.onEvent(GuiEvent.MouseClick(Mouse.Button.fromMouseCode(button), Mouse.Action.Click, rescaleMouse(mouseX, mouseY))) return true } + /** + * Handles mouse release events by dispatching a corresponding event to the layout system. + * + * This method rescales the provided mouse coordinates and converts the button code to trigger a mouse click event + * with a release action. + * + * @param mouseX the horizontal coordinate of the mouse pointer + * @param mouseY the vertical coordinate of the mouse pointer + * @param button the code of the mouse button that was released + * @return true indicating the event was handled + */ override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { layout.onEvent(GuiEvent.MouseClick(Mouse.Button.fromMouseCode(button), Mouse.Action.Release, rescaleMouse(mouseX, mouseY))) return true } + /** + * Handles mouse movement events by rescaling window coordinates and dispatching them to the layout. + * + * The method converts the given mouse coordinates from window space to the screen's coordinate system + * and then triggers a corresponding mouse move event in the layout system. + * + * @param mouseX the horizontal position of the mouse cursor in window coordinates. + * @param mouseY the vertical position of the mouse cursor in window coordinates. + */ override fun mouseMoved(mouseX: Double, mouseY: Double) { layout.onEvent(GuiEvent.MouseMove(rescaleMouse(mouseX, mouseY))) } + /** + * Processes a mouse scroll event by rescaling the mouse coordinates and dispatching a vertical scroll event to the layout. + * + * @param mouseX The x-coordinate of the mouse in window space. + * @param mouseY The y-coordinate of the mouse in window space. + * @param horizontalAmount The horizontal scroll amount; this value is currently ignored. + * @param verticalAmount The vertical scroll delta. + * @return Always returns true to indicate the event was handled. + */ override fun mouseScrolled( mouseX: Double, mouseY: Double, @@ -119,6 +200,16 @@ class LambdaScreen( return true } + /** + * Rescales the provided mouse coordinates from the window's coordinate system to the screen's coordinate system. + * + * The function normalizes the mouse position by dividing it by the window dimensions, then scales + * it using the current screen size. + * + * @param mouseX the x-coordinate of the mouse in window space. + * @param mouseY the y-coordinate of the mouse in window space. + * @return the rescaled mouse position as a Vec2d in the screen's coordinate system. + */ private fun rescaleMouse(mouseX: Double, mouseY: Double): Vec2d { val mcMouse = Vec2d(mouseX, mouseY) val mcWindow = Vec2d(mc.window.scaledWidth, mc.window.scaledHeight) diff --git a/common/src/main/kotlin/com/lambda/gui/ScreenLayout.kt b/common/src/main/kotlin/com/lambda/gui/ScreenLayout.kt index 07c642ebc..72a5dc8d0 100644 --- a/common/src/main/kotlin/com/lambda/gui/ScreenLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/ScreenLayout.kt @@ -30,8 +30,15 @@ class ScreenLayout : Layout(owner = null) { companion object { /** - * Creates gui layout - */ + * Creates a new GUI layout screen. + * + * This function instantiates a new ScreenLayout, applies the provided configuration block to it, + * and returns a LambdaScreen identified by the supplied name. + * + * @param name a unique identifier for the screen + * @param block a configuration block that customizes the new ScreenLayout instance + * @return a LambdaScreen instance containing the configured ScreenLayout + */ @UIBuilder fun gui(name: String, block: ScreenLayout.() -> Unit) = LambdaScreen(name, ScreenLayout().apply(block)) diff --git a/common/src/main/kotlin/com/lambda/gui/component/DockingRect.kt b/common/src/main/kotlin/com/lambda/gui/component/DockingRect.kt index 785f3e826..32c6dfbdd 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/DockingRect.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/DockingRect.kt @@ -68,6 +68,14 @@ abstract class DockingRect { private fun relativeToAbs(posIn: Vec2d) = posIn + dockingOffset private fun absToRelative(posIn: Vec2d) = posIn - dockingOffset + /** + * Automatically adjusts the docking alignment of the component based on its position relative to the screen center. + * + * This function calculates horizontal and vertical center ranges (approximately the middle third of the screen) and compares + * the component's center position (dockingBase) against these ranges. If the component is outside the center range and alignment is allowed, + * it sets the horizontal docking (dockingH) to LEFT or RIGHT and the vertical docking (dockingV) to TOP or BOTTOM accordingly. + * If horizontal or vertical alignment is disallowed, the docking defaults to LEFT or TOP respectively. + */ fun autoDocking() { val screenCenterX = (screenSize.x * 0.3333)..(screenSize.x * 0.6666) val screenCenterY = (screenSize.y * 0.3333)..(screenSize.y * 0.6666) diff --git a/common/src/main/kotlin/com/lambda/gui/component/core/FilledRect.kt b/common/src/main/kotlin/com/lambda/gui/component/core/FilledRect.kt index bcf8a0266..82663f408 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/core/FilledRect.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/core/FilledRect.kt @@ -64,6 +64,13 @@ class FilledRect( } } + /** + * Sets a uniform radius for all four corners of the rectangle. + * + * This method assigns the given radius to each of the rectangle's corner properties. + * + * @param radius the radius value to apply to all corners. + */ fun setRadius(radius: Double) { leftTopRadius = radius rightTopRadius = radius @@ -71,6 +78,14 @@ class FilledRect( leftBottomRadius = radius } + /** + * Sets the same color for all corners of the filled rectangle. + * + * Updates the left top, right top, right bottom, and left bottom corner colors + * to the specified value. + * + * @param color the color to apply to all corners. + */ fun setColor(color: Color) { leftTopColor = color rightTopColor = color @@ -78,6 +93,12 @@ class FilledRect( leftBottomColor = color } + /** + * Applies a horizontal gradient by setting the left corners to [colorL] and the right corners to [colorR]. + * + * @param colorL the color for the top-left and bottom-left corners. + * @param colorR the color for the top-right and bottom-right corners. + */ fun setColorH(colorL: Color, colorR: Color) { leftTopColor = colorL rightTopColor = colorR @@ -85,6 +106,14 @@ class FilledRect( leftBottomColor = colorL } + /** + * Updates the rectangle's corner colors with a vertical gradient. + * + * Sets both top corners to the specified [colorT] and both bottom corners to [colorB]. + * + * @param colorT the color for the top corners. + * @param colorB the color for the bottom corners. + */ fun setColorV(colorT: Color, colorB: Color) { leftTopColor = colorT rightTopColor = colorT @@ -94,8 +123,14 @@ class FilledRect( companion object { /** - * Creates a [FilledRect] component - layout-based rect representation - */ + * Creates and adds a new [FilledRect] component to the current layout. + * + * The function instantiates a [FilledRect] using the receiver as its owner, automatically adds it to the layout's children, + * and then applies an optional configuration block for further customization. + * + * @param block an optional lambda to configure the newly created [FilledRect]. + * @return the newly created and configured [FilledRect] component. + */ @UIBuilder fun Layout.rect(block: FilledRect.() -> Unit = {}) = FilledRect(this).apply(children::add).apply(block) diff --git a/common/src/main/kotlin/com/lambda/gui/component/core/OutlineRect.kt b/common/src/main/kotlin/com/lambda/gui/component/core/OutlineRect.kt index c34124c2a..f057dd507 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/core/OutlineRect.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/core/OutlineRect.kt @@ -53,6 +53,13 @@ class OutlineRect( } } + /** + * Sets all the corner colors of the outline to the specified [color]. + * + * This method assigns the given [color] to the top-left, top-right, bottom-right, and bottom-left corners. + * + * @param color the color to apply to all corners. + */ fun setColor(color: Color) { leftTopColor = color rightTopColor = color @@ -62,8 +69,10 @@ class OutlineRect( companion object { /** - * Creates a [OutlineRect] component - layout-based rect representation - */ + * Creates an OutlineRect component, adds it to the parent layout's children, and applies the optional configuration. + * + * @param block an optional lambda to configure the [OutlineRect] after its creation. + */ @UIBuilder fun Layout.outline(block: OutlineRect.() -> Unit = {}) = OutlineRect(this).apply(children::add).apply(block) diff --git a/common/src/main/kotlin/com/lambda/gui/component/core/TextField.kt b/common/src/main/kotlin/com/lambda/gui/component/core/TextField.kt index 7b14c69e9..17f7d7c1b 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/core/TextField.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/core/TextField.kt @@ -56,7 +56,13 @@ class TextField( companion object { /** - * Creates a [TextField] component + * Creates a new [TextField] component. + * + * This function instantiates a [TextField] with the current [Layout] as its owner, automatically + * adds it to the layout's children list, and applies an optional configuration block. + * + * @param block an optional lambda that configures the [TextField] instance. + * @return the newly created and configured [TextField] component. */ @UIBuilder fun Layout.textField( diff --git a/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt b/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt index 5d97358ba..85a17fcc8 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/layout/Layout.kt @@ -142,9 +142,12 @@ open class Layout( private var mouseScrollActions = mutableListOf Unit>() /** - * Performs the action on this layout + * Applies the provided configuration block to this layout. * - * @param action The action to be performed. + * This extension function executes the given lambda with the layout as its receiver, + * enabling convenient DSL-style configuration. + * + * @param action the configuration block to be executed on the layout. */ @LayoutBuilder fun T.use(action: T.() -> Unit) { @@ -152,9 +155,12 @@ open class Layout( } /** - * Sets the action to be performed when the element gets shown. + * Registers an action to be executed when this layout is shown. + * + * The provided lambda is added to the list of callbacks that trigger when the layout becomes visible. + * The action is executed with the layout as its receiver. * - * @param action The action to be performed. + * @param action the lambda to execute when the layout is shown */ @LayoutBuilder fun T.onShow(action: T.() -> Unit) { @@ -162,9 +168,12 @@ open class Layout( } /** - * Sets the action to be performed when the element gets hidden. + * Registers a callback to be executed when the layout is hidden. + * + * The provided lambda is added to this layout's hide event actions, allowing you to define + * custom behavior that occurs when the layout transitions from visible to hidden. * - * @param action The action to be performed. + * @param action the lambda to execute when the layout is hidden */ @LayoutBuilder fun T.onHide(action: T.() -> Unit) { @@ -172,9 +181,11 @@ open class Layout( } /** - * Sets the action to be performed on each tick. + * Registers an action to be executed on every tick event for the layout. * - * @param action The action to be performed. + * Multiple tick actions can be registered, and each will be invoked during the tick update cycle. + * + * @param action a lambda function with receiver that specifies the operation to perform on each tick. */ @LayoutBuilder fun T.onTick(action: T.() -> Unit) { @@ -182,9 +193,12 @@ open class Layout( } /** - * Sets the update action to be performed before each frame. + * Registers an update action to be executed before each frame. + * + * The provided lambda is appended to the layout's update actions and is invoked during each update cycle, + * allowing dynamic modifications to the layout prior to rendering. * - * @param action The action to be performed. + * @param action the lambda specifying the update behavior. */ @LayoutBuilder fun T.onUpdate(action: T.() -> Unit) { @@ -192,9 +206,12 @@ open class Layout( } /** - * Sets the action to be performed on each frame. + * Registers a callback to be executed on every render frame. * - * @param action The action to be performed. + * This function adds the provided lambda to the layout's render actions, ensuring it is invoked + * during each frame when rendering the layout. + * + * @param action The lambda to execute during the layout's render cycle. */ @LayoutBuilder fun T.onRender(action: T.() -> Unit) { @@ -202,9 +219,12 @@ open class Layout( } /** - * Sets the action to be performed when a key gets pressed. + * Registers a callback to handle key press events on the layout. + * + * When a key press event occurs, the provided [action] is executed with the [KeyCode] corresponding + * to the pressed key. * - * @param action The action to be performed. + * @param action Lambda to be invoked on key press, receiving a [KeyCode] as its parameter. */ @LayoutBuilder fun T.onKeyPress(action: T.(key: KeyCode) -> Unit) { @@ -212,9 +232,11 @@ open class Layout( } /** - * Sets the action to be performed when user types a char. + * Registers an action to be executed when a character is typed. * - * @param action The action to be performed. + * The provided lambda is invoked with the typed character whenever a character input event occurs. + * + * @param action a lambda function executed with the character input. */ @LayoutBuilder fun T.onCharTyped(action: T.(char: Char) -> Unit) { @@ -222,9 +244,11 @@ open class Layout( } /** - * Sets the action to be performed when mouse button gets clicked. + * Registers a mouse click event handler on this layout. + * + * The provided lambda is executed when a mouse button is clicked, receiving both the mouse button and the associated click action. * - * @param action The action to be performed. + * @param action the callback to invoke on a mouse click event, with the clicked button and action as parameters. */ @LayoutBuilder fun T.onMouseClick(action: T.(button: Mouse.Button, action: Mouse.Action) -> Unit) { @@ -232,9 +256,11 @@ open class Layout( } /** - * Sets the action to be performed when mouse moves. + * Registers an action to execute when the mouse moves over the layout. * - * @param action The action to be performed. + * The provided lambda is invoked with the current mouse position (a Vec2d), allowing custom behavior in response to mouse movement. + * + * @param action A lambda that processes the mouse position during a mouse move event. */ @LayoutBuilder fun T.onMouseMove(action: T.(mouse: Vec2d) -> Unit) { @@ -242,9 +268,12 @@ open class Layout( } /** - * Sets the action to be performed on mouse scroll. + * Registers a mouse scroll event handler for the layout. + * + * The provided lambda receives the scroll delta, allowing you to define custom behavior in response + * to mouse scroll input. * - * @param action The action to be performed. + * @param action the lambda to be executed on a mouse scroll event, with the scroll delta as its parameter. */ @LayoutBuilder fun T.onMouseScroll(action: T.(delta: Double) -> Unit) { @@ -252,7 +281,12 @@ open class Layout( } /** - * Force overrides drawn x position of the layout + * Sets a custom lambda to override the layout's computed x position. + * + * The provided lambda is invoked during rendering to determine the x coordinate, + * allowing for custom horizontal positioning that bypasses the default layout logic. + * + * @param transform a lambda returning the x coordinate for the layout. */ @LayoutBuilder fun overrideX(transform: () -> Double) { @@ -260,7 +294,11 @@ open class Layout( } /** - * Force overrides drawn y position of the layout + * Overrides the layout's y-coordinate during rendering. + * + * Sets a transformation function to compute a forced y position for the layout, bypassing its default positioning. + * + * @param transform A lambda that calculates and returns the new y-coordinate. */ @LayoutBuilder fun overrideY(transform: () -> Double) { @@ -268,7 +306,13 @@ open class Layout( } /** - * Force overrides drawn position of the layout + * Overrides the layout's drawn position by applying custom transformation functions. + * + * The provided lambda functions compute new x and y coordinates that will be used during rendering, + * effectively bypassing the layout's default position calculation. + * + * @param x lambda function returning the new x-coordinate. + * @param y lambda function returning the new y-coordinate. */ @LayoutBuilder fun overridePosition(x: () -> Double, y: () -> Double) { @@ -277,7 +321,11 @@ open class Layout( } /** - * Force overrides drawn width of the layout + * Overrides the layout's drawn width with a custom computed value. + * + * This function sets a lambda that computes the width during rendering, enabling dynamic adjustments. + * + * @param transform A lambda that returns the desired width as a Double. */ @LayoutBuilder fun overrideWidth(transform: () -> Double) { @@ -285,7 +333,9 @@ open class Layout( } /** - * Force overrides drawn height of the layout + * Overrides the layout's drawn height using a custom transform function. + * + * @param transform A lambda returning the new height value to be applied during rendering. */ @LayoutBuilder fun overrideHeight(transform: () -> Double) { @@ -293,7 +343,13 @@ open class Layout( } /** - * Force overrides drawn size of the layout + * Overrides the layout's rendered dimensions. + * + * By supplying custom lambda expressions for width and height, this function + * forces the layout to use the specified dimensions instead of its default size. + * + * @param width Lambda that returns the new width value. + * @param height Lambda that returns the new height value. */ @LayoutBuilder fun overrideSize(width: () -> Double, height: () -> Double) { @@ -302,7 +358,20 @@ open class Layout( } /** - * Makes this layout expand up to parents rect + * Expands the layout to match its parent's bounding rectangle. + * + * This method sets override functions for the layout's x-position, y-position, width, and height, + * causing the layout to fill its parent's render area. By default, if a parent exists, the layout adopts + * the parent's render properties; otherwise, it falls back to the layout's existing dimensions. + * + * @param overrideX Lambda that computes the x-coordinate. Defaults to the parent's render x-position or + * the layout's own x value if no parent is present. + * @param overrideY Lambda that computes the y-coordinate. Defaults to the parent's render y-position or + * the layout's own y value if no parent is present. + * @param overrideWidth Lambda that computes the width. Defaults to the parent's render width or + * the layout's own width if no parent is present. + * @param overrideHeight Lambda that computes the height. Defaults to the parent's render height or + * the layout's own height if no parent is present. */ @LayoutBuilder fun fillParent( @@ -318,7 +387,12 @@ open class Layout( } /** - * Removes this layout from its parent + * Removes this layout from its parent. + * + * This function removes the layout from its parent's list of children, ensuring that it is not a root layout and preventing duplicate removals. + * It will throw an IllegalStateException if the layout has no owner (i.e., is a root layout) or if it has already been removed. + * + * @throws IllegalStateException if the layout is a root layout or if it has been destroyed previously. */ fun destroy() { check(owner != null) { @@ -352,6 +426,16 @@ open class Layout( } } + /** + * Processes a GUI event by updating the layout's state and dispatching the event to registered actions and child layouts. + * + * Depending on the event type, it updates internal properties such as mouse position, triggers specific action lists + * (e.g., show, hide, tick, key press, char typed, mouse move, mouse scroll, and mouse click), and propagates the event + * to its children. For mouse click events, the action is adjusted based on the child's hover or selection state. + * In the case of a render event, it executes render actions and conditionally applies scissor clipping before rendering children. + * + * @param e the GUI event to process + */ fun onEvent(e: GuiEvent) { // Update self when (e) { @@ -421,12 +505,12 @@ open class Layout( companion object { /** - * Creates an empty [Layout]. - * - * @param block Actions to perform within this component. - * - * Check [Layout] description for more info about batching. - */ + * Creates a new child [Layout] instance within the current layout. + * + * The new layout is added to the parent's children list and configured using the provided lambda. + * + * @param block Optional lambda to configure the newly created [Layout]. + */ @UIBuilder fun Layout.layout( block: Layout.() -> Unit = {}, @@ -434,14 +518,14 @@ open class Layout( .apply(children::add).apply(block) /** - * Creates new [AnimationTicker]. - * - * Use it to create and manage animations. + * Creates an [AnimationTicker] for the layout. * - * It's ok to have multiple tickers per component if you need to tick different animations at different timings. + * The ticker manages animation updates and, if registered (default), is automatically + * ticked on the layout's tick events. Otherwise, you must invoke its tick() method manually. + * Multiple tickers can be attached to a layout to handle animations with distinct timings. * - * @param register Whether to tick this [AnimationTicker]. - * Otherwise, you will have to tick it manually + * @param register whether the ticker should be automatically ticked on each layout tick. + * @return the newly created [AnimationTicker] instance. */ @UIBuilder fun Layout.animationTicker(register: Boolean = true) = AnimationTicker().apply { @@ -451,9 +535,10 @@ open class Layout( } /** - * Creates new [Mouse.CursorController]. + * Creates a [Mouse.CursorController] for managing the mouse cursor state. * - * Use it to set the mouse cursor type for various conditions: hovering, resizing, typing etc... + * Use the returned controller to specify the cursor appearance for various UI conditions such as hovering, + * resizing, or typing. The controller is automatically reset when the layout is hidden. */ @UIBuilder fun Layout.cursorController(): Mouse.CursorController { diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/TitleBar.kt b/common/src/main/kotlin/com/lambda/gui/component/window/TitleBar.kt index 746e064b9..5125e2135 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/TitleBar.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/TitleBar.kt @@ -89,6 +89,15 @@ class TitleBar( } companion object { + /** + * Adds a title bar component to the window. + * + * This extension function creates a [TitleBar] with the specified title text and dragging capability, + * then adds it to the window's children. + * + * @param text The text to display on the title bar. + * @param drag If true, the title bar can be dragged. + */ @UIBuilder fun Window.titleBar( text: String, diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt index 890808b6c..c46e8c88c 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/Window.kt @@ -217,26 +217,20 @@ open class Window( companion object { /** - * Creates new empty [Window] + * Creates a new [Window] instance, adds it to the current layout, and configures it with the specified properties. * - * @param position The initial position of the window + * The window is initialized with the given position, size, title, and behavior settings. Its content can be customized + * further via the provided lambda. * - * @param size The initial size of the window - * - * @param title The title of the window - * - * @param draggable Whether to allow user to drag the window - * - * @param scrollable Whether to let user scroll the content - * This will also make your elements be vertically ordered - * - * @param minimizing The [Minimizing] mode. - * - * @param resizable Whether to allow user to resize the window - * - * @param autoResize Indicates if this window could be automatically resized based on content height - * - * @param block Actions to perform within content space of the window + * @param position the initial position of the window. + * @param size the initial size of the window. + * @param title the title displayed in the window's title bar. + * @param draggable if true, allows the window to be dragged. + * @param scrollable if true, enables vertical scrolling for the window’s content. + * @param minimizing the minimizing behavior of the window (e.g., [Minimizing.Relative]). + * @param resizable if true, permits the window to be resized. + * @param autoResize if enabled, allows the window to automatically adjust its size based on its content height. + * @param block a lambda to configure the window's content. */ @UIBuilder fun Layout.window( diff --git a/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt b/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt index 52b94f6c7..a3e4edfc1 100644 --- a/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt +++ b/common/src/main/kotlin/com/lambda/gui/component/window/WindowContent.kt @@ -58,7 +58,12 @@ class WindowContent( } /** - * Overrides the summary height of the content + * Overrides the default content height calculation. + * + * Allows specifying a custom lambda that computes the total height of the content, + * typically based on factors such as padding, spacing, and the dimensions of child elements. + * + * @param block a lambda that returns the new content height as a Double. */ @LayoutBuilder fun overrideContentHeight(block: () -> Double) { @@ -66,7 +71,12 @@ class WindowContent( } /** - * Overrides the action performed on ordering update + * Sets a custom reordering action for updating the positions of the window content's children. + * + * This function allows you to override the default layout update behavior by providing a lambda + * that will be executed when the children order is recalculated. + * + * @param block The lambda that defines the custom reordering logic. */ @LayoutBuilder fun reorderChildren(block: () -> Unit) { @@ -115,15 +125,25 @@ class WindowContent( } } - fun getContentHeight() = contentHeight() + /** + * Returns the calculated content height. + * + * This function computes the total height of the content by invoking the designated lambda, + * which factors in elements such as padding, spacing, and the dimensions of its child components. + * + * @return the total height of the content. + */ +fun getContentHeight() = contentHeight() companion object { /** - * Creates an empty [WindowContent] component - * - * @param scrollable Whether to let user scroll this layout - * This will also make your elements be vertically ordered - */ + * Creates and attaches a [WindowContent] component to this [Window]. + * + * The new component is automatically added to the window's children. It is configured to be scrollable if + * the [scrollable] parameter is true, which also enforces vertical ordering for its child elements. + * + * @param scrollable if true, enables user scrolling for the layout. + */ @UIBuilder fun Window.windowContent(scrollable: Boolean) = WindowContent(this, scrollable).apply(children::add) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/ModuleLayout.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/ModuleLayout.kt index e7f0fe369..927bc1c16 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/ModuleLayout.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/ModuleLayout.kt @@ -145,6 +145,12 @@ class ModuleLayout( } } + /** + * Adjusts the corner radii of the filled rectangle based on its position and auto-resize configuration. + * + * If the rectangle is not the last element or auto-resizing is disabled, all corner radii are reset to 0. + * Otherwise, the top corner radii are reset and the bottom corner radii are reduced by the configured padding. + */ private fun FilledRect.correctRadius() { if (!isLast || !ClickGui.autoResize) { setRadius(0.0) @@ -159,8 +165,12 @@ class ModuleLayout( companion object { /** - * Creates a [ModuleLayout] - visual representation of the [Module] - */ + * Creates a ModuleLayout that visually represents the specified module and appends it + * to the children of the current layout. + * + * @param module the module instance to be displayed. + * @return the newly created ModuleLayout that has been added to the layout. + */ @UIBuilder fun Layout.moduleLayout(module: Module) = ModuleLayout(this, module).apply(children::add) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/ModuleWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/ModuleWindow.kt index db2fd5e8c..c98f40fbf 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/ModuleWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/ModuleWindow.kt @@ -44,7 +44,15 @@ class ModuleWindow( companion object { /** - * Creates a [ModuleWindow] + * Creates and attaches a [ModuleWindow] to the current layout. + * + * This extension function instantiates a [ModuleWindow] with the provided module [tag] + * and optional initial [position]. It adds the new window to the layout's children and applies + * the given [block] to configure the window's [WindowContent]. + * + * @param tag the module tag used to filter the modules displayed in the window. + * @param position the window's initial position; defaults to [Vec2d.ZERO]. + * @param block an optional lambda for configuring the window's content. */ @UIBuilder fun Layout.moduleWindow( diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/settings/BooleanButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/settings/BooleanButton.kt index aa048667c..ad0296175 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/settings/BooleanButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/settings/BooleanButton.kt @@ -81,8 +81,11 @@ class BooleanButton( companion object { /** - * Creates a [BooleanButton] - visual representation of the [BooleanSetting] - */ + * Creates a [BooleanButton] representing the specified [BooleanSetting] and adds it to the layout. + * + * @param setting the boolean setting whose state is controlled by the button. + * @return the newly created [BooleanButton]. + */ @UIBuilder fun Layout.booleanSetting(setting: BooleanSetting) = BooleanButton(this, setting).apply(children::add) diff --git a/common/src/main/kotlin/com/lambda/module/HudModule.kt b/common/src/main/kotlin/com/lambda/module/HudModule.kt index 820728a6b..db722acfc 100644 --- a/common/src/main/kotlin/com/lambda/module/HudModule.kt +++ b/common/src/main/kotlin/com/lambda/module/HudModule.kt @@ -76,7 +76,15 @@ abstract class HudModule( val rect by rectHandler::rect val animation = AnimationTicker() - protected fun onRender(block: () -> Unit) = + /** + * Registers a callback to be executed when the HUD rendering event occurs. + * + * The supplied block is invoked each time a RenderEvent.GUI.HUD event is triggered, + * allowing for custom rendering logic during the HUD update phase. + * + * @param block Lambda to execute on each HUD render event. + */ + protected fun onRender(block: () -> Unit) = listen { block() } init { diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index 5519ec086..f9e43fffa 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -159,6 +159,11 @@ abstract class Module( } } + /** + * Enables the module. + * + * Sets the module's enabled state to true, triggering any registered on-enable actions. + */ fun enable() { isEnabled = true } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt b/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt index b97bca99b..8c69e3f11 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/LambdaMoji.kt @@ -55,7 +55,23 @@ object LambdaMoji : Module( } } - // FixMe: Doesn't render properly when the chat scale is modified + /** + * Parses the provided ordered text to replace emoji characters with whitespace placeholders while + * enqueuing their corresponding glyphs for later rendering. + * + * The function processes each character in the input text, preserving style information. Detected + * emoji characters are replaced with computed whitespace (based on current font metrics and scaling) + * to maintain text layout. For every emoji found, its glyph, along with positional and color + * adjustments, is added to the render queue. + * + * Note: This function may not render correctly when the chat scale is modified. + * + * @param text The ordered text containing the characters and associated style information. + * @param x The x-coordinate offset used for calculating the emoji rendering position. + * @param y The y-coordinate offset used for calculating the emoji rendering position. + * @param color The base color used for rendering, with its alpha component adjusted for emojis. + * @return An OrderedText object with emojis replaced by whitespace to preserve layout and styles. + */ fun parse(text: OrderedText, x: Float, y: Float, color: Int): OrderedText { val saved = mutableMapOf() val builder = StringBuilder() diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/MapDownloader.kt b/common/src/main/kotlin/com/lambda/module/modules/player/MapDownloader.kt index 74a5da185..fab841e4b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/MapDownloader.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/MapDownloader.kt @@ -59,6 +59,14 @@ object MapDownloader : Module( private val MapState.hash: String get() = colors.hash("SHA-256") + /** + * Converts the MapState into a 128x128 ARGB BufferedImage. + * + * This extension creates an image by iterating over each pixel in the map state's color array, + * retrieving the corresponding render color with [MapColor.getRenderColor], and assembling the ARGB value. + * + * @return the generated [BufferedImage] representing the map state. + */ fun MapState.toBufferedImage(): BufferedImage { val image = BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB)