From 26c3f4eba34ab759facb2539ba64873c517c3f59 Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Sat, 4 Jan 2025 10:37:50 +0100 Subject: [PATCH 1/9] Refactoring: Extracted method --- src/main/java/workspace/GraphicsPImpl.java | 64 +++++++++++----------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/main/java/workspace/GraphicsPImpl.java b/src/main/java/workspace/GraphicsPImpl.java index 12ceadf2..ee21e21d 100644 --- a/src/main/java/workspace/GraphicsPImpl.java +++ b/src/main/java/workspace/GraphicsPImpl.java @@ -135,13 +135,30 @@ private void applyTransform(Matrix4f transform) { } /** - * Applies the specified texture to the current rendering context, setting the appropriate texture - * sampling mode based on the filter mode of the texture. + * Applies the specified texture to the current rendering context with the appropriate sampling + * mode. * - *

This method checks the texture's filter mode and applies the corresponding texture sampling - * mode. It also ensures that the texture is properly initialized before usage. + *

The filter mode of the texture is used to determine the sampling mode, which is then applied + * to the rendering context. If the texture is not initialized, this method does nothing. If an + * unexpected filter mode is encountered, a warning is logged, and BILINEAR mode is used as a + * fallback. * - *

The available filter modes are mapped to the following sampling modes: + * @see #getSamplingMode(FilterMode) + * @see processing.opengl.Texture + */ + private void applyTexture() { + if (texture == null || texture.getImage() == null) { + return; // Ensure texture is properly initialized before applying it. + } + ((PGraphicsOpenGL) g).textureSampling(getSamplingMode(texture.getFilterMode())); + g.textureMode(PApplet.NORMAL); + g.texture(texture.getImage()); + } + + /** + * Maps the given filter mode to the corresponding texture sampling mode. + * + *

Supported mappings: * *

* - * If an unexpected filter mode is encountered, a warning is logged, and the default BILINEAR mode - * (sampling mode 4) is applied. + *

If an unexpected filter mode is provided, a warning is logged, and BILINEAR mode (4) is + * returned. * - * @throws IllegalArgumentException if the filter mode is unexpected and no fallback is defined. + * @param filterMode the filter mode to map + * @return the corresponding sampling mode * @see processing.opengl.Texture */ - private void applyTexture() { - if (texture == null || texture.getImage() == null) { - return; // Ensure texture is properly initialized before applying it. - } - - FilterMode filterMode = texture.getFilterMode(); - int textureSampling; - + private int getSamplingMode(FilterMode filterMode) { switch (filterMode) { case POINT: - textureSampling = 2; - break; + return 2; case LINEAR: - textureSampling = 3; - break; + return 3; case BILINEAR: - textureSampling = 4; - break; + return 4; case TRILINEAR: - textureSampling = 5; - break; + return 5; default: - // Log a warning for unexpected filter modes (in case of future extensions). System.err.println("Warning: Unexpected filter mode value: " + filterMode); - textureSampling = 4; // Default to BILINEAR if undefined - break; + return 4; // Default to BILINEAR } - - // Apply the texture with the corresponding sampling mode - ((PGraphicsOpenGL) g).textureSampling(textureSampling); - g.texture(texture.getImage()); - g.textureMode(PApplet.NORMAL); } private void drawMeshFaces(Mesh3D mesh) { From c70560b2380c35f4e056701712be92c3dcd727c0 Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Sun, 5 Jan 2025 18:46:43 +0100 Subject: [PATCH 2/9] Implemented intensity support for point light rendering. --- .../engine/processing/LightRendererImpl.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/engine/processing/LightRendererImpl.java b/src/main/java/engine/processing/LightRendererImpl.java index c7f7f636..e3184e30 100644 --- a/src/main/java/engine/processing/LightRendererImpl.java +++ b/src/main/java/engine/processing/LightRendererImpl.java @@ -73,6 +73,27 @@ public void render(PointLight light) { light.getPosition().getX(), light.getPosition().getY(), light.getPosition().getZ()); + + float intensity = light.getIntensity(); + + // Retrieve light color + float red = light.getColor().getRed(); // Gives a value from 0 to 1 + float green = light.getColor().getGreen(); // Same for green + float blue = light.getColor().getBlue(); // Same for blue + + // Apply intensity to each color component + red *= intensity; + green *= intensity; + blue *= intensity; + + // Call pointLight() with adjusted color + p.pointLight( + red * 255, // Scale back to 0-255 range for Processing's pointLight + green * 255, // Same for green + blue * 255, // Same for blue + light.getPosition().getX(), + light.getPosition().getY(), + light.getPosition().getZ()); } public void render(DirectionalLight light) { From 096dbe9a68767ac78a94875a8e1145b496d9261a Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Mon, 6 Jan 2025 11:43:01 +0100 Subject: [PATCH 3/9] Fix: Removed duplicated code. --- src/main/java/engine/processing/LightRendererImpl.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/engine/processing/LightRendererImpl.java b/src/main/java/engine/processing/LightRendererImpl.java index e3184e30..2d1828a3 100644 --- a/src/main/java/engine/processing/LightRendererImpl.java +++ b/src/main/java/engine/processing/LightRendererImpl.java @@ -66,13 +66,6 @@ public void render(SpotLight light) { public void render(PointLight light) { renderCommon(light.getColor(), light.getIntensity()); - p.pointLight( - light.getColor().getRedInt(), - light.getColor().getGreenInt(), - light.getColor().getBlueInt(), - light.getPosition().getX(), - light.getPosition().getY(), - light.getPosition().getZ()); float intensity = light.getIntensity(); From 9c72cc805390ec19d7f54dcb14efe03048ac4d53 Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Tue, 7 Jan 2025 02:02:57 +0100 Subject: [PATCH 4/9] feat(engine.components): Add customizable key mappings to ControlWASD component - Introduced new setter methods to customize movement keys: - `setLeftKey(Key leftKey)` - `setRightKey(Key rightKey)` - `setForwardKey(Key forwardKey)` - `setBackwardKey(Key backwardKey)` - Updated JavaDoc for all methods to reflect the new functionality. - Ensured consistent movement handling with customizable keys for better flexibility and user experience. This change allows developers to configure custom key mappings for movement, making the ControlWASD component more adaptable to different input schemes. --- .../java/engine/components/ControlWASD.java | 77 ++++++++++++++++--- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/src/main/java/engine/components/ControlWASD.java b/src/main/java/engine/components/ControlWASD.java index 7b1c49c4..e8a2c67f 100644 --- a/src/main/java/engine/components/ControlWASD.java +++ b/src/main/java/engine/components/ControlWASD.java @@ -7,16 +7,16 @@ /** * ControlWASD is a movement component that allows moving a node in the scene graph using keyboard - * input (W/A/S/D). It integrates with the scene's transformation system to control position - * changes, allowing basic 3D navigation in a scene graph. + * input (W/A/S/D) or custom key mappings. It integrates with the scene's transformation system to + * control position changes, allowing basic 3D navigation in a scene graph. * *

Movement is normalized to ensure consistent speed, even when moving diagonally. This class * supports velocity adjustments via the speed multiplier and works by translating the owning node * within the 3D world space. * - *

Example usage: Attach this component to a {@link SceneNode} to allow movement using WASD - * keyboard inputs. The position updates depend on the elapsed time per frame to ensure smooth and - * consistent movement over varying frame rates. + *

Example usage: Attach this component to a {@link SceneNode} to allow movement using keyboard + * inputs. The position updates depend on the elapsed time per frame to ensure smooth and consistent + * movement over varying frame rates. */ public class ControlWASD extends AbstractComponent { @@ -26,6 +26,18 @@ public class ControlWASD extends AbstractComponent { /** The Input instance to monitor keyboard events (injected dependency). */ private Input input; + /** Key used for moving left. Default is 'A'. */ + private Key leftKey = Key.A; + + /** Key used for moving right. Default is 'D'. */ + private Key rightKey = Key.D; + + /** Key used for moving forward. Default is 'W'. */ + private Key forwardKey = Key.W; + + /** Key used for moving backward. Default is 'S'. */ + private Key backwardKey = Key.S; + /** * Constructs a new ControlWASD component, injecting the Input logic needed for detecting key * presses. @@ -63,7 +75,7 @@ public ControlWASD(Input input, float speed) { /** * Updates the movement of the owning node based on keyboard input. * - *

This method calculates the velocity vector by processing the WASD input and applies it to + *

This method calculates the velocity vector by processing the input keys and applies it to * move the node smoothly in the 3D space by adjusting its position over time with respect to * `tpf` (time per frame). * @@ -81,8 +93,9 @@ public void update(float tpf) { } /** - * Processes keyboard input to determine velocity. Handles the W/A/S/D keys to allow movement in - * the 3D plane. Normalizes the vector to ensure diagonal movement doesn't lead to faster speeds. + * Processes keyboard input to determine velocity. Handles the configured keys to allow movement + * in the 3D plane. Normalizes the vector to ensure diagonal movement doesn't lead to faster + * speeds. * * @return A velocity vector representing the computed movement direction and speed. */ @@ -90,10 +103,10 @@ private Vector3f handleInput() { Vector3f velocity = new Vector3f(); // Check for movement inputs - if (input.isKeyPressed(Key.W)) velocity.addLocal(0, 0, -1); - if (input.isKeyPressed(Key.A)) velocity.addLocal(-1, 0, 0); - if (input.isKeyPressed(Key.S)) velocity.addLocal(0, 0, 1); - if (input.isKeyPressed(Key.D)) velocity.addLocal(1, 0, 0); + if (input.isKeyPressed(forwardKey)) velocity.addLocal(0, 0, -1); + if (input.isKeyPressed(leftKey)) velocity.addLocal(-1, 0, 0); + if (input.isKeyPressed(backwardKey)) velocity.addLocal(0, 0, 1); + if (input.isKeyPressed(rightKey)) velocity.addLocal(1, 0, 0); // Normalize diagonal movement to prevent unintended speed boosts if (velocity.length() > 0) { @@ -128,9 +141,49 @@ public void setSpeed(float speed) { this.speed = speed; } + /** + * Sets the key used to move left. + * + * @param leftKey The new key to use for left movement. + */ + public void setLeftKey(Key leftKey) { + this.leftKey = leftKey; + } + + /** + * Sets the key used to move right. + * + * @param rightKey The new key to use for right movement. + */ + public void setRightKey(Key rightKey) { + this.rightKey = rightKey; + } + + /** + * Sets the key used to move forward. + * + * @param forwardKey The new key to use for forward movement. + */ + public void setForwardKey(Key forwardKey) { + this.forwardKey = forwardKey; + } + + /** + * Sets the key used to move backward. + * + * @param backwardKey The new key to use for backward movement. + */ + public void setBackwardKey(Key backwardKey) { + this.backwardKey = backwardKey; + } + + /** Called when the component is attached to a {@link SceneNode}. Override for custom behavior. */ @Override public void onAttach() {} + /** + * Called when the component is detached from a {@link SceneNode}. Override for custom behavior. + */ @Override public void onDetach() {} } From d335725b6ac55afa00707f6ef201485b21697870 Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Tue, 7 Jan 2025 02:12:10 +0100 Subject: [PATCH 5/9] Feat: Added drawFaces(Mesh mesh) to render wire frame representations --- src/main/java/workspace/GraphicsPImpl.java | 7 +++++++ src/main/java/workspace/ui/Graphics3D.java | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/workspace/GraphicsPImpl.java b/src/main/java/workspace/GraphicsPImpl.java index ee21e21d..06e03ed6 100644 --- a/src/main/java/workspace/GraphicsPImpl.java +++ b/src/main/java/workspace/GraphicsPImpl.java @@ -96,6 +96,13 @@ public void fillFaces(Mesh3D mesh) { } } + @Override + public void drawFaces(Mesh3D mesh) { + g.noFill(); + stroke(); + drawMeshFaces(mesh); + } + @Override public void renderInstances(Mesh3D mesh, List instanceTransforms) { if (mesh.getFaces().isEmpty() || mesh.getVertices().isEmpty()) { diff --git a/src/main/java/workspace/ui/Graphics3D.java b/src/main/java/workspace/ui/Graphics3D.java index c0c0621e..531f7d58 100644 --- a/src/main/java/workspace/ui/Graphics3D.java +++ b/src/main/java/workspace/ui/Graphics3D.java @@ -25,6 +25,8 @@ public interface Graphics3D extends Graphics2D { void render(Light light); + void drawFaces(Mesh3D mesh); + void fillFaces(Mesh3D mesh); void renderInstances(Mesh3D mesh, List instanceTransforms); From 122b98bb2a8f4609736b6ca190fe159533a2ca8d Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Tue, 7 Jan 2025 08:45:11 +0100 Subject: [PATCH 6/9] Feat: Added camera position --- src/main/java/engine/debug/DebugInfoUpdater.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/engine/debug/DebugInfoUpdater.java b/src/main/java/engine/debug/DebugInfoUpdater.java index abdcd2c1..a9177341 100644 --- a/src/main/java/engine/debug/DebugInfoUpdater.java +++ b/src/main/java/engine/debug/DebugInfoUpdater.java @@ -104,6 +104,9 @@ private void updateCameraInfo(Camera camera) { setInfo(CATEGORY_CAMERA, "FOV", Mathf.toDegrees(camera.getFieldOfView())); setInfo(CATEGORY_CAMERA, "Near", camera.getNearPlane()); setInfo(CATEGORY_CAMERA, "Far", camera.getFarPlane()); + setInfo(CATEGORY_CAMERA, "PositionX", camera.getTransform().getPosition().getX()); + setInfo(CATEGORY_CAMERA, "PositionY", camera.getTransform().getPosition().getY()); + setInfo(CATEGORY_CAMERA, "PositionZ", camera.getTransform().getPosition().getZ()); } private void updateOsMetrics() { From e7a425b4d07b038ec574c3ee0b8061edfedf1af2 Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Tue, 7 Jan 2025 08:49:42 +0100 Subject: [PATCH 7/9] Feat: Added Reticle Ui --- .../java/engine/components/RoundReticle.java | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/main/java/engine/components/RoundReticle.java diff --git a/src/main/java/engine/components/RoundReticle.java b/src/main/java/engine/components/RoundReticle.java new file mode 100644 index 00000000..0972da06 --- /dev/null +++ b/src/main/java/engine/components/RoundReticle.java @@ -0,0 +1,160 @@ +package engine.components; + +import math.Color; +import workspace.ui.Graphics; + +/** + * A component that renders a round reticle in the center of the screen. The reticle consists of an + * outer circle and a smaller, filled inner circle, both customizable in terms of radius and color. + */ +public class RoundReticle extends AbstractComponent implements RenderableComponent { + + private float outerRadius; + + private float innerRadius; + + private Color color; + + /** + * Creates a default RoundReticle with preset values. Default configuration: - Outer radius: 30 - + * Inner radius: 6 - Color: White + */ + public RoundReticle() { + this(30, 6, Color.WHITE); + } + + /** + * Creates a RoundReticle with specified outer radius, inner radius, and color. + * + * @param outerRadius the radius of the outer circle. + * @param innerRadius the radius of the inner filled circle. + * @param color the color of the reticle. + * @throws IllegalArgumentException if any radius is non-positive or color is null. + */ + public RoundReticle(float outerRadius, float innerRadius, Color color) { + setOuterRadius(outerRadius); + setInnerRadius(innerRadius); + setColor(color); + } + + /** + * Renders the reticle on the screen, centered in the viewport. The reticle includes an outer + * circle and a filled inner circle. + * + * @param g the {@link Graphics} context used for rendering. + */ + @Override + public void render(Graphics g) { + int centerX = g.getWidth() / 2; + int centerY = g.getHeight() / 2; + + // Render reticle circles + g.setColor(color); + renderCenteredOval(g, centerX, centerY, outerRadius, false); + renderCenteredOval(g, centerX, centerY, innerRadius, true); + } + + /** + * Draws a centered oval at the specified position. + * + * @param g the {@link Graphics} context used for rendering. + * @param centerX the x-coordinate of the oval's center. + * @param centerY the y-coordinate of the oval's center. + * @param radius the radius of the oval. + * @param filled whether the oval should be filled. + */ + private void renderCenteredOval( + Graphics g, int centerX, int centerY, float radius, boolean filled) { + int diameter = (int) radius; + int topLeftX = centerX - diameter / 2; + int topLeftY = centerY - diameter / 2; + + if (filled) { + g.fillOval(topLeftX, topLeftY, diameter, diameter); + } else { + g.drawOval(topLeftX, topLeftY, diameter, diameter); + } + } + + /** + * Updates the component. Currently, this method does nothing. + * + * @param tpf time per frame, used for animations or updates. + */ + @Override + public void update(float tpf) {} + + /** Called when the component is attached to a {@link engine.SceneNode}. */ + @Override + public void onAttach() {} + + /** Called when the component is detached from a {@link engine.SceneNode}. */ + @Override + public void onDetach() {} + + /** + * Gets the outer radius of the reticle. + * + * @return the outer radius. + */ + public float getOuterRadius() { + return outerRadius; + } + + /** + * Sets the outer radius of the reticle. + * + * @param outerRadius the new outer radius. + * @throws IllegalArgumentException if the radius is non-positive. + */ + public void setOuterRadius(float outerRadius) { + if (outerRadius <= 0) { + throw new IllegalArgumentException("Outer radius must be greater than 0."); + } + this.outerRadius = outerRadius; + } + + /** + * Gets the inner radius of the reticle. + * + * @return the inner radius. + */ + public float getInnerRadius() { + return innerRadius; + } + + /** + * Sets the inner radius of the reticle. + * + * @param innerRadius the new inner radius. + * @throws IllegalArgumentException if the radius is non-positive. + */ + public void setInnerRadius(float innerRadius) { + if (innerRadius <= 0) { + throw new IllegalArgumentException("Inner radius must be greater than 0."); + } + this.innerRadius = innerRadius; + } + + /** + * Gets the color of the reticle. + * + * @return the color. + */ + public Color getColor() { + return color; + } + + /** + * Sets the color of the reticle. + * + * @param color the new color. + * @throws IllegalArgumentException if the color is null. + */ + public void setColor(Color color) { + if (color == null) { + throw new IllegalArgumentException("Color cannot be null."); + } + this.color = color; + } +} From 117afe36df8f6ac68d413fc76dbf253562f04755 Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Tue, 7 Jan 2025 09:46:44 +0100 Subject: [PATCH 8/9] Add TextureWrapMode enum with detailed JavaDoc for texture wrapping abstraction Introduced the TextureWrapMode enum to abstract texture wrapping modes for the engine. Added support for CLAMP and REPEAT modes to handle how textures are mapped when texture coordinates exceed the [0, 1] range. --- .../engine/resources/TextureWrapMode.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/java/engine/resources/TextureWrapMode.java diff --git a/src/main/java/engine/resources/TextureWrapMode.java b/src/main/java/engine/resources/TextureWrapMode.java new file mode 100644 index 00000000..fce2ed44 --- /dev/null +++ b/src/main/java/engine/resources/TextureWrapMode.java @@ -0,0 +1,55 @@ +package engine.resources; + +/** + * Represents the texture wrapping mode used to determine how textures are applied when texture + * coordinates exceed the standard [0, 1] range. + * + *

This enum provides two wrapping modes: + * + *

+ * + *

Depending on the rendering backend, more wrapping modes may be supported. This abstraction + * allows for easy integration with different graphics libraries while keeping the codebase flexible + * and maintainable. + * + * @see engine.texture.Texture + */ +public enum TextureWrapMode { + + /** + * Clamps texture coordinates to the edges of the texture. + * + *

This mode ensures that any texture coordinate outside the [0, 1] range will be mapped to the + * nearest edge of the texture, effectively stretching the border pixels. It is commonly used to + * avoid visible seams on models when the texture should not repeat. + * + *

Example Use Case: + * + *

+ */ + CLAMP, + + /** + * Repeats the texture in both directions when the texture coordinates exceed the [0, 1] range. + * This mode is useful for creating seamless, tiled patterns such as terrain, walls, or floors in + * games and 3D environments. + * + *

Example Use Case: + * + *

+ */ + REPEAT +} From 020781941184a4667d536a552540f488fdda9e79 Mon Sep 17 00:00:00 2001 From: Simon Dietz Date: Tue, 7 Jan 2025 10:21:54 +0100 Subject: [PATCH 9/9] Feat: Added texture wrap support --- .../engine/processing/ProcessingTexture.java | 14 +++++ src/main/java/engine/resources/Texture.java | 23 +++++++ src/main/java/engine/resources/Texture2D.java | 61 ++++++++++++++++++- src/main/java/workspace/GraphicsPImpl.java | 27 ++++++++ 4 files changed, 123 insertions(+), 2 deletions(-) diff --git a/src/main/java/engine/processing/ProcessingTexture.java b/src/main/java/engine/processing/ProcessingTexture.java index 3712dcba..829ce539 100644 --- a/src/main/java/engine/processing/ProcessingTexture.java +++ b/src/main/java/engine/processing/ProcessingTexture.java @@ -2,6 +2,7 @@ import engine.resources.FilterMode; import engine.resources.Texture; +import engine.resources.TextureWrapMode; import processing.core.PImage; public class ProcessingTexture implements Texture { @@ -10,9 +11,12 @@ public class ProcessingTexture implements Texture { private FilterMode filterMode; + private TextureWrapMode textureWrapMode; + public ProcessingTexture(PImage image) { this.image = image; this.filterMode = FilterMode.BILINEAR; + this.textureWrapMode = TextureWrapMode.CLAMP; } @Override @@ -61,6 +65,16 @@ public void setFilterMode(FilterMode filterMode) { this.filterMode = filterMode; } + @Override + public TextureWrapMode getTextureWrapMode() { + return textureWrapMode; + } + + @Override + public void setTextureWrapMode(TextureWrapMode textureWrapMode) { + this.textureWrapMode = textureWrapMode; + } + @Override public Texture getBackendTexture() { return this; diff --git a/src/main/java/engine/resources/Texture.java b/src/main/java/engine/resources/Texture.java index af14417e..33571f5e 100644 --- a/src/main/java/engine/resources/Texture.java +++ b/src/main/java/engine/resources/Texture.java @@ -55,6 +55,29 @@ public interface Texture { */ void setFilterMode(FilterMode filterMode); + /** + * Gets the current texture wrapping mode. + * + *

The texture wrap mode determines how the texture is applied when texture coordinates exceed + * the [0, 1] range. For example, in {@link TextureWrapMode#REPEAT}, the texture will repeat, + * whereas in {@link TextureWrapMode#CLAMP}, the texture will extend the edge pixels. + * + * @return the {@link TextureWrapMode} currently applied to the texture. + */ + TextureWrapMode getTextureWrapMode(); + + /** + * Sets the texture wrapping mode. + * + *

This method controls how the texture is mapped outside the standard [0, 1] texture + * coordinate range. Use {@link TextureWrapMode#REPEAT} to tile the texture across a surface, or + * {@link TextureWrapMode#CLAMP} to stretch the texture's edge pixels when coordinates exceed the + * valid range. + * + * @param textureWrapMode the new {@link TextureWrapMode} to apply to the texture. + */ + void setTextureWrapMode(TextureWrapMode textureWrapMode); + /** * Gets the underlying backend texture implementation. This is useful for accessing * engine-specific or platform-specific features. diff --git a/src/main/java/engine/resources/Texture2D.java b/src/main/java/engine/resources/Texture2D.java index 2a3bcb99..c6279dea 100644 --- a/src/main/java/engine/resources/Texture2D.java +++ b/src/main/java/engine/resources/Texture2D.java @@ -55,20 +55,33 @@ public int getHeight() { /** * Binds the texture to the specified texture unit for rendering. * + *

Binding a texture makes it available for use in subsequent rendering operations. The texture + * unit represents a specific slot in the graphics pipeline to which the texture is assigned. + * * @param unit the texture unit to bind this texture to. + * @see #unbind() */ @Override public void bind(int unit) { texture.bind(unit); } - /** Unbinds the texture from its currently bound texture unit. */ + /** + * Unbinds the texture from its currently bound texture unit. + * + *

Once a texture is unbound, it is no longer available for rendering until it is bound again. + */ @Override public void unbind() { texture.unbind(); } - /** Deletes the texture and releases any associated resources. */ + /** + * Deletes the texture and releases any associated resources. + * + *

This method should be called when the texture is no longer needed to free up GPU memory. + * After a texture is deleted, it cannot be used for rendering unless it is recreated. + */ @Override public void delete() { texture.delete(); @@ -77,7 +90,11 @@ public void delete() { /** * Updates the pixel data of the texture. * + *

This method replaces the existing texture data with new pixel values. The pixel data must + * match the dimensions of the texture. + * * @param pixels an array of pixel data to set for this texture. + * @throws IllegalArgumentException if the pixel array size does not match the texture dimensions. */ @Override public void setPixels(int[] pixels) { @@ -87,6 +104,9 @@ public void setPixels(int[] pixels) { /** * Gets the filter mode currently applied to the texture. * + *

The filter mode determines how the texture is sampled when it is magnified or minified + * during rendering. Common filter modes include nearest-neighbor and linear filtering. + * * @return the filter mode of the texture. * @see FilterMode */ @@ -98,6 +118,10 @@ public FilterMode getFilterMode() { /** * Sets the filter mode for the texture. * + *

The filter mode controls how the texture is sampled when rendered at different sizes. For + * example, linear filtering smooths the texture when scaled, while nearest-neighbor filtering + * preserves sharp edges. + * * @param filterMode the desired filter mode to apply to the texture. * @see FilterMode */ @@ -106,9 +130,42 @@ public void setFilterMode(FilterMode filterMode) { texture.setFilterMode(filterMode); } + /** + * Gets the texture wrapping mode currently applied to this texture. + * + *

The texture wrap mode controls how the texture is applied when texture coordinates exceed + * the [0, 1] range. For example, in {@link TextureWrapMode#REPEAT}, the texture will tile + * infinitely, whereas in {@link TextureWrapMode#CLAMP}, the texture's edge pixels are stretched. + * + * @return the {@link TextureWrapMode} applied to the texture. + */ + @Override + public TextureWrapMode getTextureWrapMode() { + return texture.getTextureWrapMode(); + } + + /** + * Sets the texture wrapping mode for this texture. + * + *

This method controls how the texture is mapped outside the standard [0, 1] texture + * coordinate range. Use {@link TextureWrapMode#REPEAT} to tile the texture across a surface, or + * {@link TextureWrapMode#CLAMP} to stretch the texture's edge pixels when coordinates exceed the + * valid range. + * + * @param textureWrapMode the new {@link TextureWrapMode} to apply to the texture. + */ + @Override + public void setTextureWrapMode(TextureWrapMode textureWrapMode) { + this.texture.setTextureWrapMode(textureWrapMode); + } + /** * Retrieves the backend texture instance used by this facade. * + *

This method provides access to the underlying texture object managed by the {@link + * TextureManager}. It is useful for accessing low-level or engine-specific features not exposed + * by the {@link Texture2D} class. + * * @return the underlying {@link Texture} instance. */ @Override diff --git a/src/main/java/workspace/GraphicsPImpl.java b/src/main/java/workspace/GraphicsPImpl.java index 06e03ed6..c0c923f3 100644 --- a/src/main/java/workspace/GraphicsPImpl.java +++ b/src/main/java/workspace/GraphicsPImpl.java @@ -9,6 +9,7 @@ import engine.resources.FilterMode; import engine.resources.Image; import engine.resources.Texture; +import engine.resources.TextureWrapMode; import engine.scene.camera.Camera; import engine.scene.light.Light; import engine.scene.light.LightRenderer; @@ -158,10 +159,36 @@ private void applyTexture() { return; // Ensure texture is properly initialized before applying it. } ((PGraphicsOpenGL) g).textureSampling(getSamplingMode(texture.getFilterMode())); + g.textureWrap(getTextureWrapMode()); g.textureMode(PApplet.NORMAL); g.texture(texture.getImage()); } + /** + * Converts the internal {@link TextureWrapMode} to a corresponding Processing constant. + * + *

This method maps the engine's {@link TextureWrapMode} values to the equivalent constants + * defined by the {@link PApplet} class for use in Processing-based rendering. The supported + * mappings are: + * + *

+ * + *

If an unsupported or unexpected wrap mode is encountered, a warning is printed to {@code + * System.err}, and {@link PApplet#CLAMP} is returned as a fallback. + * + * @return the corresponding Processing constant for the texture wrap mode. + */ + private int getTextureWrapMode() { + TextureWrapMode textureWrapMode = texture.getTextureWrapMode(); + if (textureWrapMode == TextureWrapMode.CLAMP) return PApplet.CLAMP; + if (textureWrapMode == TextureWrapMode.REPEAT) return PApplet.REPEAT; + System.err.println("Warning: Unexpected texture wrap mode value: " + textureWrapMode); + return PApplet.CLAMP; + } + /** * Maps the given filter mode to the corresponding texture sampling mode. *