diff --git a/src/main/java/engine/application/Application.java b/src/main/java/engine/application/Application.java new file mode 100644 index 00000000..23957ac0 --- /dev/null +++ b/src/main/java/engine/application/Application.java @@ -0,0 +1,17 @@ +package engine.application; + +import engine.input.Input; +import workspace.ui.Graphics; + +public interface Application { + + void initialize(); + + void update(); + + void render(Graphics g); + + void cleanup(); + + void setInput(Input input); +} diff --git a/src/main/java/engine/application/ApplicationContainer.java b/src/main/java/engine/application/ApplicationContainer.java new file mode 100644 index 00000000..6cf1ef82 --- /dev/null +++ b/src/main/java/engine/application/ApplicationContainer.java @@ -0,0 +1,63 @@ +package engine.application; + +import engine.input.Input; +import workspace.ui.Graphics; + +public class ApplicationContainer { + + private boolean isInitialized = false; + + private Graphics graphics; + + private Application application; + + public ApplicationContainer(Application application) { + if (application == null) { + throw new IllegalArgumentException("Application cannot be null."); + } + this.application = application; + } + + public void initialize() { + if (isInitialized) { + return; + } + application.initialize(); + isInitialized = true; + } + + public void update() { + application.update(); + } + + public void render() { + checkInitialization(); + if (graphics == null) { + throw new IllegalStateException( + "Graphics context is not initialized. Call setGraphics() first."); + } + application.render(graphics); + } + + public void cleanup() { + application.cleanup(); + } + + private void checkInitialization() { + if (!isInitialized) { + throw new IllegalStateException( + "ApplicationContainer is not initialized. Call initialize() first."); + } + } + + public void setInput(Input input) { + application.setInput(input); + } + + public void setGraphics(Graphics g) { + if (g == null) { + throw new IllegalArgumentException("Graphics cannot be null."); + } + this.graphics = g; + } +} diff --git a/src/main/java/engine/application/ApplicationSettings.java b/src/main/java/engine/application/ApplicationSettings.java new file mode 100644 index 00000000..c8c42499 --- /dev/null +++ b/src/main/java/engine/application/ApplicationSettings.java @@ -0,0 +1,135 @@ +package engine.application; + +/** + * Encapsulates configuration settings for an application, including dimensions, fullscreen mode, + * and title. Provides default values and validation for each setting. + */ +public class ApplicationSettings { + + private static final String DEFAULT_TITLE = "Untitled-Application"; + + private int width; + + private int height; + + private boolean fullscreen; + + private String title; + + /** + * Constructs an ApplicationSettings instance with default values: + * + *
- * This class provides a shared implementation of common functionality across - * all components, reducing boilerplate and centralizing shared logic for ease - * of maintenance. - *
+ * + *This class provides a shared implementation of common functionality across all components, + * reducing boilerplate and centralizing shared logic for ease of maintenance. */ public abstract class AbstractComponent implements Component { - /** Reference to the owning SceneNode */ - protected SceneNode owner; + /** Reference to the owning SceneNode */ + protected SceneNode owner; - /** - * Sets the owner (parent node) of this component. - *
- * This is common logic provided by the abstract class to ensure consistency - * among all components. - *
- * - * @param owner The SceneNode that owns this component. - */ - @Override - public void setOwner(SceneNode owner) { - this.owner = owner; - } + /** + * Sets the owner (parent node) of this component. + * + *This is common logic provided by the abstract class to ensure consistency among all + * components. + * + * @param owner The SceneNode that owns this component. + */ + @Override + public void setOwner(SceneNode owner) { + this.owner = owner; + } - /** - * Retrieves the owning node for convenience. - * - * @return The owning SceneNode instance. - */ - public SceneNode getOwner() { - return owner; - } - -} \ No newline at end of file + /** + * Retrieves the owning node for convenience. + * + * @return The owning SceneNode instance. + */ + public SceneNode getOwner() { + return owner; + } +} diff --git a/src/main/java/engine/components/CinematicBlackBarsRenderer.java b/src/main/java/engine/components/CinematicBlackBarsRenderer.java index 7a397239..76bab0da 100644 --- a/src/main/java/engine/components/CinematicBlackBarsRenderer.java +++ b/src/main/java/engine/components/CinematicBlackBarsRenderer.java @@ -4,200 +4,178 @@ import workspace.ui.Graphics; /** - * CinematicBlackBarsRenderer is responsible for rendering cinematic black bars - * on the top and bottom of the screen. It supports smooth fade-in and fade-out - * animations for cinematic effects during transitions. - *
- * This component renders black bars with configurable speed and handles dynamic - * animations like appearing or disappearing smoothly over time. The fade - * effects are controlled by the speed of transition and a constant bar size (in - * pixels). - *
+ * CinematicBlackBarsRenderer is responsible for rendering cinematic black bars on the top and + * bottom of the screen. It supports smooth fade-in and fade-out animations for cinematic effects + * during transitions. + * + *This component renders black bars with configurable speed and handles dynamic animations like + * appearing or disappearing smoothly over time. The fade effects are controlled by the speed of + * transition and a constant bar size (in pixels). */ -public class CinematicBlackBarsRenderer extends AbstractComponent - implements RenderableComponent { - - /** Default height (in pixels) of the cinematic bars at full fade-in. */ - private static final int DEFAULT_TARGET_BAR_HEIGHT = 200; - - /** - * Default speed at which the fade-in and fade-out effects occur (in pixels - * per second). - */ - private static final float DEFAULT_FADE_SPEED = 70.0f; - - /** Indicates whether the black bars are currently fading in. */ - private boolean fadingIn; - - /** Indicates whether the black bars are currently fading out. */ - private boolean fadingOut; - - /** Current visual size of the black bars (interpolated over time). */ - private float currentSize; - - /** The speed at which fade animations are applied (in pixels/second). */ - private float fadeSpeed; - - /** The target bar height at full fade-in (in pixels). */ - private float targetBarHeight; - - /** - * Default constructor for initializing the renderer instance. - *
- * This sets the default fade speed and target bar height to predefined - * constants. - *
- */ - public CinematicBlackBarsRenderer() { - this.fadeSpeed = DEFAULT_FADE_SPEED; - this.targetBarHeight = DEFAULT_TARGET_BAR_HEIGHT; - } - - /** - * Constructor with configurable fade speed and target bar height. - * - * @param fadeSpeed Speed at which fade animations will play (in - * pixels/second). - * @param targetBarHeight The target height of the cinematic bars at full - * fade-in. - */ - public CinematicBlackBarsRenderer(float fadeSpeed, float targetBarHeight) { - this.fadeSpeed = fadeSpeed; - this.targetBarHeight = targetBarHeight; - } - - /** - * Updates the animation state over time. - *- * This method advances the fade-in and fade-out animations by incrementally - * updating the `currentSize` value based on elapsed time and speed. Stops - * animations when they reach their final states. - *
- * - * @param tpf Time per frame (time elapsed since the last frame in seconds). - */ - @Override - public void update(float tpf) { - if (!isFading()) { - return; - } - if (fadingIn) { - animateFadeIn(tpf); - } - if (fadingOut) { - animateFaceOut(tpf); - } - } - - /** - * Handles the fade-in animation by incrementally increasing the size of the - * black bars over time until the target bar height is reached. - * - * @param tpf Time per frame (time elapsed since the last frame in seconds). - */ - private void animateFadeIn(float tpf) { - currentSize += fadeSpeed * tpf; - if (currentSize >= targetBarHeight) { - currentSize = targetBarHeight; - fadingIn = false; - } - } - - /** - * Handles the fade-out animation by decrementally decreasing the size of the - * black bars over time until their visual size is zero. - * - * @param tpf Time per frame (time elapsed since the last frame in seconds). - */ - private void animateFaceOut(float tpf) { - currentSize -= fadeSpeed * tpf; - if (currentSize <= 0) { - currentSize = 0; - fadingOut = false; - } - } - - /** - * Determines if a fade animation (either in or out) is currently active. - * - * @return {@code true} if either fading in or fading out is active, - * {@code false} otherwise. - */ - public boolean isFading() { - return fadingIn || fadingOut; - } - - /** - * Starts the fade-in animation. - *- * This stops any ongoing fade-out animation and sets the internal state to - * begin the fade-in effect from the current position. - *
- */ - public void fadeIn() { - if (fadingIn) - return; - fadingIn = true; - fadingOut = false; - } - - /** - * Starts the fade-out animation. - *- * This stops any ongoing fade-in animation and sets the internal state to - * begin the fade-out effect from the current position. - *
- */ - public void fadeOut() { - if (fadingOut) - return; - fadingOut = true; - fadingIn = false; - } - - /** - * Renders the cinematic black bars at the top and bottom of the screen. - *- * The rendering uses the current interpolated size value (`currentSize`) to - * draw smooth black bars transitioning in and out. This rendering respects - * the screen's current dimensions and is dynamically updated during - * animation. - *
- * - * @param g The graphics context used to perform the rendering. - */ - @Override - public void render(Graphics g) { - g.setColor(Color.BLACK); - renderTopBar(g); - renderBottomBar(g); - } - - /** - * Renders the top cinematic black bar using the current animation state. - * - * @param g The graphics context used for rendering. - */ - private void renderTopBar(Graphics g) { - g.fillRect(0, 0, g.getWidth(), (int) currentSize); - } - - /** - * Renders the bottom cinematic black bar using the current animation state. - * - * @param g The graphics context used for rendering. - */ - private void renderBottomBar(Graphics g) { - g.fillRect(0, g.getHeight() - (int) currentSize, g.getWidth(), - (int) currentSize); - } - - @Override - public void onAttach() { - } - - @Override - public void onDetach() { - } - -} \ No newline at end of file +public class CinematicBlackBarsRenderer extends AbstractComponent implements RenderableComponent { + + /** Default height (in pixels) of the cinematic bars at full fade-in. */ + private static final int DEFAULT_TARGET_BAR_HEIGHT = 200; + + /** Default speed at which the fade-in and fade-out effects occur (in pixels per second). */ + private static final float DEFAULT_FADE_SPEED = 70.0f; + + /** Indicates whether the black bars are currently fading in. */ + private boolean fadingIn; + + /** Indicates whether the black bars are currently fading out. */ + private boolean fadingOut; + + /** Current visual size of the black bars (interpolated over time). */ + private float currentSize; + + /** The speed at which fade animations are applied (in pixels/second). */ + private float fadeSpeed; + + /** The target bar height at full fade-in (in pixels). */ + private float targetBarHeight; + + /** + * Default constructor for initializing the renderer instance. + * + *This sets the default fade speed and target bar height to predefined constants. + */ + public CinematicBlackBarsRenderer() { + this.fadeSpeed = DEFAULT_FADE_SPEED; + this.targetBarHeight = DEFAULT_TARGET_BAR_HEIGHT; + } + + /** + * Constructor with configurable fade speed and target bar height. + * + * @param fadeSpeed Speed at which fade animations will play (in pixels/second). + * @param targetBarHeight The target height of the cinematic bars at full fade-in. + */ + public CinematicBlackBarsRenderer(float fadeSpeed, float targetBarHeight) { + this.fadeSpeed = fadeSpeed; + this.targetBarHeight = targetBarHeight; + } + + /** + * Updates the animation state over time. + * + *
This method advances the fade-in and fade-out animations by incrementally updating the + * `currentSize` value based on elapsed time and speed. Stops animations when they reach their + * final states. + * + * @param tpf Time per frame (time elapsed since the last frame in seconds). + */ + @Override + public void update(float tpf) { + if (!isFading()) { + return; + } + if (fadingIn) { + animateFadeIn(tpf); + } + if (fadingOut) { + animateFaceOut(tpf); + } + } + + /** + * Handles the fade-in animation by incrementally increasing the size of the black bars over time + * until the target bar height is reached. + * + * @param tpf Time per frame (time elapsed since the last frame in seconds). + */ + private void animateFadeIn(float tpf) { + currentSize += fadeSpeed * tpf; + if (currentSize >= targetBarHeight) { + currentSize = targetBarHeight; + fadingIn = false; + } + } + + /** + * Handles the fade-out animation by decrementally decreasing the size of the black bars over time + * until their visual size is zero. + * + * @param tpf Time per frame (time elapsed since the last frame in seconds). + */ + private void animateFaceOut(float tpf) { + currentSize -= fadeSpeed * tpf; + if (currentSize <= 0) { + currentSize = 0; + fadingOut = false; + } + } + + /** + * Determines if a fade animation (either in or out) is currently active. + * + * @return {@code true} if either fading in or fading out is active, {@code false} otherwise. + */ + public boolean isFading() { + return fadingIn || fadingOut; + } + + /** + * Starts the fade-in animation. + * + *
This stops any ongoing fade-out animation and sets the internal state to begin the fade-in + * effect from the current position. + */ + public void fadeIn() { + if (fadingIn) return; + fadingIn = true; + fadingOut = false; + } + + /** + * Starts the fade-out animation. + * + *
This stops any ongoing fade-in animation and sets the internal state to begin the fade-out + * effect from the current position. + */ + public void fadeOut() { + if (fadingOut) return; + fadingOut = true; + fadingIn = false; + } + + /** + * Renders the cinematic black bars at the top and bottom of the screen. + * + *
The rendering uses the current interpolated size value (`currentSize`) to draw smooth black + * bars transitioning in and out. This rendering respects the screen's current dimensions and is + * dynamically updated during animation. + * + * @param g The graphics context used to perform the rendering. + */ + @Override + public void render(Graphics g) { + g.setColor(Color.BLACK); + renderTopBar(g); + renderBottomBar(g); + } + + /** + * Renders the top cinematic black bar using the current animation state. + * + * @param g The graphics context used for rendering. + */ + private void renderTopBar(Graphics g) { + g.fillRect(0, 0, g.getWidth(), (int) currentSize); + } + + /** + * Renders the bottom cinematic black bar using the current animation state. + * + * @param g The graphics context used for rendering. + */ + private void renderBottomBar(Graphics g) { + g.fillRect(0, g.getHeight() - (int) currentSize, g.getWidth(), (int) currentSize); + } + + @Override + public void onAttach() {} + + @Override + public void onDetach() {} +} diff --git a/src/main/java/engine/components/CircularAnimationComponent.java b/src/main/java/engine/components/CircularAnimationComponent.java index 4e4aa193..2c3117b1 100644 --- a/src/main/java/engine/components/CircularAnimationComponent.java +++ b/src/main/java/engine/components/CircularAnimationComponent.java @@ -4,107 +4,89 @@ import math.Mathf; /** - * A simple animation component that makes the owner SceneNode move in circular - * motion over the XZ plane. - *
- * This component calculates circular motion based on a defined radius and - * angular speed, updating the position of the owning SceneNode in real-time. - * The motion occurs on the horizontal plane (XZ plane) while keeping the - * Y-coordinate constant. - *
- *- * Properties: + * A simple animation component that makes the owner SceneNode move in circular motion over the XZ + * plane. + * + *
This component calculates circular motion based on a defined radius and angular speed, + * updating the position of the owning SceneNode in real-time. The motion occurs on the horizontal + * plane (XZ plane) while keeping the Y-coordinate constant. + * + *
Properties: + * *
- * This component updates the position of the owning SceneNode every frame, - * creating smooth circular motion over time. - *
- * + * + *This component updates the position of the owning SceneNode every frame, creating smooth + * circular motion over time. + * * @see SceneNode * @see Transform */ public class CircularAnimationComponent extends AbstractComponent { - /** - * Radius of circular motion. Determines how far from the center the object - * orbits. - */ - private float radius; + /** Radius of circular motion. Determines how far from the center the object orbits. */ + private float radius; - /** - * Angular speed in radians per second that determines how fast the object - * orbits. - */ - private float angularSpeed; + /** Angular speed in radians per second that determines how fast the object orbits. */ + private float angularSpeed; - /** Tracks the total elapsed time since the start of the animation. */ - private float timeElapsed; + /** Tracks the total elapsed time since the start of the animation. */ + private float timeElapsed; - /** - * Default constructor that initializes with default values: - radius = 30.0f - * - angularSpeed = 1.0f - */ - public CircularAnimationComponent() { - this.radius = 30.0f; - this.angularSpeed = 1.0f; - this.timeElapsed = 0.0f; - } + /** + * Default constructor that initializes with default values: - radius = 30.0f - angularSpeed = + * 1.0f + */ + public CircularAnimationComponent() { + this.radius = 30.0f; + this.angularSpeed = 1.0f; + this.timeElapsed = 0.0f; + } - /** - * Creates a CircularAnimationComponent with specified radius and angular - * speed. - * - * @param radius The distance from the center of circular motion to the - * object. - * @param angularSpeed The speed at which the object orbits, in radians per - * second. - */ - public CircularAnimationComponent(float radius, float angularSpeed) { - this.radius = radius; - this.angularSpeed = angularSpeed; - } + /** + * Creates a CircularAnimationComponent with specified radius and angular speed. + * + * @param radius The distance from the center of circular motion to the object. + * @param angularSpeed The speed at which the object orbits, in radians per second. + */ + public CircularAnimationComponent(float radius, float angularSpeed) { + this.radius = radius; + this.angularSpeed = angularSpeed; + } - /** - * Updates the animation logic each frame. - *
- * This method calculates the new position of the owning SceneNode in a - * circular path using trigonometric functions. The position is updated on the - * XZ plane with constant Y-coordinate to simulate planar circular motion. - *
- * - * @param tpf Time per frame (time in seconds since the last frame). - */ - @Override - public void update(float tpf) { - // Update the elapsed time based on the time per frame - timeElapsed += tpf; + /** + * Updates the animation logic each frame. + * + *This method calculates the new position of the owning SceneNode in a circular path using + * trigonometric functions. The position is updated on the XZ plane with constant Y-coordinate to + * simulate planar circular motion. + * + * @param tpf Time per frame (time in seconds since the last frame). + */ + @Override + public void update(float tpf) { + // Update the elapsed time based on the time per frame + timeElapsed += tpf; - // Calculate the new X and Z positions using circular motion formulas - float x = radius * Mathf.cos(angularSpeed * timeElapsed); - float z = radius * Mathf.sin(angularSpeed * timeElapsed); + // Calculate the new X and Z positions using circular motion formulas + float x = radius * Mathf.cos(angularSpeed * timeElapsed); + float z = radius * Mathf.sin(angularSpeed * timeElapsed); - // Retrieve the transform of the owning SceneNode - SceneNode node = getOwner(); - Transform transform = node.getTransform(); + // Retrieve the transform of the owning SceneNode + SceneNode node = getOwner(); + Transform transform = node.getTransform(); - // Set the new position while maintaining the current Y-coordinate - transform.setPosition(x, transform.getPosition().y, z); - } + // Set the new position while maintaining the current Y-coordinate + transform.setPosition(x, transform.getPosition().y, z); + } - @Override - public void onAttach() { - } + @Override + public void onAttach() {} - @Override - public void onDetach() { - } - -} \ No newline at end of file + @Override + public void onDetach() {} +} diff --git a/src/main/java/engine/components/Component.java b/src/main/java/engine/components/Component.java index 9cd5fc4c..e9a27e8e 100644 --- a/src/main/java/engine/components/Component.java +++ b/src/main/java/engine/components/Component.java @@ -4,62 +4,50 @@ /** * Represents a generic component within the scene graph architecture. - *
- * Components are modular pieces of behavior or functionality that can be added - * to {@link SceneNode} instances. They encapsulate logic, rendering, or other - * behaviors, following a component-based design pattern. - *
- *- * Each component should manage its lifecycle, with {@code onAttach()}, - * {@code update()}, and {@code onDetach()} methods, allowing nodes to manage - * their behavior lifecycle cleanly. - *
+ * + *Components are modular pieces of behavior or functionality that can be added to {@link + * SceneNode} instances. They encapsulate logic, rendering, or other behaviors, following a + * component-based design pattern. + * + *
Each component should manage its lifecycle, with {@code onAttach()}, {@code update()}, and + * {@code onDetach()} methods, allowing nodes to manage their behavior lifecycle cleanly. */ public interface Component { - /** - * Sets the owning {@link SceneNode} for this component. - *
- * This is called when the component is added to a {@link SceneNode}. The - * owning node serves as the context within which this component operates, - * allowing it to interact with other components or node transformations. - *
- * - * @param owner The {@link SceneNode} that owns this component; cannot be - * null. - */ - void setOwner(SceneNode owner); + /** + * Sets the owning {@link SceneNode} for this component. + * + *This is called when the component is added to a {@link SceneNode}. The owning node serves as + * the context within which this component operates, allowing it to interact with other components + * or node transformations. + * + * @param owner The {@link SceneNode} that owns this component; cannot be null. + */ + void setOwner(SceneNode owner); - /** - * Updates the component's logic every frame. - *
- * Called once per frame during the scene's update cycle. The time-per-frame - * (tpf) is passed in to allow for time-based animations or logic that depends - * on frame timing. - *
- * - * @param tpf The time per frame in seconds (time delta) since the last - * update. - */ - void update(float tpf); + /** + * Updates the component's logic every frame. + * + *Called once per frame during the scene's update cycle. The time-per-frame (tpf) is passed in + * to allow for time-based animations or logic that depends on frame timing. + * + * @param tpf The time per frame in seconds (time delta) since the last update. + */ + void update(float tpf); - /** - * Called when the component is attached to a {@link SceneNode}. - *
- * This allows the component to set up necessary resources, state, or perform - * other preparatory work specific to being added to a node. - *
- */ - void onAttach(); + /** + * Called when the component is attached to a {@link SceneNode}. + * + *This allows the component to set up necessary resources, state, or perform other preparatory + * work specific to being added to a node. + */ + void onAttach(); - /** - * Called when the component is detached from a {@link SceneNode}. - *
- * This ensures no memory is leaked, threads are terminated, or other - * resources are left hanging by cleaning up internal state and releasing - * references. - *
- */ - void onDetach(); - -} \ No newline at end of file + /** + * Called when the component is detached from a {@link SceneNode}. + * + *This ensures no memory is leaked, threads are terminated, or other resources are left + * hanging by cleaning up internal state and releasing references. + */ + void onDetach(); +} diff --git a/src/main/java/engine/components/ControlWASD.java b/src/main/java/engine/components/ControlWASD.java index 87b2d42e..7b1c49c4 100644 --- a/src/main/java/engine/components/ControlWASD.java +++ b/src/main/java/engine/components/ControlWASD.java @@ -6,158 +6,131 @@ import math.Vector3f; /** - * 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. - * - *
- * 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. - *
+ * 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. + * + *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. */ public class ControlWASD extends AbstractComponent { - /** - * The speed multiplier determines how fast the node moves per second. - */ - private float speed; - - /** - * The Input instance to monitor keyboard events (injected dependency). - */ - private Input input; - - /** - * Constructs a new ControlWASD component, injecting the Input logic needed - * for detecting key presses. - * - * @param input The Input system used to check for keyboard input events. Must - * not be null. - * @throws IllegalArgumentException if input is null. - */ - public ControlWASD(Input input) { - this(input, 1); - } - - /** - * Constructs a new ControlWASD component, injecting the Input logic needed - * for detecting key presses and allowing a custom movement speed multiplier. - * - *
- * This constructor allows developers to specify custom speeds for different - * movement styles, debugging scenarios, or game mechanics adjustments. - *
- * - * @param input The Input system used to monitor keyboard input events. Must - * not be null. - * @param speed The speed multiplier determining how fast the node moves per - * second. Must be non-negative. - * @throws IllegalArgumentException if input is null or speed is less than 0. - */ - public ControlWASD(Input input, float speed) { - if (input == null) { - throw new IllegalArgumentException("Input cannot be null."); - } - if (speed < 0) { - throw new IllegalArgumentException("Speed must be non-negative."); - } - this.input = input; - this.speed = 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 move the node smoothly in the 3D space by adjusting its - * position over time with respect to `tpf` (time per frame). - *
- * - * @param tpf Time per frame, used to ensure frame-rate-independent movement. - */ - @Override - public void update(float tpf) { - SceneNode node = getOwner(); - Vector3f velocity = handleInput(); - - Transform transform = node.getTransform(); - Vector3f position = transform.getPosition(); - - transform.setPosition(position.add(velocity.mult(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. - * - * @return A velocity vector representing the computed movement direction and - * speed. - */ - 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); - - // Normalize diagonal movement to prevent unintended speed boosts - if (velocity.length() > 0) { - velocity.normalizeLocal().multLocal(speed); - } - return velocity; - } - - /** - * Retrieves the current movement speed multiplier. - * - * @return The current speed value. - */ - public float getSpeed() { - return speed; - } - - /** - * Sets the movement speed multiplier for controlling how fast the node moves - * through the scene. - * - *- * A speed of 1.0 represents the default unit speed. Increasing this value - * will make the node move faster, while decreasing it will slow it down. - * Movement speed is scaled by elapsed time per frame to ensure - * frame-rate-independent movement. - *
- * - * @param speed The new speed value to set. Must be a non-negative number. - * @throws IllegalArgumentException if the speed is less than 0. - */ - public void setSpeed(float speed) { - if (speed < 0) { - throw new IllegalArgumentException("Speed must be non-negative."); - } - this.speed = speed; - } - - @Override - public void onAttach() { - } - - @Override - public void onDetach() { - } - -} \ No newline at end of file + /** The speed multiplier determines how fast the node moves per second. */ + private float speed; + + /** The Input instance to monitor keyboard events (injected dependency). */ + private Input input; + + /** + * Constructs a new ControlWASD component, injecting the Input logic needed for detecting key + * presses. + * + * @param input The Input system used to check for keyboard input events. Must not be null. + * @throws IllegalArgumentException if input is null. + */ + public ControlWASD(Input input) { + this(input, 1); + } + + /** + * Constructs a new ControlWASD component, injecting the Input logic needed for detecting key + * presses and allowing a custom movement speed multiplier. + * + *This constructor allows developers to specify custom speeds for different movement styles, + * debugging scenarios, or game mechanics adjustments. + * + * @param input The Input system used to monitor keyboard input events. Must not be null. + * @param speed The speed multiplier determining how fast the node moves per second. Must be + * non-negative. + * @throws IllegalArgumentException if input is null or speed is less than 0. + */ + public ControlWASD(Input input, float speed) { + if (input == null) { + throw new IllegalArgumentException("Input cannot be null."); + } + if (speed < 0) { + throw new IllegalArgumentException("Speed must be non-negative."); + } + this.input = input; + this.speed = 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 + * move the node smoothly in the 3D space by adjusting its position over time with respect to + * `tpf` (time per frame). + * + * @param tpf Time per frame, used to ensure frame-rate-independent movement. + */ + @Override + public void update(float tpf) { + SceneNode node = getOwner(); + Vector3f velocity = handleInput(); + + Transform transform = node.getTransform(); + Vector3f position = transform.getPosition(); + + transform.setPosition(position.add(velocity.mult(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. + * + * @return A velocity vector representing the computed movement direction and speed. + */ + 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); + + // Normalize diagonal movement to prevent unintended speed boosts + if (velocity.length() > 0) { + velocity.normalizeLocal().multLocal(speed); + } + return velocity; + } + + /** + * Retrieves the current movement speed multiplier. + * + * @return The current speed value. + */ + public float getSpeed() { + return speed; + } + + /** + * Sets the movement speed multiplier for controlling how fast the node moves through the scene. + * + *
A speed of 1.0 represents the default unit speed. Increasing this value will make the node + * move faster, while decreasing it will slow it down. Movement speed is scaled by elapsed time + * per frame to ensure frame-rate-independent movement. + * + * @param speed The new speed value to set. Must be a non-negative number. + * @throws IllegalArgumentException if the speed is less than 0. + */ + public void setSpeed(float speed) { + if (speed < 0) { + throw new IllegalArgumentException("Speed must be non-negative."); + } + this.speed = speed; + } + + @Override + public void onAttach() {} + + @Override + public void onDetach() {} +} diff --git a/src/main/java/engine/components/Geometry.java b/src/main/java/engine/components/Geometry.java index eb658fc6..823e599a 100644 --- a/src/main/java/engine/components/Geometry.java +++ b/src/main/java/engine/components/Geometry.java @@ -8,16 +8,14 @@ import workspace.ui.Graphics; /** - * The {@code Geometry} class represents a 3D object in a scene with a mesh and - * material applied to it. It is responsible for rendering the mesh and applying - * the appropriate material to it. The class also provides access to the mesh's - * bounding box, which is useful for purposes like culling, spatial - * partitioning, and debugging. - * - * This class implements the {@link RenderableComponent} interface, indicating - * that it has a render method to be invoked during the render loop of the - * engine. - * + * The {@code Geometry} class represents a 3D object in a scene with a mesh and material applied to + * it. It is responsible for rendering the mesh and applying the appropriate material to it. The + * class also provides access to the mesh's bounding box, which is useful for purposes like culling, + * spatial partitioning, and debugging. + * + *
This class implements the {@link RenderableComponent} interface, indicating that it has a + * render method to be invoked during the render loop of the engine. + * * @see RenderableComponent * @see Material * @see Mesh3D @@ -25,140 +23,128 @@ */ public class Geometry extends AbstractComponent implements RenderableComponent { - /** The mesh representing the geometry of the object. */ - private Mesh3D mesh; - - /** The material applied to the mesh for rendering. */ - private Material material; - - /** - * The bounding box of the mesh used for culling, spatial partitioning, and - * debugging. - */ - private Bounds bounds; - - /** - * Constructs a {@code Geometry} with the specified mesh and a default - * material. - * - * @param mesh The {@link Mesh3D} object representing the geometry of the - * object. - * @throws IllegalArgumentException If the mesh is {@code null}. - */ - public Geometry(Mesh3D mesh) { - this(mesh, Material.DEFAULT_WHITE); - } - - /** - * Constructs a {@code Geometry} with the specified mesh and material. - * - * @param mesh The {@link Mesh3D} object representing the geometry of the - * object. - * @param material The {@link Material} to be applied to the mesh. - * @throws IllegalArgumentException If the mesh or material is {@code null}. - */ - public Geometry(Mesh3D mesh, Material material) { - validate(mesh, material); - this.mesh = mesh; - this.material = material; - this.bounds = MeshBoundsCalculator.calculateBounds(mesh); - } - - /** - * Validates the mesh and material to ensure they are not {@code null}. - * - * @param mesh The {@link Mesh3D} object to validate. - * @param material The {@link Material} to validate. - * @throws IllegalArgumentException If the mesh or material is {@code null}. - */ - private void validate(Mesh3D mesh, Material material) { - if (mesh == null) { - throw new IllegalArgumentException("Mesh cannot be null."); - } - if (material == null) { - throw new IllegalArgumentException("Material cannot be null."); - } - } - - /** - * Renders the geometry by applying the material and drawing the mesh using - * the specified graphics context. - * - * @param g The {@link Graphics} context used for rendering. - */ - @Override - public void render(Graphics g) { - material.apply(g); - g.fillFaces(mesh); - material.release(g); - debugRenderBounds(g); - } - - /** - * Debugs the rendering by drawing the bounding box of the mesh using the - * specified graphics context. The bounding box is rendered in red to help - * visualize the mesh's extents. This method can be used for debugging - * purposes to ensure the mesh is properly positioned and scaled in the scene. - * - *
- * Beyond debugging, the bounding box is useful for spatial partitioning - * techniques like frustum culling, as well as for determining the overall - * size and position of the mesh in the 3D world. - *
- * - * @param g The {@link Graphics} context used for rendering the debug bounding - * box. - */ - public void debugRenderBounds(Graphics g) { - if (bounds == null) { - return; - } - - g.setColor(Color.RED); - - // Extract corner points for readability - float minX = bounds.getMin().x; - float minY = bounds.getMin().y; - float minZ = bounds.getMin().z; - float maxX = bounds.getMax().x; - float maxY = bounds.getMax().y; - float maxZ = bounds.getMax().z; - - // Draw lines for each edge of the bounding box - g.drawLine(minX, minY, minZ, maxX, minY, minZ); - g.drawLine(minX, minY, minZ, minX, maxY, minZ); - g.drawLine(minX, minY, minZ, minX, minY, maxZ); - - g.drawLine(maxX, maxY, maxZ, minX, maxY, maxZ); - g.drawLine(maxX, maxY, maxZ, maxX, minY, maxZ); - g.drawLine(maxX, maxY, maxZ, maxX, maxY, minZ); - - g.drawLine(minX, maxY, minZ, maxX, maxY, minZ); - g.drawLine(maxX, minY, minZ, maxX, maxY, minZ); - g.drawLine(maxX, minY, minZ, maxX, minY, maxZ); - - g.drawLine(minX, maxY, maxZ, minX, minY, maxZ); - g.drawLine(maxX, minY, maxZ, minX, minY, maxZ); - g.drawLine(minX, maxY, maxZ, minX, maxY, minZ); - } - - /** - * Updates the state of the geometry. This method is a placeholder for - * potential updates to the mesh state over time. - * - * @param tpf The time per frame used for the update (in seconds). - */ - @Override - public void update(float tpf) { - // Placeholder for potential mesh state updates - } - - @Override - public void onAttach() { - } - - @Override - public void onDetach() { - } - -} \ No newline at end of file + /** The mesh representing the geometry of the object. */ + private Mesh3D mesh; + + /** The material applied to the mesh for rendering. */ + private Material material; + + /** The bounding box of the mesh used for culling, spatial partitioning, and debugging. */ + private Bounds bounds; + + /** + * Constructs a {@code Geometry} with the specified mesh and a default material. + * + * @param mesh The {@link Mesh3D} object representing the geometry of the object. + * @throws IllegalArgumentException If the mesh is {@code null}. + */ + public Geometry(Mesh3D mesh) { + this(mesh, Material.DEFAULT_WHITE); + } + + /** + * Constructs a {@code Geometry} with the specified mesh and material. + * + * @param mesh The {@link Mesh3D} object representing the geometry of the object. + * @param material The {@link Material} to be applied to the mesh. + * @throws IllegalArgumentException If the mesh or material is {@code null}. + */ + public Geometry(Mesh3D mesh, Material material) { + validate(mesh, material); + this.mesh = mesh; + this.material = material; + this.bounds = MeshBoundsCalculator.calculateBounds(mesh); + } + + /** + * Validates the mesh and material to ensure they are not {@code null}. + * + * @param mesh The {@link Mesh3D} object to validate. + * @param material The {@link Material} to validate. + * @throws IllegalArgumentException If the mesh or material is {@code null}. + */ + private void validate(Mesh3D mesh, Material material) { + if (mesh == null) { + throw new IllegalArgumentException("Mesh cannot be null."); + } + if (material == null) { + throw new IllegalArgumentException("Material cannot be null."); + } + } + + /** + * Renders the geometry by applying the material and drawing the mesh using the specified graphics + * context. + * + * @param g The {@link Graphics} context used for rendering. + */ + @Override + public void render(Graphics g) { + material.apply(g); + g.fillFaces(mesh); + material.release(g); + debugRenderBounds(g); + } + + /** + * Debugs the rendering by drawing the bounding box of the mesh using the specified graphics + * context. The bounding box is rendered in red to help visualize the mesh's extents. This method + * can be used for debugging purposes to ensure the mesh is properly positioned and scaled in the + * scene. + * + *Beyond debugging, the bounding box is useful for spatial partitioning techniques like + * frustum culling, as well as for determining the overall size and position of the mesh in the 3D + * world. + * + * @param g The {@link Graphics} context used for rendering the debug bounding box. + */ + public void debugRenderBounds(Graphics g) { + if (bounds == null) { + return; + } + + g.setColor(Color.RED); + + // Extract corner points for readability + float minX = bounds.getMin().x; + float minY = bounds.getMin().y; + float minZ = bounds.getMin().z; + float maxX = bounds.getMax().x; + float maxY = bounds.getMax().y; + float maxZ = bounds.getMax().z; + + // Draw lines for each edge of the bounding box + g.drawLine(minX, minY, minZ, maxX, minY, minZ); + g.drawLine(minX, minY, minZ, minX, maxY, minZ); + g.drawLine(minX, minY, minZ, minX, minY, maxZ); + + g.drawLine(maxX, maxY, maxZ, minX, maxY, maxZ); + g.drawLine(maxX, maxY, maxZ, maxX, minY, maxZ); + g.drawLine(maxX, maxY, maxZ, maxX, maxY, minZ); + + g.drawLine(minX, maxY, minZ, maxX, maxY, minZ); + g.drawLine(maxX, minY, minZ, maxX, maxY, minZ); + g.drawLine(maxX, minY, minZ, maxX, minY, maxZ); + + g.drawLine(minX, maxY, maxZ, minX, minY, maxZ); + g.drawLine(maxX, minY, maxZ, minX, minY, maxZ); + g.drawLine(minX, maxY, maxZ, minX, maxY, minZ); + } + + /** + * Updates the state of the geometry. This method is a placeholder for potential updates to the + * mesh state over time. + * + * @param tpf The time per frame used for the update (in seconds). + */ + @Override + public void update(float tpf) { + // Placeholder for potential mesh state updates + } + + @Override + public void onAttach() {} + + @Override + public void onDetach() {} +} diff --git a/src/main/java/engine/components/RenderableComponent.java b/src/main/java/engine/components/RenderableComponent.java index b4a66416..4fad8d71 100644 --- a/src/main/java/engine/components/RenderableComponent.java +++ b/src/main/java/engine/components/RenderableComponent.java @@ -5,41 +5,33 @@ /** * Represents a renderable component within the scene graph architecture. - *
- * RenderComponents are modular pieces of rendering behavior that can be added - * to {@link SceneNode} instances. They follow the component-based design - * pattern, encapsulating rendering logic and enabling flexible and reusable - * visual behavior within a scene. - *
- *- * A RenderComponent should implement the rendering logic in its - * {@code render()} method, which is invoked during the rendering pass of a - * scene graph. It is designed to work with a graphics context (such as that - * represented by the {@link Graphics} class) to issue drawing commands. - *
- *- * Example use cases include rendering meshes, particles, UI overlays, or other - * visual elements as part of a node's rendering pipeline. - *
- * + * + *RenderComponents are modular pieces of rendering behavior that can be added to {@link + * SceneNode} instances. They follow the component-based design pattern, encapsulating rendering + * logic and enabling flexible and reusable visual behavior within a scene. + * + *
A RenderComponent should implement the rendering logic in its {@code render()} method, which + * is invoked during the rendering pass of a scene graph. It is designed to work with a graphics + * context (such as that represented by the {@link Graphics} class) to issue drawing commands. + * + *
Example use cases include rendering meshes, particles, UI overlays, or other visual elements + * as part of a node's rendering pipeline. + * * @see Component * @see SceneNode */ public interface RenderableComponent extends Component { - /** - * Renders this component using the provided graphics context. - *
- * This method is called during the rendering phase of the scene graph - * traversal. Implementations of this method should issue drawing commands - * using the provided {@link Graphics} instance. Components should encapsulate - * the logic necessary to visualize themselves or their state. - *
- * - * @param g The graphics context to use for rendering. This context provides - * methods for transformations, shading, drawing primitives, and - * other rendering operations. - */ - void render(Graphics g); - -} \ No newline at end of file + /** + * Renders this component using the provided graphics context. + * + *This method is called during the rendering phase of the scene graph traversal. + * Implementations of this method should issue drawing commands using the provided {@link + * Graphics} instance. Components should encapsulate the logic necessary to visualize themselves + * or their state. + * + * @param g The graphics context to use for rendering. This context provides methods for + * transformations, shading, drawing primitives, and other rendering operations. + */ + void render(Graphics g); +} diff --git a/src/main/java/engine/components/RotationComponent.java b/src/main/java/engine/components/RotationComponent.java index dbdab16f..f5b889f3 100644 --- a/src/main/java/engine/components/RotationComponent.java +++ b/src/main/java/engine/components/RotationComponent.java @@ -4,119 +4,110 @@ import math.Vector3f; /** - * A simple rotation component that rotates the owning SceneNode around a - * specified axis at a constant angular speed. - * - *
- * Properties: + * A simple rotation component that rotates the owning SceneNode around a specified axis at a + * constant angular speed. + * + *
Properties: + * *
- * The rotation is applied incrementally to the Transform component of the - * owning SceneNode during each frame update. - *
+ * + *The rotation is applied incrementally to the Transform component of the owning SceneNode + * during each frame update. */ public class RotationComponent extends AbstractComponent { - /** The axis around which the node will rotate. */ - private Vector3f axis; - - /** The angular speed of the rotation in radians per second. */ - private float angularSpeed; - - /** - * Constructs a RotationComponent with default axis (0, 1, 0) and angular - * speed. Default settings will rotate around the Y-axis. - */ - public RotationComponent() { - this.axis = new Vector3f(0, 1, 0); // Default rotation axis: Y-axis - this.angularSpeed = 1.0f; // Default angular speed: 1 radian/second - } - - /** - * Constructs a RotationComponent with a specified axis and angular speed. - * - * @param axis The axis of rotation (must not be null). - * @param angularSpeed The angular speed in radians per second. - */ - public RotationComponent(Vector3f axis, float angularSpeed) { - if (axis == null || axis.length() == 0) { - throw new IllegalArgumentException( - "Rotation axis cannot be null or zero-length."); - } - this.axis = axis.normalize(); // Normalize the axis to ensure proper - // rotation - this.angularSpeed = angularSpeed; - } - - /** - * Updates the rotation logic each frame. - * - * @param tpf Time per frame (in seconds since the last frame). - */ - @Override - public void update(float tpf) { - SceneNode node = getOwner(); - if (node == null) - return; - - // Calculate the incremental rotation angle - float angleIncrement = angularSpeed * tpf; - - // Apply rotation to the owning SceneNode's Transform - node.getTransform().rotate(axis.mult(angleIncrement)); - } - - /** - * Sets a new rotation axis for the component. - * - * @param axis The new axis of rotation. - */ - public void setAxis(Vector3f axis) { - if (axis == null || axis.length() == 0) { - throw new IllegalArgumentException( - "Rotation axis cannot be null or zero-length."); - } - this.axis = axis.normalize(); - } - - /** - * Sets the angular speed of the rotation. - * - * @param angularSpeed The new angular speed in radians per second. - */ - public void setAngularSpeed(float angularSpeed) { - this.angularSpeed = angularSpeed; - } - - /** - * Gets the current rotation axis. - * - * @return The axis of rotation. - */ - public Vector3f getAxis() { - return axis; - } - - /** - * Gets the current angular speed. - * - * @return The angular speed in radians per second. - */ - public float getAngularSpeed() { - return angularSpeed; - } - - @Override - public void onAttach() { - } - - @Override - public void onDetach() { - } - -} \ No newline at end of file + /** The axis around which the node will rotate. */ + private Vector3f axis; + + /** The angular speed of the rotation in radians per second. */ + private float angularSpeed; + + /** + * Constructs a RotationComponent with default axis (0, 1, 0) and angular speed. Default settings + * will rotate around the Y-axis. + */ + public RotationComponent() { + this.axis = new Vector3f(0, 1, 0); // Default rotation axis: Y-axis + this.angularSpeed = 1.0f; // Default angular speed: 1 radian/second + } + + /** + * Constructs a RotationComponent with a specified axis and angular speed. + * + * @param axis The axis of rotation (must not be null). + * @param angularSpeed The angular speed in radians per second. + */ + public RotationComponent(Vector3f axis, float angularSpeed) { + if (axis == null || axis.length() == 0) { + throw new IllegalArgumentException("Rotation axis cannot be null or zero-length."); + } + this.axis = axis.normalize(); // Normalize the axis to ensure proper + // rotation + this.angularSpeed = angularSpeed; + } + + /** + * Updates the rotation logic each frame. + * + * @param tpf Time per frame (in seconds since the last frame). + */ + @Override + public void update(float tpf) { + SceneNode node = getOwner(); + if (node == null) return; + + // Calculate the incremental rotation angle + float angleIncrement = angularSpeed * tpf; + + // Apply rotation to the owning SceneNode's Transform + node.getTransform().rotate(axis.mult(angleIncrement)); + } + + /** + * Sets a new rotation axis for the component. + * + * @param axis The new axis of rotation. + */ + public void setAxis(Vector3f axis) { + if (axis == null || axis.length() == 0) { + throw new IllegalArgumentException("Rotation axis cannot be null or zero-length."); + } + this.axis = axis.normalize(); + } + + /** + * Sets the angular speed of the rotation. + * + * @param angularSpeed The new angular speed in radians per second. + */ + public void setAngularSpeed(float angularSpeed) { + this.angularSpeed = angularSpeed; + } + + /** + * Gets the current rotation axis. + * + * @return The axis of rotation. + */ + public Vector3f getAxis() { + return axis; + } + + /** + * Gets the current angular speed. + * + * @return The angular speed in radians per second. + */ + public float getAngularSpeed() { + return angularSpeed; + } + + @Override + public void onAttach() {} + + @Override + public void onDetach() {} +} diff --git a/src/main/java/engine/components/Transform.java b/src/main/java/engine/components/Transform.java index dd4a0d84..834ba661 100644 --- a/src/main/java/engine/components/Transform.java +++ b/src/main/java/engine/components/Transform.java @@ -4,242 +4,216 @@ import workspace.ui.Graphics; /** - * Represents a transformation in 3D space, encapsulating position, rotation, - * and scale. Implements the Component interface. - *
- * The {@code Transform} class provides functionality to manipulate and apply - * transformations such as translation, rotation, and scaling to objects or - * nodes in a 3D scene. It acts as a helper utility to represent and modify the - * spatial properties of a 3D object or a scene node. - *
- *- * The transformations are defined by: + * Represents a transformation in 3D space, encapsulating position, rotation, and scale. Implements + * the Component interface. + * + *
The {@code Transform} class provides functionality to manipulate and apply transformations + * such as translation, rotation, and scaling to objects or nodes in a 3D scene. It acts as a helper + * utility to represent and modify the spatial properties of a 3D object or a scene node. + * + *
The transformations are defined by: + * *
- * This class provides methods for applying transformations to a rendering - * context, modifying transformations incrementally, and setting transformation - * properties explicitly. - *
- * + * + *This class provides methods for applying transformations to a rendering context, modifying + * transformations incrementally, and setting transformation properties explicitly. + * * @see Vector3f * @see Graphics */ public class Transform extends AbstractComponent { - /** The position of this transform in 3D space. */ - private Vector3f position; - - /** - * The rotation (in radians) of this transform around the X, Y, and Z axes. - */ - private Vector3f rotation; - - /** The scaling factors along the X, Y, and Z axes. */ - private Vector3f scale; - - /** - * Constructs a new {@code Transform} with default position, rotation, and - * scale values. - *
- * The default position is set to (0, 0, 0), the default rotation is (0, 0, - * 0), and the default scale is (1, 1, 1). - *
- */ - public Transform() { - this.position = new Vector3f(); - this.rotation = new Vector3f(); - this.scale = new Vector3f(1, 1, 1); - } - - /** - * Applies this transformation to the given graphics context. - *- * This method translates the context to the object's position, applies - * rotations around the X, Y, and Z axes, and scales the object using the - * defined scale values. - *
- * - * @param g The graphics context to which this transformation is applied. - */ - public void apply(Graphics g) { - g.translate(position.x, position.y, position.z); - g.rotateX(rotation.x); - g.rotateY(rotation.y); - g.rotateZ(rotation.z); - g.scale(scale.x, scale.y, scale.z); - } - - /** - * Translates this transformation by the given delta vector. - *- * This modifies the position of the transform by adding the provided vector - * to the current position. - *
- * - * @param delta The vector representing the change in position. - */ - public void translate(Vector3f delta) { - this.position.addLocal(delta); - } - - /** - * Rotates this transformation by the given delta vector (in radians). - *- * This modifies the rotation of the transform by adding the provided vector's - * values to the current rotation. - *
- * - * @param delta The vector representing the change in rotation (in radians). - */ - public void rotate(Vector3f delta) { - this.rotation.addLocal(delta); - } - - /** - * Scales this transformation by the provided scaling factors. - *- * This modifies the scale of the transform by multiplying the current scale - * values by the provided factors. - *
- * - * @param factor The vector representing the scale factors to apply along each - * axis. - */ - public void scale(Vector3f factor) { - this.scale.multLocal(factor); - } - - /** - * Retrieves a copy of the position vector. - * - * @return A new Vector3f instance representing the current position. - */ - public Vector3f getPosition() { - return new Vector3f(position); - } - - /** - * Sets the position of this transform to a specific value. - * - * @param position The new position vector to set. Must not be null. - * @throws IllegalArgumentException if the provided vector is null. - */ - public void setPosition(Vector3f position) { - if (position == null) { - throw new IllegalArgumentException("Position cannot be null."); - } - this.position.set(position); - } - - /** - * Sets the position of this transform to the specified coordinates. - *- * This method updates the position vector to the provided (x, y, z) values, - * effectively moving the object to the given location in 3D space. - *
- * - * @param x The X-coordinate of the new position. - * @param y The Y-coordinate of the new position. - * @param z The Z-coordinate of the new position. - */ - public void setPosition(float x, float y, float z) { - this.position.set(x, y, z); - } - - /** - * Retrieves a copy of the rotation vector. - * - * @return A new Vector3f instance representing the current rotation. - */ - public Vector3f getRotation() { - return new Vector3f(rotation); - } - - /** - * Sets the rotation of this transform to a specific value. - * - * @param rotation The new rotation vector (in radians) to set. Must not be - * null. - * @throws IllegalArgumentException if the provided vector is null. - */ - public void setRotation(Vector3f rotation) { - if (rotation == null) { - throw new IllegalArgumentException("Rotation cannot be null."); - } - this.rotation.set(rotation); - } - - /** - * Sets the rotation of this transform to the specified angles in radians. - *- * This method updates the rotation vector to the provided (rx, ry, rz) - * values, which represent the rotation of the object around the X, Y, and Z - * axes, respectively. - *
- * - * @param rx The rotation angle around the X-axis, in radians. - * @param ry The rotation angle around the Y-axis, in radians. - * @param rz The rotation angle around the Z-axis, in radians. - */ - public void setRotation(float rx, float ry, float rz) { - this.rotation.set(rx, ry, rz); - } - - /** - * Retrieves a copy of the scale vector. - * - * @return A new Vector3f instance representing the current scale. - */ - public Vector3f getScale() { - return new Vector3f(scale); - } - - /** - * Sets the scale of this transform to a specific value. - * - * @param scale The new scale vector to set. Must not be null. - * @throws IllegalArgumentException if the provided vector is null. - */ - public void setScale(Vector3f scale) { - if (scale == null) { - throw new IllegalArgumentException("Scale cannot be null."); - } - this.scale.set(scale); - } - - /** - * Sets the scale of this transform to the specified scale factors along each - * axis. - *- * This method updates the scale vector to the provided (sx, sy, sz) values, - * allowing the object to be scaled uniformly or non-uniformly along the X, Y, - * and Z axes. - *
- * - * @param sx The scale factor along the X-axis. - * @param sy The scale factor along the Y-axis. - * @param sz The scale factor along the Z-axis. - */ - public void setScale(float sx, float sy, float sz) { - this.scale.set(sx, sy, sz); - } - - @Override - public void update(float tpf) { - } - - @Override - public void onAttach() { - } - - @Override - public void onDetach() { - } - -} \ No newline at end of file + /** The position of this transform in 3D space. */ + private Vector3f position; + + /** The rotation (in radians) of this transform around the X, Y, and Z axes. */ + private Vector3f rotation; + + /** The scaling factors along the X, Y, and Z axes. */ + private Vector3f scale; + + /** + * Constructs a new {@code Transform} with default position, rotation, and scale values. + * + *The default position is set to (0, 0, 0), the default rotation is (0, 0, 0), and the default + * scale is (1, 1, 1). + */ + public Transform() { + this.position = new Vector3f(); + this.rotation = new Vector3f(); + this.scale = new Vector3f(1, 1, 1); + } + + /** + * Applies this transformation to the given graphics context. + * + *
This method translates the context to the object's position, applies rotations around the X, + * Y, and Z axes, and scales the object using the defined scale values. + * + * @param g The graphics context to which this transformation is applied. + */ + public void apply(Graphics g) { + g.translate(position.x, position.y, position.z); + g.rotateX(rotation.x); + g.rotateY(rotation.y); + g.rotateZ(rotation.z); + g.scale(scale.x, scale.y, scale.z); + } + + /** + * Translates this transformation by the given delta vector. + * + *
This modifies the position of the transform by adding the provided vector to the current + * position. + * + * @param delta The vector representing the change in position. + */ + public void translate(Vector3f delta) { + this.position.addLocal(delta); + } + + /** + * Rotates this transformation by the given delta vector (in radians). + * + *
This modifies the rotation of the transform by adding the provided vector's values to the + * current rotation. + * + * @param delta The vector representing the change in rotation (in radians). + */ + public void rotate(Vector3f delta) { + this.rotation.addLocal(delta); + } + + /** + * Scales this transformation by the provided scaling factors. + * + *
This modifies the scale of the transform by multiplying the current scale values by the + * provided factors. + * + * @param factor The vector representing the scale factors to apply along each axis. + */ + public void scale(Vector3f factor) { + this.scale.multLocal(factor); + } + + /** + * Retrieves a copy of the position vector. + * + * @return A new Vector3f instance representing the current position. + */ + public Vector3f getPosition() { + return new Vector3f(position); + } + + /** + * Sets the position of this transform to a specific value. + * + * @param position The new position vector to set. Must not be null. + * @throws IllegalArgumentException if the provided vector is null. + */ + public void setPosition(Vector3f position) { + if (position == null) { + throw new IllegalArgumentException("Position cannot be null."); + } + this.position.set(position); + } + + /** + * Sets the position of this transform to the specified coordinates. + * + *
This method updates the position vector to the provided (x, y, z) values, effectively moving + * the object to the given location in 3D space. + * + * @param x The X-coordinate of the new position. + * @param y The Y-coordinate of the new position. + * @param z The Z-coordinate of the new position. + */ + public void setPosition(float x, float y, float z) { + this.position.set(x, y, z); + } + + /** + * Retrieves a copy of the rotation vector. + * + * @return A new Vector3f instance representing the current rotation. + */ + public Vector3f getRotation() { + return new Vector3f(rotation); + } + + /** + * Sets the rotation of this transform to a specific value. + * + * @param rotation The new rotation vector (in radians) to set. Must not be null. + * @throws IllegalArgumentException if the provided vector is null. + */ + public void setRotation(Vector3f rotation) { + if (rotation == null) { + throw new IllegalArgumentException("Rotation cannot be null."); + } + this.rotation.set(rotation); + } + + /** + * Sets the rotation of this transform to the specified angles in radians. + * + *
This method updates the rotation vector to the provided (rx, ry, rz) values, which represent + * the rotation of the object around the X, Y, and Z axes, respectively. + * + * @param rx The rotation angle around the X-axis, in radians. + * @param ry The rotation angle around the Y-axis, in radians. + * @param rz The rotation angle around the Z-axis, in radians. + */ + public void setRotation(float rx, float ry, float rz) { + this.rotation.set(rx, ry, rz); + } + + /** + * Retrieves a copy of the scale vector. + * + * @return A new Vector3f instance representing the current scale. + */ + public Vector3f getScale() { + return new Vector3f(scale); + } + + /** + * Sets the scale of this transform to a specific value. + * + * @param scale The new scale vector to set. Must not be null. + * @throws IllegalArgumentException if the provided vector is null. + */ + public void setScale(Vector3f scale) { + if (scale == null) { + throw new IllegalArgumentException("Scale cannot be null."); + } + this.scale.set(scale); + } + + /** + * Sets the scale of this transform to the specified scale factors along each axis. + * + *
This method updates the scale vector to the provided (sx, sy, sz) values, allowing the
+ * object to be scaled uniformly or non-uniformly along the X, Y, and Z axes.
+ *
+ * @param sx The scale factor along the X-axis.
+ * @param sy The scale factor along the Y-axis.
+ * @param sz The scale factor along the Z-axis.
+ */
+ public void setScale(float sx, float sy, float sz) {
+ this.scale.set(sx, sy, sz);
+ }
+
+ @Override
+ public void update(float tpf) {}
+
+ @Override
+ public void onAttach() {}
+
+ @Override
+ public void onDetach() {}
+}
diff --git a/src/main/java/engine/debug/DebugInfoUpdater.java b/src/main/java/engine/debug/DebugInfoUpdater.java
new file mode 100644
index 00000000..a6c890c1
--- /dev/null
+++ b/src/main/java/engine/debug/DebugInfoUpdater.java
@@ -0,0 +1,128 @@
+package engine.debug;
+
+import java.lang.management.ManagementFactory;
+import java.util.Collection;
+
+import com.sun.management.OperatingSystemMXBean;
+
+import engine.Timer;
+import engine.input.Input;
+import engine.input.Key;
+import engine.scene.Scene;
+
+/**
+ * The {@code DebugInfoUpdater} class is responsible for updating debug information displayed by a
+ * {@code DebugOverlay}. It collects information from various sources such as the {@code Timer},
+ * {@code Scene}, and {@code Input}.
+ */
+public class DebugInfoUpdater {
+
+ private static final String CATEGORY_TIME = "Time";
+
+ private static final String CATEGORY_INPUT = "Input";
+
+ private static final String CATEGORY_SCENE = "Scene";
+
+ private static final String CATEGORY_SYSTEM = "System";
+
+ private static final String CATEGORY_OS = "OS";
+
+ private final DebugOverlay debugOverlay;
+
+ private final PerformanceMetrics performanceMetrics = new PerformanceMetrics();
+
+ private final MemoryMetrics memoryMetrics = new MemoryMetrics();
+
+ private final DrawCallCounter drawCallCounter = new DrawCallCounter();
+
+ private final OperatingSystemMXBean osBean =
+ (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
+
+ /**
+ * Constructs a new {@code DebugInfoUpdater} with the given {@code DebugOverlay}.
+ *
+ * @param debugOverlay the overlay where debug information will be displayed.
+ * @throws IllegalArgumentException if {@code debugOverlay} is {@code null}.
+ */
+ public DebugInfoUpdater(DebugOverlay debugOverlay) {
+ if (debugOverlay == null) {
+ throw new IllegalArgumentException("DebugOverlay cannot be null.");
+ }
+ this.debugOverlay = debugOverlay;
+ }
+
+ private void setInfo(String category, String key, String value) {
+ debugOverlay.setDebugItem(category, key, value);
+ }
+
+ private void setInfo(String category, String key, float value) {
+ setInfo(category, key, String.valueOf(value));
+ }
+
+ private void setInfo(String category, String key, boolean value) {
+ setInfo(category, key, String.valueOf(value));
+ }
+
+ private void setInfo(String category, String key, int value) {
+ setInfo(category, key, String.valueOf(value));
+ }
+
+ /**
+ * Updates debug information by collecting data from the given sources.
+ *
+ * @param timer the {@code Timer} providing time-related debug information.
+ * @param activeScene the active {@code Scene}, or {@code null} if no scene is active.
+ * @param input the {@code Input} providing input-related debug information.
+ */
+ public void update(Timer timer, Scene activeScene, Input input) {
+ updateTimeMetrics(timer);
+ updatePerformanceMetrics();
+ updateInputMetrics(input);
+ if (activeScene != null) {
+ updateSceneMetrics(activeScene);
+ }
+ updateOsMetrics();
+ }
+
+ private String keysToString(Collection
- * A Material defines how a 3D object interacts with light, determining its
- * visual appearance under various lighting conditions. It encapsulates
- * attributes such as ambient, diffuse, and specular light coefficients,
- * shininess, and base color, which collectively define how light reflects off
- * the object's surface.
- *
- * This class includes predefined materials for common use cases (e.g., default
- * white, black, or other colors) and supports the creation of custom materials
- * with specific properties through the use of {@link Builder}.
- *
- * The material properties can control effects like reflectivity, surface
- * roughness, and color, enabling diverse visual representations for 3D meshes
- * in a rendering engine.
- * A Material defines how a 3D object interacts with light, determining its visual appearance
+ * under various lighting conditions. It encapsulates attributes such as ambient, diffuse, and
+ * specular light coefficients, shininess, and base color, which collectively define how light
+ * reflects off the object's surface.
+ *
+ * This class includes predefined materials for common use cases (e.g., default white, black, or
+ * other colors) and supports the creation of custom materials with specific properties through the
+ * use of {@link Builder}.
+ *
+ * The material properties can control effects like reflectivity, surface roughness, and color,
+ * enabling diverse visual representations for 3D meshes in a rendering engine.
*/
public class Material {
- /**
- * Default white material with a base white color and standard light
- * coefficients.
- */
- public static final Material DEFAULT_WHITE = new Material(Color.WHITE);
-
- /**
- * Default black material with a base black color and standard light
- * coefficients.
- */
- public static final Material DEFAULT_BLACK = new Material(Color.BLACK);
-
- /**
- * Default red material with a base red color and standard light coefficients.
- */
- public static final Material DEFAULT_RED = new Material(Color.RED);
-
- /**
- * Default green material with a base green color and standard light
- * coefficients.
- */
- public static final Material DEFAULT_GREEN = new Material(Color.GREEN);
-
- /**
- * Default blue material with a base blue color and standard light
- * coefficients.
- */
- public static final Material DEFAULT_BLUE = new Material(Color.BLUE);
-
- /**
- * Metallic silver material with a shiny silver appearance.
- */
- public static final Material METALLIC_SILVER_MATERIAL = MaterialFactory
- .createMetallicSilver();
-
- /**
- * Metallic gold material with a shiny gold appearance.
- */
- public static final Material GOLDEN_METALLIC_MATERIAL = MaterialFactory
- .createGoldenMetallic();
-
- /**
- * Clear glass material with a transparent appearance.
- */
- public static final Material GLASS_CLEAR_MATERIAL = MaterialFactory
- .createGlassClear();
-
- /**
- * Stone grey material with a matte grey appearance.
- */
- public static final Material STONE_GREY_MATERIAL = MaterialFactory
- .createStoneGrey();
-
- /**
- * Water material with a reflective blue appearance.
- */
- public static final Material WATER_MATERIAL = MaterialFactory.createWater();
-
- private boolean useLighting;
-
- /**
- * Base color for the material.
- */
- private final Color color;
-
- /**
- * Ambient light coefficient (R, G, B).
- */
- private final float[] ambient;
-
- /**
- * Diffuse light coefficient (R, G, B).
- */
- private final float[] diffuse;
-
- /**
- * Specular light coefficient (R, G, B).
- */
- private final float[] specular;
-
- /**
- * Shininess factor for specular highlights.
- */
- private final float shininess;
-
- /**
- * Constructor to set the base color of the material.
- *
- * @param color The base color of the material.
- */
- public Material(Color color) {
- this(new Builder().setColor(color));
- }
-
- private Material(Builder builder) {
- this.useLighting = builder.useLighting;
- this.color = builder.color;
- this.ambient = builder.ambient;
- this.diffuse = builder.diffuse;
- this.specular = builder.specular;
- this.shininess = builder.shininess;
- }
-
- /**
- * Builder class to facilitate the creation of custom materials with specific
- * lighting and shader properties.
- */
- public static class Builder {
-
- private boolean useLighting = true;
-
- private Color color = new Color(1, 1, 1); // Default color is white
-
- private float[] ambient = new float[] { 0.2f, 0.2f, 0.2f };
-
- private float[] diffuse = new float[] { 1.0f, 1.0f, 1.0f };
-
- private float[] specular = new float[] { 1.0f, 1.0f, 1.0f };
-
- private float shininess = 10.0f;
-
- /**
- * Sets the base color of the material.
- *
- * @param color The desired base color.
- * @return The builder instance for chaining.
- */
- public Builder setColor(Color color) {
- this.color = color;
- return this;
- }
-
- /**
- * Sets the ambient light coefficient of the material.
- *
- * @param ambient The desired ambient light coefficient (R, G, B).
- * @return The builder instance for chaining.
- */
- public Builder setAmbient(float[] ambient) {
- this.ambient = ambient;
- return this;
- }
-
- /**
- * Sets the diffuse light coefficient of the material.
- *
- * @param diffuse The desired diffuse light coefficient (R, G, B).
- * @return The builder instance for chaining.
- */
- public Builder setDiffuse(float[] diffuse) {
- this.diffuse = diffuse;
- return this;
- }
-
- /**
- * Sets the specular light coefficient of the material.
- *
- * @param specular The desired specular light coefficient (R, G, B).
- * @return The builder instance for chaining.
- */
- public Builder setSpecular(float[] specular) {
- this.specular = specular;
- return this;
- }
-
- /**
- * Sets the shininess value of the material.
- *
- * @param shininess The shininess factor for specular highlights.
- * @return The builder instance for chaining.
- */
- public Builder setShininess(float shininess) {
- this.shininess = shininess;
- return this;
- }
-
- public Builder setUseLighting(boolean useLighting) {
- this.useLighting = useLighting;
- return this;
- }
-
- /**
- * Builds and returns the Material instance with the set properties.
- *
- * @return A new instance of {@link Material}.
- */
- public Material build() {
- return new Material(this);
- }
- }
-
- /**
- * Applies this material's properties to the provided rendering context.
- *
- * @param g The {@link Graphics} instance to apply this material to.
- */
- public void apply(Graphics g) {
- g.setMaterial(this);
- }
-
- /**
- * Releases this material's properties from the rendering context, useful for
- * cleaning up shaders or material-specific settings.
- *
- * @param g The {@link Graphics} instance from which this material will be
- * unbound.
- */
- public void release(Graphics g) {
- // Logic for releasing or resetting rendering context goes here
- }
-
- public boolean isUseLighting() {
- return useLighting;
- }
-
- /**
- * Retrieves the base color of the material.
- *
- * @return The {@link Color} representing the base color of the material.
- */
- public Color getColor() {
- return color;
- }
-
- /**
- * Retrieves the ambient light coefficient of the material.
- *
- * @return An array representing the ambient light coefficient (R, G, B).
- */
- public float[] getAmbient() {
- return ambient;
- }
-
- /**
- * Retrieves the diffuse light coefficient of the material.
- *
- * @return An array representing the diffuse light coefficient (R, G, B).
- */
- public float[] getDiffuse() {
- return diffuse;
- }
-
- /**
- * Retrieves the specular light coefficient of the material.
- *
- * @return An array representing the specular light coefficient (R, G, B).
- */
- public float[] getSpecular() {
- return specular;
- }
-
- /**
- * Retrieves the shininess factor of the material.
- *
- * @return The shininess factor that controls specular highlights.
- */
- public float getShininess() {
- return shininess;
- }
-
-}
\ No newline at end of file
+ /** Default white material with a base white color and standard light coefficients. */
+ public static final Material DEFAULT_WHITE = new Material(Color.WHITE);
+
+ /** Default black material with a base black color and standard light coefficients. */
+ public static final Material DEFAULT_BLACK = new Material(Color.BLACK);
+
+ /** Default red material with a base red color and standard light coefficients. */
+ public static final Material DEFAULT_RED = new Material(Color.RED);
+
+ /** Default green material with a base green color and standard light coefficients. */
+ public static final Material DEFAULT_GREEN = new Material(Color.GREEN);
+
+ /** Default blue material with a base blue color and standard light coefficients. */
+ public static final Material DEFAULT_BLUE = new Material(Color.BLUE);
+
+ /** Metallic silver material with a shiny silver appearance. */
+ public static final Material METALLIC_SILVER_MATERIAL = MaterialFactory.createMetallicSilver();
+
+ /** Metallic gold material with a shiny gold appearance. */
+ public static final Material GOLDEN_METALLIC_MATERIAL = MaterialFactory.createGoldenMetallic();
+
+ /** Clear glass material with a transparent appearance. */
+ public static final Material GLASS_CLEAR_MATERIAL = MaterialFactory.createGlassClear();
+
+ /** Stone grey material with a matte grey appearance. */
+ public static final Material STONE_GREY_MATERIAL = MaterialFactory.createStoneGrey();
+
+ /** Water material with a reflective blue appearance. */
+ public static final Material WATER_MATERIAL = MaterialFactory.createWater();
+
+ private boolean useLighting;
+
+ /** Base color for the material. */
+ private final Color color;
+
+ /** Ambient light coefficient (R, G, B). */
+ private final float[] ambient;
+
+ /** Diffuse light coefficient (R, G, B). */
+ private final float[] diffuse;
+
+ /** Specular light coefficient (R, G, B). */
+ private final float[] specular;
+
+ /** Shininess factor for specular highlights. */
+ private final float shininess;
+
+ /**
+ * Constructor to set the base color of the material.
+ *
+ * @param color The base color of the material.
+ */
+ public Material(Color color) {
+ this(new Builder().setColor(color));
+ }
+
+ private Material(Builder builder) {
+ this.useLighting = builder.useLighting;
+ this.color = builder.color;
+ this.ambient = builder.ambient;
+ this.diffuse = builder.diffuse;
+ this.specular = builder.specular;
+ this.shininess = builder.shininess;
+ }
+
+ /**
+ * Builder class to facilitate the creation of custom materials with specific lighting and shader
+ * properties.
+ */
+ public static class Builder {
+
+ private boolean useLighting = true;
+
+ private Color color = new Color(1, 1, 1); // Default color is white
+
+ private float[] ambient = new float[] {0.2f, 0.2f, 0.2f};
+
+ private float[] diffuse = new float[] {1.0f, 1.0f, 1.0f};
+
+ private float[] specular = new float[] {1.0f, 1.0f, 1.0f};
+
+ private float shininess = 10.0f;
+
+ /**
+ * Sets the base color of the material.
+ *
+ * @param color The desired base color.
+ * @return The builder instance for chaining.
+ */
+ public Builder setColor(Color color) {
+ this.color = color;
+ return this;
+ }
+
+ /**
+ * Sets the ambient light coefficient of the material.
+ *
+ * @param ambient The desired ambient light coefficient (R, G, B).
+ * @return The builder instance for chaining.
+ */
+ public Builder setAmbient(float[] ambient) {
+ this.ambient = ambient;
+ return this;
+ }
+
+ /**
+ * Sets the diffuse light coefficient of the material.
+ *
+ * @param diffuse The desired diffuse light coefficient (R, G, B).
+ * @return The builder instance for chaining.
+ */
+ public Builder setDiffuse(float[] diffuse) {
+ this.diffuse = diffuse;
+ return this;
+ }
+
+ /**
+ * Sets the specular light coefficient of the material.
+ *
+ * @param specular The desired specular light coefficient (R, G, B).
+ * @return The builder instance for chaining.
+ */
+ public Builder setSpecular(float[] specular) {
+ this.specular = specular;
+ return this;
+ }
+
+ /**
+ * Sets the shininess value of the material.
+ *
+ * @param shininess The shininess factor for specular highlights.
+ * @return The builder instance for chaining.
+ */
+ public Builder setShininess(float shininess) {
+ this.shininess = shininess;
+ return this;
+ }
+
+ public Builder setUseLighting(boolean useLighting) {
+ this.useLighting = useLighting;
+ return this;
+ }
+
+ /**
+ * Builds and returns the Material instance with the set properties.
+ *
+ * @return A new instance of {@link Material}.
+ */
+ public Material build() {
+ return new Material(this);
+ }
+ }
+
+ /**
+ * Applies this material's properties to the provided rendering context.
+ *
+ * @param g The {@link Graphics} instance to apply this material to.
+ */
+ public void apply(Graphics g) {
+ g.setMaterial(this);
+ }
+
+ /**
+ * Releases this material's properties from the rendering context, useful for cleaning up shaders
+ * or material-specific settings.
+ *
+ * @param g The {@link Graphics} instance from which this material will be unbound.
+ */
+ public void release(Graphics g) {
+ // Logic for releasing or resetting rendering context goes here
+ }
+
+ public boolean isUseLighting() {
+ return useLighting;
+ }
+
+ /**
+ * Retrieves the base color of the material.
+ *
+ * @return The {@link Color} representing the base color of the material.
+ */
+ public Color getColor() {
+ return color;
+ }
+
+ /**
+ * Retrieves the ambient light coefficient of the material.
+ *
+ * @return An array representing the ambient light coefficient (R, G, B).
+ */
+ public float[] getAmbient() {
+ return ambient;
+ }
+
+ /**
+ * Retrieves the diffuse light coefficient of the material.
+ *
+ * @return An array representing the diffuse light coefficient (R, G, B).
+ */
+ public float[] getDiffuse() {
+ return diffuse;
+ }
+
+ /**
+ * Retrieves the specular light coefficient of the material.
+ *
+ * @return An array representing the specular light coefficient (R, G, B).
+ */
+ public float[] getSpecular() {
+ return specular;
+ }
+
+ /**
+ * Retrieves the shininess factor of the material.
+ *
+ * @return The shininess factor that controls specular highlights.
+ */
+ public float getShininess() {
+ return shininess;
+ }
+}
diff --git a/src/main/java/engine/render/MaterialFactory.java b/src/main/java/engine/render/MaterialFactory.java
index 0360b0a0..24dd29ee 100644
--- a/src/main/java/engine/render/MaterialFactory.java
+++ b/src/main/java/engine/render/MaterialFactory.java
@@ -3,119 +3,107 @@
import math.Color;
/**
- * Factory class for creating predefined Material instances using a builder
- * pattern.
- *
- * This class provides various predefined material configurations, such as
- * metallic silver, golden metallic, clear glass, stone grey, and water effects.
- * Each material can be created through the builder pattern to encapsulate
- * lighting properties like ambient, diffuse, specular colors, and shininess
- * levels.
- *
- * The goal of these predefined materials is to simplify common rendering use
- * cases, such as simulating metals, glass, stone, and water effects, by
- * abstracting their creation and configuration logic.
- *
- * Each static method in this class returns a fully built Material object, ready
- * for use in rendering pipelines or graphics engines.
- * This class provides various predefined material configurations, such as metallic silver,
+ * golden metallic, clear glass, stone grey, and water effects. Each material can be created through
+ * the builder pattern to encapsulate lighting properties like ambient, diffuse, specular colors,
+ * and shininess levels.
+ *
+ * The goal of these predefined materials is to simplify common rendering use cases, such as
+ * simulating metals, glass, stone, and water effects, by abstracting their creation and
+ * configuration logic.
+ *
+ * Each static method in this class returns a fully built Material object, ready for use in
+ * rendering pipelines or graphics engines.
*/
public class MaterialFactory {
- /**
- * Creates a metallic silver material with preset properties.
- *
- * This material has a diffuse color representing silver, with ambient,
- * diffuse, and specular properties tuned for a metallic effect.
- * This material has a diffuse color representing silver, with ambient, diffuse, and specular
+ * properties tuned for a metallic effect.
+ *
+ * @return A {@link Material} instance configured as metallic silver.
+ */
+ public static Material createMetallicSilver() {
+ return new Material.Builder()
+ .setColor(new Color(0.75f, 0.75f, 0.75f))
+ .setAmbient(new float[] {0.2f, 0.2f, 0.2f})
+ .setDiffuse(new float[] {1.0f, 1.0f, 1.0f})
+ .setSpecular(new float[] {1.0f, 1.0f, 1.0f})
+ .setShininess(50.0f)
+ .build();
+ }
- /**
- * Creates a golden metallic material with preset properties.
- *
- * This material mimics the reflective, shiny properties of gold.
- * This material mimics the reflective, shiny properties of gold.
+ *
+ * @return A {@link Material} instance configured as golden metallic.
+ */
+ public static Material createGoldenMetallic() {
+ return new Material.Builder()
+ .setColor(new Color(1.0f, 0.84f, 0.0f))
+ .setAmbient(new float[] {0.3f, 0.3f, 0.3f})
+ .setDiffuse(new float[] {1.0f, 0.84f, 0.0f})
+ .setSpecular(new float[] {1.0f, 1.0f, 0.5f})
+ .setShininess(100.0f)
+ .build();
+ }
- /**
- * Creates a clear glass-like material with preset properties.
- *
- * This material simulates the transparency and reflection characteristics of
- * clear glass.
- * This material simulates the transparency and reflection characteristics of clear glass.
+ *
+ * @return A {@link Material} instance configured as clear glass.
+ */
+ public static Material createGlassClear() {
+ return new Material.Builder()
+ .setColor(new Color(0.7f, 0.9f, 1.0f))
+ .setAmbient(new float[] {0.3f, 0.3f, 0.3f})
+ .setDiffuse(new float[] {0.8f, 0.8f, 0.8f})
+ .setSpecular(new float[] {1.0f, 1.0f, 1.0f})
+ .setShininess(5.0f)
+ .build();
+ }
- /**
- * Creates a stone grey material with preset properties.
- *
- * This material has a matte stone-like appearance, suitable for terrain,
- * rocky surfaces, or architectural models.
- * This material has a matte stone-like appearance, suitable for terrain, rocky surfaces, or
+ * architectural models.
+ *
+ * @return A {@link Material} instance configured as stone grey.
+ */
+ public static Material createStoneGrey() {
+ return new Material.Builder()
+ .setColor(new Color(0.5f, 0.5f, 0.5f))
+ .setAmbient(new float[] {0.4f, 0.4f, 0.4f})
+ .setDiffuse(new float[] {0.6f, 0.6f, 0.6f})
+ .setSpecular(new float[] {0.2f, 0.2f, 0.2f})
+ .setShininess(10.0f)
+ .build();
+ }
- /**
- * Creates a water-like material with preset properties.
- *
- * This material simulates the transparency and light-reflective properties of
- * water, incorporating a mix of blue and subtle reflective colors.
- * This material simulates the transparency and light-reflective properties of water,
+ * incorporating a mix of blue and subtle reflective colors.
+ *
+ * @return A {@link Material} instance configured as water.
+ */
+ public static Material createWater() {
+ return new Material.Builder()
+ .setColor(new Color(0.0f, 0.5f, 1.0f))
+ .setAmbient(new float[] {0.1f, 0.3f, 0.5f})
+ .setDiffuse(new float[] {0.3f, 0.5f, 0.7f})
+ .setSpecular(new float[] {0.2f, 0.2f, 0.6f})
+ .setShininess(2.0f)
+ .build();
+ }
}
diff --git a/src/main/java/engine/render/effects/Particle.java b/src/main/java/engine/render/effects/Particle.java
index 7e413908..53bc0bbb 100644
--- a/src/main/java/engine/render/effects/Particle.java
+++ b/src/main/java/engine/render/effects/Particle.java
@@ -3,166 +3,153 @@
import math.Vector3f;
/**
- * Represents a single particle with physical properties and lifecycle tracking.
- * This class models particles' motion using physics principles like velocity,
- * acceleration, damping (drag), and lifetime constraints.
- *
- * Designed to support effects like trails, smoke, explosions, or other particle
- * effects by integrating old and new positions for rendering purposes.
- * Designed to support effects like trails, smoke, explosions, or other particle effects by
+ * integrating old and new positions for rendering purposes.
+ *
* @author Simon Dietz
*/
public class Particle {
- /** The current position of the particle in world-space coordinates. */
- private Vector3f position;
-
- /**
- * The previous position of the particle; used for effects like trails or
- * motion blur.
- */
- private Vector3f oldPosition;
-
- /**
- * The velocity vector of the particle, representing how it moves over time.
- */
- private Vector3f velocity;
-
- /** The acceleration vector affecting the particle's motion. */
- private Vector3f acceleration;
-
- /** Total duration (in seconds) for which the particle will live. */
- private float lifetime;
-
- /** Tracks how much time has elapsed since the particle was created. */
- private float elapsedTime;
-
- /** The damping factor simulates air resistance or drag on the particle. */
- private float dampingFactor = 0.98f;
-
- /**
- * Constructs a new particle with the specified initial position, velocity,
- * acceleration, and lifetime. Initializes the old position to match the
- * initial position.
- *
- * @param position Initial position of the particle in 3D space.
- * @param velocity Initial velocity vector of the particle.
- * @param acceleration Acceleration vector affecting the particle.
- * @param lifetime Total time (seconds) this particle will live.
- */
- public Particle(Vector3f position, Vector3f velocity, Vector3f acceleration,
- float lifetime) {
- this.position = new Vector3f(position);
- this.oldPosition = new Vector3f(position);
- this.velocity = velocity;
- this.acceleration = acceleration;
- this.lifetime = lifetime;
- this.elapsedTime = 0f;
- }
-
- /**
- * Applies a force to the particle by adding it to the acceleration vector.
- * Useful for simulating environmental effects like wind or gravity.
- *
- * Assumes that mass is constant and equals 1 for simplicity.
- * Assumes that mass is constant and equals 1 for simplicity.
+ *
+ * @param force The force vector to apply to the particle's acceleration.
+ */
+ public void applyForce(Vector3f force) {
+ acceleration.addLocal(force);
+ }
+
+ /**
+ * Applies damping to simulate drag or resistance, slowing down the particle's motion over time.
+ */
+ public void applyDamping() {
+ velocity.multLocal(dampingFactor);
+ }
+
+ /**
+ * Updates the particle's position, velocity, and applies damping effects over a given time step.
+ * Resets acceleration after each update to ensure isolated force application each frame.
+ *
+ * @param deltaTime The time elapsed since the last frame (in seconds).
+ */
+ public void update(float deltaTime) {
+ elapsedTime += deltaTime;
+
+ oldPosition.set(position);
+
+ // Apply physics: velocity changes due to acceleration
+ velocity.addLocal(acceleration.mult(deltaTime));
+
+ // Apply environmental drag/damping
+ applyDamping();
+
+ // Update position based on the new velocity
+ position.addLocal(velocity.mult(deltaTime));
+
+ // Reset acceleration for the next simulation step
+ acceleration.set(0, 0, 0);
+ }
+
+ /**
+ * Checks if the particle is still alive (i.e., has not exceeded its lifetime).
+ *
+ * @return {@code true} if the particle's elapsed time is less than its total lifetime, otherwise
+ * {@code false}.
+ */
+ public boolean isAlive() {
+ return elapsedTime < lifetime;
+ }
+
+ /**
+ * Gets the current position of the particle in 3D space.
+ *
+ * @return The current position vector of the particle.
+ */
+ public Vector3f getPosition() {
+ return position;
+ }
+
+ /**
+ * Gets the previous position of the particle. Useful for rendering trails or other visual
+ * effects.
+ *
+ * @return The old position vector of the particle.
+ */
+ public Vector3f getOldPosition() {
+ return oldPosition;
+ }
+
+ /**
+ * Gets the amount of time that has elapsed since the particle was created.
+ *
+ * @return The elapsed time in seconds.
+ */
+ public float getElapsedTime() {
+ return elapsedTime;
+ }
+
+ /**
+ * Gets the total lifetime of the particle.
+ *
+ * @return The total lifetime of the particle in seconds.
+ */
+ public float getLifetime() {
+ return lifetime;
+ }
+
+ /**
+ * Sets the particle's lifetime to a new value. This can extend or shorten how long the particle
+ * will exist.
+ *
+ * @param lifetime New lifetime value in seconds.
+ */
+ public void setLifetime(float lifetime) {
+ this.lifetime = lifetime;
+ }
}
diff --git a/src/main/java/engine/render/effects/ParticleComponent.java b/src/main/java/engine/render/effects/ParticleComponent.java
index 991d164a..e5012955 100644
--- a/src/main/java/engine/render/effects/ParticleComponent.java
+++ b/src/main/java/engine/render/effects/ParticleComponent.java
@@ -5,115 +5,102 @@
import workspace.ui.Graphics;
/**
- * A component responsible for managing and rendering particles using a
- * specified particle emitter and renderer.
- *
- * This class implements the {@link RenderableComponent} interface, allowing it to
- * integrate seamlessly with the rendering system. It uses a
- * {@link ParticleEmitter} to handle the logic of particle spawning and updates,
- * while delegating rendering operations to a {@link ParticleRenderer}.
- *
- * The ParticleComponent ensures proper lifecycle management by handling
- * initialization, updates, rendering, and cleanup for both the emitter and
- * renderer components.
- * This class implements the {@link RenderableComponent} interface, allowing it to integrate
+ * seamlessly with the rendering system. It uses a {@link ParticleEmitter} to handle the logic of
+ * particle spawning and updates, while delegating rendering operations to a {@link
+ * ParticleRenderer}.
+ *
+ * The ParticleComponent ensures proper lifecycle management by handling initialization, updates,
+ * rendering, and cleanup for both the emitter and renderer components.
+ *
* @author Simon Dietz
*/
-public class ParticleComponent extends AbstractComponent
- implements RenderableComponent {
+public class ParticleComponent extends AbstractComponent implements RenderableComponent {
- private ParticleEmitter emitter;
+ private ParticleEmitter emitter;
- private ParticleRenderer renderer;
+ private ParticleRenderer renderer;
- /**
- * Creates a new ParticleComponent with the given particle emitter and
- * renderer.
- *
- * @param emitter The particle emitter responsible for spawning and managing
- * particle lifecycles.
- * @param renderer The particle renderer responsible for drawing particles on
- * the provided graphics context.
- */
- public ParticleComponent(ParticleEmitter emitter, ParticleRenderer renderer) {
- this.emitter = emitter;
- this.renderer = renderer;
- }
+ /**
+ * Creates a new ParticleComponent with the given particle emitter and renderer.
+ *
+ * @param emitter The particle emitter responsible for spawning and managing particle lifecycles.
+ * @param renderer The particle renderer responsible for drawing particles on the provided
+ * graphics context.
+ */
+ public ParticleComponent(ParticleEmitter emitter, ParticleRenderer renderer) {
+ this.emitter = emitter;
+ this.renderer = renderer;
+ }
- /**
- * Initializes the renderer resources necessary for drawing particles.
- */
- @Override
- public void initialize() {
- renderer.initialize();
- }
+ /** Initializes the renderer resources necessary for drawing particles. */
+ @Override
+ public void onAttach() {
+ renderer.initialize();
+ }
- /**
- * Updates the particle emitter with the time-per-frame value to spawn and
- * manage particles over time.
- */
- @Override
- public void update(float tpf) {
- emitter.update(tpf);
- }
+ /**
+ * Updates the particle emitter with the time-per-frame value to spawn and manage particles over
+ * time.
+ */
+ @Override
+ public void update(float tpf) {
+ emitter.update(tpf);
+ }
- /**
- * Delegates the rendering of particles to the renderer, passing the current
- * particles to visualize.
- */
- @Override
- public void render(Graphics g) {
- renderer.render(g, emitter.getParticles());
- }
+ /**
+ * Delegates the rendering of particles to the renderer, passing the current particles to
+ * visualize.
+ */
+ @Override
+ public void render(Graphics g) {
+ renderer.render(g, emitter.getParticles());
+ }
- /**
- * Cleans up any resources used by the particle renderer.
- */
- @Override
- public void cleanup() {
- renderer.cleanup();
- }
+ /** Cleans up any resources used by the particle renderer. */
+ @Override
+ public void onDetach() {
+ renderer.cleanup();
+ }
- /**
- * Retrieves the particle emitter associated with this component.
- *
- * @return The ParticleEmitter instance used for spawning and updating
- * particles.
- */
- public ParticleEmitter getEmitter() {
- return emitter;
- }
+ /**
+ * Retrieves the particle emitter associated with this component.
+ *
+ * @return The ParticleEmitter instance used for spawning and updating particles.
+ */
+ public ParticleEmitter getEmitter() {
+ return emitter;
+ }
- /**
- * Sets a new particle emitter for this component. This can be used to
- * dynamically change the emitter's behavior or particle spawning logic at
- * runtime.
- *
- * @param emitter The new ParticleEmitter instance.
- */
- public void setEmitter(ParticleEmitter emitter) {
- this.emitter = emitter;
- }
+ /**
+ * Sets a new particle emitter for this component. This can be used to dynamically change the
+ * emitter's behavior or particle spawning logic at runtime.
+ *
+ * @param emitter The new ParticleEmitter instance.
+ */
+ public void setEmitter(ParticleEmitter emitter) {
+ this.emitter = emitter;
+ }
- /**
- * Retrieves the particle renderer associated with this component.
- *
- * @return The ParticleRenderer responsible for drawing particles.
- */
- public ParticleRenderer getRenderer() {
- return renderer;
- }
-
- /**
- * Sets a new particle renderer for this component. This allows for swapping
- * rendering strategies or visualizations dynamically at runtime.
- *
- * @param renderer The new ParticleRenderer instance.
- */
- public void setRenderer(ParticleRenderer renderer) {
- this.renderer = renderer;
- }
+ /**
+ * Retrieves the particle renderer associated with this component.
+ *
+ * @return The ParticleRenderer responsible for drawing particles.
+ */
+ public ParticleRenderer getRenderer() {
+ return renderer;
+ }
+ /**
+ * Sets a new particle renderer for this component. This allows for swapping rendering strategies
+ * or visualizations dynamically at runtime.
+ *
+ * @param renderer The new ParticleRenderer instance.
+ */
+ public void setRenderer(ParticleRenderer renderer) {
+ this.renderer = renderer;
+ }
}
diff --git a/src/main/java/engine/render/effects/ParticleEmitter.java b/src/main/java/engine/render/effects/ParticleEmitter.java
index b6aaf710..8d1999f4 100644
--- a/src/main/java/engine/render/effects/ParticleEmitter.java
+++ b/src/main/java/engine/render/effects/ParticleEmitter.java
@@ -5,206 +5,194 @@
import math.Vector3f;
/**
- * Represents a particle emitter responsible for generating and managing
- * particles based on defined parameters like velocity, acceleration, and
- * lifetime ranges. This class supports both continuous particle emission and
- * burst-based emission modes.
- *
- * The emitter allows for dynamic configuration of properties such as initial
- * velocity ranges, acceleration ranges, particle lifetime ranges, and the
- * emission rate. It uses a thread-safe queue to manage particles for efficient
- * access and updates.
- *
- * Key Features:
- * The emitter allows for dynamic configuration of properties such as initial velocity ranges,
+ * acceleration ranges, particle lifetime ranges, and the emission rate. It uses a thread-safe queue
+ * to manage particles for efficient access and updates.
+ *
+ * Key Features:
+ *
*
- * This method provides the total count of lights that are part of the scene's
- * lighting system. It operates in a thread-safe manner by synchronizing
- * access to the shared lights list to ensure accurate and safe read
- * operations, even when lights are being added or removed concurrently.
- * This method provides the total count of lights that are part of the scene's lighting system.
+ * It operates in a thread-safe manner by synchronizing access to the shared lights list to ensure
+ * accurate and safe read operations, even when lights are being added or removed concurrently.
+ *
+ * @return The total number of lights currently in the scene.
+ */
+ public int getLightCount() {
+ synchronized (lights) {
+ return lights.size();
+ }
+ }
+
+ /**
+ * Retrieves the name of the scene.
+ *
+ * @return The name of the scene.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Checks if the scene is in wireframe rendering mode.
+ *
+ * @return {@code true} if wireframe mode is enabled, {@code false} otherwise.
+ */
+ public boolean isWireframeMode() {
+ return wireframeMode;
+ }
+
+ /**
+ * Enables or disables wireframe rendering mode for the scene.
+ *
+ * @param wireframeMode {@code true} to enable wireframe mode, {@code false} to disable it.
+ */
+ public void setWireframeMode(boolean wireframeMode) {
+ this.wireframeMode = wireframeMode;
+ }
+}
diff --git a/src/main/java/engine/scene/SceneNode.java b/src/main/java/engine/scene/SceneNode.java
index 5a5a6550..64cc3808 100644
--- a/src/main/java/engine/scene/SceneNode.java
+++ b/src/main/java/engine/scene/SceneNode.java
@@ -10,366 +10,343 @@
/**
* Represents a single node within the scene graph.
- *
- * A {@code SceneNode} is a fundamental building block in a hierarchical scene
- * graph structure, managing child nodes, components, and transformations. Scene
- * nodes are organized in a parent-child relationship, where a parent node can
- * have multiple children, and each child can have its own components and
- * transformations.
- *
- * The {@code SceneNode} manages its own transformation through a
- * {@link Transform} object, handles rendering, updates logic for itself and its
- * children, and provides methods for managing components like
- * {@link RenderableComponent}.
- *
- * Example use cases include:
+ *
+ * A {@code SceneNode} is a fundamental building block in a hierarchical scene graph structure,
+ * managing child nodes, components, and transformations. Scene nodes are organized in a
+ * parent-child relationship, where a parent node can have multiple children, and each child can
+ * have its own components and transformations.
+ *
+ * The {@code SceneNode} manages its own transformation through a {@link Transform} object,
+ * handles rendering, updates logic for itself and its children, and provides methods for managing
+ * components like {@link RenderableComponent}.
+ *
+ * Example use cases include:
+ *
*
- *
- *
+ *
* @author Simon Dietz
*/
public class ParticleEmitter {
- /** The world-space origin of the particle emitter. */
- private Vector3f position;
-
- /** The range for randomizing initial particle velocities. */
- private Vector3f velocityRange;
-
- /** The range for randomizing initial particle accelerations. */
- private Vector3f accelerationRange;
-
- /** The range of possible particle lifetimes. */
- private float lifetimeRange;
-
- /** The rate at which particles are emitted (particles per second). */
- private int particlesPerSecond;
-
- /** Whether the emitter is currently configured for burst emission mode. */
- private boolean burstMode;
-
- /** Number of particles to emit during each burst. */
- private int burstCount;
-
- /**
- * Tracks elapsed time to determine when particles should be emitted during
- * continuous mode.
- */
- private float timeSinceLastEmission = 0f;
-
- /** A thread-safe queue storing active particles. */
- private ConcurrentLinkedQueue
- *
- *
- * This method applies the local transformation, renders components, and - * traverses through all child nodes to render them as well. This ensures the - * entire subtree rooted at this node is rendered properly. - *
- * - * @param g The graphics context used for rendering this node and its - * children. - */ - public void render(Graphics g) { - g.pushMatrix(); - - applyLocalTransform(g); - renderComponents(g); - - for (SceneNode child : children) { - child.render(g); - } - - g.popMatrix(); - } - - /** - * Applies the local transformation to the graphics context. - */ - private void applyLocalTransform(Graphics g) { - getTransform().apply(g); - } - - /** - * Renders all associated {@link RenderableComponent} instances attached to this - * node. - *- * This method iterates through all render components and calls their - * respective rendering logic. - *
- * - * @param g The graphics context used for rendering. - */ - protected void renderComponents(Graphics g) { - for (RenderableComponent renderer : getRenderComponents()) { - renderer.render(g); - } - } - - /** - * Updates this node's logic and propagates updates to children nodes. - * - * @param tpf The time per frame in seconds (delta time). - */ - public void update(float tpf) { - updateComponents(tpf); - updateChildren(tpf); - } - - /** - * Updates all components attached to this node. - * - * @param tpf The time per frame in seconds. - */ - protected void updateComponents(float tpf) { - for (Component component : components) { - component.update(tpf); - } - } - - /** - * Updates all child nodes of this node recursively. - * - * @param tpf The time per frame in seconds. - */ - protected void updateChildren(float tpf) { - for (SceneNode child : children) { - child.update(tpf); - } - } - - /** - * Cleans up this node's resources, components, and children recursively. - *- * Each component and child is cleaned up to ensure no resources are left - * hanging, preventing memory leaks or unwanted behavior. - *
- */ - public void cleanup() { - for (Component component : components) { - try { - component.cleanup(); - } catch (Exception e) { - System.err.println("Error cleaning up component: " + e.getMessage()); - } - } - - for (SceneNode child : children) { - child.cleanup(); - } - - components.clear(); - children.clear(); - } - - /** - * Adds a child node to this node's hierarchy. - *- * Prevents the addition of null nodes and ensures no duplicate child is - * added. - *
- * - * @param child The child {@code SceneNode} to add. - */ - public void addChild(SceneNode child) { - if (child == null) { - throw new IllegalArgumentException("Child node cannot be null."); - } - if (children.contains(child)) { - return; - } - child.parent = this; - children.add(child); - } - - /** - * Removes a child node from this node's hierarchy. - *- * Cleans up the child before removing it to ensure no resources are leaked. - *
- * - * @param child The child {@code SceneNode} to remove. - */ - public void removeChild(SceneNode child) { - if (child == null) { - return; - } - if (!children.contains(child)) { - return; - } - child.cleanup(); - child.parent = null; - children.remove(child); - } - - /** - * Adds a component to this node. - *- * Ensures that duplicate components of the same instance are not added. - *
- * - * @param component The {@link Component} to add. - * @throws IllegalArgumentException if the component is null. - */ - public void addComponent(Component component) { - if (component == null) { - throw new IllegalArgumentException("Component cannot be null."); - } - if (!components.contains(component)) { - components.add(component); - component.setOwner(this); - component.initialize(); - } - } - - /** - * Removes a component from this node. - *- * If the component is found, it is cleaned up before removal. - *
- * - * @param component The {@link Component} to remove. - * @throws IllegalArgumentException if the component is null. - */ - public void removeComponent(Component component) { - if (component == null) { - throw new IllegalArgumentException("Component cannot be null."); - } - if (components.contains(component)) { - components.remove(component); - component.cleanup(); - component.setOwner(null); - } - } - - /** - * Retrieves the first component of the specified type attached to this node. - * - * @param componentClass The class type of the component to retrieve. - * @param- * Enables querying for specific types of behavior or functionality attached - * to a node. - *
- * - * @param componentClass The class type of the component to retrieve. - * @paramThis method applies the local transformation, renders components, and traverses through all + * child nodes to render them as well. This ensures the entire subtree rooted at this node is + * rendered properly. + * + * @param g The graphics context used for rendering this node and its children. + */ + public void render(Graphics g) { + g.pushMatrix(); + + applyLocalTransform(g); + renderComponents(g); + + for (SceneNode child : children) { + child.render(g); + } + + g.popMatrix(); + } + + /** Applies the local transformation to the graphics context. */ + private void applyLocalTransform(Graphics g) { + getTransform().apply(g); + } + + /** + * Renders all associated {@link RenderableComponent} instances attached to this node. + * + *
This method iterates through all render components and calls their respective rendering + * logic. + * + * @param g The graphics context used for rendering. + */ + protected void renderComponents(Graphics g) { + for (RenderableComponent renderer : getRenderComponents()) { + renderer.render(g); + } + } + + /** + * Updates this node's logic and propagates updates to children nodes. + * + * @param tpf The time per frame in seconds (delta time). + */ + public void update(float tpf) { + updateComponents(tpf); + updateChildren(tpf); + } + + /** + * Updates all components attached to this node. + * + * @param tpf The time per frame in seconds. + */ + protected void updateComponents(float tpf) { + for (Component component : components) { + component.update(tpf); + } + } + + /** + * Updates all child nodes of this node recursively. + * + * @param tpf The time per frame in seconds. + */ + protected void updateChildren(float tpf) { + for (SceneNode child : children) { + child.update(tpf); + } + } + + /** + * Cleans up this node's resources, components, and children recursively. + * + *
Each component and child is cleaned up to ensure no resources are left hanging, preventing + * memory leaks or unwanted behavior. + */ + public void cleanup() { + for (Component component : components) { + try { + component.onDetach(); + component.setOwner(null); + } catch (Exception e) { + System.err.println("Error cleaning up component: " + e.getMessage()); + } + } + + for (SceneNode child : children) { + child.cleanup(); + } + + components.clear(); + children.clear(); + } + + /** + * Adds a child node to this node's hierarchy. + * + *
Prevents the addition of null nodes and ensures no duplicate child is added. + * + * @param child The child {@code SceneNode} to add. + */ + public void addChild(SceneNode child) { + if (child == null) { + throw new IllegalArgumentException("Child node cannot be null."); + } + if (children.contains(child)) { + return; + } + child.parent = this; + children.add(child); + } + + /** + * Removes a child node from this node's hierarchy. + * + *
Cleans up the child before removing it to ensure no resources are leaked. + * + * @param child The child {@code SceneNode} to remove. + */ + public void removeChild(SceneNode child) { + if (child == null) { + return; + } + if (!children.contains(child)) { + return; + } + child.cleanup(); + child.parent = null; + children.remove(child); + } + + /** + * Adds a component to this node. + * + *
Ensures that duplicate components of the same instance are not added. + * + * @param component The {@link Component} to add. + * @throws IllegalArgumentException if the component is null. + */ + public void addComponent(Component component) { + if (component == null) { + throw new IllegalArgumentException("Component cannot be null."); + } + if (!components.contains(component)) { + components.add(component); + component.setOwner(this); + component.onAttach(); + } + } + + /** + * Removes a component from this node. + * + *
If the component is found, it is cleaned up before removal.
+ *
+ * @param component The {@link Component} to remove.
+ * @throws IllegalArgumentException if the component is null.
+ */
+ public void removeComponent(Component component) {
+ if (component == null) {
+ throw new IllegalArgumentException("Component cannot be null.");
+ }
+ if (components.contains(component)) {
+ components.remove(component);
+ component.onDetach();
+ component.setOwner(null);
+ }
+ }
+
+ /**
+ * Retrieves the first component of the specified type attached to this node.
+ *
+ * @param componentClass The class type of the component to retrieve.
+ * @param Enables querying for specific types of behavior or functionality attached to a node.
+ *
+ * @param componentClass The class type of the component to retrieve.
+ * @param
- * Cameras are responsible for defining how 3D scenes are projected onto a 2D
- * screen and can be configured for various projection modes, such as
- * perspective and orthographic projections. Implementations should define their
- * specific logic for projection and view matrix handling, field of view
- * adjustments, clipping plane configuration, and conversion between screen and
- * world coordinates.
- * Cameras are responsible for defining how 3D scenes are projected onto a 2D screen and can be
+ * configured for various projection modes, such as perspective and orthographic projections.
+ * Implementations should define their specific logic for projection and view matrix handling, field
+ * of view adjustments, clipping plane configuration, and conversion between screen and world
+ * coordinates.
*/
public interface Camera {
- Vector3f getTarget();
-
- /**
- * Retrieves the camera's transformation.
- *
- * The transformation defines the position, rotation, and scaling of the
- * camera in the 3D world. This transformation is used to compute the camera's
- * position and orientation in world space.
- *
- * The view matrix is used to transform world-space coordinates into camera
- * (view) space. It is typically derived from the camera's position and
- * orientation in the 3D world.
- *
- * The projection matrix defines how a 3D scene is projected onto a 2D
- * viewport, depending on the camera's projection settings (perspective or
- * orthographic).
- *
- * This method should recalculate the view matrix whenever the camera's
- * position or orientation has changed.
- *
- * This method should be called whenever changes are made to parameters like
- * the field of view, near or far clipping planes, or aspect ratio.
- *
- * The field of view determines how wide or narrow the camera's view is and
- * only applies to perspective projections.
- *
- * This only has an effect on cameras configured for perspective projection.
- *
- * The near clipping plane defines the closest distance from the camera at
- * which objects are rendered. Objects closer than this distance will not be
- * visible.
- *
- * This modifies how close an object must be to the camera to be visible.
- *
- * The far clipping plane defines the furthest distance from the camera at
- * which objects are rendered. Objects farther away than this distance will
- * not be visible.
- *
- * This modifies how far objects can be from the camera to remain visible.
- *
- * The aspect ratio is defined as the ratio of the viewport's width to its
- * height and is used to adjust the projection matrix accordingly.
- *
- * Changing the aspect ratio should trigger an update to the projection
- * matrix.
- *
- * This method is essential for raycasting operations, such as determining
- * which objects in the scene correspond to a given 2D screen-space click.
- *
- * This is the inverse of projection and can be used for operations like
- * object picking or determining intersections between screen-space inputs and
- * 3D objects in the world.
- * The transformation defines the position, rotation, and scaling of the camera in the 3D
+ * world. This transformation is used to compute the camera's position and orientation in world
+ * space.
+ *
+ * @return The {@link Transform} representing the camera's position, rotation, and scaling.
+ */
+ Transform getTransform();
+
+ /**
+ * Retrieves the current view matrix of the camera.
+ *
+ * The view matrix is used to transform world-space coordinates into camera (view) space. It is
+ * typically derived from the camera's position and orientation in the 3D world.
+ *
+ * @return The view matrix as a {@link Matrix4f}.
+ */
+ Matrix4f getViewMatrix();
+
+ /**
+ * Retrieves the current projection matrix of the camera.
+ *
+ * The projection matrix defines how a 3D scene is projected onto a 2D viewport, depending on
+ * the camera's projection settings (perspective or orthographic).
+ *
+ * @return The projection matrix as a {@link Matrix4f}.
+ */
+ Matrix4f getProjectionMatrix();
+
+ /**
+ * Updates the view matrix based on the current transformation.
+ *
+ * This method should recalculate the view matrix whenever the camera's position or orientation
+ * has changed.
+ */
+ void updateViewMatrix();
+
+ /**
+ * Updates the projection matrix based on camera-specific settings.
+ *
+ * This method should be called whenever changes are made to parameters like the field of view,
+ * near or far clipping planes, or aspect ratio.
+ */
+ void updateProjectionMatrix();
+
+ /**
+ * Retrieves the field of view (FOV) for perspective cameras.
+ *
+ * The field of view determines how wide or narrow the camera's view is and only applies to
+ * perspective projections.
+ *
+ * @return The current field of view in degrees.
+ */
+ float getFieldOfView();
+
+ /**
+ * Sets the field of view (FOV) for perspective cameras.
+ *
+ * This only has an effect on cameras configured for perspective projection.
+ *
+ * @param fov The desired field of view in degrees.
+ */
+ void setFieldOfView(float fov);
+
+ /**
+ * Retrieves the near clipping plane distance.
+ *
+ * The near clipping plane defines the closest distance from the camera at which objects are
+ * rendered. Objects closer than this distance will not be visible.
+ *
+ * @return The near clipping plane distance.
+ */
+ float getNearPlane();
+
+ /**
+ * Sets the near clipping plane distance.
+ *
+ * This modifies how close an object must be to the camera to be visible.
+ *
+ * @param nearPlane The desired near clipping plane distance.
+ */
+ void setNearPlane(float nearPlane);
+
+ /**
+ * Retrieves the far clipping plane distance.
+ *
+ * The far clipping plane defines the furthest distance from the camera at which objects are
+ * rendered. Objects farther away than this distance will not be visible.
+ *
+ * @return The far clipping plane distance.
+ */
+ float getFarPlane();
+
+ /**
+ * Sets the far clipping plane distance.
+ *
+ * This modifies how far objects can be from the camera to remain visible.
+ *
+ * @param farPlane The desired far clipping plane distance.
+ */
+ void setFarPlane(float farPlane);
+
+ /**
+ * Retrieves the current aspect ratio of the camera's viewport.
+ *
+ * The aspect ratio is defined as the ratio of the viewport's width to its height and is used
+ * to adjust the projection matrix accordingly.
+ *
+ * @return The aspect ratio of the viewport.
+ */
+ float getAspectRatio();
+
+ /**
+ * Sets the aspect ratio for the camera's viewport.
+ *
+ * Changing the aspect ratio should trigger an update to the projection matrix.
+ *
+ * @param aspectRatio The desired aspect ratio.
+ */
+ void setAspectRatio(float aspectRatio);
+
+ /**
+ * Converts 2D screen coordinates to a 3D ray in world space.
+ *
+ * This method is essential for raycasting operations, such as determining which objects in the
+ * scene correspond to a given 2D screen-space click.
+ *
+ * @param screenX The x-coordinate on the screen.
+ * @param screenY The y-coordinate on the screen.
+ * @param viewportWidth The width of the viewport in pixels.
+ * @param viewportHeight The height of the viewport in pixels.
+ * @return A {@link Ray3f} representing the computed ray in 3D world space.
+ */
+ Ray3f createRay(float screenX, float screenY, int viewportWidth, int viewportHeight);
+
+ /**
+ * Converts 2D screen-space coordinates to their corresponding world-space coordinates.
+ *
+ * This is the inverse of projection and can be used for operations like object picking or
+ * determining intersections between screen-space inputs and 3D objects in the world.
+ *
+ * @param screenCoords The 2D screen-space coordinates to unproject.
+ * @param viewportWidth The width of the viewport in pixels.
+ * @param viewportHeight The height of the viewport in pixels.
+ * @return The corresponding 3D world-space coordinates as a {@link Vector3f}.
+ */
+ Vector3f unproject(Vector3f screenCoords, int viewportWidth, int viewportHeight);
+}
diff --git a/src/main/java/engine/scene/light/AmbientLight.java b/src/main/java/engine/scene/light/AmbientLight.java
index b6e52e9b..3d15772c 100644
--- a/src/main/java/engine/scene/light/AmbientLight.java
+++ b/src/main/java/engine/scene/light/AmbientLight.java
@@ -4,17 +4,13 @@
/**
* Represents an ambient light source in a 3D scene.
- *
- *
- * Ambient light provides uniform, non-directional illumination throughout the
- * entire 3D scene. Unlike other types of lights, such as point lights or
- * directional lights, ambient light does not have a specific position or
- * direction, and it affects all objects equally, regardless of their location
- * or orientation. This is ideal for simulating indirect or global lighting
- * effects that ensure objects are visible even when not directly illuminated by
- * other light sources.
- * Ambient light provides uniform, non-directional illumination throughout the entire 3D scene.
+ * Unlike other types of lights, such as point lights or directional lights, ambient light does not
+ * have a specific position or direction, and it affects all objects equally, regardless of their
+ * location or orientation. This is ideal for simulating indirect or global lighting effects that
+ * ensure objects are visible even when not directly illuminated by other light sources.
+ *
*
- * A directional light simulates light emitted from a distant source, such as
- * the sun or moon. Unlike point lights or spotlights, directional lights have
- * no specific position, and their light rays travel in a uniform direction
- * throughout the scene. This makes them ideal for creating consistent lighting
- * over large areas.
- * A directional light simulates light emitted from a distant source, such as the sun or moon.
+ * Unlike point lights or spotlights, directional lights have no specific position, and their light
+ * rays travel in a uniform direction throughout the scene. This makes them ideal for creating
+ * consistent lighting over large areas.
+ *
*
- * This constructor initializes the light with the following defaults: -
- * Color: White light. RGB(255, 255, 255) - Direction: A downward-facing
- * vector (0, 1, 0), simulating overhead light. - Intensity: 1.0 (full
- * strength).
- * This constructor initializes the light with the following defaults: - Color: White light.
+ * RGB(255, 255, 255) - Direction: A downward-facing vector (0, 1, 0), simulating overhead light.
+ * - Intensity: 1.0 (full strength).
+ */
+ public DirectionalLight() {
+ this(Color.WHITE, new Vector3f(0, 1, 0), 1.0f);
+ }
+
+ /**
+ * Creates a new DirectionalLight instance.
+ *
+ * @param color The color of the light emitted by the directional light source. Represents the RGB
+ * values of the light's color. This parameter cannot be null.
+ * @param direction The direction of the light source. This vector determines the direction in
+ * which the light rays travel. The provided vector is automatically normalized during
+ * construction, ensuring the direction's magnitude is always 1. This parameter cannot be
+ * null.
+ * @param intensity The intensity of the light emitted by the directional light source. This value
+ * must be non-negative.
+ * @throws IllegalArgumentException if the direction or color is null, or if the intensity is
+ * negative.
+ */
+ public DirectionalLight(Color color, Vector3f direction, float intensity) {
+ setColor(color);
+ setDirection(direction);
+ setIntensity(intensity);
+ }
+
+ /**
+ * Gets the direction of the light source.
+ *
+ * @return The direction of the light source.
+ * @see #setDirection(Vector3f)
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ * Sets the direction of the directional light source.
+ *
+ * The provided vector is normalized to ensure the light's direction always has a magnitude of
+ * 1, which maintains consistent light behavior. This method validates that the input is not null
+ * to avoid runtime errors.
+ *
+ * @param direction The new direction vector for the light source. This vector defines the
+ * direction in which the light rays travel.
+ * @throws IllegalArgumentException if the provided direction vector is null.
+ */
+ public void setDirection(Vector3f direction) {
+ if (direction == null) throw new IllegalArgumentException("Direction cannot be null.");
+ this.direction = direction.normalize();
+ }
+
+ /**
+ * Gets the color of the light emitted by the directional light source.
+ *
+ * @return The color of the light.
+ * @see #setColor(Color)
+ */
+ @Override
+ public Color getColor() {
+ return color;
+ }
+
+ /**
+ * Sets the color of the directional light source.
+ *
+ * This method updates the light's emitted color. It validates that the provided color is not
+ * null to ensure the light's color is always valid.
+ *
+ * @param color The new color of the light to set. Represents the RGB values of the light's color.
+ * @throws IllegalArgumentException if the provided color is null.
+ */
+ public void setColor(Color color) {
+ if (color == null) throw new IllegalArgumentException("Color cannot be null.");
+ this.color = color;
+ }
+
+ /**
+ * Gets the intensity of the light emitted by the directional light source.
+ *
+ * @return The intensity of the light.
+ * @see #setIntensity(float)
+ */
+ public float getIntensity() {
+ return intensity;
+ }
+
+ /**
+ * Sets the intensity of the light emitted by the directional light source.
+ *
+ * The intensity value determines how bright the light appears in the scene. This method
+ * ensures that the value is non-negative, as negative intensity does not make logical sense in
+ * this context.
+ *
+ * @param intensity The new intensity value to set for the light source. Must be non-negative to
+ * represent valid light brightness.
+ * @throws IllegalArgumentException if the provided intensity is negative.
+ */
+ public void setIntensity(float intensity) {
+ if (intensity < 0) throw new IllegalArgumentException("Intensity must be non-negative.");
+ this.intensity = intensity;
+ }
+
+ /**
+ * Gets the type of the light source.
+ *
+ * @return The type of the light source, which is `LightType.DIRECTIONAL`.
+ */
+ @Override
+ public LightType getType() {
+ return LightType.DIRECTIONAL;
+ }
+
+ /**
+ * Renders the directional light source using the provided renderer.
+ *
+ * @param renderer The renderer to use for rendering the light source.
+ */
+ @Override
+ public void render(LightRenderer renderer) {
+ renderer.render(this);
+ }
+
+ /**
+ * Provides a string representation of this directional light instance for debugging.
+ *
+ * @return String describing the current state of the directional light.
+ */
+ @Override
+ public String toString() {
+ return "DirectionalLight [color="
+ + color
+ + ", direction="
+ + direction
+ + ", intensity="
+ + intensity
+ + "]";
+ }
+}
diff --git a/src/main/java/engine/scene/light/Light.java b/src/main/java/engine/scene/light/Light.java
index 18765910..d14ec7c6 100644
--- a/src/main/java/engine/scene/light/Light.java
+++ b/src/main/java/engine/scene/light/Light.java
@@ -4,46 +4,39 @@
/**
* Interface for defining light sources within a 3D scene.
- *
- *
- * This interface serves as a contract for all light types (e.g., PointLight,
- * DirectionalLight, SpotLight) by defining essential behaviors and properties
- * that any light source should possess. It provides mechanisms to query a
- * light's type, retrieve its color, and delegate rendering logic to a given
- * renderer.
- * This interface serves as a contract for all light types (e.g., PointLight, DirectionalLight,
+ * SpotLight) by defining essential behaviors and properties that any light source should possess.
+ * It provides mechanisms to query a light's type, retrieve its color, and delegate rendering logic
+ * to a given renderer.
*/
public interface Light {
- /**
- * Gets the color of the light emitted by the light source.
- *
- * @return The {@link Color} object representing the light's color. The color
- * should define the RGB components that determine the light's hue and
- * saturation.
- */
- Color getColor();
+ /**
+ * Gets the color of the light emitted by the light source.
+ *
+ * @return The {@link Color} object representing the light's color. The color should define the
+ * RGB components that determine the light's hue and saturation.
+ */
+ Color getColor();
- /**
- * Gets the type of the light source.
- *
- * @return The {@link LightType} that identifies the specific type of light
- * (e.g., POINT, DIRECTIONAL, or SPOT) this instance represents.
- */
- LightType getType();
+ /**
+ * Gets the type of the light source.
+ *
+ * @return The {@link LightType} that identifies the specific type of light (e.g., POINT,
+ * DIRECTIONAL, or SPOT) this instance represents.
+ */
+ LightType getType();
- /**
- * Gets the light source using the provided renderer to draw the light's
- * effects.
- *
- * This method allows the implementation to delegate rendering logic to the
- * given {@link LightRenderer}. The rendering logic could involve adding
- * effects like shadows, light rays, or other visual representations specific
- * to the light's type.
- *
- * @param renderer The {@link LightRenderer} implementation responsible for
- * rendering this light's effects in the scene.
- */
- void render(LightRenderer renderer);
-
-}
\ No newline at end of file
+ /**
+ * Gets the light source using the provided renderer to draw the light's effects.
+ *
+ * This method allows the implementation to delegate rendering logic to the given {@link
+ * LightRenderer}. The rendering logic could involve adding effects like shadows, light rays, or
+ * other visual representations specific to the light's type.
+ *
+ * @param renderer The {@link LightRenderer} implementation responsible for rendering this light's
+ * effects in the scene.
+ */
+ void render(LightRenderer renderer);
+}
diff --git a/src/main/java/engine/scene/light/LightRenderer.java b/src/main/java/engine/scene/light/LightRenderer.java
index 4e519fef..638d9c2b 100644
--- a/src/main/java/engine/scene/light/LightRenderer.java
+++ b/src/main/java/engine/scene/light/LightRenderer.java
@@ -4,94 +4,78 @@
/**
* Interface for rendering various light sources in a 3D scene.
- *
- * This interface establishes a contract for rendering different types of light
- * sources in a 3D environment. It provides specific rendering methods for each
- * type of light, such as {@link PointLight}, {@link DirectionalLight}, and
- * {@link SpotLight}. Implementations of this interface handle the actual
- * rendering logic for these light types within a 3D graphics or game engine.
- * This interface establishes a contract for rendering different types of light sources in a 3D
+ * environment. It provides specific rendering methods for each type of light, such as {@link
+ * PointLight}, {@link DirectionalLight}, and {@link SpotLight}. Implementations of this interface
+ * handle the actual rendering logic for these light types within a 3D graphics or game engine.
*/
public interface LightRenderer {
- /**
- * Sets the graphics context for the light renderer.
- *
- * This method initializes the rendering environment by associating the given
- * {@link Graphics} instance with the light renderer. The graphics context is
- * responsible for rendering commands, shader bindings, and light
- * computations. Implementations can use this context to issue rendering
- * commands for different light types or configure the rendering pipeline as
- * needed.
- * This method initializes the rendering environment by associating the given {@link Graphics}
+ * instance with the light renderer. The graphics context is responsible for rendering commands,
+ * shader bindings, and light computations. Implementations can use this context to issue
+ * rendering commands for different light types or configure the rendering pipeline as needed.
+ *
+ * @param g The {@link Graphics} instance to be used by the light renderer. Must not be null.
+ */
+ void setGraphics(Graphics g);
- /**
- * Renders a generic light source.
- *
- * This method is a catch-all for rendering any light source that implements
- * the {@link Light} interface. Specific rendering logic for the light type
- * may be determined by the implementation.
- * This method is a catch-all for rendering any light source that implements the {@link Light}
+ * interface. Specific rendering logic for the light type may be determined by the implementation.
+ *
+ * @param light The light source to render. Must not be null.
+ */
+ void render(Light light);
- /**
- * Renders a spotlight.
- *
- * This method is responsible for rendering a spotlight with specific
- * directionality, cone angles, and attenuation effects. Spotlights are used
- * to simulate focused beams of light, such as those from flashlights, lamps,
- * or theater lighting.
- * This method is responsible for rendering a spotlight with specific directionality, cone
+ * angles, and attenuation effects. Spotlights are used to simulate focused beams of light, such
+ * as those from flashlights, lamps, or theater lighting.
+ *
+ * @param light The spotlight to render. Must not be null.
+ */
+ void render(SpotLight light);
- /**
- * Renders a point light source.
- *
- * This method handles the rendering of a point light, which emits light
- * uniformly in all directions from a single point in 3D space. Point lights
- * are commonly used to simulate small localized light sources such as light
- * bulbs or torches.
- * This method handles the rendering of a point light, which emits light uniformly in all
+ * directions from a single point in 3D space. Point lights are commonly used to simulate small
+ * localized light sources such as light bulbs or torches.
+ *
+ * @param light The point light source to render. Must not be null.
+ */
+ void render(PointLight light);
- /**
- * Renders a directional light source.
- *
- * This method handles rendering for a directional light, which simulates
- * light coming from a distant, uniform direction (e.g., sunlight or
- * moonlight). Directional lights are ideal for simulating natural light
- * sources that do not have an attenuation effect based on distance.
- * This method handles rendering for a directional light, which simulates light coming from a
+ * distant, uniform direction (e.g., sunlight or moonlight). Directional lights are ideal for
+ * simulating natural light sources that do not have an attenuation effect based on distance.
+ *
+ * @param light The directional light source to render. Must not be null.
+ */
+ void render(DirectionalLight light);
- /**
- * Renders an ambient light source.
- *
- * This method handles the rendering of ambient light, which provides uniform
- * illumination across the entire scene without directionality or position.
- * Ambient light is used to simulate indirect lighting and ensures that
- * objects are visible even when not directly lit by other light sources.
- * This method handles the rendering of ambient light, which provides uniform illumination
+ * across the entire scene without directionality or position. Ambient light is used to simulate
+ * indirect lighting and ensures that objects are visible even when not directly lit by other
+ * light sources.
+ *
+ * @param light The ambient light source to render. Must not be null.
+ */
+ void render(AmbientLight light);
+}
diff --git a/src/main/java/engine/scene/light/LightType.java b/src/main/java/engine/scene/light/LightType.java
index 8d641565..86ac016e 100644
--- a/src/main/java/engine/scene/light/LightType.java
+++ b/src/main/java/engine/scene/light/LightType.java
@@ -3,22 +3,22 @@
/**
* Enum representing different types of lights.
*
- * This enum defines the four primary types of lights commonly used in 3D
- * graphics:
+ * This enum defines the four primary types of lights commonly used in 3D graphics:
*
*
- * A point light simulates a light-emitting point in space, radiating light
- * uniformly in all directions. It is characterized by its position, color,
- * intensity, and range. This class is ideal for simulating localized light
- * sources such as lightbulbs, torches, or other small light emitters in a 3D
- * environment.
- * A point light simulates a light-emitting point in space, radiating light uniformly in all
+ * directions. It is characterized by its position, color, intensity, and range. This class is ideal
+ * for simulating localized light sources such as lightbulbs, torches, or other small light emitters
+ * in a 3D environment.
+ *
*
- * This constructor initializes the point light with the following default
- * values: - Color: White (RGB(255, 255, 255)). - Position: (0, 0, 0). -
- * Intensity: 1.0. - Range: 10.0.
- * This constructor initializes the point light with the following default values: - Color:
+ * White (RGB(255, 255, 255)). - Position: (0, 0, 0). - Intensity: 1.0. - Range: 10.0.
+ */
+ public PointLight() {
+ this(Color.WHITE, new Vector3f(0, 0, 0), 1.0f, 10.0f);
+ }
+
+ /**
+ * Creates a new PointLight instance with specified parameters.
+ *
+ * @param color The color of the light. Must not be null.
+ * @param position The 3D position of the light source in the scene. Must not be null.
+ * @param intensity The intensity of the light. Must be a non-negative value.
+ * @param range The maximum distance of the light's effect. Must be non-negative.
+ * @throws IllegalArgumentException if any argument is invalid (e.g., null values or negative
+ * numbers).
+ */
+ public PointLight(Color color, Vector3f position, float intensity, float range) {
+ setColor(color);
+ setPosition(position);
+ setIntensity(intensity);
+ setRange(range);
+ }
+
+ /**
+ * Gets the maximum range at which the light's effect is felt.
+ *
+ * @return The range of the light's effect in world units.
+ */
+ public float getRange() {
+ return range;
+ }
+
+ /**
+ * Sets the maximum range of the light's influence in the scene.
+ *
+ * @param range The new range value. Must be non-negative.
+ * @throws IllegalArgumentException if the provided range is less than 0.
+ */
+ public void setRange(float range) {
+ if (range < 0) {
+ throw new IllegalArgumentException("Range must be non-negative.");
+ }
+ this.range = range;
+ }
+
+ /**
+ * Gets the current intensity of the light.
+ *
+ * @return The intensity value, a non-negative float.
+ */
+ public float getIntensity() {
+ return intensity;
+ }
+
+ /**
+ * Sets the intensity of the light source.
+ *
+ * @param intensity The new intensity value to apply. Must be non-negative.
+ * @throws IllegalArgumentException if intensity is less than 0.
+ */
+ public void setIntensity(float intensity) {
+ if (intensity < 0) {
+ throw new IllegalArgumentException("Intensity must be non-negative.");
+ }
+ this.intensity = intensity;
+ }
+
+ /**
+ * Gets the color of the light emitted by the point light source.
+ *
+ * @return The current {@link math.Color} of the point light.
+ */
+ @Override
+ public Color getColor() {
+ return color;
+ }
+
+ /**
+ * Sets the color of the light source.
+ *
+ * @param color The new color value for the light source. Must not be null.
+ * @throws IllegalArgumentException if color is null.
+ */
+ public void setColor(Color color) {
+ if (color == null) {
+ throw new IllegalArgumentException("Color cannot be null.");
+ }
+ this.color = color;
+ }
+
+ /**
+ * Gets the 3D position of the light source.
+ *
+ * @return The current position of the light as a {@link math.Vector3f}.
+ */
+ public Vector3f getPosition() {
+ return position;
+ }
+
+ /**
+ * Sets the 3D position of the light source within the 3D scene.
+ *
+ * @param position The new position value to set. Must not be null.
+ * @throws IllegalArgumentException if position is null.
+ */
+ public void setPosition(Vector3f position) {
+ if (position == null) {
+ throw new IllegalArgumentException("Position cannot be null.");
+ }
+ this.position = position;
+ }
+
+ /**
+ * Gets the type of light.
+ *
+ * @return The type of the light, represented as `LightType.POINT`.
+ */
+ @Override
+ public LightType getType() {
+ return LightType.POINT;
+ }
+
+ /**
+ * Renders this point light source using the provided renderer.
+ *
+ * @param renderer The renderer responsible for rendering the light in the 3D scene.
+ */
+ @Override
+ public void render(LightRenderer renderer) {
+ renderer.render(this);
+ }
+
+ /**
+ * Generates a string representation of this {@link PointLight}.
+ *
+ * @return A string describing the current state of this point light.
+ */
+ @Override
+ public String toString() {
+ return "PointLight [color="
+ + color
+ + ", position="
+ + position
+ + ", intensity="
+ + intensity
+ + ", range="
+ + range
+ + "]";
+ }
+}
diff --git a/src/main/java/engine/scene/light/SpotLight.java b/src/main/java/engine/scene/light/SpotLight.java
index 72bc0cf1..b789a840 100644
--- a/src/main/java/engine/scene/light/SpotLight.java
+++ b/src/main/java/engine/scene/light/SpotLight.java
@@ -6,10 +6,9 @@
/**
* Represents a spotlight in a 3D scene.
*
- * A spotlight emits light in a cone shape, with a defined position, direction,
- * cone angle, and concentration (center bias). This class models the essential
- * properties of a spotlight, allowing users to specify its behavior and
- * appearance in a 3D environment.
+ * A spotlight emits light in a cone shape, with a defined position, direction, cone angle, and
+ * concentration (center bias). This class models the essential properties of a spotlight, allowing
+ * users to specify its behavior and appearance in a 3D environment.
*
*
- * Initializes the spotlight with the provided position, direction, color,
- * concentration, and cone angle values. Each input is validated to ensure it
- * adheres to acceptable ranges or requirements.
- * Initializes the spotlight with the provided position, direction, color, concentration, and
+ * cone angle values. Each input is validated to ensure it adheres to acceptable ranges or
+ * requirements.
+ *
+ * @param position The 3D position of the spotlight. Must not be null.
+ * @param direction The direction the spotlight points towards. Must not be null.
+ * @param color The emitted light's color. Must not be null.
+ * @param concentration The center bias (intensity focus) of the spotlight cone. Must be
+ * non-negative.
+ * @param angle The cone angle in radians. Must be greater than 0 and less than or equal to π
+ * radians.
+ * @throws IllegalArgumentException if any of the following conditions are met: - `position` is
+ * null. - `direction` is null. - `color` is null. - `concentration` is negative. - `angle` is
+ * less than or equal to 0, or greater than π radians.
+ */
+ public SpotLight(
+ Vector3f position, Vector3f direction, Color color, float concentration, float angle) {
+ setPosition(position);
+ setDirection(direction);
+ setColor(color);
+ setConcentration(concentration);
+ setAngle(angle);
+ }
+
+ /**
+ * Gets the angle of the spotlight cone.
+ *
+ * @return The cone's angle in radians.
+ */
+ public float getAngle() {
+ return angle;
+ }
+
+ /**
+ * Sets the cone angle, ensuring it is within valid physical limits.
+ *
+ * @param angle The new angle of the spotlight cone.
+ * @throws IllegalArgumentException if the value is less than or equal to 0 or exceeds π radians.
+ */
+ public void setAngle(float angle) {
+ if (angle <= 0 || angle > Math.PI) {
+ throw new IllegalArgumentException("Angle must be between 0 and PI radians.");
+ }
+ this.angle = angle;
+ }
+
+ /**
+ * Gets the concentration (center bias) of the spotlight's cone.
+ *
+ * @return The concentration value of the spotlight.
+ */
+ public float getConcentration() {
+ return concentration;
+ }
+
+ /**
+ * Sets the concentration value for the spotlight cone's focus.
+ *
+ * @param concentration The new concentration value.
+ * @throws IllegalArgumentException if the value is negative.
+ */
+ public void setConcentration(float concentration) {
+ if (concentration < 0) {
+ throw new IllegalArgumentException("Concentration must be non-negative.");
+ }
+ this.concentration = concentration;
+ }
+
+ /**
+ * Retrieves the direction vector of the spotlight.
+ *
+ * @return The current direction vector.
+ */
+ public Vector3f getDirection() {
+ return direction;
+ }
+
+ /**
+ * Sets the direction vector of the spotlight.
+ *
+ * @param direction The new direction vector.
+ * @throws IllegalArgumentException if the provided vector is null.
+ */
+ public void setDirection(Vector3f direction) {
+ if (direction == null) {
+ throw new IllegalArgumentException("Direction cannot be null.");
+ }
+ this.direction = direction;
+ }
+
+ /**
+ * Retrieves the position of the spotlight.
+ *
+ * @return The position vector.
+ */
+ public Vector3f getPosition() {
+ return position;
+ }
+
+ /**
+ * Sets the position of the spotlight in 3D space.
+ *
+ * @param position The new position vector.
+ * @throws IllegalArgumentException if the provided vector is null.
+ */
+ public void setPosition(Vector3f position) {
+ if (position == null) {
+ throw new IllegalArgumentException("Position cannot be null.");
+ }
+ this.position = position;
+ }
+
+ /**
+ * Retrieves the color of the spotlight's light.
+ *
+ * @return The spotlight's color.
+ */
+ @Override
+ public Color getColor() {
+ return color;
+ }
+
+ /**
+ * Sets the color of the spotlight's emitted light.
+ *
+ * @param color The new color value.
+ * @throws IllegalArgumentException if the provided color is null.
+ */
+ public void setColor(Color color) {
+ if (color == null) {
+ throw new IllegalArgumentException("Color cannot be null.");
+ }
+ this.color = color;
+ }
+
+ /**
+ * Determines the type of light, specifically `LightType.SPOT`.
+ *
+ * @return The type of light.
+ */
+ @Override
+ public LightType getType() {
+ return LightType.SPOT;
+ }
+
+ /**
+ * Renders the spotlight using the provided rendering system.
+ *
+ * Delegates rendering logic to the specified {@link LightRenderer}.
+ *
+ * @param renderer The renderer responsible for spotlight rendering.
+ */
+ @Override
+ public void render(LightRenderer renderer) {
+ renderer.render(this);
+ }
+
+ /**
+ * Provides a string representation of this spotlight instance for debugging.
+ *
+ * @return String describing the current state of the spotlight.
+ */
+ @Override
+ public String toString() {
+ return "SpotLight [angle="
+ + angle
+ + ", concentration="
+ + concentration
+ + ", position="
+ + position
+ + ", direction="
+ + direction
+ + ", color="
+ + color
+ + "]";
+ }
+}
diff --git a/src/main/java/math/Bounds.java b/src/main/java/math/Bounds.java
index 57f62990..b21f770b 100644
--- a/src/main/java/math/Bounds.java
+++ b/src/main/java/math/Bounds.java
@@ -1,229 +1,225 @@
package math;
/**
- * The {@code Bounds} class represents a 3D axis-aligned bounding box (AABB),
- * defined by two {@link Vector3f} points: the minimum and maximum corners. This
- * class provides various utility methods for manipulating and querying the
- * bounding box, such as checking if a point is contained within the bounds,
- * expanding the bounds, and testing for intersections with rays or other
- * bounds.
- *
- *
- * A bounding box is often used in 3D graphics for collision detection, frustum
- * culling, and other spatial queries.
- * A bounding box is often used in 3D graphics for collision detection, frustum culling, and
+ * other spatial queries.
*/
public class Bounds {
- /**
- * The minimum corner of the bounding box.
- */
- private Vector3f min;
-
- /**
- * The maximum corner of the bounding box.
- */
- private Vector3f max;
-
- /**
- * Constructs a new {@code Bounds} object with the specified minimum and
- * maximum corners.
- *
- * @param min the minimum corner of the bounding box
- * @param max the maximum corner of the bounding box
- * @throws IllegalArgumentException if either {@code min} or {@code max} is
- * {@code null}
- */
- public Bounds(Vector3f min, Vector3f max) {
- if (min == null) {
- throw new IllegalArgumentException("Min cannot be null.");
- }
- if (max == null) {
- throw new IllegalArgumentException("Max cannot be null.");
- }
- this.min = new Vector3f(min);
- this.max = new Vector3f(max);
- }
-
- /**
- * Returns the closest point on the bounding box to the given {@code point}.
- * The closest point is determined by clamping each coordinate of the point
- * between the minimum and maximum bounds of the box.
- *
- * @param point the point to clamp to the bounding box
- * @return a new {@code Vector3f} representing the closest point on the
- * bounding box
- */
- public Vector3f closestPoint(Vector3f point) {
- float x = Math.max(min.x, Math.min(max.x, point.x));
- float y = Math.max(min.y, Math.min(max.y, point.y));
- float z = Math.max(min.z, Math.min(max.z, point.z));
- return new Vector3f(x, y, z);
- }
-
- /**
- * Checks if the given {@code point} is inside the bounding box. The point is
- * considered inside if all of its coordinates are between the minimum and
- * maximum coordinates of the box.
- *
- * @param p the point to check
- * @return {@code true} if the point is inside the bounding box, {@code false}
- * otherwise
- */
- public boolean contains(Vector3f p) {
- return p.x >= min.x && p.x <= max.x && p.y >= min.y
- && p.y <= max.y && p.z >= min.z && p.z <= max.z;
- }
-
- /**
- * Expands the bounding box to encompass the given {@code point}. If the point
- * is outside the current bounds, the box will be enlarged to include it.
- *
- * @param p the point to include in the bounding box
- */
- public void encapsulate(Vector3f p) {
- min.set(Math.min(min.x, p.x), Math.min(min.y, p.y), Math.min(min.z, p.z));
- max.set(Math.max(max.x, p.x), Math.max(max.y, p.y), Math.max(max.z, p.z));
- }
-
- /**
- * Expands the bounding box by the given {@code amount}. The expansion is done
- * uniformly along all axes, increasing the size of the bounding box by the
- * specified amount.
- *
- * @param amount the amount to expand the bounding box by
- */
- public void expand(float amount) {
- float halfAmount = amount / 2;
- min.subtractLocal(halfAmount, halfAmount, halfAmount);
- max.addLocal(halfAmount, halfAmount, halfAmount);
- }
-
- /**
- * Tests whether the given ray intersects this axis-aligned bounding box
- * (AABB).
- *
- * The method uses the slab method to compute the intersection by checking the
- * ray's position and direction relative to the box's bounds in each axis (x,
- * y, z). It accounts for parallel rays and updates intersection intervals to
- * determine if there is an overlap.
- * The method uses the slab method to compute the intersection by checking the ray's position
+ * and direction relative to the box's bounds in each axis (x, y, z). It accounts for parallel
+ * rays and updates intersection intervals to determine if there is an overlap.
+ *
+ * @param ray the {@link Ray3f} to test for intersection with this AABB. The ray must have its
+ * inverse direction precomputed and accessible via {@code ray.getDirectionInv()} for optimal
+ * performance.
+ * @return {@code true} if the ray intersects the AABB, {@code false} otherwise.
+ */
+ public boolean intersectsRay(Ray3f ray) {
+ if (ray.getDirection().isZero()) {
+ return false; // A ray with zero direction cannot intersect anything
+ }
+
+ if (min.equals(max)) {
+ return ray.getOrigin().equals(min);
+ }
+
+ float tmin = 0.0f;
+ float tmax = Float.POSITIVE_INFINITY;
+
+ for (int d = 0; d < 3; ++d) {
+ float invDir = ray.getDirectionInv().get(d);
+
+ // Handle zero direction component
+ if (invDir == 0.0f) {
+ if (ray.getOrigin().get(d) < min.get(d) || ray.getOrigin().get(d) > max.get(d)) {
+ return false;
+ }
+ continue;
+ }
+
+ float bmin, bmax;
+ if (invDir < 0.0f) {
+ bmin = max.get(d);
+ bmax = min.get(d);
+ } else {
+ bmin = min.get(d);
+ bmax = max.get(d);
+ }
+
+ float dmin = (bmin - ray.getOrigin().get(d)) * invDir;
+ float dmax = (bmax - ray.getOrigin().get(d)) * invDir;
+
+ tmin = Math.max(tmin, dmin);
+ tmax = Math.min(tmax, dmax);
+
+ if (tmin > tmax) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Tests if this bounding box intersects another {@code Bounds}. The intersection is checked by
+ * comparing the min and max coordinates of both boxes.
+ *
+ * @param other the other bounding box to check for intersection
+ * @return {@code true} if the bounding boxes intersect, {@code false} otherwise
+ */
+ public boolean intersects(Bounds other) {
+ return min.x <= other.max.x
+ && max.x >= other.min.x
+ && min.y <= other.max.y
+ && max.y >= other.min.y
+ && min.z <= other.max.z
+ && max.z >= other.min.z;
+ }
+
+ /**
+ * Calculates the squared distance from the given {@code point} to the closest point on the
+ * bounding box. This method avoids calculating the square root for performance reasons, returning
+ * the squared distance instead.
+ *
+ * @param point the point to calculate the squared distance from
+ * @return the squared distance from the point to the closest point on the bounding box
+ */
+ public float sqrDistance(Vector3f point) {
+ float dx = Math.max(0, Math.max(min.x - point.x, point.x - max.x));
+ float dy = Math.max(0, Math.max(min.y - point.y, point.y - max.y));
+ float dz = Math.max(0, Math.max(min.z - point.z, point.z - max.z));
+ return dx * dx + dy * dy + dz * dz;
+ }
+
+ /**
+ * Sets the minimum and maximum corners of the bounding box to the specified values.
+ *
+ * @param min the new minimum corner
+ * @param max the new maximum corner
+ * @throws IllegalArgumentException if min or max is null.
+ */
+ public void setMinMax(Vector3f min, Vector3f max) {
+ if (min == null || max == null) {
+ throw new IllegalArgumentException("Min and Max cannot be null.");
+ }
+ this.min = new Vector3f(min);
+ this.max = new Vector3f(max);
+ }
+
+ /**
+ * Returns the minimum corner of the bounding box.
+ *
+ * @return the minimum corner of the bounding box
+ */
+ public Vector3f getMin() {
+ return min;
+ }
+
+ /**
+ * Returns the maximum corner of the bounding box.
+ *
+ * @return the maximum corner of the bounding box
+ */
+ public Vector3f getMax() {
+ return max;
+ }
+
+ /**
+ * Returns a string representation of the Bounds object. The string includes the minimum and
+ * maximum points of the bounds.
+ *
+ * @return a string representation of this Bounds object in the format: "Bounds [min= The centroid is computed as the average of the x, y, and z coordinates of the three
+ * vertices.
+ *
+ * @param a The first vertex of the triangle.
+ * @param b The second vertex of the triangle.
+ * @param c The third vertex of the triangle.
+ * @return The centroid of the triangle as a new Vector3f.
+ */
+ public static Vector3f calculateCentroid(Vector3f a, Vector3f b, Vector3f c) {
+ float x = (a.x + b.x + c.x) / 3f;
+ float y = (a.y + b.y + c.y) / 3f;
+ float z = (a.z + b.z + c.z) / 3f;
+ return new Vector3f(x, y, z);
+ }
- /**
- * Calculates the centroid (or center of mass) of a triangle defined by
- * three 2D points.
- *
- * @param a The first point of the triangle.
- * @param b The second point of the triangle.
- * @param c The third point of the triangle.
- * @return The centroid of the triangle.
- */
- public static Vector2f getMainEmphasis(Vector2f a, Vector2f b, Vector2f c) {
- Vector2f m = a.add(b).add(c).mult(Mathf.ONE_THIRD);
- return m;
- }
+ /**
+ * Calculates the centroid (or center of mass) of a triangle defined by three 2D points.
+ *
+ * @param a The first point of the triangle.
+ * @param b The second point of the triangle.
+ * @param c The third point of the triangle.
+ * @return The centroid of the triangle.
+ */
+ public static Vector2f getMainEmphasis(Vector2f a, Vector2f b, Vector2f c) {
+ Vector2f m = a.add(b).add(c).mult(Mathf.ONE_THIRD);
+ return m;
+ }
- /**
- * Calculates the center point of a quadrilateral defined by four 2D points.
- *
- * @param a The first point of the quadrilateral.
- * @param b The second point of the quadrilateral.
- * @param c The third point of the quadrilateral.
- * @param d The fourth point of the quadrilateral.
- * @return The center point of the quadrilateral.
- */
- public static Vector2f calculateCenter(Vector2f a, Vector2f b, Vector2f c,
- Vector2f d) {
- float x = (a.x + b.x + c.x + d.x) * 0.25f;
- float y = (a.y + b.y + c.y + d.y) * 0.25f;
- return new Vector2f(x, y);
- }
+ /**
+ * Calculates the center point of a quadrilateral defined by four 2D points.
+ *
+ * @param a The first point of the quadrilateral.
+ * @param b The second point of the quadrilateral.
+ * @param c The third point of the quadrilateral.
+ * @param d The fourth point of the quadrilateral.
+ * @return The center point of the quadrilateral.
+ */
+ public static Vector2f calculateCenter(Vector2f a, Vector2f b, Vector2f c, Vector2f d) {
+ float x = (a.x + b.x + c.x + d.x) * 0.25f;
+ float y = (a.y + b.y + c.y + d.y) * 0.25f;
+ return new Vector2f(x, y);
+ }
- /**
- * Calculates the center point of a quadrilateral defined by four 3D points.
- *
- * @param a The first point of the quadrilateral.
- * @param b The second point of the quadrilateral.
- * @param c The third point of the quadrilateral.
- * @param d The fourth point of the quadrilateral.
- * @return The center point of the quadrilateral.
- */
- public static Vector3f calculateCenter(Vector3f a, Vector3f b, Vector3f c,
- Vector3f d) {
- float x = (a.x + b.x + c.x + d.x) * 0.25f;
- float y = (a.y + b.y + c.y + d.y) * 0.25f;
- float z = (a.z + b.z + c.z + d.z) * 0.25f;
- return new Vector3f(x, y, z);
- }
+ /**
+ * Calculates the center point of a quadrilateral defined by four 3D points.
+ *
+ * @param a The first point of the quadrilateral.
+ * @param b The second point of the quadrilateral.
+ * @param c The third point of the quadrilateral.
+ * @param d The fourth point of the quadrilateral.
+ * @return The center point of the quadrilateral.
+ */
+ public static Vector3f calculateCenter(Vector3f a, Vector3f b, Vector3f c, Vector3f d) {
+ float x = (a.x + b.x + c.x + d.x) * 0.25f;
+ float y = (a.y + b.y + c.y + d.y) * 0.25f;
+ float z = (a.z + b.z + c.z + d.z) * 0.25f;
+ return new Vector3f(x, y, z);
+ }
- /**
- * Calculates the midpoint between two 3D points.
- *
- * @param start The starting point.
- * @param end The ending point.
- * @return The midpoint between the two points.
- */
- public static Vector3f getMidpoint(Vector3f start, Vector3f end) {
- return start.add(end).mult(0.5f);
- }
+ /**
+ * Calculates the midpoint between two 3D points.
+ *
+ * @param start The starting point.
+ * @param end The ending point.
+ * @return The midpoint between the two points.
+ */
+ public static Vector3f getMidpoint(Vector3f start, Vector3f end) {
+ return start.add(end).mult(0.5f);
+ }
- /**
- * Calculates the midpoint between two 2D points.
- *
- * @param start The starting point.
- * @param end The ending point.
- * @return The midpoint between the two points.
- */
- public static Vector2f getMidpoint(Vector2f start, Vector2f end) {
- return start.add(end).mult(0.5f);
- }
+ /**
+ * Calculates the midpoint between two 2D points.
+ *
+ * @param start The starting point.
+ * @param end The ending point.
+ * @return The midpoint between the two points.
+ */
+ public static Vector2f getMidpoint(Vector2f start, Vector2f end) {
+ return start.add(end).mult(0.5f);
+ }
- /**
- * Calculates the angle in radians between two 3D vectors.
- *
- * @param v1 The first vector.
- * @param v2 The second vector.
- * @return The angle between the two vectors in radians.
- * @throws IllegalArgumentException if either vector has zero length.
- */
- public static double angleBetweenVectors(Vector3f v1, Vector3f v2) {
- double dotProduct = v1.dot(v2);
- double magnitude1 = v1.length();
- double magnitude2 = v2.length();
-
- if (magnitude1 == 0 || magnitude2 == 0) {
- throw new IllegalArgumentException(
- "Vectors cannot have zero length");
- }
-
- // Handle floating-point precision issues
- double cosTheta = Math.max(-1.0,
- Math.min(1.0, dotProduct / (magnitude1 * magnitude2)));
-
- double angle = Math.acos(cosTheta);
-
- return angle;
- }
+ /**
+ * Calculates the angle in radians between two 3D vectors.
+ *
+ * @param v1 The first vector.
+ * @param v2 The second vector.
+ * @return The angle between the two vectors in radians.
+ * @throws IllegalArgumentException if either vector has zero length.
+ */
+ public static double angleBetweenVectors(Vector3f v1, Vector3f v2) {
+ double dotProduct = v1.dot(v2);
+ double magnitude1 = v1.length();
+ double magnitude2 = v2.length();
- /**
- * Calculates a point along the line segment between two 2D points.
- *
- * @param start The starting point of the line segment.
- * @param end The ending point of the line segment.
- * @param lambda A value between 0 and 1 that determines the position of the
- * point along the line segment. A value of 0 will return the
- * start point, a value of 1 will return the end point, and
- * values between 0 and 1 will return points in between.
- * @return The point along the line segment.
- */
- public static Vector2f getDistributionPoint(Vector2f start, Vector2f end,
- float lambda) {
- float scalar = 1f / (1f + lambda);
- return start.add(end.mult(lambda)).mult(scalar);
+ if (magnitude1 == 0 || magnitude2 == 0) {
+ throw new IllegalArgumentException("Vectors cannot have zero length");
}
- /**
- * Calculates a point on a circle given a center point, radius, angle, and
- * direction.
- *
- * @param center The center point of the circle.
- * @param radius The radius of the circle.
- * @param angle The angle in radians.
- * @param cw If true, the angle is measured clockwise from the positive
- * x-axis. If false, the angle is measured counterclockwise.
- * @return A point on the circle.
- */
- public static Vector2f pointOnCircle(Vector2f center, float radius,
- float angle, boolean cw) {
- angle = cw ? angle : -angle;
- float x = (float) (center.x + radius * Math.cos(angle));
- float y = (float) (center.y + radius * Math.sin(angle));
- return new Vector2f(x, y);
- }
+ // Handle floating-point precision issues
+ double cosTheta = Math.max(-1.0, Math.min(1.0, dotProduct / (magnitude1 * magnitude2)));
+
+ double angle = Math.acos(cosTheta);
+
+ return angle;
+ }
+
+ /**
+ * Calculates a point along the line segment between two 2D points.
+ *
+ * @param start The starting point of the line segment.
+ * @param end The ending point of the line segment.
+ * @param lambda A value between 0 and 1 that determines the position of the point along the line
+ * segment. A value of 0 will return the start point, a value of 1 will return the end point,
+ * and values between 0 and 1 will return points in between.
+ * @return The point along the line segment.
+ */
+ public static Vector2f getDistributionPoint(Vector2f start, Vector2f end, float lambda) {
+ float scalar = 1f / (1f + lambda);
+ return start.add(end.mult(lambda)).mult(scalar);
+ }
+
+ /**
+ * Calculates a point on a circle given a center point, radius, angle, and direction.
+ *
+ * @param center The center point of the circle.
+ * @param radius The radius of the circle.
+ * @param angle The angle in radians.
+ * @param cw If true, the angle is measured clockwise from the positive x-axis. If false, the
+ * angle is measured counterclockwise.
+ * @return A point on the circle.
+ */
+ public static Vector2f pointOnCircle(Vector2f center, float radius, float angle, boolean cw) {
+ angle = cw ? angle : -angle;
+ float x = (float) (center.x + radius * Math.cos(angle));
+ float y = (float) (center.y + radius * Math.sin(angle));
+ return new Vector2f(x, y);
+ }
+
+ /**
+ * Computes an arbitrary vector orthogonal to the given vector.
+ *
+ * @param vector The input vector to find an orthogonal vector to.
+ * @return A vector orthogonal to the provided vector.
+ */
+ public static Vector3f getOrthogonalVector(Vector3f vector) {
+ // Choose an arbitrary vector that isn't parallel to the input vector
+ Vector3f arbitrary = Math.abs(vector.x) < 0.9 ? new Vector3f(1, 0, 0) : new Vector3f(0, 1, 0);
+ // Compute a cross product to ensure orthogonality
+ return vector.cross(arbitrary).normalize();
+ }
}
diff --git a/src/main/java/math/Mathf.java b/src/main/java/math/Mathf.java
index 70c06d16..2099fb22 100644
--- a/src/main/java/math/Mathf.java
+++ b/src/main/java/math/Mathf.java
@@ -3,981 +3,891 @@
import java.util.Random;
/**
- * This class provides a collection of mathematical utility functions that are
- * commonly used in various numerical computations and game development. It
- * offers a range of functionalities, including trigonometric functions,
- * logarithmic functions, rounding, clamping, and random number generation.
+ * This class provides a collection of mathematical utility functions that are commonly used in
+ * various numerical computations and game development. It offers a range of functionalities,
+ * including trigonometric functions, logarithmic functions, rounding, clamping, and random number
+ * generation.
*/
public class Mathf {
- /**
- * A random number generator used to generate random values.
- */
- private static Random random = new Random();
-
- /**
- * A float representation of the golden ratio, approximately 1.618.
- */
- public static final float GOLDEN_RATIO = (1 + sqrt(5)) / 2.0f;
-
- /**
- * A float representation of the reciprocal of the golden ratio, , which is
- * exactly 1 less than the golden ratio itself; approximately 0.618.
- */
- public static final float GOLDEN_RATIO_RECIPROCAL = 2 / (1 + sqrt(5));
-
- /**
- * Euler's number, the base of the natural logarithm, approximately 2.718.
- */
- public static final float E = (float) Math.E;
-
- /**
- * A representation of negative infinity.
- */
- public static final float NEGATIVE_INFINITY = Float.NEGATIVE_INFINITY;
-
- /**
- * A representation of positive infinity.
- */
- public static final float POSITIVE_INFINITY = Float.POSITIVE_INFINITY;
-
- /**
- * The smallest positive nonzero value representable as a float.
- */
- public static final float MIN_VALUE = Float.MIN_VALUE;
-
- /**
- * The largest finite value representable as a float.
- */
- public static final float MAX_VALUE = Float.MAX_VALUE;
-
- /**
- * A small value used for floating-point comparisons, approximately 2.22E-16.
- */
- public static final double DBL_EPSILON = 2.220446049250313E-16d;
-
- /**
- * A small value used for floating-point comparisons, approximately 1.19E-7.
- */
- public static final float FLT_EPSILON = 1.1920928955078125E-7f;
-
- /**
- * A small tolerance value for comparing floating-point numbers.
- */
- public static final float ZERO_TOLERANCE = 0.0001f;
-
- /**
- * A float representation of one-third, approximately 0.33333334.
- */
- public static final float ONE_THIRD = 1f / 3f;
-
- /**
- * The value of Pi, approximately 3.14159.
- */
- public static final float PI = (float) Math.PI;
-
- /**
- * Twice the value of Pi, approximately 6.283185.
- */
- public static final float TWO_PI = 2.0f * PI;
-
- /**
- * Half the value of Pi, approximately 1.570796.
- */
- public static final float HALF_PI = 0.5f * PI;
-
- /**
- * A quarter of the value of Pi, approximately 0.785398.
- */
- public static final float QUARTER_PI = 0.25f * PI;
-
- /**
- * The reciprocal of Pi, approximately 0.3183099.
- */
- public static final float INV_PI = 1.0f / PI;
-
- /**
- * The reciprocal of two times Pi, approximately 0.1591549.
- */
- public static final float INV_TWO_PI = 1.0f / TWO_PI;
-
- /**
- * A factor to convert degrees to radians, approximately 0.0174533.
- */
- public static final float DEG_TO_RAD = PI / 180.0f;
-
- /**
- * A factor to convert radians to degrees, approximately 57.29578.
- */
- public static final float RAD_TO_DEG = 180.0f / PI;
-
- /**
- * The Tribonacci constant, often denoted as t, is the real root of the cubic
- * equation x³ - x² - x - 1 = 0. It is approximately equal to
- * 1.83928675521416.
- */
- public static final float TRIBONACCI_CONSTANT = 1.83928675521416f;
-
- /**
- * Converts a 2D index (row, column) into a 1D index for a matrix or array.
- *
- *
- * This method is useful when working with matrices or arrays that are stored
- * in a 1D array. It calculates the 1D index corresponding to the specified
- * row and column in a matrix with the given number of columns.
- *
- * @param rowIndex The zero-based index of the row.
- * @param colIndex The zero-based index of the column.
- * @param numberOfColumns The total number of columns in the matrix.
- * @return The 1D index corresponding to the given row and column.
- *
- * @throws IllegalArgumentException if `rowIndex` or `colIndex` is negative,
- * or if `numberOfColumns` is less than or
- * equal to zero.
- */
- public static int toOneDimensionalIndex(int rowIndex, int colIndex,
- int numberOfColumns) {
- if (rowIndex < 0 || colIndex < 0)
- throw new IllegalArgumentException();
-
- if (numberOfColumns <= 0)
- throw new IllegalArgumentException();
-
- return rowIndex * numberOfColumns + colIndex;
- }
-
- /**
- * Returns the smaller of two int values. That is, the result is the argument
- * closer to {@link Integer#MIN_VALUE}. If the arguments have the same value,
- * the result is that same value.
- *
- * @param a The first integer.
- * @param b The second integer.
- * @return The smaller of `a` and `b`.
- */
- public static int min(int a, int b) {
- return Math.min(a, b);
- }
-
- /**
- * Returns the larger of two int values. That is, the result is the argument
- * closer to {@link Integer#MAX_VALUE}. If the arguments have the same value,
- * the result is that same value.
- *
- * @param a The first integer.
- * @param b The second integer.
- * @return The larger of `a` and `b`.
- */
- public static int max(int a, int b) {
- return Math.max(a, b);
- }
-
- /**
- * Returns the minimum value in the given array.
- *
- * @param values The array of integers.
- * @return The minimum value in the array, or 0 if the array is empty.
- */
- public static int min(int[] values) {
- if (values.length == 0)
- return 0;
-
- int min = values[0];
- for (int i = 1; i < values.length; i++)
- min = Math.min(min, values[i]);
- return min;
- }
-
- /**
- * Returns the maximum value in the given array.
- *
- * @param values The array of integers.
- * @return The maximum value in the array, or 0 if the array is empty.
- */
- public static int max(int[] values) {
- if (values.length == 0)
- return 0;
-
- int max = values[0];
- for (int i = 1; i < values.length; i++)
- max = Math.max(max, values[i]);
- return max;
- }
-
- /**
- * Returns the larger of the two given float values.
- *
- * @param a The first float value.
- * @param b The second float value.
- * @return The larger of `a` and `b`.
- */
- public static float max(float a, float b) {
- return Math.max(a, b);
- }
-
- /**
- * Returns the smaller of the two given float values.
- *
- * @param a The first float value.
- * @param b The second float value.
- * @return The smaller of `a` and `b`.
- */
- public static float min(float a, float b) {
- return Math.min(a, b);
- }
-
- /**
- * Returns the maximum float value in the given array.
- *
- * @param values The array of float values.
- * @return The maximum value in the array, or {@link Float#NaN} if the array
- * is empty.
- */
- public static float max(float... values) {
- if (values.length == 0)
- return Float.NaN;
-
- float max = values[0];
- for (int i = 1; i < values.length; i++)
- max = Math.max(max, values[i]);
- return max;
- }
-
- /**
- * Returns the minimum float value in the given array.
- *
- * @param values The array of float values.
- * @return The minimum value in the array, or {@link Float#NaN} if the array
- * is empty.
- */
- public static float min(float... values) {
- if (values.length == 0)
- return Float.NaN;
-
- float min = values[0];
- for (int i = 1; i < values.length; i++)
- min = Math.min(min, values[i]);
- return min;
- }
-
- /**
- * Rounds a float value to the nearest integer, rounding ties towards positive
- * infinity.
- *
- * @param a The float value to be rounded.
- * @return The rounded integer value.
- */
- public static int roundToInt(float a) {
- return Math.round(a);
- }
-
- /**
- * Rounds a float value to the nearest integer.
- *
- *
- * This method rounds the given float value to the nearest integer. If the
- * fractional part is 0.5 or greater, the value is rounded up. Otherwise, it
- * is rounded down.
- *
- * @param a The float value to be rounded.
- * @return The rounded float value.
- */
- public static float round(float a) {
- return Math.round(a);
- }
-
- /**
- * Clamps a value between a minimum and maximum value.
- *
- * @param a The value to clamp.
- * @param min The minimum value.
- * @param max The maximum value-
- * @return The clamped value.
- */
- public static float clamp(float a, float min, float max) {
- return Math.max(min, Math.min(max, a));
- }
-
- /**
- * Clamps a between min and max and returns the clamped value.
- *
- * @param a The value to clamp
- * @param min The minimum for a.
- * @param max The maximum for a.
- * @return The clamped value.
- */
- public static int clampInt(int a, int min, int max) {
- return a < min ? min : (a > max ? max : a);
- }
-
- /**
- * Clamps the given float value to be between 0 and 1. This method is
- * equivalent to {@link #saturate(float)}.
- *
- * @param a The value to clamp.
- * @return A clamped value between 0 and 1-
- * @see #saturate(float)
- */
- public static float clamp01(float a) {
- return clamp(a, 0f, 1f);
- }
-
- /**
- * Converts an angle measured in degrees to an approximately equivalent angle
- * measured in radians. The conversion from degrees to radians is generally
- * inexact; users should not expect cos(toRadians(90.0)) to exactly equal 0.0.
- *
- * @param angdeg The angle, in degrees.
- * @return The angle in radians.
- */
- public static float toRadians(float angdeg) {
- return (float) Math.toRadians((double) angdeg);
- }
-
- /**
- * Converts an angle measured in radians to an approximately equivalent angle
- * measured in degrees. The conversion from radians to degreees is generally
- * inexact; users should not expect cos(toRadians(90.0)) to exactlyequal 0.0.
- *
- * @param angrad The angle, in radians.
- * @return The angle in degrees.
- */
- public static float toDegrees(float angrad) {
- return (float) Math.toDegrees((double) angrad);
- }
-
- /**
- * Returns a hash code for a float value; compatible with Float.hashCode().
- *
- * @param value The value to hash.
- * @return A hash code value for a float value.
- */
- public static int hashCode(float value) {
- return Float.hashCode(value);
- }
-
- /**
- * Returns the absolute value of a.
- *
- * @param a The argument whose absolute value is to be determined.
- * @return The absolute value of the argument.
- */
- public static float abs(float a) {
- return Math.abs(a);
- }
-
- /**
- * Returns the trigonometric tangent of an angle. Special cases:
- *
- * For example:
- * This method is useful when working with matrices or arrays that are stored in a 1D array. It
+ * calculates the 1D index corresponding to the specified row and column in a matrix with the
+ * given number of columns.
+ *
+ * @param rowIndex The zero-based index of the row.
+ * @param colIndex The zero-based index of the column.
+ * @param numberOfColumns The total number of columns in the matrix.
+ * @return The 1D index corresponding to the given row and column.
+ * @throws IllegalArgumentException if `rowIndex` or `colIndex` is negative, or if
+ * `numberOfColumns` is less than or equal to zero.
+ */
+ public static int toOneDimensionalIndex(int rowIndex, int colIndex, int numberOfColumns) {
+ if (rowIndex < 0 || colIndex < 0) throw new IllegalArgumentException();
+
+ if (numberOfColumns <= 0) throw new IllegalArgumentException();
+
+ return rowIndex * numberOfColumns + colIndex;
+ }
+
+ /**
+ * Returns the smaller of two int values. That is, the result is the argument closer to {@link
+ * Integer#MIN_VALUE}. If the arguments have the same value, the result is that same value.
+ *
+ * @param a The first integer.
+ * @param b The second integer.
+ * @return The smaller of `a` and `b`.
+ */
+ public static int min(int a, int b) {
+ return Math.min(a, b);
+ }
+
+ /**
+ * Returns the larger of two int values. That is, the result is the argument closer to {@link
+ * Integer#MAX_VALUE}. If the arguments have the same value, the result is that same value.
+ *
+ * @param a The first integer.
+ * @param b The second integer.
+ * @return The larger of `a` and `b`.
+ */
+ public static int max(int a, int b) {
+ return Math.max(a, b);
+ }
+
+ /**
+ * Returns the minimum value in the given array.
+ *
+ * @param values The array of integers.
+ * @return The minimum value in the array, or 0 if the array is empty.
+ */
+ public static int min(int[] values) {
+ if (values.length == 0) return 0;
+
+ int min = values[0];
+ for (int i = 1; i < values.length; i++) min = Math.min(min, values[i]);
+ return min;
+ }
+
+ /**
+ * Returns the maximum value in the given array.
+ *
+ * @param values The array of integers.
+ * @return The maximum value in the array, or 0 if the array is empty.
+ */
+ public static int max(int[] values) {
+ if (values.length == 0) return 0;
+
+ int max = values[0];
+ for (int i = 1; i < values.length; i++) max = Math.max(max, values[i]);
+ return max;
+ }
+
+ /**
+ * Returns the larger of the two given float values.
+ *
+ * @param a The first float value.
+ * @param b The second float value.
+ * @return The larger of `a` and `b`.
+ */
+ public static float max(float a, float b) {
+ return Math.max(a, b);
+ }
+
+ /**
+ * Returns the smaller of the two given float values.
+ *
+ * @param a The first float value.
+ * @param b The second float value.
+ * @return The smaller of `a` and `b`.
+ */
+ public static float min(float a, float b) {
+ return Math.min(a, b);
+ }
+
+ /**
+ * Returns the maximum float value in the given array.
+ *
+ * @param values The array of float values.
+ * @return The maximum value in the array, or {@link Float#NaN} if the array is empty.
+ */
+ public static float max(float... values) {
+ if (values.length == 0) return Float.NaN;
+
+ float max = values[0];
+ for (int i = 1; i < values.length; i++) max = Math.max(max, values[i]);
+ return max;
+ }
+
+ /**
+ * Returns the minimum float value in the given array.
+ *
+ * @param values The array of float values.
+ * @return The minimum value in the array, or {@link Float#NaN} if the array is empty.
+ */
+ public static float min(float... values) {
+ if (values.length == 0) return Float.NaN;
+
+ float min = values[0];
+ for (int i = 1; i < values.length; i++) min = Math.min(min, values[i]);
+ return min;
+ }
+
+ /**
+ * Rounds a float value to the nearest integer, rounding ties towards positive infinity.
+ *
+ * @param a The float value to be rounded.
+ * @return The rounded integer value.
+ */
+ public static int roundToInt(float a) {
+ return Math.round(a);
+ }
+
+ /**
+ * Rounds a float value to the nearest integer.
+ *
+ * This method rounds the given float value to the nearest integer. If the fractional part is
+ * 0.5 or greater, the value is rounded up. Otherwise, it is rounded down.
+ *
+ * @param a The float value to be rounded.
+ * @return The rounded float value.
+ */
+ public static float round(float a) {
+ return Math.round(a);
+ }
+
+ /**
+ * Clamps a value between a minimum and maximum value.
+ *
+ * @param a The value to clamp.
+ * @param min The minimum value.
+ * @param max The maximum value-
+ * @return The clamped value.
+ */
+ public static float clamp(float a, float min, float max) {
+ return Math.max(min, Math.min(max, a));
+ }
+
+ /**
+ * Clamps a between min and max and returns the clamped value.
+ *
+ * @param a The value to clamp
+ * @param min The minimum for a.
+ * @param max The maximum for a.
+ * @return The clamped value.
+ */
+ public static int clampInt(int a, int min, int max) {
+ return a < min ? min : (a > max ? max : a);
+ }
+
+ /**
+ * Clamps the given float value to be between 0 and 1. This method is equivalent to {@link
+ * #saturate(float)}.
+ *
+ * @param a The value to clamp.
+ * @return A clamped value between 0 and 1-
+ * @see #saturate(float)
+ */
+ public static float clamp01(float a) {
+ return clamp(a, 0f, 1f);
+ }
+
+ /**
+ * Converts an angle measured in degrees to an approximately equivalent angle measured in radians.
+ * The conversion from degrees to radians is generally inexact; users should not expect
+ * cos(toRadians(90.0)) to exactly equal 0.0.
+ *
+ * @param angdeg The angle, in degrees.
+ * @return The angle in radians.
+ */
+ public static float toRadians(float angdeg) {
+ return (float) Math.toRadians((double) angdeg);
+ }
+
+ /**
+ * Converts an angle measured in radians to an approximately equivalent angle measured in degrees.
+ * The conversion from radians to degreees is generally inexact; users should not expect
+ * cos(toRadians(90.0)) to exactlyequal 0.0.
+ *
+ * @param angrad The angle, in radians.
+ * @return The angle in degrees.
+ */
+ public static float toDegrees(float angrad) {
+ return (float) Math.toDegrees((double) angrad);
+ }
+
+ /**
+ * Returns a hash code for a float value; compatible with Float.hashCode().
+ *
+ * @param value The value to hash.
+ * @return A hash code value for a float value.
+ */
+ public static int hashCode(float value) {
+ return Float.hashCode(value);
+ }
+
+ /**
+ * Returns the absolute value of a.
+ *
+ * @param a The argument whose absolute value is to be determined.
+ * @return The absolute value of the argument.
+ */
+ public static float abs(float a) {
+ return Math.abs(a);
+ }
+
+ /**
+ * Returns the trigonometric tangent of an angle. Special cases:
+ *
+ * Return value is the angle between the x-axis and a 2D vector starting at zero and
+ * terminating at (x,y).
+ *
+ * @param y The ordinate coordinate.
+ * @param x The abscissa coordinate.
+ * @return The theta component of the point (r, theta) in polar coordinates that corresponds to
+ * the point (x, y) in Cartesian coordinates.
+ */
+ public static float atan2(float y, float x) {
+ return (float) Math.atan2((double) y, (double) x);
+ }
+
+ /**
+ * Returns the smallest mathematical integer greater to or equal to a.
+ *
+ * @param a value
+ * @return The smallest (closest to negative infinity) floating-point value that is greater than
+ * or equal to the argument and is equal to a mathematical integer.
+ */
+ public static float ceil(float a) {
+ return (float) Math.ceil((double) a);
+ }
+
+ /**
+ * Returns the value of the first argument raised to the power of the second argument.
+ *
+ * @param a The base.
+ * @param b The exponent.
+ * @return The base raised to the power of b.
+ * @see Math#pow(double, double)
+ */
+ public static float pow(float a, float b) {
+ return (float) Math.pow((double) a, (double) b);
+ }
+
+ /**
+ * Returns the base 10 logarithm of a double value. Special cases:
+ *
+ * For example:
+ *
+ * This method uses a random number generator with a specified seed to ensure reproducibility.
+ *
+ * @return A random float value between 0.0 (inclusive) and 1.0 (exclusive).
+ */
+ public static float randomFloat() {
+ return random.nextFloat();
+ }
+
+ /**
+ * Calculates a smooth, oscillating value between 0 and `length` over time `t`.
+ *
+ * This function is commonly used in game development to create various effects, such as
+ * character movement, object animations, camera effects, and particle systems.
+ *
+ * The function works by repeating the input time `t` over an interval of `length * 2`, and
+ * then calculating the distance between the repeated time and the midpoint `length`. This
+ * distance is then subtracted from `length` to produce the final oscillating value.
+ *
+ * @param t The input time.
+ * @param length The desired range of oscillation.
+ * @return The calculated oscillating value.
+ */
+ public static float pingPong(float t, float length) {
+ t = repeat(t, length * 2f);
+ return length - abs(t - length);
+ }
+
+ /**
+ * Normalizes an angle to a specific range centered around a given center angle.
+ *
+ * This method ensures that the returned angle is within a specific range, typically between -Ï€
+ * and π or 0 and 2π.
+ *
+ * @param a The angle to be normalized.
+ * @param center The center angle of the desired range.
+ * @return The normalized angle.
+ */
+ public static float normalizeAngle(float a, float center) {
+ return a - TWO_PI * floor((a + PI - center) / TWO_PI);
+ }
+
+ /**
+ * Wraps a value cyclically within a specified range.
+ *
+ * This method takes a value `t` and maps it to a value within the interval [0, length). The
+ * value is repeatedly decreased by `length` until it becomes less than `length`. This creates a
+ * cyclic effect, where the value continuously cycles from 0 to `length` and then back to 0.
+ *
+ * **Example:** For `t = 12` and `length = 5`, the result is: - `floor(12 / 5) = 2` (number of
+ * full cycles) - `2 * 5 = 10` (value exceeding the range) - `12 - 10 = 2` (the returned value)
+ *
+ * @param t The value to be wrapped.
+ * @param length The length of the interval within which the value is wrapped.
+ * @return The wrapped value within the interval [0, length).
+ */
+ public static float repeat(float t, float length) {
+ return t - floor(t / length) * length;
+ }
+
+ /**
+ * Determines if two floating-point numbers are approximately equal.
+ *
+ * This method compares two floating-point numbers, `a` and `b`, considering the limited
+ * precision of floating-point numbers. It accounts for both relative and absolute tolerances to
+ * provide a robust comparison method.
+ *
+ * **How it works:** 1. **Calculates absolute difference:** The absolute difference between `a`
+ * and `b` is calculated. 2. **Determines relative tolerance:** The larger of the two absolute
+ * values of `a` and `b` is multiplied by a small factor (e.g., 1e-6) to obtain a relative
+ * tolerance. 3. **Determines absolute tolerance:** A small fixed value (e.g., `FLT_EPSILON * 8`)
+ * is set as the absolute tolerance. 4. **Comparison:** The absolute difference is compared with
+ * the larger of the two tolerances. If the difference is smaller, the numbers are considered
+ * approximately equal.
+ *
+ * **Why such a method is necessary:** Due to the limited precision of floating-point numbers,
+ * small rounding errors can occur, causing two mathematically equal values to not be represented
+ * exactly equal in a computer. This method allows ignoring such small differences.
+ *
+ * @param a The first floating-point number.
+ * @param b The second floating-point number.
+ * @return `true` if `a` and `b` are approximately equal, otherwise `false`.
+ */
+ public static boolean approximately(float a, float b) {
+ return abs(b - a) < max(1E-06f * max(abs(a), abs(b)), FLT_EPSILON * 8f);
+ }
+
+ /**
+ * Clamps the given float value to be between 0 and 1. This method is equivalent to {@link
+ * #clamp01(float)}.
+ *
+ * @param a The value to clamp.
+ * @return A clamped between 0 and 1.
+ * @see #clamp01(float)
+ */
+ public static float saturate(float a) {
+ return clamp(a, 0f, 1f);
+ }
+
+ /**
+ * Returns the next power of two of the given value.
+ *
+ * E.g. for a value of 100, this returns 128. Returns 1 for all numbers <= 1.
+ *
+ * @param value The number to obtain the power of two for.
+ * @return The closest power of two.
+ */
+ public static int closestPowerOfTwo(int value) {
+ value--;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value++;
+ value += (value == 0) ? 1 : 0;
+ return value;
+ }
+
+ /**
+ * Maps a value from one range to another using linear interpolation.
+ *
+ * @param value The value to be mapped.
+ * @param from0 The lower bound of the input range.
+ * @param to0 The upper bound of the input range.
+ * @param from1 The lower bound of the output range.
+ * @param to1 The upper bound of the output range.
+ * @return The mapped value.
+ * @throws IllegalArgumentException if `from0 == to0` or `from1 == to1`.
+ */
+ public static float map(float value, float from0, float to0, float from1, float to1) {
+ if (from0 == to0 || from1 == to1) {
+ throw new IllegalArgumentException("Invalid input ranges");
+ }
+
+ float result = from1 + (to1 - from1) * ((value - from0) / (to0 - from0));
+
+ if (Float.isNaN(result)) {
+ throw new IllegalArgumentException("Result is NaN");
+ } else if (Float.isInfinite(result)) {
+ throw new IllegalArgumentException("Result is infinite");
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the floating-point remainder of dividing two values. This method works similarly to
+ * the fmod function in other programming languages.
+ *
+ * @param a the dividend
+ * @param b the divisor
+ * @return the remainder when a is divided by b
+ */
+ public static float fmod(float a, float b) {
+ return (float) (a - b * Math.floor(a / b));
+ }
+
+ /**
+ * Normalizes the input angle to the range [0, 2Ï€] in radians.
+ *
+ * Small values close to zero (less than 1e-6) are snapped to zero to handle floating-point
+ * precision issues.
+ *
+ * @param angle The input angle in radians.
+ * @return The normalized angle in the range [0, 2Ï€].
+ */
+ public static float normalizeAngle(float angle) {
+ float smallAngleThreshold = 1e-6f;
+
+ angle = angle % (2 * Mathf.PI);
+
+ if (Mathf.abs(angle) < smallAngleThreshold) {
+ angle = 0;
+ }
+
+ if (angle < 0) {
+ angle += 2 * Mathf.PI;
+ }
+
+ return angle;
+ }
}
diff --git a/src/main/java/math/Matrix4f.java b/src/main/java/math/Matrix4f.java
index f148193e..2494300d 100644
--- a/src/main/java/math/Matrix4f.java
+++ b/src/main/java/math/Matrix4f.java
@@ -4,188 +4,378 @@
public class Matrix4f {
- public static final Matrix4f ZERO = new Matrix4f();
-
- public static final Matrix4f IDENTITY = new Matrix4f().identity();
-
- private final float[] values;
-
- public Matrix4f() {
- this.values = new float[16];
- }
-
- public Matrix4f(float... elements) {
- if (elements.length != 16) {
- throw new IllegalArgumentException("Matrix4f requires 16 elements.");
- }
- this.values = Arrays.copyOf(elements, 16);
- }
-
- public Matrix4f(Matrix4f other) {
- this.values = Arrays.copyOf(other.values, 16);
- }
-
- public Matrix4f identity() {
- Arrays.fill(values, 0);
- values[0] = values[5] = values[10] = values[15] = 1;
- return this;
- }
-
- public Matrix4f transpose() {
- return new Matrix4f(values[0], values[4], values[8], values[12], values[1],
- values[5], values[9], values[13], values[2], values[6], values[10],
- values[14], values[3], values[7], values[11], values[15]);
- }
-
- public Matrix4f add(Matrix4f other) {
- float[] result = new float[16];
- for (int i = 0; i < 16; i++) {
- result[i] = this.values[i] + other.values[i];
- }
- return new Matrix4f(result);
- }
-
- public Matrix4f addLocal(Matrix4f other) {
- for (int i = 0; i < 16; i++) {
- this.values[i] += other.values[i];
- }
- return this;
- }
-
- public Matrix4f multiply(Matrix4f other) {
- float[] m = new float[16];
- for (int row = 0; row < 4; row++) {
- for (int col = 0; col < 4; col++) {
- m[row * 4 + col] = values[row * 4 + 0] * other.values[0 * 4 + col]
- + values[row * 4 + 1] * other.values[1 * 4 + col]
- + values[row * 4 + 2] * other.values[2 * 4 + col]
- + values[row * 4 + 3] * other.values[3 * 4 + col];
- }
- }
- return new Matrix4f(m);
- }
-
- public float[] getValues() {
- return Arrays.copyOf(values, values.length);
- }
-
- public float get(int row, int col) {
- return values[row * 4 + col];
- }
-
- public void set(int row, int col, float value) {
- values[row * 4 + col] = value;
- }
-
- public static Matrix4f createTranslation(float x, float y, float z) {
- return new Matrix4f(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1);
- }
-
- /**
- * Sets the matrix to represent a perspective projection matrix.
- *
- *
- * This method computes a standard perspective projection matrix based on the
- * provided field of view, aspect ratio, near clipping plane, and far clipping
- * plane. The resulting matrix transforms 3D points into normalized device
- * coordinates for rendering with perspective projection.
- *
- * The view matrix is computed based on the camera's position (`eye`), pitch,
- * and yaw angles. It assumes a right-handed coordinate system where:
- * This method assumes a right-handed coordinate system where:
+ *
+ * This method computes a standard perspective projection matrix based on the provided field of
+ * view, aspect ratio, near clipping plane, and far clipping plane. The resulting matrix
+ * transforms 3D points into normalized device coordinates for rendering with perspective
+ * projection.
+ *
+ * @param fov Field of view in radians (vertical field of view). Must be between 0 and π radians.
+ * @param aspect Aspect ratio of the viewport (width / height). Must be positive.
+ * @param nearPlane Distance to the near clipping plane. Must be less than `farPlane`.
+ * @param farPlane Distance to the far clipping plane. Must be greater than `nearPlane`.
+ * @return This matrix for chaining calls.
+ * @throws IllegalArgumentException if the input parameters are invalid.
+ */
+ public Matrix4f setPerspective(float fov, float aspect, float nearPlane, float farPlane) {
+ if (nearPlane > farPlane) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Near plane (%.2f) cannot be greater than far plane (%.2f).", nearPlane, farPlane));
+ }
+ if (aspect <= 0) {
+ throw new IllegalArgumentException("Aspect ratio must be a positive number.");
+ }
+ if (fov <= 0.0 || fov >= Math.PI) {
+ throw new IllegalArgumentException("Field of view must be between 0 and π radians.");
+ }
+
+ float f = (float) (1.0 / Math.tan(fov / 2.0));
+ Arrays.fill(values, 0);
+ values[0] = f / aspect;
+ values[5] = f;
+ values[10] = (farPlane + nearPlane) / (nearPlane - farPlane);
+ values[11] = -1;
+ values[14] = (2 * farPlane * nearPlane) / (nearPlane - farPlane);
+
+ return this;
+ }
+
+ // /**
+ // * Constructs a right-handed view matrix for an FPS (First-Person Shooter)
+ // * style camera with -Y as up.
+ // *
+ // * The view matrix is computed based on the camera's position (`eye`), pitch,
+ // * and yaw angles. It assumes a right-handed coordinate system where:
+ // * The view matrix is computed based on the camera's position (`eye`), pitch, and yaw angles.
+ * It assumes a right-handed coordinate system where:
+ *
+ *
- * The equation of the plane is represented as: The equation of the plane is represented as:
* Key Characteristics of an ambient light:
* - Color: Defined by an instance of {@link math.Color},
@@ -24,76 +20,70 @@
* - Non-directional: Ambient light lacks a specific directional focus and has
* equal influence everywhere.
*
- *
- * Usage: This class allows you to dynamically configure the ambient light's
- * color during runtime. Integration with rendering systems can be achieved via
- * the {@link LightRenderer}.
+ *
+ * Usage: This class allows you to dynamically configure the ambient light's color during runtime.
+ * Integration with rendering systems can be achieved via the {@link LightRenderer}.
*/
public class AmbientLight implements Light {
- /**
- * Ambient color.
- */
- private Color color;
+ /** Ambient color. */
+ private Color color;
- /**
- * Constructs a new AmbientLight with a default color of white.
- */
- public AmbientLight() {
- this(Color.WHITE);
- }
+ /** Constructs a new AmbientLight with a default color of white. */
+ public AmbientLight() {
+ this(Color.WHITE);
+ }
- /**
- * Constructs a new AmbientLight with the specified color.
- *
- * @param color the color of the ambient light. Must not be null.
- * @throws IllegalArgumentException if the color is null.
- */
- public AmbientLight(Color color) {
- setColor(color);
- }
+ /**
+ * Constructs a new AmbientLight with the specified color.
+ *
+ * @param color the color of the ambient light. Must not be null.
+ * @throws IllegalArgumentException if the color is null.
+ */
+ public AmbientLight(Color color) {
+ setColor(color);
+ }
- /**
- * Gets the type of the light.
- *
- * @return the light type, which is {@link LightType#AMBIENT}.
- */
- @Override
- public LightType getType() {
- return LightType.AMBIENT;
- }
+ /**
+ * Gets the type of the light.
+ *
+ * @return the light type, which is {@link LightType#AMBIENT}.
+ */
+ @Override
+ public LightType getType() {
+ return LightType.AMBIENT;
+ }
- /**
- * Renders the ambient light using the specified {@link LightRenderer}.
- *
- * @param renderer the renderer to use for rendering the light.
- */
- @Override
- public void render(LightRenderer renderer) {
- renderer.render(this);
- }
+ /**
+ * Renders the ambient light using the specified {@link LightRenderer}.
+ *
+ * @param renderer the renderer to use for rendering the light.
+ */
+ @Override
+ public void render(LightRenderer renderer) {
+ renderer.render(this);
+ }
- /**
- * Gets the color of the ambient light.
- *
- * @return the color of the light. Never null.
- */
- @Override
- public Color getColor() {
- return color;
- }
+ /**
+ * Gets the color of the ambient light.
+ *
+ * @return the color of the light. Never null.
+ */
+ @Override
+ public Color getColor() {
+ return color;
+ }
- /**
- * Sets the color of the ambient light.
- *
- * @param color the new color for the ambient light. Must not be null.
- * @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;
- }
-
-}
\ No newline at end of file
+ /**
+ * Sets the color of the ambient light.
+ *
+ * @param color the new color for the ambient light. Must not be null.
+ * @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;
+ }
+}
diff --git a/src/main/java/engine/scene/light/DirectionalLight.java b/src/main/java/engine/scene/light/DirectionalLight.java
index ccfd8661..b1399d92 100644
--- a/src/main/java/engine/scene/light/DirectionalLight.java
+++ b/src/main/java/engine/scene/light/DirectionalLight.java
@@ -5,196 +5,176 @@
/**
* Represents a directional light source in a 3D scene.
- *
- *
* Key characteristics of a directional light:
* - The light's direction is defined by a normalized vector.
- * - It emits light uniformly in the specified direction, without attenuation
+ * - It emits light uniformly in the specified direction, without attenuation
* (intensity does not decrease with distance).
* - It is commonly used to simulate natural light sources like sunlight during
* the day or moonlight at night.
*
- *
- * This class provides methods to configure the light's direction, color, and
- * intensity, as well as integration with rendering systems via the
- * {@link LightRenderer}.
+ *
+ * This class provides methods to configure the light's direction, color, and intensity, as well as
+ * integration with rendering systems via the {@link LightRenderer}.
*/
public class DirectionalLight implements Light {
- /**
- * The color of the light emitted by the directional light source.
- */
- private Color color;
-
- /**
- * The direction of the light source.
- */
- private Vector3f direction;
-
- /**
- * The intensity of the light emitted by the directional light source.
- */
- private float intensity;
-
- /**
- * Creates a new DirectionalLight instance with default settings.
- *
- *
* - POINT: A point light emits light uniformly in all directions.
- * - DIRECTIONAL: A directional light emits light in parallel rays from
+ * - DIRECTIONAL: A directional light emits light in parallel rays from
* a specific direction.
- * - SPOT: A spotlight emits light in a cone shape, with a specific
+ * - SPOT: A spotlight emits light in a cone shape, with a specific
* direction and angle.
- * - AMBIENT: An ambient light provides uniform illumination across the
- * entire scene, simulating indirect lighting with no specific direction
+ * - AMBIENT: An ambient light provides uniform illumination across the
+ * entire scene, simulating indirect lighting with no specific direction
* or position.
*
*/
public enum LightType {
-
- POINT, DIRECTIONAL, SPOT, AMBIENT
-
-}
\ No newline at end of file
+ POINT,
+ DIRECTIONAL,
+ SPOT,
+ AMBIENT
+}
diff --git a/src/main/java/engine/scene/light/PointLight.java b/src/main/java/engine/scene/light/PointLight.java
index 6262f402..9a71633d 100644
--- a/src/main/java/engine/scene/light/PointLight.java
+++ b/src/main/java/engine/scene/light/PointLight.java
@@ -5,18 +5,15 @@
/**
* Represents a point light source in a 3D scene.
- *
- *
* Key Characteristics of a point light:
- * Position: A 3D vector representing the spatial location of the light in
+ * Position: A 3D vector representing the spatial location of the light in
* the scene.
* Color: The color of light the point light emits. Represented by an instance
* of {@link math.Color}.
@@ -25,186 +22,176 @@
* Range: The maximum distance at which the light's effect is visible, beyond
* which the light has no influence.
*
- *
- * Usage: This class provides methods to dynamically configure light properties,
- * such as changing the light's intensity, range, or color at runtime.
- * Integration with rendering systems can be accomplished via the
- * {@link LightRenderer}.
+ *
+ * Usage: This class provides methods to dynamically configure light properties, such as changing
+ * the light's intensity, range, or color at runtime. Integration with rendering systems can be
+ * accomplished via the {@link LightRenderer}.
*/
public class PointLight implements Light {
- /**
- * The color of the light emitted by the point light source.
- */
- private Color color;
-
- /**
- * The 3D position of the point light source within the scene.
- */
- private Vector3f position;
-
- /**
- * The intensity of the light emitted by the point light source.
- */
- private float intensity;
-
- /**
- * The maximum distance at which the light's effect can influence objects.
- */
- private float range;
-
- /**
- * Creates a new PointLight instance with default settings.
- *
* Key properties include:
@@ -20,249 +19,236 @@
* - Concentration: The exponent controlling how focused the spotlight
* is on its center.
*
- *
- * This class supports both a default spotlight configuration and customizable
- * initialization via its constructors. Input values are validated to ensure
- * realistic and meaningful spotlight behavior.
+ *
+ * This class supports both a default spotlight configuration and customizable initialization via
+ * its constructors. Input values are validated to ensure realistic and meaningful spotlight
+ * behavior.
*/
public class SpotLight implements Light {
- /**
- * 45° in radians, the default cone angle for a standard spotlight.
- */
- private static final float DEFAULT_ANGLE = (float) Math.PI / 4;
-
- /**
- * Default center bias value for the spotlight's cone.
- */
- private static final float DEFAULT_CONCENTRATION = 10.0f;
-
- /**
- * The default position of the spotlight, located at the origin.
- */
- private static final Vector3f DEFAULT_POSITION = new Vector3f(0, 0, 0);
-
- /**
- * The default direction for the spotlight, pointing along the negative
- * Z-axis.
- */
- private static final Vector3f DEFAULT_DIRECTION = new Vector3f(0, 0, -1);
-
- /**
- * The default color of the spotlight's emitted light (white light).
- */
- private static final Color DEFAULT_COLOR = Color.WHITE;
-
- /** The angle of the spotlight's cone in radians. */
- private float angle;
-
- /** Determines the spotlight's intensity concentration toward its center. */
- private float concentration;
-
- /** The position of the spotlight in 3D space. */
- private Vector3f position;
-
- /** The direction vector indicating the spotlight's orientation. */
- private Vector3f direction;
-
- /** The color of the emitted spotlight's light. */
- private Color color;
-
- /**
- * Default constructor initializes the spotlight with pre-defined defaults.
- *
- *
- * The defaults include:
- * - Position at (0,0,0).
- * - Direction pointing along the negative Z-axis.
- * - White color.
- * - A cone angle of 45° (π/4 radians).
- * - A concentration value of 10.0 (focused light)
- *
- */
- public SpotLight() {
- this(DEFAULT_POSITION, DEFAULT_DIRECTION, DEFAULT_COLOR,
- DEFAULT_CONCENTRATION, DEFAULT_ANGLE);
- }
-
- /**
- * Constructs a new SpotLight instance with the specified properties.
- *
- *
+ * The defaults include:
+ * - Position at (0,0,0).
+ * - Direction pointing along the negative Z-axis.
+ * - White color.
+ * - A cone angle of 45° (π/4 radians).
+ * - A concentration value of 10.0 (focused light)
+ *
+ */
+ public SpotLight() {
+ this(DEFAULT_POSITION, DEFAULT_DIRECTION, DEFAULT_COLOR, DEFAULT_CONCENTRATION, DEFAULT_ANGLE);
+ }
+
+ /**
+ * Constructs a new SpotLight instance with the specified properties.
+ *
+ *
- *
- *
- *
- * @param a An angle, in radians.
- * @return The cosine of the argument.
- */
- public static float cos(float a) {
- return (float) Math.cos((double) a);
- }
-
- /**
- * Returns the trigonometric sine of an angle. Special cases:
- *
- *
- * The computed result must be within 1 ulp of the exact result. Results must
- * be semi-monotonic.
- *
- * @param a An angle, in radians.
- * @return The sine of the argument.
- */
- public static float sin(float a) {
- return (float) Math.sin((double) a);
- }
-
- /**
- * Determines whether or not the given value a is in range of min and max.
- *
- * @param a The value to check.
- * @param min The minimum value for a.
- * @param max The maximum value for a.
- * @return true if the value is in range of [min,max], false otherwise.
- */
- public static boolean isInRange(float a, int min, int max) {
- return a >= min && a <= max;
- }
-
- /**
- * Returns the signum function of the argument; zero if the argument is zero,
- * 1.0f if the argument is greater than zero, -1.0f if the argument is less
- * than zero. Special Cases:
- *
- *
- *
- * @param a The floating-point value whose signum is to be returned.
- * @return The signum function of the argument.
- */
- public static float sign(float a) {
- return Math.signum(a);
- }
-
- /**
- * Returns the correctly rounded positive square root of a float value.
- * Special cases:
- *
- *
- *
- * @param a A value.
- * @return The positive square root of a. If the argument is NaN or less than
- * zero, the result is NaN.
- */
- public static float sqrt(float a) {
- return (float) Math.sqrt((double) a);
- }
-
- /**
- * Returns the largest (closest to positive infinity) float value that is less
- * than or equal to the argument and is equal to a mathematical integer.
- * Special cases:
- *
- * Otherwise, the result is the double value closest to the true mathematical
- * square root of the argument value.
- *
- *
- *
- * @param a A value.
- * @return The largest (closest to positive infinity) floating-point value
- * that less than or equal to the argument and is equal to a
- * mathematical integer.
- */
- public static float floor(float a) {
- return (float) Math.floor((double) a);
- }
-
- /**
- * Returns Euler's number e raised to the power of a float value. Special
- * cases:
- *
- *
- * The computed result must be within 1 ulp of the exact result. Results must
- * be semi-monotonic.
- *
- * @param a The exponent to raise e to.
- * @return The value ea, where e is the base of the natural
- * logarithms.
- */
- public static float exp(float a) {
- return (float) Math.exp((double) a);
- }
-
- /**
- * Returns true if the argument is a finite floating-point value; returns
- * false otherwise (for NaN and infinity arguments).
- *
- * @param f The float value to be tested.
- * @return true if the argument is a finite floating-point value, false
- * otherwise.
- */
- public static boolean isFinite(float f) {
- return Float.isFinite(f);
- }
-
- /**
- * Returns true if the specified number is infinitely large in magnitude,
- * false otherwise.
- *
- * @param v The value to be tested.
- * @return true if the argument is positive infinity or negative infinity;
- * false otherwise.
- */
- public static boolean isInfinite(float v) {
- return Float.isInfinite(v);
- }
-
- /**
- * Returns true if the specified number is a Not-a-Number (NaN) value, false
- * otherwise.
- *
- * @param v The value to be tested.
- * @return true if the argument is NaN; false otherwise.
- */
- public static boolean isNaN(float v) {
- return Float.isNaN(v);
- }
-
- /**
- * Returns the arc cosine of a (the angle in radians whose cosine is a).
- *
- * @param a The value whose arc cosine is to be returned.
- * @return The arc cosine of a.
- */
- public static float acos(float a) {
- return (float) Math.acos((double) a);
- }
-
- /**
- * Returns the arc-sine of a - the angle in radians whose sine is a.
- *
- * @param a The value whose arc sine is to be returned.
- * @return The arc sine of the argument.
- */
- public static float asin(float a) {
- return (float) Math.asin((double) a);
- }
-
- /**
- * Returns the arc tangent of a (the angle in radians whose tangent is a).
- *
- * @param a The value whose arc tangent is to be returned.
- * @return The arc tangent of the argument.
- */
- public static float atan(float a) {
- return (float) Math.atan((double) a);
- }
-
- /**
- * Returns the angle in radians whose Tan is y/x.
- *
- * Return value is the angle between the x-axis and a 2D vector starting at
- * zero and terminating at (x,y).
- *
- * @param y The ordinate coordinate.
- * @param x The abscissa coordinate.
- * @return The theta component of the point (r, theta) in polar coordinates
- * that corresponds to the point (x, y) in Cartesian coordinates.
- */
- public static float atan2(float y, float x) {
- return (float) Math.atan2((double) y, (double) x);
- }
-
- /**
- * Returns the smallest mathematical integer greater to or equal to a.
- *
- * @param a value
- * @return The smallest (closest to negative infinity) floating-point value
- * that is greater than or equal to the argument and is equal to a
- * mathematical integer.
- */
- public static float ceil(float a) {
- return (float) Math.ceil((double) a);
- }
-
- /**
- * Returns the value of the first argument raised to the power of the second
- * argument.
- *
- * @param a The base.
- * @param b The exponent.
- * @return The base raised to the power of b.
- * @see Math#pow(double, double)
- */
- public static float pow(float a, float b) {
- return (float) Math.pow((double) a, (double) b);
- }
-
- /**
- * Returns the base 10 logarithm of a double value. Special cases:
- *
- *
- *
- * @param a A value.
- * @return The base 10 logarithm of a.
- */
- public static float log10(float a) {
- return (float) Math.log10((double) a);
- }
-
- /**
- * Returns the natural logarithm (base e) of a float value. Special cases:
- *
- *
- * The computed result must be within 1 ulp of the exact result. Results must
- * be semi-monotonic.
- *
- * @param a A value.
- * @return The value ln a, the natural logarithm of a.
- */
- public static float log(float a) {
- return (float) Math.log((double) a);
- }
-
- /**
- * Returns the largest (closest to positive infinity) integer value that is
- * less than or equal to the argument.
- *
- *
- *
- * @param a A value.
- * @return The largest (closest to positive infinity) integer value that is
- * less than or equal to the argument.
- */
- public static int floorToInt(float a) {
- return (int) Math.floor((double) a);
- }
-
- /**
- * Returns the smallest mathematical integer greater to or equal to a.
- *
- * @param a The value to ceil.
- * @return The smallest (closest to negative infinity) integer value that is
- * greater than or equal to the argument and is equal to a
- * mathematical integer.
- */
- public static int ceilToInt(float a) {
- return (int) Math.ceil((double) a);
- }
-
- /**
- * Linearly interpolates between a and b by t. The parameter t is not clamped
- * and values outside the range [0, 1] will result in a return value outside
- * the range [a, /b/].
- *
- *
- * When t = 0 returns a.
- * When t = 1 returns b.
- * When t = 0.5 returns the midpoint of a and b.
- *
- *
- * @param a The value to interpolate from.
- * @param b The value to interpolate to.
- * @param t The value to interpolate by.
- * @return The resultant interpolated value.
- */
- public static float lerpUnclamped(float a, float b, float t) {
- return a + (b - a) * t;
- }
-
- /**
- * Linearly interpolates between from and to by t. The parameter t is clamped
- * to the range [0, 1].
- *
- *
- * When t = 0 returns a.
- * When t = 1 return b.
- * When t = 0.5 returns the midpoint of a and b.
- *
- *
- * @param from The value to interpolate from.
- * @param to The value to interpolate to.
- * @param t The value to interpolate by.
- * @return The resultant interpolated value.
- */
- public static float lerp(float from, float to, float t) {
- return from + (to - from) * clamp01(t);
- }
-
- /**
- * Returns the next power of two greater than or equal to the given value.
- *
- *
- *
- *
- * @param value the input value
- * @return the next power of two greater than or equal to the input value
- */
- public static int nextPowerOfTwo(int value) {
- return value > 0 ? Integer.highestOneBit(value - 1) << 1 : 1;
- }
-
- /**
- * Smoothly interpolates between two values. This function provides a smoother
- * transition between the two values compared to linear interpolation. It uses
- * a cubic Hermite spline to achieve a smooth curve.
- *
- * @param from The starting value.
- * @param to The ending value.
- * @param t The interpolation factor, clamped to the range [0, 1].
- * @return The interpolated value.
- */
- public static float smoothStep(float from, float to, float t) {
- t = clamp01(t);
- t = -2f * t * t * t + 3f * t * t;
- return to * t + from * (1f - t);
- }
-
- /**
- * Returns a random float value between the specified minimum and maximum
- * values, inclusive.
- *
- * @param min The minimum value.
- * @param max The maximum value.
- * @return A random float value between min and max, inclusive.
- */
- public static float random(float min, float max) {
- return random.nextFloat() * (max - min) + min;
- }
-
- /**
- * Returns a random integer value between the specified minimum and maximum
- * values, inclusive.
- *
- * @param min The minimum value.
- * @param max The maximum value.
- * @return A random integer value between min and max, inclusive.
- */
- public static int random(int min, int max) {
- return random.nextInt(max - min + 1) + min;
- }
-
- /**
- * Sets the seed for the random number generator. This allows for reproducible
- * random sequences.
- *
- * @param seed The seed value.
- */
- public static void setSeed(long seed) {
- random.setSeed(seed);
- }
-
- /**
- * Returns a random float value between 0.0 (inclusive) and 1.0 (exclusive).
- *
- * This method uses a random number generator with a specified seed to ensure
- * reproducibility.
- *
- * @return A random float value between 0.0 (inclusive) and 1.0 (exclusive).
- */
- public static float randomFloat() {
- return random.nextFloat();
- }
-
- /**
- * Calculates a smooth, oscillating value between 0 and `length` over time
- * `t`.
- *
- * This function is commonly used in game development to create various
- * effects, such as character movement, object animations, camera effects, and
- * particle systems.
- *
- * The function works by repeating the input time `t` over an interval of
- * `length * 2`, and then calculating the distance between the repeated time
- * and the midpoint `length`. This distance is then subtracted from `length`
- * to produce the final oscillating value.
- *
- * @param t The input time.
- * @param length The desired range of oscillation.
- * @return The calculated oscillating value.
- */
- public static float pingPong(float t, float length) {
- t = repeat(t, length * 2f);
- return length - abs(t - length);
- }
-
- /**
- * Normalizes an angle to a specific range centered around a given center
- * angle.
- *
- * This method ensures that the returned angle is within a specific range,
- * typically between -π and π or 0 and 2π.
- *
- * @param a The angle to be normalized.
- * @param center The center angle of the desired range.
- * @return The normalized angle.
- */
- public static float normalizeAngle(float a, float center) {
- return a - TWO_PI * floor((a + PI - center) / TWO_PI);
- }
-
- /**
- * Wraps a value cyclically within a specified range.
- *
- * This method takes a value `t` and maps it to a value within the interval
- * [0, length). The value is repeatedly decreased by `length` until it becomes
- * less than `length`. This creates a cyclic effect, where the value
- * continuously cycles from 0 to `length` and then back to 0.
- *
- * **Example:** For `t = 12` and `length = 5`, the result is: - `floor(12 / 5)
- * = 2` (number of full cycles) - `2 * 5 = 10` (value exceeding the range) -
- * `12 - 10 = 2` (the returned value)
- *
- * @param t The value to be wrapped.
- * @param length The length of the interval within which the value is wrapped.
- * @return The wrapped value within the interval [0, length).
- */
- public static float repeat(float t, float length) {
- return t - floor(t / length) * length;
- }
-
- /**
- * Determines if two floating-point numbers are approximately equal.
- *
- * This method compares two floating-point numbers, `a` and `b`, considering
- * the limited precision of floating-point numbers. It accounts for both
- * relative and absolute tolerances to provide a robust comparison method.
- *
- * **How it works:** 1. **Calculates absolute difference:** The absolute
- * difference between `a` and `b` is calculated. 2. **Determines relative
- * tolerance:** The larger of the two absolute values of `a` and `b` is
- * multiplied by a small factor (e.g., 1e-6) to obtain a relative tolerance.
- * 3. **Determines absolute tolerance:** A small fixed value (e.g.,
- * `FLT_EPSILON * 8`) is set as the absolute tolerance. 4. **Comparison:** The
- * absolute difference is compared with the larger of the two tolerances. If
- * the difference is smaller, the numbers are considered approximately equal.
- *
- * **Why such a method is necessary:** Due to the limited precision of
- * floating-point numbers, small rounding errors can occur, causing two
- * mathematically equal values to not be represented exactly equal in a
- * computer. This method allows ignoring such small differences.
- *
- * @param a The first floating-point number.
- * @param b The second floating-point number.
- * @return `true` if `a` and `b` are approximately equal, otherwise `false`.
- */
- public static boolean approximately(float a, float b) {
- return abs(b - a) < max(1E-06f * max(abs(a), abs(b)), FLT_EPSILON * 8f);
- }
-
- /**
- * Clamps the given float value to be between 0 and 1. This method is
- * equivalent to {@link #clamp01(float)}.
- *
- * @param a The value to clamp.
- * @return A clamped between 0 and 1.
- * @see #clamp01(float)
- */
- public static float saturate(float a) {
- return clamp(a, 0f, 1f);
- }
-
- /**
- * Returns the next power of two of the given value.
- *
- * E.g. for a value of 100, this returns 128. Returns 1 for all numbers <= 1.
- *
- * @param value The number to obtain the power of two for.
- * @return The closest power of two.
- */
- public static int closestPowerOfTwo(int value) {
- value--;
- value |= value >> 1;
- value |= value >> 2;
- value |= value >> 4;
- value |= value >> 8;
- value |= value >> 16;
- value++;
- value += (value == 0) ? 1 : 0;
- return value;
- }
-
- /**
- * Maps a value from one range to another using linear interpolation.
- *
- * @param value The value to be mapped.
- * @param from0 The lower bound of the input range.
- * @param to0 The upper bound of the input range.
- * @param from1 The lower bound of the output range.
- * @param to1 The upper bound of the output range.
- * @return The mapped value.
- *
- * @throws IllegalArgumentException if `from0 == to0` or `from1 == to1`.
- */
- public static float map(float value, float from0, float to0, float from1,
- float to1) {
- if (from0 == to0 || from1 == to1) {
- throw new IllegalArgumentException("Invalid input ranges");
- }
-
- float result = from1 + (to1 - from1) * ((value - from0) / (to0 - from0));
-
- if (Float.isNaN(result)) {
- throw new IllegalArgumentException("Result is NaN");
- } else if (Float.isInfinite(result)) {
- throw new IllegalArgumentException("Result is infinite");
- }
-
- return result;
- }
-
- /**
- * Calculates the floating-point remainder of dividing two values. This method
- * works similarly to the fmod function in other programming languages.
- *
- * @param a the dividend
- * @param b the divisor
- * @return the remainder when a is divided by b
- */
- public static float fmod(float a, float b) {
- return (float) (a - b * Math.floor(a / b));
- }
-
- /**
- * Normalizes the input angle to the range [0, 2Ï€] in radians.
- *
- * Small values close to zero (less than 1e-6) are snapped to zero to handle
- * floating-point precision issues.
- *
- * @param angle The input angle in radians.
- * @return The normalized angle in the range [0, 2Ï€].
- */
- public static float normalizeAngle(float angle) {
- float smallAngleThreshold = 1e-6f;
-
- angle = angle % (2 * Mathf.PI);
-
- if (Mathf.abs(angle) < smallAngleThreshold) {
- angle = 0;
- }
-
- if (angle < 0) {
- angle += 2 * Mathf.PI;
- }
-
- return angle;
- }
+ /** A random number generator used to generate random values. */
+ private static Random random = new Random();
+ /** A float representation of the golden ratio, approximately 1.618. */
+ public static final float GOLDEN_RATIO = (1 + sqrt(5)) / 2.0f;
+
+ /**
+ * A float representation of the reciprocal of the golden ratio, , which is exactly 1 less than
+ * the golden ratio itself; approximately 0.618.
+ */
+ public static final float GOLDEN_RATIO_RECIPROCAL = 2 / (1 + sqrt(5));
+
+ /** Euler's number, the base of the natural logarithm, approximately 2.718. */
+ public static final float E = (float) Math.E;
+
+ /** A representation of negative infinity. */
+ public static final float NEGATIVE_INFINITY = Float.NEGATIVE_INFINITY;
+
+ /** A representation of positive infinity. */
+ public static final float POSITIVE_INFINITY = Float.POSITIVE_INFINITY;
+
+ /** The smallest positive nonzero value representable as a float. */
+ public static final float MIN_VALUE = Float.MIN_VALUE;
+
+ /** The largest finite value representable as a float. */
+ public static final float MAX_VALUE = Float.MAX_VALUE;
+
+ /** A small value used for floating-point comparisons, approximately 2.22E-16. */
+ public static final double DBL_EPSILON = 2.220446049250313E-16d;
+
+ /** A small value used for floating-point comparisons, approximately 1.19E-7. */
+ public static final float FLT_EPSILON = 1.1920928955078125E-7f;
+
+ /** A small tolerance value for comparing floating-point numbers. */
+ public static final float ZERO_TOLERANCE = 0.0001f;
+
+ /** A float representation of one-third, approximately 0.33333334. */
+ public static final float ONE_THIRD = 1f / 3f;
+
+ /** The value of Pi, approximately 3.14159. */
+ public static final float PI = (float) Math.PI;
+
+ /** Twice the value of Pi, approximately 6.283185. */
+ public static final float TWO_PI = 2.0f * PI;
+
+ /** Half the value of Pi, approximately 1.570796. */
+ public static final float HALF_PI = 0.5f * PI;
+
+ /** A quarter of the value of Pi, approximately 0.785398. */
+ public static final float QUARTER_PI = 0.25f * PI;
+
+ /** The reciprocal of Pi, approximately 0.3183099. */
+ public static final float INV_PI = 1.0f / PI;
+
+ /** The reciprocal of two times Pi, approximately 0.1591549. */
+ public static final float INV_TWO_PI = 1.0f / TWO_PI;
+
+ /** A factor to convert degrees to radians, approximately 0.0174533. */
+ public static final float DEG_TO_RAD = PI / 180.0f;
+
+ /** A factor to convert radians to degrees, approximately 57.29578. */
+ public static final float RAD_TO_DEG = 180.0f / PI;
+
+ /**
+ * The Tribonacci constant, often denoted as t, is the real root of the cubic equation x³ - x² - x
+ * - 1 = 0. It is approximately equal to 1.83928675521416.
+ */
+ public static final float TRIBONACCI_CONSTANT = 1.83928675521416f;
+
+ /**
+ * Converts a 2D index (row, column) into a 1D index for a matrix or array.
+ *
+ *
+ *
+ *
+ *
+ * @param a An angle, in radians.
+ * @return The cosine of the argument.
+ */
+ public static float cos(float a) {
+ return (float) Math.cos((double) a);
+ }
+
+ /**
+ * Returns the trigonometric sine of an angle. Special cases:
+ *
+ *
+ *
+ *
+ * The computed result must be within 1 ulp of the exact result. Results must be semi-monotonic.
+ *
+ * @param a An angle, in radians.
+ * @return The sine of the argument.
+ */
+ public static float sin(float a) {
+ return (float) Math.sin((double) a);
+ }
+
+ /**
+ * Determines whether or not the given value a is in range of min and max.
+ *
+ * @param a The value to check.
+ * @param min The minimum value for a.
+ * @param max The maximum value for a.
+ * @return true if the value is in range of [min,max], false otherwise.
+ */
+ public static boolean isInRange(float a, int min, int max) {
+ return a >= min && a <= max;
+ }
+
+ /**
+ * Returns the signum function of the argument; zero if the argument is zero, 1.0f if the argument
+ * is greater than zero, -1.0f if the argument is less than zero. Special Cases:
+ *
+ *
+ *
+ *
+ * @param a The floating-point value whose signum is to be returned.
+ * @return The signum function of the argument.
+ */
+ public static float sign(float a) {
+ return Math.signum(a);
+ }
+
+ /**
+ * Returns the correctly rounded positive square root of a float value. Special cases:
+ *
+ *
+ *
+ *
+ * @param a A value.
+ * @return The positive square root of a. If the argument is NaN or less than zero, the result is
+ * NaN.
+ */
+ public static float sqrt(float a) {
+ return (float) Math.sqrt((double) a);
+ }
+
+ /**
+ * Returns the largest (closest to positive infinity) float value that is less than or equal to
+ * the argument and is equal to a mathematical integer. Special cases:
+ *
+ *
+ * Otherwise, the result is the double value closest to the true mathematical square root of
+ * the argument value.
+ *
+ *
+ *
+ * @param a A value.
+ * @return The largest (closest to positive infinity) floating-point value that less than or equal
+ * to the argument and is equal to a mathematical integer.
+ */
+ public static float floor(float a) {
+ return (float) Math.floor((double) a);
+ }
+
+ /**
+ * Returns Euler's number e raised to the power of a float value. Special cases:
+ *
+ *
+ *
+ *
+ * The computed result must be within 1 ulp of the exact result. Results must be semi-monotonic.
+ *
+ * @param a The exponent to raise e to.
+ * @return The value ea, where e is the base of the natural logarithms.
+ */
+ public static float exp(float a) {
+ return (float) Math.exp((double) a);
+ }
+
+ /**
+ * Returns true if the argument is a finite floating-point value; returns false otherwise (for NaN
+ * and infinity arguments).
+ *
+ * @param f The float value to be tested.
+ * @return true if the argument is a finite floating-point value, false otherwise.
+ */
+ public static boolean isFinite(float f) {
+ return Float.isFinite(f);
+ }
+
+ /**
+ * Returns true if the specified number is infinitely large in magnitude, false otherwise.
+ *
+ * @param v The value to be tested.
+ * @return true if the argument is positive infinity or negative infinity; false otherwise.
+ */
+ public static boolean isInfinite(float v) {
+ return Float.isInfinite(v);
+ }
+
+ /**
+ * Returns true if the specified number is a Not-a-Number (NaN) value, false otherwise.
+ *
+ * @param v The value to be tested.
+ * @return true if the argument is NaN; false otherwise.
+ */
+ public static boolean isNaN(float v) {
+ return Float.isNaN(v);
+ }
+
+ /**
+ * Returns the arc cosine of a (the angle in radians whose cosine is a).
+ *
+ * @param a The value whose arc cosine is to be returned.
+ * @return The arc cosine of a.
+ */
+ public static float acos(float a) {
+ return (float) Math.acos((double) a);
+ }
+
+ /**
+ * Returns the arc-sine of a - the angle in radians whose sine is a.
+ *
+ * @param a The value whose arc sine is to be returned.
+ * @return The arc sine of the argument.
+ */
+ public static float asin(float a) {
+ return (float) Math.asin((double) a);
+ }
+
+ /**
+ * Returns the arc tangent of a (the angle in radians whose tangent is a).
+ *
+ * @param a The value whose arc tangent is to be returned.
+ * @return The arc tangent of the argument.
+ */
+ public static float atan(float a) {
+ return (float) Math.atan((double) a);
+ }
+
+ /**
+ * Returns the angle in radians whose Tan is y/x.
+ *
+ *
+ *
+ *
+ * @param a A value.
+ * @return The base 10 logarithm of a.
+ */
+ public static float log10(float a) {
+ return (float) Math.log10((double) a);
+ }
+
+ /**
+ * Returns the natural logarithm (base e) of a float value. Special cases:
+ *
+ *
+ *
+ *
+ * The computed result must be within 1 ulp of the exact result. Results must be semi-monotonic.
+ *
+ * @param a A value.
+ * @return The value ln a, the natural logarithm of a.
+ */
+ public static float log(float a) {
+ return (float) Math.log((double) a);
+ }
+
+ /**
+ * Returns the largest (closest to positive infinity) integer value that is less than or equal to
+ * the argument.
+ *
+ *
+ *
+ *
+ * @param a A value.
+ * @return The largest (closest to positive infinity) integer value that is less than or equal to
+ * the argument.
+ */
+ public static int floorToInt(float a) {
+ return (int) Math.floor((double) a);
+ }
+
+ /**
+ * Returns the smallest mathematical integer greater to or equal to a.
+ *
+ * @param a The value to ceil.
+ * @return The smallest (closest to negative infinity) integer value that is greater than or equal
+ * to the argument and is equal to a mathematical integer.
+ */
+ public static int ceilToInt(float a) {
+ return (int) Math.ceil((double) a);
+ }
+
+ /**
+ * Linearly interpolates between a and b by t. The parameter t is not clamped and values outside
+ * the range [0, 1] will result in a return value outside the range [a, /b/].
+ *
+ *
+ * When t = 0 returns a.
+ * When t = 1 returns b.
+ * When t = 0.5 returns the midpoint of a and b.
+ *
+ *
+ * @param a The value to interpolate from.
+ * @param b The value to interpolate to.
+ * @param t The value to interpolate by.
+ * @return The resultant interpolated value.
+ */
+ public static float lerpUnclamped(float a, float b, float t) {
+ return a + (b - a) * t;
+ }
+
+ /**
+ * Linearly interpolates between from and to by t. The parameter t is clamped to the range [0, 1].
+ *
+ *
+ * When t = 0 returns a.
+ * When t = 1 return b.
+ * When t = 0.5 returns the midpoint of a and b.
+ *
+ *
+ * @param from The value to interpolate from.
+ * @param to The value to interpolate to.
+ * @param t The value to interpolate by.
+ * @return The resultant interpolated value.
+ */
+ public static float lerp(float from, float to, float t) {
+ return from + (to - from) * clamp01(t);
+ }
+
+ /**
+ * Returns the next power of two greater than or equal to the given value.
+ *
+ *
+ *
+ *
+ * @param value the input value
+ * @return the next power of two greater than or equal to the input value
+ */
+ public static int nextPowerOfTwo(int value) {
+ return value > 0 ? Integer.highestOneBit(value - 1) << 1 : 1;
+ }
+
+ /**
+ * Smoothly interpolates between two values. This function provides a smoother transition between
+ * the two values compared to linear interpolation. It uses a cubic Hermite spline to achieve a
+ * smooth curve.
+ *
+ * @param from The starting value.
+ * @param to The ending value.
+ * @param t The interpolation factor, clamped to the range [0, 1].
+ * @return The interpolated value.
+ */
+ public static float smoothStep(float from, float to, float t) {
+ t = clamp01(t);
+ t = -2f * t * t * t + 3f * t * t;
+ return to * t + from * (1f - t);
+ }
+
+ /**
+ * Returns a random float value between the specified minimum and maximum values, inclusive.
+ *
+ * @param min The minimum value.
+ * @param max The maximum value.
+ * @return A random float value between min and max, inclusive.
+ */
+ public static float random(float min, float max) {
+ return random.nextFloat() * (max - min) + min;
+ }
+
+ /**
+ * Returns a random integer value between the specified minimum and maximum values, inclusive.
+ *
+ * @param min The minimum value.
+ * @param max The maximum value.
+ * @return A random integer value between min and max, inclusive.
+ */
+ public static int random(int min, int max) {
+ return random.nextInt(max - min + 1) + min;
+ }
+
+ /**
+ * Sets the seed for the random number generator. This allows for reproducible random sequences.
+ *
+ * @param seed The seed value.
+ */
+ public static void setSeed(long seed) {
+ random.setSeed(seed);
+ }
+
+ /**
+ * Returns a random float value between 0.0 (inclusive) and 1.0 (exclusive).
+ *
+ *
- *
- * This method is particularly useful for creating a camera that can move and
- * rotate freely in a 3D scene, such as in games or visualization
- * applications.
- *
+ *
+ *
+ * The look direction vector should be a normalized vector representing the direction the camera
+ * is facing. The up vector defines the vertical direction for the camera.
+ *
+ * @param eye The position of the camera in world space, represented as a {@link Vector3f}.
+ * @param look The normalized direction vector of the camera, represented as a {@link Vector3f}.
+ * @param up The up vector defining the camera's vertical direction, represented as a {@link
+ * Vector3f}. This vector should be orthogonal to the look direction vector.
+ * @return A {@link Matrix4f} representing the view matrix for the specified camera configuration.
+ */
+ // public Matrix4f lookAt(Vector3f eye, Vector3f look, Vector3f up) {
+ // // Calculate the right vector (orthogonal to look and up)
+ // Vector3f right = look.cross(up).normalizeLocal();
+ // // Calculate the orthogonal up vector (orthogonal to look and right)
+ // up = right.cross(look).normalizeLocal();
+ //
+ // float[][] view = new float[4][4];
+ // view[0][0] = right.x;
+ // view[0][1] = up.x;
+ // view[0][2] = look.x;
+ // view[0][3] = -eye.dot(right);
+ // view[1][0] = right.y;
+ // view[1][1] = up.y;
+ // view[1][2] = look.y;
+ // view[1][3] = -eye.dot(up);
+ // view[2][0] = right.z;
+ // view[2][1] = up.z;
+ // view[2][2] = look.z;
+ // view[2][3] = -eye.dot(look);
+ // view[3][0] = 0.0f;
+ // view[3][1] = 0.0f;
+ // view[3][2] = 0.0f;
+ // view[3][3] = 1.0f;
+ //
+ // // Convert view matrix to a Matrix4f object
+ // for (int row = 0; row < 4; row++) {
+ // for (int col = 0; col < 4; col++) {
+ // this.values[row * 4 + col] = view[row][col];
+ // }
+ // }
+ // return this;
+ // }
+
+ public static Matrix4f lookAt(Vector3f eye, Vector3f target, Vector3f up) {
+ Vector3f forward = target.subtract(eye).normalize();
+ Vector3f right = forward.cross(up).normalize();
+ Vector3f cameraUp = right.cross(forward);
+
+ Matrix4f result = new Matrix4f();
+ result.set(0, 0, right.x);
+ result.set(0, 1, right.y);
+ result.set(0, 2, right.z);
+ result.set(0, 3, -right.dot(eye));
+
+ result.set(1, 0, cameraUp.x);
+ result.set(1, 1, cameraUp.y);
+ result.set(1, 2, cameraUp.z);
+ result.set(1, 3, -cameraUp.dot(eye));
+
+ result.set(2, 0, -forward.x);
+ result.set(2, 1, -forward.y);
+ result.set(2, 2, -forward.z);
+ result.set(2, 3, forward.dot(eye));
+
+ result.set(3, 0, 0);
+ result.set(3, 1, 0);
+ result.set(3, 2, 0);
+ result.set(3, 3, 1);
+
+ return result;
+ }
+
+ /**
+ * Sets the matrix to represent a perspective projection matrix.
+ *
+ *
+ // *
+ // *
+ *
+ *
+ * This method is particularly useful for creating a camera that can move and rotate freely in a
+ * 3D scene, such as in games or visualization applications.
+ *
+ * @param eye the position of the camera in world space, represented as a {@link Vector3f}.
+ * @param pitch the pitch angle (rotation around the X-axis), in radians. A positive value tilts
+ * the camera upward.
+ * @param yaw the yaw angle (rotation around the Y-axis), in radians. A positive value rotates the
+ * camera to the right.
+ * @return a {@link Matrix4f} representing the view matrix for the specified position and
+ * orientation.
+ * @see Understanding the View
+ * Matrix
+ */
+ public static Matrix4f fpsViewRH(Vector3f eye, float pitch, float yaw) {
+ float cosPitch = Mathf.cos(pitch);
+ float sinPitch = Mathf.sin(pitch);
+ float cosYaw = Mathf.cos(yaw);
+ float sinYaw = Mathf.sin(yaw);
+
+ Vector3f right = new Vector3f(cosYaw, 0, -sinYaw);
+ Vector3f up = new Vector3f(sinYaw * sinPitch, cosPitch, cosYaw * sinPitch);
+ Vector3f forward = new Vector3f(sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw);
+
+ Matrix4f viewMatrix =
+ new Matrix4f(
+ right.x,
+ up.x,
+ forward.x,
+ 0,
+ right.y,
+ up.y,
+ forward.y,
+ 0,
+ right.z,
+ up.z,
+ forward.z,
+ 0,
+ -right.dot(eye),
+ -up.dot(eye),
+ -forward.dot(eye),
+ 1);
+
+ return viewMatrix;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Matrix4f:\n");
+ for (int row = 0; row < 4; row++) {
+ sb.append("[ ");
+ for (int col = 0; col < 4; col++) {
+ sb.append(get(row, col)).append(" ");
+ }
+ sb.append("]\n");
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/math/Plane.java b/src/main/java/math/Plane.java
index 31000126..3dba9b2a 100644
--- a/src/main/java/math/Plane.java
+++ b/src/main/java/math/Plane.java
@@ -1,95 +1,89 @@
package math;
/**
- * Represents a geometric plane in 3D space defined by a normal vector and a
- * distance from the origin.
- *
+ * Represents a geometric plane in 3D space defined by a normal vector and a distance from the
+ * origin.
+ *
+ *
* Ax + By + Cz + D = 0, where:
+ *
*
- *
- * (A, B, C) is the normalized normal vector of the plane.D is the distance of the plane from the origin, along the
- * normal direction.(A, B, C) is the normalized normal vector of the plane.
+ * D is the distance of the plane from the origin, along the normal direction.
*
- * The resulting plane is uninitialized and must be configured using the - * {@link #set(float, float, float, float)} method. - *
- */ - public Plane() { - this.normal = new Vector3f(); - this.distance = 0; - } + /** + * Constructs a plane with a default normal vector (0, 0, 0) and a distance of 0. + * + *The resulting plane is uninitialized and must be configured using the {@link #set(float, + * float, float, float)} method. + */ + public Plane() { + this.normal = new Vector3f(); + this.distance = 0; + } - /** - * Sets the plane parameters using its coefficients. - *
- * The coefficients (A, B, C, D) define the plane equation
- * Ax + By + Cz + D = 0. The normal vector is automatically
- * normalized during this operation.
- *
The coefficients (A, B, C, D) define the plane equation Ax + By + Cz + D = 0.
+ * The normal vector is automatically normalized during this operation.
+ *
+ * @param a the x-component of the normal vector.
+ * @param b the y-component of the normal vector.
+ * @param c the z-component of the normal vector.
+ * @param d the distance from the origin along the plane's normal vector.
+ */
+ public void set(float a, float b, float c, float d) {
+ this.normal.set(a, b, c);
+ normal.normalizeLocal();
+ this.distance = d;
+ }
- /**
- * Calculates the signed distance from a given point to the plane.
- *
- * The signed distance is computed as:
- * distance = dot(normal, point) + D, where:
- *
normal is the plane's normal vector.point is the 3D point to test.D is the distance parameter of the plane.The signed distance is computed as:
+ * distance = dot(normal, point) + D, where:
+ *
+ *
normal is the plane's normal vector.
+ * point is the 3D point to test.
+ * D is the distance parameter of the plane.
+ * - * A ray is a mathematical abstraction used in 3D graphics, physics simulations, - * and computational geometry. It extends infinitely from its origin in the - * specified direction. The direction vector is automatically normalized during - * initialization to ensure consistent calculations. - *
+ * Represents a ray in 3D space, defined by an origin point and a direction vector. + * + *A ray is a mathematical abstraction used in 3D graphics, physics simulations, and + * computational geometry. It extends infinitely from its origin in the specified direction. The + * direction vector is automatically normalized during initialization to ensure consistent + * calculations. */ public class Ray3f { - /** - * The starting point of the ray. - */ - private final Vector3f origin; + /** The starting point of the ray. */ + private final Vector3f origin; - /** - * The normalized direction vector of the ray. - */ - private final Vector3f direction; + /** The normalized direction vector of the ray. */ + private final Vector3f direction; - /** - * The reciprocal of the direction vector, used for optimized ray-box - * intersection. - */ - private final Vector3f directionInv; + /** The reciprocal of the direction vector, used for optimized ray-box intersection. */ + private final Vector3f directionInv; - /** - * Constructs a new {@code Ray3f} with the given origin and direction. - *
- * The direction vector will be normalized internally to ensure correctness in - * calculations. Both {@code origin} and {@code direction} must be non-null. - *
- * - * @param origin The starting point of the ray (non-null) - * @param direction The direction vector of the ray (non-null, normalized - * internally) - * @throws IllegalArgumentException if either {@code origin} or - * {@code direction} is null - */ - public Ray3f(Vector3f origin, Vector3f direction) { - if (origin == null) { - throw new IllegalArgumentException("Origin cannot be null."); - } - if (direction == null) { - throw new IllegalArgumentException("Direction cannot be null."); - } - this.origin = origin; - this.direction = direction; - this.direction.normalizeLocal(); - this.directionInv = direction.reciprocal(); - } + /** + * Constructs a new {@code Ray3f} with the given origin and direction. + * + *The direction vector will be normalized internally to ensure correctness in calculations. + * Both {@code origin} and {@code direction} must be non-null. + * + * @param origin The starting point of the ray (non-null) + * @param direction The direction vector of the ray (non-null, normalized internally) + * @throws IllegalArgumentException if either {@code origin} or {@code direction} is null + */ + public Ray3f(Vector3f origin, Vector3f direction) { + if (origin == null) { + throw new IllegalArgumentException("Origin cannot be null."); + } + if (direction == null) { + throw new IllegalArgumentException("Direction cannot be null."); + } + this.origin = origin; + this.direction = direction; + this.direction.normalizeLocal(); + this.directionInv = direction.reciprocal(); + } - /** - * Returns the origin of the ray. - *
- * The origin is the starting point from which the ray emanates. - *
- * - * @return The origin of the ray. - */ - public Vector3f getOrigin() { - return origin; - } + /** + * Returns the origin of the ray. + * + *The origin is the starting point from which the ray emanates. + * + * @return The origin of the ray. + */ + public Vector3f getOrigin() { + return origin; + } - /** - * Returns the normalized direction vector of the ray. - *
- * The direction vector defines the direction in which the ray travels. The - * vector is normalized, ensuring consistent calculations for operations like - * intersections. - *
- * - * @return The direction vector of the ray. - */ - public Vector3f getDirection() { - return direction; - } + /** + * Returns the normalized direction vector of the ray. + * + *The direction vector defines the direction in which the ray travels. The vector is + * normalized, ensuring consistent calculations for operations like intersections. + * + * @return The direction vector of the ray. + */ + public Vector3f getDirection() { + return direction; + } - /** - * Returns the reciprocal of the direction vector of the ray. - *
- * The reciprocal of the direction vector is precomputed to optimize ray-box - * intersection tests, where division by components of the direction vector is - * required. - *
- * - * @return The reciprocal of the direction vector of the ray. - */ - public Vector3f getDirectionInv() { - return directionInv; - } + /** + * Returns the reciprocal of the direction vector of the ray. + * + *The reciprocal of the direction vector is precomputed to optimize ray-box intersection + * tests, where division by components of the direction vector is required. + * + * @return The reciprocal of the direction vector of the ray. + */ + public Vector3f getDirectionInv() { + return directionInv; + } - /** - * Computes the point along the ray at a given parameter {@code t}. - *
- * The formula for the point is: - * - *
- * {@code
- * point = origin + t * direction
- * }
- *
- *
- * where {@code t} is a scalar representing the distance along the ray from
- * the origin. Positive values of {@code t} will give points in the direction
- * the ray is pointing, while negative values will give points in the opposite
- * direction.
- *
- *
- * @param t The parameter along the ray (can be negative, zero, or positive).
- * @return The point at parameter {@code t}.
- */
- public Vector3f getPointAt(float t) {
- return origin.add(direction.mult(t));
- }
-
-}
\ No newline at end of file
+ /**
+ * Computes the point along the ray at a given parameter {@code t}.
+ *
+ * The formula for the point is: + * + *
{@code
+ * point = origin + t * direction
+ * }
+ *
+ * where {@code t} is a scalar representing the distance along the ray from the origin. Positive
+ * values of {@code t} will give points in the direction the ray is pointing, while negative
+ * values will give points in the opposite direction.
+ *
+ * @param t The parameter along the ray (can be negative, zero, or positive).
+ * @return The point at parameter {@code t}.
+ */
+ public Vector3f getPointAt(float t) {
+ return origin.add(direction.mult(t));
+ }
+}
diff --git a/src/main/java/math/Vector2f.java b/src/main/java/math/Vector2f.java
index 0f413c3d..409eec4f 100644
--- a/src/main/java/math/Vector2f.java
+++ b/src/main/java/math/Vector2f.java
@@ -2,1026 +2,952 @@
/**
* Representation of 2D vectors and points.
- *
+ *
* @author Simon
* @version 0.3, 11 June 2016
- *
*/
public class Vector2f {
- /**
- * Shorthand for writing Vector2f(0, -1).
- */
- public static final Vector2f DOWN = new Vector2f(0, -1);
-
- /**
- * Shorthand for writing Vector2f(-1, 0).
- */
- public static final Vector2f LEFT = new Vector2f(-1, 0);
-
- /**
- * Shorthand for writing Vector2f(1, 0).
- */
- public static final Vector2f RIGHT = new Vector2f(1, 0);
-
- /**
- * Shorthand for writing Vector2f(0, 1).
- */
- public static final Vector2f UP = new Vector2f(0, 1);
-
- /**
- * Shorthand for writing Vector2f(1, 1).
- */
- public static final Vector2f ONE = new Vector2f(1, 1);
-
- /**
- * Shorthand for writing Vector2f(0, 0).
- */
- public static final Vector2f ZERO = new Vector2f(0, 0);
-
- /**
- * Shorthand for writing Vector2f(Float.MIN_VALUE, Float.MIN_VALUE).
- */
- public static final Vector2f MIN = new Vector2f(Float.MIN_VALUE,
- Float.MIN_VALUE);
-
- /**
- * Shorthand for writing Vector2f(Float.MAX_VALUE, Float.MAX_VALUE).
- */
- public static final Vector2f MAX = new Vector2f(Float.MAX_VALUE,
- Float.MAX_VALUE);
-
- /**
- * Shorthand for writing Vector2f(Float.POSITIVE_INFINITY,
- * Float.POSITIVE_INFINITY).
- */
- public static final Vector2f POSITIVE_INFINITY = new Vector2f(
- Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
-
- /**
- * Shorthand for writing Vector2f(Float.NEGATIVE_INFINITY,
- * Float.NEGATIVE_INFINITY).
- */
- public static final Vector2f NEGATIVE_INFINITY = new Vector2f(
- Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
-
- /**
- * The x component of the vector.
- */
- public float x;
-
- /**
- * The y component of the vector.
- */
- public float y;
-
- /**
- * Constructs a new instance of this {@link Vector2f} with x and y set to 0.
- * Equivalent to Vector2f(0, 0).
- */
- public Vector2f() {
- x = y = 0;
- }
-
- /**
- * Constructs a new instance of the {@link Vector2f} with the given initial
- * x and y values.
- *
- * @param x the x value of this {@link Vector2f}
- * @param y the y value of this {@link Vector2f}
- */
- public Vector2f(float x, float y) {
- this.x = x;
- this.y = y;
- }
-
- /**
- * Constructs a new instance of this {@link Vector2f} that contains the
- * passed vector's information.
- *
- * @param v the vector to copy
- */
- public Vector2f(Vector2f v) {
- this.x = v.x;
- this.y = v.y;
- }
-
- /**
- * Returns a new {@link Vector2f} containing the absolute x,y values of this
- * vector. The values of this vector remain untouched.
- *
- * @return the newly created vector
- */
- public Vector2f abs() {
- return new Vector2f(Mathf.abs(x), Mathf.abs(y));
- }
-
- /**
- * Sets the x,y components of this vector to their absolute values
- * internally.
- *
- * @return this
- */
- public Vector2f absLocal() {
- this.x = Mathf.abs(x);
- this.y = Mathf.abs(y);
- return this;
- }
-
- /**
- * Returns a resultant vector that is made from the smallest components of
- * this vector and the provided vector v. If the provided vector is null,
- * null is returned. The values of this vector remain untouched.
- *
- * @param v the provided vector v
- * @return the resultant vector or null if the provided vector is null
- */
- public Vector2f min(Vector2f v) {
- if (v == null) {
- return null;
- }
- return new Vector2f(Mathf.min(x, v.x), Mathf.min(y, v.y));
- }
-
- /**
- * Returns a resultant vector that is made from the largest components of
- * this vector and the provided vector v. If the provided vector is null,
- * null is returned. The values of this vector remain untouched.
- *
- * @param v the provided vector v
- * @return the resultant vector or null if the provided vector is null
- */
- public Vector2f max(Vector2f v) {
- if (v == null) {
- return null;
- }
- return new Vector2f(Mathf.max(x, v.x), Mathf.max(y, v.y));
- }
-
- /**
- * Clamps the x,y components of this vector between 0 and 1 creating a
- * resultant vector which is returned. The values of this vector remain
- * untouched.
- *
- * @return the resultant vector
- */
- public Vector2f clamp01() {
- return new Vector2f(Mathf.clamp01(x), Mathf.clamp01(y));
- }
-
- /**
- * Clamps the x,y components of this vector between 0 and 1 internally, and
- * returns a handle to this vector for easy chaining of calls.
- *
- * @return this
- */
- public Vector2f clamp01Local() {
- x = Mathf.clamp01(x);
- y = Mathf.clamp01(y);
- return this;
- }
-
- /**
- * Clamps the x,y components of this vector between min and max creating a
- * resultant vector which is returned. The values of this vector remain
- * untouched.
- *
- * @param min the minimum value for x and y
- * @param max the maximum value for x and y
- * @return the resultant vector
- */
- public Vector2f clamp(float min, float max) {
- return new Vector2f(Mathf.clamp(x, min, max), Mathf.clamp(y, min, max));
- }
-
- /**
- * Clamps the x,y components of this vector between min and max internally,
- * and returns a handle to this vector for easy chaining of calls.
- *
- * @param min the minimum value for x and y
- * @param max the maximum value for x and y
- * @return this
- */
- public Vector2f clampLocal(float min, float max) {
- x = Mathf.clamp(x, min, max);
- y = Mathf.clamp(y, min, max);
- return this;
- }
-
- /**
- * Clamps the x,y components of this vector between the given values
- * creating a resultant vector which is returned. The values of this vector
- * remain untouched.
- *
- * @param minX the minimum value for x
- * @param minY the minimum value for y
- * @param maxX the maximum value for x
- * @param maxY the maximum value for y
- * @return the resultant vector
- */
- public Vector2f clamp(float minX, float minY, float maxX, float maxY) {
- return new Vector2f(Mathf.clamp(x, minX, maxX),
- Mathf.clamp(y, minY, maxY));
- }
-
- /**
- * Clamps the x,y components of this vector between the given values
- * internally, and returns a handle to this vector for easy chaining of
- * calls.
- *
- * @param minX the minimum value for x
- * @param minY the minimum value for y
- * @param maxX the maximum value for x
- * @param maxY the maximum value for y
- * @return this
- */
- public Vector2f clampLocal(float minX, float minY, float maxX, float maxY) {
- x = Mathf.clamp(x, minX, maxX);
- y = Mathf.clamp(y, minY, maxY);
- return this;
- }
-
- /**
- * Converts the given angle (in radians) to this vector.
- *
- * @param angrad the angle to convert this vector into
- * @return this
- */
- public Vector2f setToAngleRadLocal(float angrad) {
- this.x = Mathf.cos(angrad);
- this.y = Mathf.sin(angrad);
- return this;
- }
-
- /**
- * Converts the given angle (in degrees) to this vector.
- *
- * @param angdeg the angle to convert this vector into
- * @return this
- */
- public Vector2f setToAngleDegLocal(float angdeg) {
- this.x = Mathf.cos(Mathf.toRadians(angdeg));
- this.y = Mathf.sin(Mathf.toRadians(angdeg));
- return this;
- }
-
- /**
- * Set the components of this vector to the values provided by the float
- * array (values[0] = x, values[1] = y).
- *
- * @param values the values to copy from
- * @return this
- * @throws IndexOutOfBoundsException if values.length < 0 || values.length <
- * 2
- */
- public Vector2f set(float[] values) {
- return set(values[0], values[1]);
- }
-
- /**
- * Sets the x,y components of this vector to the given values.
- *
- * @param x the x component of the vector
- * @param y the y component of the vector
- * @return this
- */
- public Vector2f set(float x, float y) {
- this.x = x;
- this.y = y;
- return this;
- }
-
- /**
- * Sets the x,y components of this vector to the values provided by the
- * given vector v.
- *
- * @param v the vector to copy from
- * @return this
- */
- public Vector2f set(Vector2f v) {
- this.x = v.x;
- this.y = v.y;
- return this;
- }
-
- /**
- * Adds the provided vector v to this vector creating a resultant vector
- * which is returned. The values of this vector remain untouched. If the
- * provided vector is null, null is returned.
- *
- * @param v the vector to add to this vector
- * @return the resultant vector
- */
- public Vector2f add(Vector2f v) {
- if (null == v) {
- return null;
- }
- return new Vector2f(x + v.x, y + v.y);
- }
-
- /**
- * Adds the provided vector v to this vector internally, and returns a
- * handle to this vector for easy chaining of calls. If the provided vector
- * is null, null is returned.
- *
- * @param v the vector to add to this vector
- * @return this
- */
- public Vector2f addLocal(Vector2f v) {
- if (null == v) {
- return null;
- }
- x += v.x;
- y += v.y;
- return this;
- }
-
- /**
- * Adds the provided values to this vector internally, and returns a handle
- * to this vector for easy chaining of calls.
- *
- * @param x value to add to x
- * @param y value to add to y
- * @return this
- */
- public Vector2f addLocal(float x, float y) {
- this.x += x;
- this.y += y;
- return this;
- }
-
- /**
- * Adds this vector to the given vector v and stores the result in the
- * provided result vector. The values of this vector remain untouched. If
- * the provided vector v is null, null is returned. If the provided result
- * vector is null a new vector is created to store the result in.
- *
- * @param v the vector to add
- * @param result the vector to store the result in
- * @return the result vector, after adding
- */
- public Vector2f add(Vector2f v, Vector2f result) {
- if (null == v) {
- return null;
- }
- if (result == null)
- result = new Vector2f();
- result.x = x + v.x;
- result.y = y + v.y;
- return result;
- }
-
- public Vector2f add(float x, float y) {
- return new Vector2f(this.x + x, this.y + y);
- }
-
- /**
- * Calculates the dot product of this vector and the provided vector v. If
- * the provided vector is null, 0 is returned.
- *
- * @param v the vector to dot with this vector
- * @return the resultant dot product of this vector and a given vector
- */
- public float dot(Vector2f v) {
- if (null == v) {
- return 0;
- }
- return x * v.x + y * v.y;
- }
-
- /**
- * Calculates the cross product of this vector with a parameter vector v.
- *
- * @param v the vector to take the cross product of with this
- * @return the cross product vector.
- */
- public Vector3f cross(Vector2f v) {
- return new Vector3f(0, 0, determinant(v));
- }
-
- /**
- * Calculates the determinant of this vector and the given vector v.
- *
- * @param v the given vector v
- * @return the resulting determinant
- */
- public float determinant(Vector2f v) {
- return (x * v.y) - (y * v.x);
- }
-
- /**
- * Sets this vector to the interpolation by changeAmnt from this to the
- * finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
- *
- * @param finalVec the final vector to interpolate towards
- * @param changeAmnt an amount between 0.0 - 1.0 representing a percentage
- * change from this towards finalVec
- */
- public Vector2f interpolateLocal(Vector2f finalVec, float changeAmnt) {
- this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
- this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
- return this;
- }
-
- /**
- * Sets this vector to the interpolation by changeAmnt from beginVec to
- * finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
- *
- * @param beginVec the beginning vector (delta=0)
- * @param finalVec the final vector to interpolate towards (delta=1)
- * @param changeAmnt an amount between 0.0 - 1.0 representing a percentage
- * change from beginVec towards finalVec
- */
- public Vector2f interpolateLocal(Vector2f beginVec, Vector2f finalVec,
- float changeAmnt) {
- this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
- this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
- return this;
- }
-
- /**
- * Checks if the given vector v is valid. A vector is not valid if it is
- * null or its floats are NaN or infinite, return false. Else return true.
- *
- * @param v the vector to check
- * @return true or false as stated above
- */
- public static boolean isValidVector(Vector2f v) {
- if (v == null)
- return false;
- if (Float.isNaN(v.x) || Float.isNaN(v.y))
- return false;
- if (Float.isInfinite(v.x) || Float.isInfinite(v.y))
- return false;
- return true;
- }
-
- /**
- * Calculates the magnitude of this vector.
- *
- * @return the length (magnitude) of this vector
- */
- public float length() {
- return Mathf.sqrt(lengthSquared());
- }
-
- /**
- * Calculates the squared value of the magnitude of this vector.
- *
- * @return the magnitude squared of this vector
- */
- public float lengthSquared() {
- return x * x + y * y;
- }
-
- /**
- * Performs a linear interpolation between this vector and another.
- *
- * Linear interpolation calculates a new vector that is a portion of the way
- * between two given vectors. The parameter `t` determines how far along
- * this line the new vector will be.
- *
- * Mathematically, the interpolation is calculated as follows: result = this
- * + t * (other - this)
- *
- * Where: - `this` is the current vector - `other` is the target vector -
- * `t` is the interpolation factor (between 0 and 1)
- *
- * @param other The vector to interpolate towards.
- * @param t The interpolation factor. A value of 0 returns this vector,
- * a value of 1 returns the other vector, and values between 0
- * and 1 return a vector between the two.
- * @return A new vector representing the interpolated result.
- */
- public Vector2f lerp(Vector2f other, float t) {
- return new Vector2f(this.x + t * (other.x - this.x),
- this.y + t * (other.y - this.y));
- }
-
- /**
- * Calculates the distance squared between this vector and the given vector
- * v.
- *
- * @param v the second vector to determine the distance squared
- * @return the distance squared between the two vectors
- */
- public float distanceSquared(Vector2f v) {
- float dx = x - v.x;
- float dy = y - v.y;
- return dx * dx + dy * dy;
- }
-
- /**
- * Calculates the distance squared between this vector and the given vector
- * v.
- *
- * @param x the x coordinate of the v vector
- * @param y the y coordinate of the v vector
- * @return the distance squared between the two vectors
- */
- public float distanceSquared(float x, float y) {
- float dx = this.x - x;
- float dy = this.y - y;
- return dx * dx + dy * dy;
- }
-
- /**
- * Calculates the distance between this vector and vector v.
- *
- * @param v the second vector to determine the distance
- * @return the distance between the two vectors
- */
- public float distance(Vector2f v) {
- return Mathf.sqrt(distanceSquared(v));
- }
-
- /**
- * Multiplies this vector by a scalar and resultant vector is returned. The
- * values of this vector remain untouched.
- *
- * @param scalar the value to multiply this vector by
- * @return the resultant vector
- */
- public Vector2f mult(float scalar) {
- return new Vector2f(x * scalar, y * scalar);
- }
-
- /**
- * Project this vector onto the given vector b. If the provided vector v is
- * null, null is returned. If the provided result vector is null a new
- * vector is created to store the result in.
- *
- * @param v the vector to project onto
- * @param result the projected vector
- * @param result
- */
- public Vector2f projectOntoUnit(Vector2f v, Vector2f result) {
- if (v == null) {
- return null;
- }
- if (result == null)
- result = new Vector2f();
- float dot = v.dot(this);
- result.x = dot * v.getX();
- result.y = dot * v.getY();
- return result;
- }
-
- /**
- * Multiplies this vector by a scalar internally, and returns a handle to
- * this vector for easy chaining of calls.
- *
- * @param scalar the value to multiply this vector by
- * @return this
- */
- public Vector2f multLocal(float scalar) {
- x *= scalar;
- y *= scalar;
- return this;
- }
-
- /**
- * Multiplies a provided vector to this vector internally, and returns a
- * handle to this vector for easy chaining of calls. If the provided vector
- * is null, null is returned.
- *
- * @param v the vector to multiply with this vector
- * @return this
- */
- public Vector2f multLocal(Vector2f v) {
- if (null == v) {
- return null;
- }
- x *= v.x;
- y *= v.y;
- return this;
- }
-
- public Vector2f mult(Vector2f v) {
- if (null == v) {
- return null;
- }
- return new Vector2f(x * v.x, y * v.y);
- }
-
- /**
- * Multiplies the x and y of this vector by the scalar and stores the result
- * in product. The values of this vector remain untouched. The result is
- * returned for chaining.
- *
- * @param scalar the scalar to multiply by
- * @param product the vector to store the result in
- * @return product, after multiplication.
- */
- public Vector2f mult(float scalar, Vector2f product) {
- if (null == product) {
- product = new Vector2f();
- }
- product.x = x * scalar;
- product.y = y * scalar;
- return product;
- }
-
- /**
- * Divides the values of this vector by a scalar and returns the result. The
- * values of this vector remain untouched. Dividing by zero will result in
- * an exception.
- *
- * @param scalar the value to divide this vectors attributes by
- * @return the result vector
- */
- public Vector2f divide(float scalar) {
- return new Vector2f(x / scalar, y / scalar);
- }
-
- /**
- * Divides this vector by a scalar internally, and returns a handle to this
- * vector for easy chaining of calls. Dividing by zero will result in an
- * exception.
- *
- * @param scalar the value to divide this vector by
- * @return this
- */
- public Vector2f divideLocal(float scalar) {
- x /= scalar;
- y /= scalar;
- return this;
- }
-
- /**
- * Divides each component of this vector by the corresponding component of
- * the given vector v internally.
- *
- * @param v the vector providing the two divisors
- * @return this
- */
- public Vector2f divideLocal(Vector2f v) {
- x /= v.x;
- y /= v.y;
- return this;
- }
-
- /**
- * Returns the negative of this vector. All values are negated and set to a
- * new vector. The values of this vector remain untouched.
- *
- * @return the negated vector
- */
- public Vector2f negate() {
- return new Vector2f(-x, -y);
- }
-
- /**
- * Negates the values of this vector internally.
- *
- * @return this
- */
- public Vector2f negateLocal() {
- x = -x;
- y = -y;
- return this;
- }
-
- /**
- * Subtracts the values of a given vector from those of this vector creating
- * a new vector object. If the provided vector is null, an exception is
- * thrown.
- *
- * @param v the vector to subtract from this vector
- * @return the resultant vector
- */
- public Vector2f subtract(Vector2f v) {
- return subtract(v, null);
- }
-
- /**
- * Subtracts the values of a given vector from those of this vector storing
- * the result in the given vector. If the provided vector v is null, an
- * exception is thrown. If the provided vector result is null, a new vector
- * is created.
- *
- * @param v the vector to subtract from this vector
- * @param result the vector to store the result in
- * @return the resultant vector
- */
- public Vector2f subtract(Vector2f v, Vector2f result) {
- if (result == null)
- result = new Vector2f();
- result.x = x - v.x;
- result.y = y - v.y;
- return result;
- }
-
- /**
- * Subtracts the given x,y values from those of this vector creating a new
- * vector object.
- *
- * @param x value to subtract from x
- * @param y value to subtract from y
- * @return the resultant vector
- */
- public Vector2f subtract(float x, float y) {
- return new Vector2f(this.x - x, this.y - y);
- }
-
- /**
- * Subtracts a provided vector to this vector internally, and returns a
- * handle to this vector for easy chaining of calls. If the provided vector
- * is null, null is returned.
- *
- * @param v the vector to subtract
- * @return this
- */
- public Vector2f subtractLocal(Vector2f v) {
- if (null == v) {
- return null;
- }
- x -= v.x;
- y -= v.y;
- return this;
- }
-
- /**
- * Subtracts the provided values from this vector internally, and returns a
- * handle to this vector for easy chaining of calls.
- *
- * @param x value to subtract from x
- * @param y value to subtract from y
- * @return this
- */
- public Vector2f subtractLocal(float x, float y) {
- this.x -= x;
- this.y -= y;
- return this;
- }
-
- /**
- * Returns the unit vector of this vector. The values of this vector remain
- * untouched.
- *
- * @return the newly created unit vector of this vector
- */
- public Vector2f normalize() {
- float length = length();
- if (length != 0) {
- return divide(length);
- }
- return divide(1);
- }
-
- /**
- * Makes this vector into a unit vector of itself internally.
- *
- * @return this
- */
- public Vector2f normalizeLocal() {
- float length = length();
- if (length != 0) {
- return divideLocal(length);
- }
- return divideLocal(1);
- }
-
- /**
- * Returns the minimum angle (in radians) between two vectors. It is assumed
- * that both this vector and the given vector are unit vectors (iow,
- * normalized).
- *
- * @param otherVector a unit vector to find the angle against
- * @return the angle in radians
- */
- public float smallestAngleBetween(Vector2f otherVector) {
- float dotProduct = dot(otherVector);
- float angle = Mathf.acos(dotProduct);
- return angle;
- }
-
- /**
- * Sets the closest int to the x and y components locally, with ties
- * rounding to positive infinity.
- *
- * @return this
- */
- public Vector2f roundToIntLocal() {
- x = Mathf.roundToInt(x);
- y = Mathf.roundToInt(y);
- return this;
- }
-
- public Vector2f rotate(float[][] rotationMatrix) {
- float x0 = x * rotationMatrix[0][0] + y * rotationMatrix[0][1];
- float y0 = x * rotationMatrix[1][0] + y * rotationMatrix[1][1];
- this.x = x0;
- this.y = y0;
- return this;
- }
-
- public Vector2f rotate(float angle) {
- float cos = Mathf.cos(angle);
- float sin = Mathf.sin(angle);
- return new Vector2f(x * cos - y * sin, x * sin + y * cos);
- }
-
- /**
- * Returns (in radians) the angle required to rotate a ray represented by
- * this vector to lie colinear to a ray described by the given vector. It is
- * assumed that both this vector and the given vector are unit vectors (iow,
- * normalized).
- *
- * @param otherVector the "destination" unit vector
- * @return the angle in radians
- */
- public float angleBetween(Vector2f otherVector) {
- float angle = Mathf.atan2(otherVector.y, otherVector.x)
- - Mathf.atan2(y, x);
- return angle;
- }
-
-// /**
-// * Computes the angle (in radians) between the vector represented
-// * by this vector and the specified vector.
-// * @param x the X magnitude of the other vector
-// * @param y the Y magnitude of the other vector
-// * @return the angle between the two vectors measured in radians
-// */
-// public float angle(float x, float y) {
-// final float ax = getX();
-// final float ay = getY();
-//
-// final float delta = (ax * x + ay * y) / Mathf.sqrt(
-// (ax * ax + ay * ay) * (x * x + y * y));
-//
-// if (delta > 1.0) {
-// return 0.0f;
-// }
-// if (delta < -1.0) {
-// return Mathf.PI;
-// }
-//
-// return Mathf.acos(delta);
-// }
-
- public Vector2f frac() {
- // TODO Check if this is the correct way
- return new Vector2f(x - (int) x, y - (int) y);
- }
-
- /**
- * Returns the x component of this vector.
- *
- * @return the x component of this vector
- */
- public float getX() {
- return x;
- }
-
- /**
- * Sets the x component of this vector to the specified new value, and
- * returns a handle to this vector for easy chaining of calls.
- *
- * @param x the specified new x component
- * @return this
- */
- public Vector2f setX(float x) {
- this.x = x;
- return this;
- }
-
- /**
- * Returns the y component of this vector.
- *
- * @return the y component of this vector
- */
- public float getY() {
- return y;
- }
-
- /**
- * Sets the y component of this vector to the specified new value, and
- * returns a handle to this vector for easy chaining of calls.
- *
- * @param y the specified new y component
- * @return this
- */
- public Vector2f setY(float y) {
- this.y = y;
- return this;
- }
-
- /**
- * Returns the angle (in radians) represented by this vector as expressed by
- * a conversion from rectangular coordinates ( x,
- * y) to polar coordinates (r, theta).
- *
- * @return the angle in radians. [-pi, pi)
- */
- public float getAngle() {
- return Mathf.atan2(y, x);
- }
-
- /**
- * Resets this vector's data to zero internally, and returns a handle to
- * this vector for easy chaining of calls.
- */
- public Vector2f zero() {
- x = y = 0;
- return this;
- }
-
- /**
- * Returns the perpendicular vector to this vector.
- *
- * @return the newly created perpendicular to this vector
- */
- public Vector2f getPerpendicular() {
- return new Vector2f(-y, x);
- }
-
- /**
- * Stores this vector into the given array.
- *
- * @param floats the array to store this vector in, if null, a new float
- * array with the size of two is created
- * @return the array, with x, y float values in that order
- */
- public float[] toArray(float[] floats) {
- if (floats == null) {
- floats = new float[2];
- }
- floats[0] = x;
- floats[1] = y;
- return floats;
- }
-
- /**
- * Stores this vector into a newly created float array with a size of two.
- *
- * @return the array that stores the vector with x,y component in that order
- */
- public float[] toArray() {
- return toArray(null);
- }
-
- /**
- * Rotates this vector around the origin.
- *
- * @param angle the angle to rotate (in radians)
- * @param cw true to rotate clockwise, false to rotate counterclockwise
- */
- public Vector2f rotate(float angle, boolean cw) {
- if (cw)
- angle = -angle;
- float newX = Mathf.cos(angle) * x - Mathf.sin(angle) * y;
- float newY = Mathf.sin(angle) * x + Mathf.cos(angle) * y;
- return new Vector2f(newX, newY);
- }
-
- /**
- * Returns a unique hash code for this vector object based on it's values.
- * If two vectors are logically equivalent, they will return the same hash
- * code value.
- *
- * @return the hash code value of this vector
- */
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Float.floatToIntBits(x);
- result = prime * result + Float.floatToIntBits(y);
- return result;
- }
-
- /**
- * Determines if this vector is equals to the given object obj.
- *
- * @param obj the object to compare for equality
- * @return true if they are equal
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Vector2f other = (Vector2f) obj;
- if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x))
- return false;
- if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y))
- return false;
- return true;
- }
-
- /**
- * Returns a string representation of this {@link Vector2f}.
- *
- * @return a string representation of this {@link Vector2f}
- */
- @Override
- public String toString() {
- return "Vector2f [x=" + x + ", y=" + y + "]";
- }
-
-}
\ No newline at end of file
+ /** Shorthand for writing Vector2f(0, -1). */
+ public static final Vector2f DOWN = new Vector2f(0, -1);
+
+ /** Shorthand for writing Vector2f(-1, 0). */
+ public static final Vector2f LEFT = new Vector2f(-1, 0);
+
+ /** Shorthand for writing Vector2f(1, 0). */
+ public static final Vector2f RIGHT = new Vector2f(1, 0);
+
+ /** Shorthand for writing Vector2f(0, 1). */
+ public static final Vector2f UP = new Vector2f(0, 1);
+
+ /** Shorthand for writing Vector2f(1, 1). */
+ public static final Vector2f ONE = new Vector2f(1, 1);
+
+ /** Shorthand for writing Vector2f(0, 0). */
+ public static final Vector2f ZERO = new Vector2f(0, 0);
+
+ /** Shorthand for writing Vector2f(Float.MIN_VALUE, Float.MIN_VALUE). */
+ public static final Vector2f MIN = new Vector2f(Float.MIN_VALUE, Float.MIN_VALUE);
+
+ /** Shorthand for writing Vector2f(Float.MAX_VALUE, Float.MAX_VALUE). */
+ public static final Vector2f MAX = new Vector2f(Float.MAX_VALUE, Float.MAX_VALUE);
+
+ /** Shorthand for writing Vector2f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY). */
+ public static final Vector2f POSITIVE_INFINITY =
+ new Vector2f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
+
+ /** Shorthand for writing Vector2f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY). */
+ public static final Vector2f NEGATIVE_INFINITY =
+ new Vector2f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+
+ /** The x component of the vector. */
+ public float x;
+
+ /** The y component of the vector. */
+ public float y;
+
+ /**
+ * Constructs a new instance of this {@link Vector2f} with x and y set to 0. Equivalent to
+ * Vector2f(0, 0).
+ */
+ public Vector2f() {
+ x = y = 0;
+ }
+
+ /**
+ * Constructs a new instance of the {@link Vector2f} with the given initial x and y values.
+ *
+ * @param x the x value of this {@link Vector2f}
+ * @param y the y value of this {@link Vector2f}
+ */
+ public Vector2f(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * Constructs a new instance of this {@link Vector2f} that contains the passed vector's
+ * information.
+ *
+ * @param v the vector to copy
+ */
+ public Vector2f(Vector2f v) {
+ this.x = v.x;
+ this.y = v.y;
+ }
+
+ /**
+ * Returns a new {@link Vector2f} containing the absolute x,y values of this vector. The values of
+ * this vector remain untouched.
+ *
+ * @return the newly created vector
+ */
+ public Vector2f abs() {
+ return new Vector2f(Mathf.abs(x), Mathf.abs(y));
+ }
+
+ /**
+ * Sets the x,y components of this vector to their absolute values internally.
+ *
+ * @return this
+ */
+ public Vector2f absLocal() {
+ this.x = Mathf.abs(x);
+ this.y = Mathf.abs(y);
+ return this;
+ }
+
+ /**
+ * Returns a resultant vector that is made from the smallest components of this vector and the
+ * provided vector v. If the provided vector is null, null is returned. The values of this vector
+ * remain untouched.
+ *
+ * @param v the provided vector v
+ * @return the resultant vector or null if the provided vector is null
+ */
+ public Vector2f min(Vector2f v) {
+ if (v == null) {
+ return null;
+ }
+ return new Vector2f(Mathf.min(x, v.x), Mathf.min(y, v.y));
+ }
+
+ /**
+ * Returns a resultant vector that is made from the largest components of this vector and the
+ * provided vector v. If the provided vector is null, null is returned. The values of this vector
+ * remain untouched.
+ *
+ * @param v the provided vector v
+ * @return the resultant vector or null if the provided vector is null
+ */
+ public Vector2f max(Vector2f v) {
+ if (v == null) {
+ return null;
+ }
+ return new Vector2f(Mathf.max(x, v.x), Mathf.max(y, v.y));
+ }
+
+ /**
+ * Clamps the x,y components of this vector between 0 and 1 creating a resultant vector which is
+ * returned. The values of this vector remain untouched.
+ *
+ * @return the resultant vector
+ */
+ public Vector2f clamp01() {
+ return new Vector2f(Mathf.clamp01(x), Mathf.clamp01(y));
+ }
+
+ /**
+ * Clamps the x,y components of this vector between 0 and 1 internally, and returns a handle to
+ * this vector for easy chaining of calls.
+ *
+ * @return this
+ */
+ public Vector2f clamp01Local() {
+ x = Mathf.clamp01(x);
+ y = Mathf.clamp01(y);
+ return this;
+ }
+
+ /**
+ * Clamps the x,y components of this vector between min and max creating a resultant vector which
+ * is returned. The values of this vector remain untouched.
+ *
+ * @param min the minimum value for x and y
+ * @param max the maximum value for x and y
+ * @return the resultant vector
+ */
+ public Vector2f clamp(float min, float max) {
+ return new Vector2f(Mathf.clamp(x, min, max), Mathf.clamp(y, min, max));
+ }
+
+ /**
+ * Clamps the x,y components of this vector between min and max internally, and returns a handle
+ * to this vector for easy chaining of calls.
+ *
+ * @param min the minimum value for x and y
+ * @param max the maximum value for x and y
+ * @return this
+ */
+ public Vector2f clampLocal(float min, float max) {
+ x = Mathf.clamp(x, min, max);
+ y = Mathf.clamp(y, min, max);
+ return this;
+ }
+
+ /**
+ * Clamps the x,y components of this vector between the given values creating a resultant vector
+ * which is returned. The values of this vector remain untouched.
+ *
+ * @param minX the minimum value for x
+ * @param minY the minimum value for y
+ * @param maxX the maximum value for x
+ * @param maxY the maximum value for y
+ * @return the resultant vector
+ */
+ public Vector2f clamp(float minX, float minY, float maxX, float maxY) {
+ return new Vector2f(Mathf.clamp(x, minX, maxX), Mathf.clamp(y, minY, maxY));
+ }
+
+ /**
+ * Clamps the x,y components of this vector between the given values internally, and returns a
+ * handle to this vector for easy chaining of calls.
+ *
+ * @param minX the minimum value for x
+ * @param minY the minimum value for y
+ * @param maxX the maximum value for x
+ * @param maxY the maximum value for y
+ * @return this
+ */
+ public Vector2f clampLocal(float minX, float minY, float maxX, float maxY) {
+ x = Mathf.clamp(x, minX, maxX);
+ y = Mathf.clamp(y, minY, maxY);
+ return this;
+ }
+
+ /**
+ * Converts the given angle (in radians) to this vector.
+ *
+ * @param angrad the angle to convert this vector into
+ * @return this
+ */
+ public Vector2f setToAngleRadLocal(float angrad) {
+ this.x = Mathf.cos(angrad);
+ this.y = Mathf.sin(angrad);
+ return this;
+ }
+
+ /**
+ * Converts the given angle (in degrees) to this vector.
+ *
+ * @param angdeg the angle to convert this vector into
+ * @return this
+ */
+ public Vector2f setToAngleDegLocal(float angdeg) {
+ this.x = Mathf.cos(Mathf.toRadians(angdeg));
+ this.y = Mathf.sin(Mathf.toRadians(angdeg));
+ return this;
+ }
+
+ /**
+ * Set the components of this vector to the values provided by the float array (values[0] = x,
+ * values[1] = y).
+ *
+ * @param values the values to copy from
+ * @return this
+ * @throws IndexOutOfBoundsException if values.length < 0 || values.length < 2
+ */
+ public Vector2f set(float[] values) {
+ return set(values[0], values[1]);
+ }
+
+ /**
+ * Sets the x,y components of this vector to the given values.
+ *
+ * @param x the x component of the vector
+ * @param y the y component of the vector
+ * @return this
+ */
+ public Vector2f set(float x, float y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ /**
+ * Sets the x,y components of this vector to the values provided by the given vector v.
+ *
+ * @param v the vector to copy from
+ * @return this
+ */
+ public Vector2f set(Vector2f v) {
+ this.x = v.x;
+ this.y = v.y;
+ return this;
+ }
+
+ /**
+ * Adds the provided vector v to this vector creating a resultant vector which is returned. The
+ * values of this vector remain untouched. If the provided vector is null, null is returned.
+ *
+ * @param v the vector to add to this vector
+ * @return the resultant vector
+ */
+ public Vector2f add(Vector2f v) {
+ if (null == v) {
+ return null;
+ }
+ return new Vector2f(x + v.x, y + v.y);
+ }
+
+ /**
+ * Adds the provided vector v to this vector internally, and returns a handle to this vector for
+ * easy chaining of calls. If the provided vector is null, null is returned.
+ *
+ * @param v the vector to add to this vector
+ * @return this
+ */
+ public Vector2f addLocal(Vector2f v) {
+ if (null == v) {
+ return null;
+ }
+ x += v.x;
+ y += v.y;
+ return this;
+ }
+
+ /**
+ * Adds the provided values to this vector internally, and returns a handle to this vector for
+ * easy chaining of calls.
+ *
+ * @param x value to add to x
+ * @param y value to add to y
+ * @return this
+ */
+ public Vector2f addLocal(float x, float y) {
+ this.x += x;
+ this.y += y;
+ return this;
+ }
+
+ /**
+ * Adds this vector to the given vector v and stores the result in the provided result vector. The
+ * values of this vector remain untouched. If the provided vector v is null, null is returned. If
+ * the provided result vector is null a new vector is created to store the result in.
+ *
+ * @param v the vector to add
+ * @param result the vector to store the result in
+ * @return the result vector, after adding
+ */
+ public Vector2f add(Vector2f v, Vector2f result) {
+ if (null == v) {
+ return null;
+ }
+ if (result == null) result = new Vector2f();
+ result.x = x + v.x;
+ result.y = y + v.y;
+ return result;
+ }
+
+ public Vector2f add(float x, float y) {
+ return new Vector2f(this.x + x, this.y + y);
+ }
+
+ /**
+ * Calculates the dot product of this vector and the provided vector v. If the provided vector is
+ * null, 0 is returned.
+ *
+ * @param v the vector to dot with this vector
+ * @return the resultant dot product of this vector and a given vector
+ */
+ public float dot(Vector2f v) {
+ if (null == v) {
+ return 0;
+ }
+ return x * v.x + y * v.y;
+ }
+
+ /**
+ * Calculates the cross product of this vector with a parameter vector v.
+ *
+ * @param v the vector to take the cross product of with this
+ * @return the cross product vector.
+ */
+ public Vector3f cross(Vector2f v) {
+ return new Vector3f(0, 0, determinant(v));
+ }
+
+ /**
+ * Calculates the determinant of this vector and the given vector v.
+ *
+ * @param v the given vector v
+ * @return the resulting determinant
+ */
+ public float determinant(Vector2f v) {
+ return (x * v.y) - (y * v.x);
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from this to the finalVec
+ * this=(1-changeAmnt)*this + changeAmnt * finalVec
+ *
+ * @param finalVec the final vector to interpolate towards
+ * @param changeAmnt an amount between 0.0 - 1.0 representing a percentage change from this
+ * towards finalVec
+ */
+ public Vector2f interpolateLocal(Vector2f finalVec, float changeAmnt) {
+ this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
+ return this;
+ }
+
+ /**
+ * Sets this vector to the interpolation by changeAmnt from beginVec to finalVec
+ * this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
+ *
+ * @param beginVec the beginning vector (delta=0)
+ * @param finalVec the final vector to interpolate towards (delta=1)
+ * @param changeAmnt an amount between 0.0 - 1.0 representing a percentage change from beginVec
+ * towards finalVec
+ */
+ public Vector2f interpolateLocal(Vector2f beginVec, Vector2f finalVec, float changeAmnt) {
+ this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
+ return this;
+ }
+
+ /**
+ * Checks if the given vector v is valid. A vector is not valid if it is null or its floats are
+ * NaN or infinite, return false. Else return true.
+ *
+ * @param v the vector to check
+ * @return true or false as stated above
+ */
+ public static boolean isValidVector(Vector2f v) {
+ if (v == null) return false;
+ if (Float.isNaN(v.x) || Float.isNaN(v.y)) return false;
+ if (Float.isInfinite(v.x) || Float.isInfinite(v.y)) return false;
+ return true;
+ }
+
+ /**
+ * Calculates the magnitude of this vector.
+ *
+ * @return the length (magnitude) of this vector
+ */
+ public float length() {
+ return Mathf.sqrt(lengthSquared());
+ }
+
+ /**
+ * Calculates the squared value of the magnitude of this vector.
+ *
+ * @return the magnitude squared of this vector
+ */
+ public float lengthSquared() {
+ return x * x + y * y;
+ }
+
+ /**
+ * Performs a linear interpolation between this vector and another.
+ *
+ * Linear interpolation calculates a new vector that is a portion of the way between two given + * vectors. The parameter `t` determines how far along this line the new vector will be. + * + *
Mathematically, the interpolation is calculated as follows: result = this + t * (other - + * this) + * + *
Where: - `this` is the current vector - `other` is the target vector - `t` is the
+ * interpolation factor (between 0 and 1)
+ *
+ * @param other The vector to interpolate towards.
+ * @param t The interpolation factor. A value of 0 returns this vector, a value of 1 returns the
+ * other vector, and values between 0 and 1 return a vector between the two.
+ * @return A new vector representing the interpolated result.
+ */
+ public Vector2f lerp(Vector2f other, float t) {
+ return new Vector2f(this.x + t * (other.x - this.x), this.y + t * (other.y - this.y));
+ }
+
+ /**
+ * Calculates the distance squared between this vector and the given vector v.
+ *
+ * @param v the second vector to determine the distance squared
+ * @return the distance squared between the two vectors
+ */
+ public float distanceSquared(Vector2f v) {
+ float dx = x - v.x;
+ float dy = y - v.y;
+ return dx * dx + dy * dy;
+ }
+
+ /**
+ * Calculates the distance squared between this vector and the given vector v.
+ *
+ * @param x the x coordinate of the v vector
+ * @param y the y coordinate of the v vector
+ * @return the distance squared between the two vectors
+ */
+ public float distanceSquared(float x, float y) {
+ float dx = this.x - x;
+ float dy = this.y - y;
+ return dx * dx + dy * dy;
+ }
+
+ /**
+ * Calculates the distance between this vector and vector v.
+ *
+ * @param v the second vector to determine the distance
+ * @return the distance between the two vectors
+ */
+ public float distance(Vector2f v) {
+ return Mathf.sqrt(distanceSquared(v));
+ }
+
+ /**
+ * Multiplies this vector by a scalar and resultant vector is returned. The values of this vector
+ * remain untouched.
+ *
+ * @param scalar the value to multiply this vector by
+ * @return the resultant vector
+ */
+ public Vector2f mult(float scalar) {
+ return new Vector2f(x * scalar, y * scalar);
+ }
+
+ /**
+ * Project this vector onto the given vector b. If the provided vector v is null, null is
+ * returned. If the provided result vector is null a new vector is created to store the result in.
+ *
+ * @param v the vector to project onto
+ * @param result the projected vector
+ * @param result
+ */
+ public Vector2f projectOntoUnit(Vector2f v, Vector2f result) {
+ if (v == null) {
+ return null;
+ }
+ if (result == null) result = new Vector2f();
+ float dot = v.dot(this);
+ result.x = dot * v.getX();
+ result.y = dot * v.getY();
+ return result;
+ }
+
+ /**
+ * Multiplies this vector by a scalar internally, and returns a handle to this vector for easy
+ * chaining of calls.
+ *
+ * @param scalar the value to multiply this vector by
+ * @return this
+ */
+ public Vector2f multLocal(float scalar) {
+ x *= scalar;
+ y *= scalar;
+ return this;
+ }
+
+ /**
+ * Multiplies a provided vector to this vector internally, and returns a handle to this vector for
+ * easy chaining of calls. If the provided vector is null, null is returned.
+ *
+ * @param v the vector to multiply with this vector
+ * @return this
+ */
+ public Vector2f multLocal(Vector2f v) {
+ if (null == v) {
+ return null;
+ }
+ x *= v.x;
+ y *= v.y;
+ return this;
+ }
+
+ public Vector2f mult(Vector2f v) {
+ if (null == v) {
+ return null;
+ }
+ return new Vector2f(x * v.x, y * v.y);
+ }
+
+ /**
+ * Multiplies the x and y of this vector by the scalar and stores the result in product. The
+ * values of this vector remain untouched. The result is returned for chaining.
+ *
+ * @param scalar the scalar to multiply by
+ * @param product the vector to store the result in
+ * @return product, after multiplication.
+ */
+ public Vector2f mult(float scalar, Vector2f product) {
+ if (null == product) {
+ product = new Vector2f();
+ }
+ product.x = x * scalar;
+ product.y = y * scalar;
+ return product;
+ }
+
+ /**
+ * Divides the values of this vector by a scalar and returns the result. The values of this vector
+ * remain untouched. Dividing by zero will result in an exception.
+ *
+ * @param scalar the value to divide this vectors attributes by
+ * @return the result vector
+ */
+ public Vector2f divide(float scalar) {
+ return new Vector2f(x / scalar, y / scalar);
+ }
+
+ /**
+ * Divides this vector by a scalar internally, and returns a handle to this vector for easy
+ * chaining of calls. Dividing by zero will result in an exception.
+ *
+ * @param scalar the value to divide this vector by
+ * @return this
+ */
+ public Vector2f divideLocal(float scalar) {
+ x /= scalar;
+ y /= scalar;
+ return this;
+ }
+
+ /**
+ * Divides each component of this vector by the corresponding component of the given vector v
+ * internally.
+ *
+ * @param v the vector providing the two divisors
+ * @return this
+ */
+ public Vector2f divideLocal(Vector2f v) {
+ x /= v.x;
+ y /= v.y;
+ return this;
+ }
+
+ /**
+ * Returns the negative of this vector. All values are negated and set to a new vector. The values
+ * of this vector remain untouched.
+ *
+ * @return the negated vector
+ */
+ public Vector2f negate() {
+ return new Vector2f(-x, -y);
+ }
+
+ /**
+ * Negates the values of this vector internally.
+ *
+ * @return this
+ */
+ public Vector2f negateLocal() {
+ x = -x;
+ y = -y;
+ return this;
+ }
+
+ /**
+ * Subtracts the values of a given vector from those of this vector creating a new vector object.
+ * If the provided vector is null, an exception is thrown.
+ *
+ * @param v the vector to subtract from this vector
+ * @return the resultant vector
+ */
+ public Vector2f subtract(Vector2f v) {
+ return subtract(v, null);
+ }
+
+ /**
+ * Subtracts the values of a given vector from those of this vector storing the result in the
+ * given vector. If the provided vector v is null, an exception is thrown. If the provided vector
+ * result is null, a new vector is created.
+ *
+ * @param v the vector to subtract from this vector
+ * @param result the vector to store the result in
+ * @return the resultant vector
+ */
+ public Vector2f subtract(Vector2f v, Vector2f result) {
+ if (result == null) result = new Vector2f();
+ result.x = x - v.x;
+ result.y = y - v.y;
+ return result;
+ }
+
+ /**
+ * Subtracts the given x,y values from those of this vector creating a new vector object.
+ *
+ * @param x value to subtract from x
+ * @param y value to subtract from y
+ * @return the resultant vector
+ */
+ public Vector2f subtract(float x, float y) {
+ return new Vector2f(this.x - x, this.y - y);
+ }
+
+ /**
+ * Subtracts a provided vector to this vector internally, and returns a handle to this vector for
+ * easy chaining of calls. If the provided vector is null, null is returned.
+ *
+ * @param v the vector to subtract
+ * @return this
+ */
+ public Vector2f subtractLocal(Vector2f v) {
+ if (null == v) {
+ return null;
+ }
+ x -= v.x;
+ y -= v.y;
+ return this;
+ }
+
+ /**
+ * Subtracts the provided values from this vector internally, and returns a handle to this vector
+ * for easy chaining of calls.
+ *
+ * @param x value to subtract from x
+ * @param y value to subtract from y
+ * @return this
+ */
+ public Vector2f subtractLocal(float x, float y) {
+ this.x -= x;
+ this.y -= y;
+ return this;
+ }
+
+ /**
+ * Returns the unit vector of this vector. The values of this vector remain untouched.
+ *
+ * @return the newly created unit vector of this vector
+ */
+ public Vector2f normalize() {
+ float length = length();
+ if (length != 0) {
+ return divide(length);
+ }
+ return divide(1);
+ }
+
+ /**
+ * Makes this vector into a unit vector of itself internally.
+ *
+ * @return this
+ */
+ public Vector2f normalizeLocal() {
+ float length = length();
+ if (length != 0) {
+ return divideLocal(length);
+ }
+ return divideLocal(1);
+ }
+
+ /**
+ * Returns the minimum angle (in radians) between two vectors. It is assumed that both this vector
+ * and the given vector are unit vectors (iow, normalized).
+ *
+ * @param otherVector a unit vector to find the angle against
+ * @return the angle in radians
+ */
+ public float smallestAngleBetween(Vector2f otherVector) {
+ float dotProduct = dot(otherVector);
+ float angle = Mathf.acos(dotProduct);
+ return angle;
+ }
+
+ /**
+ * Sets the closest int to the x and y components locally, with ties rounding to positive
+ * infinity.
+ *
+ * @return this
+ */
+ public Vector2f roundToIntLocal() {
+ x = Mathf.roundToInt(x);
+ y = Mathf.roundToInt(y);
+ return this;
+ }
+
+ public Vector2f rotate(float[][] rotationMatrix) {
+ float x0 = x * rotationMatrix[0][0] + y * rotationMatrix[0][1];
+ float y0 = x * rotationMatrix[1][0] + y * rotationMatrix[1][1];
+ this.x = x0;
+ this.y = y0;
+ return this;
+ }
+
+ public Vector2f rotate(float angle) {
+ float cos = Mathf.cos(angle);
+ float sin = Mathf.sin(angle);
+ return new Vector2f(x * cos - y * sin, x * sin + y * cos);
+ }
+
+ /**
+ * Returns (in radians) the angle required to rotate a ray represented by this vector to lie
+ * colinear to a ray described by the given vector. It is assumed that both this vector and the
+ * given vector are unit vectors (iow, normalized).
+ *
+ * @param otherVector the "destination" unit vector
+ * @return the angle in radians
+ */
+ public float angleBetween(Vector2f otherVector) {
+ float angle = Mathf.atan2(otherVector.y, otherVector.x) - Mathf.atan2(y, x);
+ return angle;
+ }
+
+ // /**
+ // * Computes the angle (in radians) between the vector represented
+ // * by this vector and the specified vector.
+ // * @param x the X magnitude of the other vector
+ // * @param y the Y magnitude of the other vector
+ // * @return the angle between the two vectors measured in radians
+ // */
+ // public float angle(float x, float y) {
+ // final float ax = getX();
+ // final float ay = getY();
+ //
+ // final float delta = (ax * x + ay * y) / Mathf.sqrt(
+ // (ax * ax + ay * ay) * (x * x + y * y));
+ //
+ // if (delta > 1.0) {
+ // return 0.0f;
+ // }
+ // if (delta < -1.0) {
+ // return Mathf.PI;
+ // }
+ //
+ // return Mathf.acos(delta);
+ // }
+
+ public Vector2f frac() {
+ // TODO Check if this is the correct way
+ return new Vector2f(x - (int) x, y - (int) y);
+ }
+
+ /**
+ * Returns the x component of this vector.
+ *
+ * @return the x component of this vector
+ */
+ public float getX() {
+ return x;
+ }
+
+ /**
+ * Sets the x component of this vector to the specified new value, and returns a handle to this
+ * vector for easy chaining of calls.
+ *
+ * @param x the specified new x component
+ * @return this
+ */
+ public Vector2f setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ /**
+ * Returns the y component of this vector.
+ *
+ * @return the y component of this vector
+ */
+ public float getY() {
+ return y;
+ }
+
+ /**
+ * Sets the y component of this vector to the specified new value, and returns a handle to this
+ * vector for easy chaining of calls.
+ *
+ * @param y the specified new y component
+ * @return this
+ */
+ public Vector2f setY(float y) {
+ this.y = y;
+ return this;
+ }
+
+ /**
+ * Returns the angle (in radians) represented by this vector as expressed by a conversion from
+ * rectangular coordinates ( x, y) to polar coordinates (r,
+ * theta).
+ *
+ * @return the angle in radians. [-pi, pi)
+ */
+ public float getAngle() {
+ return Mathf.atan2(y, x);
+ }
+
+ /**
+ * Resets this vector's data to zero internally, and returns a handle to this vector for easy
+ * chaining of calls.
+ */
+ public Vector2f zero() {
+ x = y = 0;
+ return this;
+ }
+
+ /**
+ * Returns the perpendicular vector to this vector.
+ *
+ * @return the newly created perpendicular to this vector
+ */
+ public Vector2f getPerpendicular() {
+ return new Vector2f(-y, x);
+ }
+
+ /**
+ * Stores this vector into the given array.
+ *
+ * @param floats the array to store this vector in, if null, a new float array with the size of
+ * two is created
+ * @return the array, with x, y float values in that order
+ */
+ public float[] toArray(float[] floats) {
+ if (floats == null) {
+ floats = new float[2];
+ }
+ floats[0] = x;
+ floats[1] = y;
+ return floats;
+ }
+
+ /**
+ * Stores this vector into a newly created float array with a size of two.
+ *
+ * @return the array that stores the vector with x,y component in that order
+ */
+ public float[] toArray() {
+ return toArray(null);
+ }
+
+ /**
+ * Rotates this vector around the origin.
+ *
+ * @param angle the angle to rotate (in radians)
+ * @param cw true to rotate clockwise, false to rotate counterclockwise
+ */
+ public Vector2f rotate(float angle, boolean cw) {
+ if (cw) angle = -angle;
+ float newX = Mathf.cos(angle) * x - Mathf.sin(angle) * y;
+ float newY = Mathf.sin(angle) * x + Mathf.cos(angle) * y;
+ return new Vector2f(newX, newY);
+ }
+
+ /**
+ * Returns a unique hash code for this vector object based on it's values. If two vectors are
+ * logically equivalent, they will return the same hash code value.
+ *
+ * @return the hash code value of this vector
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Float.floatToIntBits(x);
+ result = prime * result + Float.floatToIntBits(y);
+ return result;
+ }
+
+ /**
+ * Determines if this vector is equals to the given object obj.
+ *
+ * @param obj the object to compare for equality
+ * @return true if they are equal
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Vector2f other = (Vector2f) obj;
+ if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) return false;
+ if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) return false;
+ return true;
+ }
+
+ /**
+ * Returns a string representation of this {@link Vector2f}.
+ *
+ * @return a string representation of this {@link Vector2f}
+ */
+ @Override
+ public String toString() {
+ return "Vector2f [x=" + x + ", y=" + y + "]";
+ }
+}
diff --git a/src/main/java/math/Vector3f.java b/src/main/java/math/Vector3f.java
index 935b0f5f..b75ad8a2 100644
--- a/src/main/java/math/Vector3f.java
+++ b/src/main/java/math/Vector3f.java
@@ -2,569 +2,553 @@
public class Vector3f {
- public static final Vector3f BACK = new Vector3f(0, 0, -1);
+ public static final Vector3f BACK = new Vector3f(0, 0, -1);
- public static final Vector3f DOWN = new Vector3f(0, -1, 0);
+ public static final Vector3f DOWN = new Vector3f(0, -1, 0);
- public static final Vector3f FORWARD = new Vector3f(0, 0, 1);
+ public static final Vector3f FORWARD = new Vector3f(0, 0, 1);
- public static final Vector3f LEFT = new Vector3f(-1, 0, 0);
+ public static final Vector3f LEFT = new Vector3f(-1, 0, 0);
- public static final Vector3f MAX = new Vector3f(Float.MAX_VALUE,
- Float.MAX_VALUE, Float.MAX_VALUE);
+ public static final Vector3f MAX =
+ new Vector3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
- public static final Vector3f MIN = new Vector3f(Float.MIN_VALUE,
- Float.MIN_VALUE, Float.MIN_VALUE);
+ public static final Vector3f MIN =
+ new Vector3f(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
- public static final Vector3f NAN = new Vector3f(Float.NaN, Float.NaN,
- Float.NaN);
+ public static final Vector3f NAN = new Vector3f(Float.NaN, Float.NaN, Float.NaN);
- public static final Vector3f NEGATIVE_INFINITY = new Vector3f(
- Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY,
- Float.NEGATIVE_INFINITY);
+ public static final Vector3f NEGATIVE_INFINITY =
+ new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
- public static final Vector3f ONE = new Vector3f(1, 1, 1);
+ public static final Vector3f ONE = new Vector3f(1, 1, 1);
- public static final Vector3f POSITIVE_INFINITY = new Vector3f(
- Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
- Float.POSITIVE_INFINITY);
+ public static final Vector3f POSITIVE_INFINITY =
+ new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
- public static final Vector3f RIGHT = new Vector3f(1, 0, 0);
+ public static final Vector3f RIGHT = new Vector3f(1, 0, 0);
- public static final Vector3f UP = new Vector3f(0, 1, 0);
+ public static final Vector3f UP = new Vector3f(0, 1, 0);
- public static final Vector3f ZERO = new Vector3f(0, 0, 0);
+ public static final Vector3f ZERO = new Vector3f(0, 0, 0);
- public float x;
+ public float x;
- public float y;
+ public float y;
- public float z;
+ public float z;
- public Vector3f() {
- x = y = z = 0;
- }
+ public Vector3f() {
+ x = y = z = 0;
+ }
- public Vector3f(float value) {
- x = y = z = value;
- }
+ public Vector3f(float value) {
+ x = y = z = value;
+ }
- public Vector3f(float x, float y) {
- this(x, y, 0);
- }
+ public Vector3f(float x, float y) {
+ this(x, y, 0);
+ }
- public Vector3f(float x, float y, float z) {
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- public Vector3f(float[] values) {
- x = values[0];
- y = values[1];
- z = values[2];
- }
+ public Vector3f(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
- public Vector3f(Vector3f v) {
- set(v);
- }
-
- /**
- * Rounds the x, y, and z components of this vector to the specified number of
- * decimal places.
- *
- * @param decimalPlaces The number of decimal places to round to.
- */
- public void roundLocalDecimalPlaces(int decimalPlaces) {
- float factor = Mathf.pow(10, decimalPlaces);
- x = Mathf.round(x * factor) / factor;
- y = Mathf.round(y * factor) / factor;
- z = Mathf.round(z * factor) / factor;
- }
-
- public boolean approximatelyEquals(Vector3f v, float threshold) {
- if (threshold < 0.0f)
- throw new IllegalArgumentException(
- "Threshold must be greater or equal to 0.0f.");
-
- float diffX = Math.abs(x - v.x);
- float diffY = Math.abs(y - v.y);
- float diffZ = Math.abs(z - v.z);
-
- return (diffX <= threshold && diffY <= threshold && diffZ <= threshold);
- }
-
- public float angle(Vector3f v) {
- return (float) Math.acos(dot(v));
- }
-
- public float signedAngle(Vector3f v, Vector3f normal) {
- float unsignedAngle = (float) Math.acos(dot(v));
- return unsignedAngle * Math.signum(normal.dot(cross(v)));
- }
-
- public Vector3f project(Vector3f v) {
- float scalar = dot(v) / v.lengthSquared();
- return v.mult(scalar);
- }
-
- public Vector3f projectLocal(Vector3f v) {
- float scalar = dot(v) / v.lengthSquared();
- set(v.mult(scalar));
- return this;
- }
-
- public Vector3f projectOnPlane(Vector3f planeNormal) {
- // FIXME Check if this implementation is correct.
- float scalar = dot(planeNormal) / planeNormal.lengthSquared();
- return subtract(planeNormal.mult(scalar));
- }
-
- public Vector3f projectOnPlaneLocal(Vector3f planeNormal) {
- // FIXME Check if this implementation is correct.
- float scalar = dot(planeNormal) / planeNormal.lengthSquared();
- subtractLocal(planeNormal.mult(scalar));
- return this;
- }
-
- public Vector3f clampLength(float maxLength) {
- return normalize().mult(maxLength);
- }
-
- public Vector3f clampLengthLocal(float maxLength) {
- normalizeLocal();
- multLocal(maxLength);
- return this;
- }
-
- public float lengthSquared() {
- return (x * x) + (y * y) + (z * z);
- }
-
- public float length() {
- return (float) Math.sqrt(lengthSquared());
- }
-
- public float distanceSquared(float x, float y, float z) {
- float dx = this.x - x;
- float dy = this.y - y;
- float dz = this.z - z;
- return (dx * dx) + (dy * dy) + (dz * dz);
- }
-
- public float distance(float x, float y, float z) {
- return (float) Math.sqrt(distanceSquared(x, y, z));
- }
-
- public float distanceSquared(Vector3f v) {
- return distanceSquared(v.x, v.y, v.z);
- }
-
- public float distance(Vector3f v) {
- return (float) Math.sqrt(distanceSquared(v));
- }
-
- public Vector3f normalize() {
- float length = length();
- if (length != 0) {
- return divide(length);
- }
- return divide(1);
- }
-
- public Vector3f normalizeLocal() {
- float length = length();
- if (length != 0) {
- return divideLocal(length);
- }
- return divideLocal(1);
- }
-
- public float dot(Vector3f v) {
- return (x * v.x) + (y * v.y) + z * (v.z);
- }
-
- public Vector3f cross(float x, float y, float z) {
- return new Vector3f((this.y * z) - (this.z * y),
- (this.z * x) - (this.x * z), (this.x * y) - (this.y * x));
- }
-
- public Vector3f crossLocal(float x, float y, float z) {
- this.x = (this.y * z) - (this.z * y);
- this.y = (this.z * x) - (this.x * z);
- this.z = (this.x * y) - (this.y * x);
- return this;
- }
-
- public Vector3f cross(Vector3f v) {
- return new Vector3f((y * v.z) - (z * v.y), (z * v.x) - (x * v.z),
- (x * v.y) - (y * v.x));
- }
-
- public Vector3f crossLocal(Vector3f v) {
- x = (y * v.z) - (z * v.y);
- y = (z * v.x) - (x * v.z);
- z = (x * v.y) - (y * v.x);
- return this;
- }
-
- public Vector3f cross(Vector3f v, Vector3f result) {
- if (result == null)
- result = new Vector3f();
- return result.set(cross(v));
- }
-
- public Vector3f negate() {
- return new Vector3f(-x, -y, -z);
- }
-
- public Vector3f negateLocal() {
- x = -x;
- y = -y;
- z = -z;
- return this;
- }
-
- public Vector3f add(float x, float y, float z) {
- return new Vector3f(this.x + x, this.y + y, this.z + z);
- }
-
- public Vector3f addLocal(float x, float y, float z) {
- this.x += x;
- this.y += y;
- this.z += z;
- return this;
- }
-
- public Vector3f add(Vector3f v) {
- return new Vector3f(x + v.x, y + v.y, z + v.z);
- }
-
- public Vector3f addLocal(Vector3f v) {
- x += v.x;
- y += v.y;
- z += v.z;
- return this;
- }
-
- public Vector3f add(Vector3f v, Vector3f result) {
- if (result == null)
- result = new Vector3f();
- return result.set(add(v));
- }
-
- public Vector3f subtract(float x, float y, float z) {
- return new Vector3f(this.x - x, this.y - y, this.z - z);
- }
-
- public Vector3f subtractLocal(float x, float y, float z) {
- this.x -= x;
- this.y -= y;
- this.z -= z;
- return this;
- }
-
- public Vector3f subtract(Vector3f v) {
- return new Vector3f(x - v.x, y - v.y, z - v.z);
- }
-
- public Vector3f subtractLocal(Vector3f v) {
- x -= v.x;
- y -= v.y;
- z -= v.z;
- return this;
- }
-
- public Vector3f subtract(Vector3f v, Vector3f result) {
- if (result == null)
- result = new Vector3f();
- return result.set(subtract(v));
- }
-
- public Vector3f mult(float x, float y, float z) {
- return new Vector3f(this.x * x, this.y * y, this.z * z);
- }
-
- public Vector3f multLocal(float x, float y, float z) {
- this.x *= x;
- this.y *= y;
- this.z *= z;
- return this;
- }
-
- public Vector3f mult(Vector3f v) {
- return new Vector3f(x * v.x, y * v.y, z * v.z);
- }
-
- public Vector3f multLocal(Vector3f v) {
- x *= v.x;
- y *= v.y;
- z *= v.z;
- return this;
- }
-
- public Vector3f mult(Vector3f v, Vector3f result) {
- if (result == null)
- result = new Vector3f();
- return result.set(mult(v));
- }
-
- public Vector3f divide(float x, float y, float z) {
- return new Vector3f(this.x / x, this.y / y, this.z / z);
- }
-
- public Vector3f divideLocal(float x, float y, float z) {
- this.x /= x;
- this.y /= y;
- this.z /= z;
- return this;
- }
-
- public Vector3f divide(Vector3f v) {
- return new Vector3f(x / v.x, y / v.y, z / v.z);
- }
-
- public Vector3f divideLocal(Vector3f v) {
- x /= v.x;
- y /= v.y;
- z /= v.z;
- return this;
- }
-
- public Vector3f mult(float scalar) {
- return new Vector3f(x * scalar, y * scalar, z * scalar);
- }
-
- public Vector3f multLocal(float scalar) {
- x *= scalar;
- y *= scalar;
- z *= scalar;
- return this;
- }
-
- public Vector3f mult(Matrix3f m) {
- float x0 = m.values[0] * x + m.values[1] * y + m.values[2] * z;
- float y0 = m.values[3] * x + m.values[4] * y + m.values[5] * z;
- float z0 = m.values[6] * x + m.values[7] * y + m.values[8] * z;
- return new Vector3f(x0, y0, z0);
- }
-
- public Vector3f multLocal(Matrix3f m) {
- float x0 = m.values[0] * x + m.values[1] * y + m.values[2] * z;
- float y0 = m.values[3] * x + m.values[4] * y + m.values[5] * z;
- float z0 = m.values[6] * x + m.values[7] * y + m.values[8] * z;
- set(x0, y0, z0);
- return this;
- }
-
- public Vector3f divide(float scalar) {
- return new Vector3f(x / scalar, y / scalar, z / scalar);
- }
-
- public Vector3f divideLocal(float scalar) {
- x /= scalar;
- y /= scalar;
- z /= scalar;
- return this;
- }
-
- public Vector3f divide(Vector3f v, Vector3f result) {
- if (result == null)
- result = new Vector3f();
- return result.set(divide(v));
- }
-
- public Vector3f abs() {
- return new Vector3f(Math.abs(x), Math.abs(y), Math.abs(z));
- }
-
- public Vector3f absLocal() {
- x = Math.abs(x);
- y = Math.abs(y);
- z = Math.abs(z);
- return this;
- }
-
- public Vector3f min(Vector3f v) {
- return new Vector3f(Math.min(x, v.x), Math.min(y, v.y), Math.min(z, v.z));
- }
-
- public Vector3f minLocal(Vector3f v) {
- x = Math.min(x, v.x);
- y = Math.min(y, v.y);
- z = Math.min(z, v.z);
- return this;
- }
-
- public Vector3f minLocal(Vector3f a, Vector3f b) {
- x = Math.min(a.x, b.x);
- y = Math.min(a.y, b.y);
- z = Math.min(a.z, b.z);
- return this;
- }
-
- public Vector3f max(Vector3f v) {
- return new Vector3f(Math.max(x, v.x), Math.max(y, v.y), Math.max(z, v.z));
- }
-
- public Vector3f maxLocal(Vector3f v) {
- x = Math.max(x, v.x);
- y = Math.max(y, v.y);
- z = Math.max(z, v.z);
- return this;
- }
-
- public Vector3f maxLocal(Vector3f a, Vector3f b) {
- x = Math.max(a.x, b.x);
- y = Math.max(a.y, b.y);
- z = Math.max(a.z, b.z);
- return this;
- }
-
- public Vector3f reciprocal() {
- return new Vector3f((x != 0 ? 1.0f / x : Float.POSITIVE_INFINITY),
- (y != 0 ? 1.0f / y : Float.POSITIVE_INFINITY),
- (z != 0 ? 1.0f / z : Float.POSITIVE_INFINITY));
- }
-
- public Vector3f lerpLocal(Vector3f finalVec, float changeAmnt) {
- if (changeAmnt == 0) {
- return this;
- }
- if (changeAmnt == 1) {
- this.x = finalVec.x;
- this.y = finalVec.y;
- this.z = finalVec.z;
- return this;
- }
- this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
- this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
- this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z;
- return this;
- }
-
- public Vector3f lerpLocal(Vector3f beginVec, Vector3f finalVec,
- float changeAmnt) {
- if (changeAmnt == 0) {
- this.x = beginVec.x;
- this.y = beginVec.y;
- this.z = beginVec.z;
- return this;
- }
- if (changeAmnt == 1) {
- this.x = finalVec.x;
- this.y = finalVec.y;
- this.z = finalVec.z;
- return this;
- }
- this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
- this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
- this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z;
- return this;
- }
-
- public static boolean isValid(Vector3f v) {
- if (v == null)
- return false;
- if (Float.isNaN(v.x) || Float.isNaN(v.y) || Float.isNaN(v.z))
- return false;
- if (Float.isInfinite(v.x) || Float.isInfinite(v.y) || Float.isInfinite(v.z))
- return false;
- return true;
- }
-
- public Vector3f set(float x, float y, float z) {
- this.x = x;
- this.y = y;
- this.z = z;
- return this;
- }
-
- public Vector3f set(Vector3f v) {
- x = v.x;
- y = v.y;
- z = v.z;
- return this;
- }
-
- public Vector3f set(float[] values) {
- x = values[0];
- y = values[1];
- z = values[2];
- return this;
- }
-
- public float getX() {
- return x;
- }
-
- public Vector3f setX(float x) {
- this.x = x;
- return this;
- }
-
- public float getY() {
- return y;
- }
-
- public Vector3f setY(float y) {
- this.y = y;
- return this;
- }
-
- public float getZ() {
- return z;
- }
-
- public Vector3f setZ(float z) {
- this.z = z;
- return this;
- }
-
- public float get(int i) {
- switch (i) {
- case 0:
- return x;
- case 1:
- return y;
- case 2:
- return z;
- default:
- throw new IndexOutOfBoundsException("Index must be 0, 1, or 2.");
- }
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Float.floatToIntBits(x);
- result = prime * result + Float.floatToIntBits(y);
- result = prime * result + Float.floatToIntBits(z);
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Vector3f other = (Vector3f) obj;
- if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x))
- return false;
- if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y))
- return false;
- if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z))
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "Vector3f [x=" + x + ", y=" + y + ", z=" + z + "]";
- }
+ public Vector3f(float[] values) {
+ x = values[0];
+ y = values[1];
+ z = values[2];
+ }
+ public Vector3f(Vector3f v) {
+ set(v);
+ }
+
+ /**
+ * Rounds the x, y, and z components of this vector to the specified number of decimal places.
+ *
+ * @param decimalPlaces The number of decimal places to round to.
+ */
+ public void roundLocalDecimalPlaces(int decimalPlaces) {
+ float factor = Mathf.pow(10, decimalPlaces);
+ x = Mathf.round(x * factor) / factor;
+ y = Mathf.round(y * factor) / factor;
+ z = Mathf.round(z * factor) / factor;
+ }
+
+ public boolean approximatelyEquals(Vector3f v, float threshold) {
+ if (threshold < 0.0f)
+ throw new IllegalArgumentException("Threshold must be greater or equal to 0.0f.");
+
+ float diffX = Math.abs(x - v.x);
+ float diffY = Math.abs(y - v.y);
+ float diffZ = Math.abs(z - v.z);
+
+ return (diffX <= threshold && diffY <= threshold && diffZ <= threshold);
+ }
+
+ public float angle(Vector3f v) {
+ return (float) Math.acos(dot(v));
+ }
+
+ public float signedAngle(Vector3f v, Vector3f normal) {
+ float unsignedAngle = (float) Math.acos(dot(v));
+ return unsignedAngle * Math.signum(normal.dot(cross(v)));
+ }
+
+ public Vector3f project(Vector3f v) {
+ float scalar = dot(v) / v.lengthSquared();
+ return v.mult(scalar);
+ }
+
+ public Vector3f projectLocal(Vector3f v) {
+ float scalar = dot(v) / v.lengthSquared();
+ set(v.mult(scalar));
+ return this;
+ }
+
+ public Vector3f projectOnPlane(Vector3f planeNormal) {
+ // FIXME Check if this implementation is correct.
+ float scalar = dot(planeNormal) / planeNormal.lengthSquared();
+ return subtract(planeNormal.mult(scalar));
+ }
+
+ public Vector3f projectOnPlaneLocal(Vector3f planeNormal) {
+ // FIXME Check if this implementation is correct.
+ float scalar = dot(planeNormal) / planeNormal.lengthSquared();
+ subtractLocal(planeNormal.mult(scalar));
+ return this;
+ }
+
+ public Vector3f clampLength(float maxLength) {
+ return normalize().mult(maxLength);
+ }
+
+ public Vector3f clampLengthLocal(float maxLength) {
+ normalizeLocal();
+ multLocal(maxLength);
+ return this;
+ }
+
+ public float lengthSquared() {
+ return (x * x) + (y * y) + (z * z);
+ }
+
+ public float length() {
+ return (float) Math.sqrt(lengthSquared());
+ }
+
+ public float distanceSquared(float x, float y, float z) {
+ float dx = this.x - x;
+ float dy = this.y - y;
+ float dz = this.z - z;
+ return (dx * dx) + (dy * dy) + (dz * dz);
+ }
+
+ public float distance(float x, float y, float z) {
+ return (float) Math.sqrt(distanceSquared(x, y, z));
+ }
+
+ public float distanceSquared(Vector3f v) {
+ return distanceSquared(v.x, v.y, v.z);
+ }
+
+ public float distance(Vector3f v) {
+ return (float) Math.sqrt(distanceSquared(v));
+ }
+
+ public Vector3f normalize() {
+ float length = length();
+ if (length != 0) {
+ return divide(length);
+ }
+ return divide(1);
+ }
+
+ public Vector3f normalizeLocal() {
+ float length = length();
+ if (length != 0) {
+ return divideLocal(length);
+ }
+ return divideLocal(1);
+ }
+
+ public float dot(Vector3f v) {
+ return (x * v.x) + (y * v.y) + z * (v.z);
+ }
+
+ public Vector3f cross(float x, float y, float z) {
+ return new Vector3f(
+ (this.y * z) - (this.z * y), (this.z * x) - (this.x * z), (this.x * y) - (this.y * x));
+ }
+
+ public Vector3f crossLocal(float x, float y, float z) {
+ this.x = (this.y * z) - (this.z * y);
+ this.y = (this.z * x) - (this.x * z);
+ this.z = (this.x * y) - (this.y * x);
+ return this;
+ }
+
+ public Vector3f cross(Vector3f v) {
+ return new Vector3f((y * v.z) - (z * v.y), (z * v.x) - (x * v.z), (x * v.y) - (y * v.x));
+ }
+
+ public Vector3f crossLocal(Vector3f v) {
+ x = (y * v.z) - (z * v.y);
+ y = (z * v.x) - (x * v.z);
+ z = (x * v.y) - (y * v.x);
+ return this;
+ }
+
+ public Vector3f cross(Vector3f v, Vector3f result) {
+ if (result == null) result = new Vector3f();
+ return result.set(cross(v));
+ }
+
+ public Vector3f negate() {
+ return new Vector3f(-x, -y, -z);
+ }
+
+ public Vector3f negateLocal() {
+ x = -x;
+ y = -y;
+ z = -z;
+ return this;
+ }
+
+ public Vector3f add(float x, float y, float z) {
+ return new Vector3f(this.x + x, this.y + y, this.z + z);
+ }
+
+ public Vector3f addLocal(float x, float y, float z) {
+ this.x += x;
+ this.y += y;
+ this.z += z;
+ return this;
+ }
+
+ public Vector3f add(Vector3f v) {
+ return new Vector3f(x + v.x, y + v.y, z + v.z);
+ }
+
+ public Vector3f addLocal(Vector3f v) {
+ x += v.x;
+ y += v.y;
+ z += v.z;
+ return this;
+ }
+
+ public Vector3f add(Vector3f v, Vector3f result) {
+ if (result == null) result = new Vector3f();
+ return result.set(add(v));
+ }
+
+ public Vector3f subtract(float x, float y, float z) {
+ return new Vector3f(this.x - x, this.y - y, this.z - z);
+ }
+
+ public Vector3f subtractLocal(float x, float y, float z) {
+ this.x -= x;
+ this.y -= y;
+ this.z -= z;
+ return this;
+ }
+
+ public Vector3f subtract(Vector3f v) {
+ return new Vector3f(x - v.x, y - v.y, z - v.z);
+ }
+
+ public Vector3f subtractLocal(Vector3f v) {
+ x -= v.x;
+ y -= v.y;
+ z -= v.z;
+ return this;
+ }
+
+ public Vector3f subtract(Vector3f v, Vector3f result) {
+ if (result == null) result = new Vector3f();
+ return result.set(subtract(v));
+ }
+
+ public Vector3f mult(float x, float y, float z) {
+ return new Vector3f(this.x * x, this.y * y, this.z * z);
+ }
+
+ public Vector3f multLocal(float x, float y, float z) {
+ this.x *= x;
+ this.y *= y;
+ this.z *= z;
+ return this;
+ }
+
+ public Vector3f mult(Vector3f v) {
+ return new Vector3f(x * v.x, y * v.y, z * v.z);
+ }
+
+ public Vector3f multLocal(Vector3f v) {
+ x *= v.x;
+ y *= v.y;
+ z *= v.z;
+ return this;
+ }
+
+ public Vector3f mult(Vector3f v, Vector3f result) {
+ if (result == null) result = new Vector3f();
+ return result.set(mult(v));
+ }
+
+ public Vector3f divide(float x, float y, float z) {
+ return new Vector3f(this.x / x, this.y / y, this.z / z);
+ }
+
+ public Vector3f divideLocal(float x, float y, float z) {
+ this.x /= x;
+ this.y /= y;
+ this.z /= z;
+ return this;
+ }
+
+ public Vector3f divide(Vector3f v) {
+ return new Vector3f(x / v.x, y / v.y, z / v.z);
+ }
+
+ public Vector3f divideLocal(Vector3f v) {
+ x /= v.x;
+ y /= v.y;
+ z /= v.z;
+ return this;
+ }
+
+ public Vector3f mult(float scalar) {
+ return new Vector3f(x * scalar, y * scalar, z * scalar);
+ }
+
+ public Vector3f multLocal(float scalar) {
+ x *= scalar;
+ y *= scalar;
+ z *= scalar;
+ return this;
+ }
+
+ public Vector3f mult(Matrix3f m) {
+ float x0 = m.values[0] * x + m.values[1] * y + m.values[2] * z;
+ float y0 = m.values[3] * x + m.values[4] * y + m.values[5] * z;
+ float z0 = m.values[6] * x + m.values[7] * y + m.values[8] * z;
+ return new Vector3f(x0, y0, z0);
+ }
+
+ public Vector3f multLocal(Matrix3f m) {
+ float x0 = m.values[0] * x + m.values[1] * y + m.values[2] * z;
+ float y0 = m.values[3] * x + m.values[4] * y + m.values[5] * z;
+ float z0 = m.values[6] * x + m.values[7] * y + m.values[8] * z;
+ set(x0, y0, z0);
+ return this;
+ }
+
+ public Vector3f divide(float scalar) {
+ return new Vector3f(x / scalar, y / scalar, z / scalar);
+ }
+
+ public Vector3f divideLocal(float scalar) {
+ x /= scalar;
+ y /= scalar;
+ z /= scalar;
+ return this;
+ }
+
+ public Vector3f divide(Vector3f v, Vector3f result) {
+ if (result == null) result = new Vector3f();
+ return result.set(divide(v));
+ }
+
+ public Vector3f abs() {
+ return new Vector3f(Math.abs(x), Math.abs(y), Math.abs(z));
+ }
+
+ public Vector3f absLocal() {
+ x = Math.abs(x);
+ y = Math.abs(y);
+ z = Math.abs(z);
+ return this;
+ }
+
+ public Vector3f min(Vector3f v) {
+ return new Vector3f(Math.min(x, v.x), Math.min(y, v.y), Math.min(z, v.z));
+ }
+
+ public Vector3f minLocal(Vector3f v) {
+ x = Math.min(x, v.x);
+ y = Math.min(y, v.y);
+ z = Math.min(z, v.z);
+ return this;
+ }
+
+ public Vector3f minLocal(Vector3f a, Vector3f b) {
+ x = Math.min(a.x, b.x);
+ y = Math.min(a.y, b.y);
+ z = Math.min(a.z, b.z);
+ return this;
+ }
+
+ public Vector3f max(Vector3f v) {
+ return new Vector3f(Math.max(x, v.x), Math.max(y, v.y), Math.max(z, v.z));
+ }
+
+ public Vector3f maxLocal(Vector3f v) {
+ x = Math.max(x, v.x);
+ y = Math.max(y, v.y);
+ z = Math.max(z, v.z);
+ return this;
+ }
+
+ public Vector3f maxLocal(Vector3f a, Vector3f b) {
+ x = Math.max(a.x, b.x);
+ y = Math.max(a.y, b.y);
+ z = Math.max(a.z, b.z);
+ return this;
+ }
+
+ public Vector3f reciprocal() {
+ return new Vector3f(
+ (x != 0 ? 1.0f / x : Float.POSITIVE_INFINITY),
+ (y != 0 ? 1.0f / y : Float.POSITIVE_INFINITY),
+ (z != 0 ? 1.0f / z : Float.POSITIVE_INFINITY));
+ }
+
+ public Vector3f lerpLocal(Vector3f finalVec, float changeAmnt) {
+ if (changeAmnt == 0) {
+ return this;
+ }
+ if (changeAmnt == 1) {
+ this.x = finalVec.x;
+ this.y = finalVec.y;
+ this.z = finalVec.z;
+ return this;
+ }
+ this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
+ this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z;
+ return this;
+ }
+
+ public Vector3f lerpLocal(Vector3f beginVec, Vector3f finalVec, float changeAmnt) {
+ if (changeAmnt == 0) {
+ this.x = beginVec.x;
+ this.y = beginVec.y;
+ this.z = beginVec.z;
+ return this;
+ }
+ if (changeAmnt == 1) {
+ this.x = finalVec.x;
+ this.y = finalVec.y;
+ this.z = finalVec.z;
+ return this;
+ }
+ this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
+ this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
+ this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z;
+ return this;
+ }
+
+ public static boolean isValid(Vector3f v) {
+ if (v == null) return false;
+ if (Float.isNaN(v.x) || Float.isNaN(v.y) || Float.isNaN(v.z)) return false;
+ if (Float.isInfinite(v.x) || Float.isInfinite(v.y) || Float.isInfinite(v.z)) return false;
+ return true;
+ }
+
+ public boolean isZero() {
+ float threshold = 1e-6f;
+ return Math.abs(x) < threshold && Math.abs(y) < threshold && Math.abs(z) < threshold;
+ }
+
+ public Vector3f set(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+
+ public Vector3f set(Vector3f v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ return this;
+ }
+
+ public Vector3f set(float[] values) {
+ x = values[0];
+ y = values[1];
+ z = values[2];
+ return this;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public Vector3f setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public Vector3f setY(float y) {
+ this.y = y;
+ return this;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public Vector3f setZ(float z) {
+ this.z = z;
+ return this;
+ }
+
+ public float get(int i) {
+ switch (i) {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ default:
+ throw new IndexOutOfBoundsException("Index must be 0, 1, or 2.");
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Float.floatToIntBits(x);
+ result = prime * result + Float.floatToIntBits(y);
+ result = prime * result + Float.floatToIntBits(z);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Vector3f other = (Vector3f) obj;
+ if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) return false;
+ if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) return false;
+ if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z)) return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Vector3f [x=" + x + ", y=" + y + ", z=" + z + "]";
+ }
}
diff --git a/src/main/java/workspace/FirstPersonView.java b/src/main/java/workspace/FirstPersonView.java
index d6b71a7e..9480aad7 100644
--- a/src/main/java/workspace/FirstPersonView.java
+++ b/src/main/java/workspace/FirstPersonView.java
@@ -1,5 +1,6 @@
package workspace;
+import engine.world.PlayerMock;
import math.Mathf;
import math.Matrix3f;
import math.Matrix4f;
@@ -10,145 +11,130 @@
public class FirstPersonView {
- private boolean enabled;
+ private boolean enabled;
- private boolean left;
+ private boolean left;
- private boolean right;
+ private boolean right;
- private boolean forward;
+ private boolean forward;
- private boolean back;
+ private boolean back;
- private boolean up;
+ private boolean up;
- private boolean down;
+ private boolean down;
- private float pitch = Mathf.PI;
+ private float pitch = Mathf.PI;
- private float yaw = 0;
+ private float yaw = 0;
- private Vector3f eye = new Vector3f(-1000, 0, 1000);
+ private Vector3f eye = new Vector3f(-1000, 0, 1000);
- private float speed = 10;
+ private float speed = 10;
- private PApplet context;
+ private PApplet context;
- public FirstPersonView(PApplet context) {
- this.context = context;
- context.registerMethod("pre", this);
- context.registerMethod("keyEvent", this);
- }
+ public FirstPersonView(PApplet context) {
+ this.context = context;
+ context.registerMethod("pre", this);
+ context.registerMethod("keyEvent", this);
+ }
- public void pre() {
- if (!enabled)
- return;
- yaw = Mathf.map(context.mouseX, 0, context.width, Mathf.PI, -Mathf.PI);
- pitch = Mathf.map(context.mouseY, 0, context.height, -Mathf.PI, Mathf.PI);
+ public void pre() {
+ if (!enabled) return;
+ yaw = Mathf.map(context.mouseX, 0, context.width, Mathf.PI, -Mathf.PI);
+ pitch = Mathf.map(context.mouseY, 0, context.height, -Mathf.PI, Mathf.PI);
-// if (pitch > 89)
-// pitch = 89;
-// if (pitch < -89)
-// pitch = -89;
+ // if (pitch > 89)
+ // pitch = 89;
+ // if (pitch < -89)
+ // pitch = -89;
- Vector3f front = new Vector3f();
- float x = Mathf.cos(Mathf.toRadians(yaw))
- * Mathf.cos(Mathf.toRadians(pitch));
- float y = Mathf.sin(Mathf.toRadians(pitch));
- float z = Mathf.cos(Mathf.toRadians(yaw))
- * Mathf.cos(Mathf.toRadians(pitch));
- front.set(x, y, z);
+ Vector3f front = new Vector3f();
+ float x = Mathf.cos(Mathf.toRadians(yaw)) * Mathf.cos(Mathf.toRadians(pitch));
+ float y = Mathf.sin(Mathf.toRadians(pitch));
+ float z = Mathf.cos(Mathf.toRadians(yaw)) * Mathf.cos(Mathf.toRadians(pitch));
+ front.set(x, y, z);
- Vector3f velocity = new Vector3f();
+ Vector3f velocity = new Vector3f();
- if (left) {
- velocity.addLocal(-1, 0, 0);
- }
+ if (left) {
+ velocity.addLocal(-1, 0, 0);
+ }
- if (right) {
- velocity.addLocal(1, 0, 0);
- }
+ if (right) {
+ velocity.addLocal(1, 0, 0);
+ }
- if (back) {
- velocity.addLocal(0, 0, 1);
- }
+ if (back) {
+ velocity.addLocal(0, 0, 1);
+ }
- if (forward) {
- velocity.addLocal(0, 0, -1);
- }
+ if (forward) {
+ velocity.addLocal(0, 0, -1);
+ }
- velocity.multLocal(getRotationMatrix(yaw));
+ velocity.multLocal(getRotationMatrix(yaw));
- eye.addLocal(velocity.mult(speed));
- eye.setY(-300);
- }
+ eye.addLocal(velocity.mult(speed));
+ eye.setY(-300);
- public void apply() {
- Matrix4f m = Matrix4f.fpsViewRH(eye, pitch, yaw).transpose();
- PMatrix matrix = context.getMatrix();
- matrix.set(m.getValues());
- context.setMatrix(matrix);
- }
+ PlayerMock.playerPosition.addLocal(velocity.mult(0.1f));
+ }
- public void keyEvent(KeyEvent key) {
- if (key.getAction() == KeyEvent.PRESS)
- onKeyPressed(key.getKey());
- if (key.getAction() == KeyEvent.RELEASE)
- onKeyReleased(key.getKey());
- }
+ public void apply() {
+ Matrix4f m = Matrix4f.fpsViewRH(eye, pitch, yaw).transpose();
+ PMatrix matrix = context.getMatrix();
+ matrix.set(m.getValues());
+ context.setMatrix(matrix);
+ }
- public void onKeyPressed(char key) {
- if (key == 'w' || key == 'W')
- forward = true;
+ public void keyEvent(KeyEvent key) {
+ if (key.getAction() == KeyEvent.PRESS) onKeyPressed(key.getKey());
+ if (key.getAction() == KeyEvent.RELEASE) onKeyReleased(key.getKey());
+ }
- if (key == 's' || key == 'S')
- back = true;
+ public void onKeyPressed(char key) {
+ if (key == 'w' || key == 'W') forward = true;
- if (key == 'a' || key == 'A')
- left = true;
+ if (key == 's' || key == 'S') back = true;
- if (key == 'd' || key == 'D')
- right = true;
+ if (key == 'a' || key == 'A') left = true;
- if (key == ' ')
- up = true;
+ if (key == 'd' || key == 'D') right = true;
- if (key == 'c' || key == 'C')
- down = true;
- }
+ if (key == ' ') up = true;
- public void onKeyReleased(char key) {
- if (key == 'w' || key == 'W')
- forward = false;
+ if (key == 'c' || key == 'C') down = true;
+ }
- if (key == 's' || key == 'S')
- back = false;
+ public void onKeyReleased(char key) {
+ if (key == 'w' || key == 'W') forward = false;
- if (key == 'a' || key == 'A')
- left = false;
+ if (key == 's' || key == 'S') back = false;
- if (key == 'd' || key == 'D')
- right = false;
+ if (key == 'a' || key == 'A') left = false;
- if (key == ' ')
- up = false;
+ if (key == 'd' || key == 'D') right = false;
- if (key == 'c' || key == 'C')
- down = false;
- }
+ if (key == ' ') up = false;
- public Matrix3f getRotationMatrix(float angle) {
- Matrix3f m = new Matrix3f(Mathf.cos(angle), 0, Mathf.sin(angle), 0, 1, 0,
- -Mathf.sin(angle), 0, Mathf.cos(angle));
- return m;
- }
+ if (key == 'c' || key == 'C') down = false;
+ }
- public boolean isEnabled() {
- return enabled;
- }
+ public Matrix3f getRotationMatrix(float angle) {
+ Matrix3f m =
+ new Matrix3f(
+ Mathf.cos(angle), 0, Mathf.sin(angle), 0, 1, 0, -Mathf.sin(angle), 0, Mathf.cos(angle));
+ return m;
+ }
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
+ public boolean isEnabled() {
+ return enabled;
+ }
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
}
diff --git a/src/main/java/workspace/GraphicsPImpl.java b/src/main/java/workspace/GraphicsPImpl.java
index 161358d5..4a22e96c 100644
--- a/src/main/java/workspace/GraphicsPImpl.java
+++ b/src/main/java/workspace/GraphicsPImpl.java
@@ -1,215 +1,592 @@
package workspace;
+import java.util.List;
+
+import engine.processing.LightGizmoRenderer;
+import engine.processing.LightRendererImpl;
+import engine.render.Material;
+import engine.scene.camera.Camera;
+import engine.scene.light.Light;
+import engine.scene.light.LightRenderer;
+import math.Matrix4f;
+import math.Vector3f;
+import mesh.Face3D;
import mesh.Mesh3D;
import processing.core.PApplet;
import processing.core.PGraphics;
+import processing.opengl.PShader;
import workspace.render.Mesh3DRenderer;
import workspace.ui.Color;
import workspace.ui.Graphics;
public class GraphicsPImpl implements Graphics {
- private Color color;
-
- private PGraphics g;
-
- private Mesh3DRenderer renderer;
-
- public GraphicsPImpl(PApplet p) {
- this.g = p.g;
- renderer = new Mesh3DRenderer(p);
- color = Color.BLACK;
- }
-
- @Override
- public void fillFaces(Mesh3D mesh) {
- g.noStroke();
- fill();
- renderer.drawFaces(mesh);
- }
-
- @Override
- public int getWidth() {
- return g.width;
- }
-
- @Override
- public int getHeight() {
- return g.height;
- }
-
- private void stroke() {
- g.stroke(color.getRed(), color.getGreen(), color.getBlue(),
- color.getAlpha());
- }
-
- private void fill() {
- g.fill(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
- }
-
- @Override
- public void pushMatrix() {
- g.pushMatrix();
- }
-
- @Override
- public void popMatrix() {
- g.popMatrix();
- }
-
- @Override
- public void scale(float sx, float sy, float sz) {
- g.scale(sx, sy, sz);
- }
-
- @Override
- public void translate(float x, float y) {
- g.translate(x, y);
- }
-
- @Override
- public void translate(float x, float y, float z) {
- g.translate(x, y, z);
- }
-
- @Override
- public void strokeWeight(float weight) {
- g.strokeWeight(weight);
- }
-
- @Override
- public void setColor(Color color) {
- this.color = color;
- }
-
- @Override
- public void setColor(int red, int green, int blue) {
- color = new Color(red, green, blue);
- }
-
- @Override
- public void setColor(math.Color color) {
- setColor(color.getRedInt(), color.getGreenInt(), color.getBlueInt());
- }
-
- @Override
- public void drawRect(float x, float y, float width, float height) {
- g.pushStyle();
- g.noFill();
- stroke();
- g.rectMode(PApplet.CORNER);
- g.rect(x, y, width, height);
- g.popStyle();
- }
-
- @Override
- public void drawLine(float x1, float y1, float x2, float y2) {
- g.pushStyle();
- g.noFill();
- stroke();
- g.line(x1, y1, x2, y2);
- g.popStyle();
- }
-
- @Override
- public void drawLine(float x1, float y1, float z1, float x2, float y2,
- float z2) {
- g.pushStyle();
- g.noFill();
- stroke();
- g.line(x1, y1, z1, x2, y2, z2);
- g.popStyle();
- }
-
- @Override
- public void fillRect(float x, float y, float width, float height) {
- g.pushStyle();
- g.noStroke();
- fill();
- g.rectMode(PApplet.CORNER);
- g.rect(x, y, width, height);
- g.popStyle();
- }
-
- @Override
- public void drawOval(float x, float y, float width, float height) {
- g.pushStyle();
- g.noFill();
- stroke();
- g.ellipseMode(PApplet.CORNER);
- g.ellipse(x, y, height, width);
- g.popStyle();
- }
-
- @Override
- public void fillOval(float x, float y, float width, float height) {
- g.pushStyle();
- g.noStroke();
- fill();
- g.ellipseMode(PApplet.CORNER);
- g.ellipse(x, y, height, width);
- g.popStyle();
- }
-
- @Override
- public void textSize(float size) {
- g.textSize(size);
- }
-
- @Override
- public float getTextSize() {
- return g.textSize;
- }
-
- @Override
- public float textWidth(String text) {
- return g.textWidth(text);
- }
-
- @Override
- public float textAscent() {
- return g.textAscent();
- }
-
- @Override
- public float textDescent() {
- return g.textDescent();
- }
-
- @Override
- public void text(String text, float x, float y) {
- fill();
- g.text(text, x, y);
- }
-
- @Override
- public void enableDepthTest() {
- g.hint(PApplet.ENABLE_DEPTH_TEST);
- }
-
- @Override
- public void disableDepthTest() {
- g.hint(PApplet.DISABLE_DEPTH_TEST);
- }
-
- @Override
- public void rotate(float angle) {
- g.rotate(angle);
- }
-
- @Override
- public void rotateX(float angle) {
- g.rotateX(angle);
- }
-
- @Override
- public void rotateY(float angle) {
- g.rotateY(angle);
- }
-
- @Override
- public void rotateZ(float angle) {
- g.rotate(angle);
- }
+ private boolean wireframeMode;
+
+ private Color color;
+
+ private math.Color ambientColor;
+
+ private PGraphics g;
+
+ private Mesh3DRenderer renderer;
+
+ private LightRenderer lightRenderer;
+
+ private LightGizmoRenderer lightGizmoRenderer;
+
+ @Override
+ public void setAmbientColor(math.Color ambientColor) {
+ this.ambientColor = ambientColor;
+ }
+
+ @Override
+ public math.Color getAmbientColor() {
+ return ambientColor;
+ }
+
+ @Override
+ public void setWireframeMode(boolean wireframeMode) {
+ this.wireframeMode = wireframeMode;
+ }
+
+ public GraphicsPImpl(PApplet p) {
+ this.g = p.g;
+ renderer = new Mesh3DRenderer(p);
+
+ lightRenderer = new LightRendererImpl(p);
+ lightRenderer.setGraphics(this);
+
+ lightGizmoRenderer = new LightGizmoRenderer(p);
+ lightGizmoRenderer.setGraphics(this);
+
+ color = Color.BLACK;
+ ambientColor = math.Color.WHITE;
+ }
+
+ @Override
+ public void fillFaces(Mesh3D mesh) {
+ if (wireframeMode) {
+ g.noFill();
+ stroke();
+ renderer.drawFaces(mesh);
+ } else {
+ g.noStroke();
+ fill();
+ renderer.drawFaces(mesh);
+ }
+ }
+
+ @Override
+ public void renderInstances(Mesh3D mesh, List