From 06ffe8ec75590ea61895ae5f048cec074cd7208a Mon Sep 17 00:00:00 2001
From: Simon Dietz
+ * The width represents the horizontal size of the UI element. It is used
+ * during rendering and layout calculations to determine how much horizontal
+ * space the element occupies.
+ *
+ * The height represents the vertical size of the UI element. It is used
+ * during rendering and layout calculations to determine how much vertical
+ * space the element occupies.
+ *
@@ -130,30 +186,6 @@ public interface UiElement {
*/
Insets getInsets();
- /**
- * Retrieves the width of this {@code UiElement}.
- *
- * The width represents the horizontal size of the UI element. It is used
- * during rendering and layout calculations to determine how much horizontal
- * space the element occupies.
- *
- * The height represents the vertical size of the UI element. It is used
- * during rendering and layout calculations to determine how much vertical
- * space the element occupies.
- *
From ef6487297ffbe0361681a9c7d4be88faa18b1b58 Mon Sep 17 00:00:00 2001
From: Simon Dietz
+ * Anchors define how this element is aligned relative to its parent or
+ * specific layout logic. Examples include {@code TOP_LEFT}, {@code CENTER},
+ * and {@code BOTTOM_RIGHT}.
+ *
+ * The anchor determines how this element aligns in its container based on the
+ * defined anchor type, such as centering, top-left, or bottom-right
+ * alignment.
+ *
+ * This class provides the logic for creating and rendering a 3D spatial grid
+ * with a given number of rows, columns, and cell size. It is designed to work
+ * with a custom 3D graphics rendering context, allowing visualization of UI
+ * layouts or spatial relationships.
+ *
+ * This method only performs rendering if the grid is currently visible. The
+ * rendering logic draws a series of rectangles in a 3D space to represent the
+ * cells of the grid. It applies necessary transformations for proper
+ * alignment and orientation in a 3D context.
+ *
+ * When set to {@code true}, the grid will be rendered during the next
+ * invocation of the {@code render} method. If set to {@code false}, rendering
+ * will be skipped.
+ *
+ * The axis lines are rendered in the X, Y, and Z directions, with their lengths
+ * determined by the specified size parameter.
+ *
+ * The view matrix is computed based on the camera's position (`eye`), pitch,
+ * and yaw angles. It assumes a right-handed coordinate system where:
+ *
+ * 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.
+ *
+ * The direction vector will be normalized internally to ensure correctness in
+ * calculations. Both {@code origin} and {@code direction} must be non-null.
+ *
+ * The formula for the point is:
+ *
+ *
+ *
+ * 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.
+ *
+ * {@code
+ * point = origin + t * direction
+ * }
+ *
+ *
+ * where {@code t} is a scalar representing the distance along the ray from
+ * the origin.
+ *
+ * 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 { + + /** + * 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. + *
+ * + * @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); + +} \ No newline at end of file From b4ced13ce79eedd201ee4e488a10c80965de2aa7 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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; } /** @@ -124,11 +168,9 @@ public static Matrix4f fpsViewRH(Vector3f eye, float pitch, float yaw) { 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); + 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; } From aad5b850cb7df5b69678cce9ca1deface55cb5c6 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * Key features include: + *
+ * This interface provides methods for basic 2D graphics rendering operations + * such as drawing geometric shapes (rectangles, ovals, and lines), text + * rendering, color setting, transformations (translate, scale, rotate), and + * text metrics calculations. It serves as the foundation for implementing 2D + * rendering capabilities in a graphics pipeline. + *
+ * + *+ * Implementations of this interface are responsible for providing concrete + * rendering logic with support for transformations and state management (e.g., + * push and pop matrix operations). + *
+ */ public interface Graphics2D { - + + /** + * Retrieves the current width of the rendering context's viewport. + * + * @return The width in pixels. + */ int getWidth(); + /** + * Retrieves the current height of the rendering context's viewport. + * + * @return The height in pixels. + */ int getHeight(); - + + /** + * Sets the current drawing color using a {@link Color}. + * + * @param color The color to set for rendering operations. + */ void setColor(Color color); + /** + * Sets the current drawing color using a math-defined {@link math.Color}. + * + * @param color The math-defined color to use for rendering operations. + */ void setColor(math.Color color); - + + /** + * Sets the current drawing color using RGB integer values. + * + * @param red The red channel value (0-255). + * @param green The green channel value (0-255). + * @param blue The blue channel value (0-255). + */ void setColor(int red, int green, int blue); - + + /** + * Sets the thickness of strokes (lines) used in subsequent drawing commands. + * + * @param weight The stroke weight to apply (e.g., 1.0 for standard line + * thickness). + */ void strokeWeight(float weight); - + + /** + * Saves the current transformation matrix onto a stack for future + * restoration. + * + * This allows temporary transformations without permanently altering the + * rendering state. + */ void pushMatrix(); + /** + * Restores the last saved transformation matrix from the stack. + * + * This undoes any temporary transformations applied since the last + * pushMatrix(). + */ void popMatrix(); - + + /** + * Translates the rendering context by a specified distance in 2D space. + * + * @param x The amount to translate along the x-axis. + * @param y The amount to translate along the y-axis. + */ void translate(float x, float y); - + + /** + * Scales the rendering context by specified scaling factors along the x and y + * axes. + * + * @param sx The scaling factor along the x-axis. + * @param sy The scaling factor along the y-axis. + */ void scale(float sx, float sy); - + + /** + * Rotates the rendering context by the given angle in radians. + * + * @param angle The angle to rotate by, in radians. + */ void rotate(float angle); + /** + * Draws an unfilled rectangle at the specified coordinates with the given + * dimensions. + * + * @param x The x-coordinate of the top-left corner of the rectangle. + * @param y The y-coordinate of the top-left corner of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + */ void drawRect(float x, float y, float width, float height); + /** + * Draws a filled rectangle at the specified coordinates with the given + * dimensions. + * + * @param x The x-coordinate of the top-left corner of the rectangle. + * @param y The y-coordinate of the top-left corner of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + */ void fillRect(float x, float y, float width, float height); + /** + * Draws an unfilled oval at the specified coordinates with the given + * dimensions. + * + * @param x The x-coordinate of the top-left corner of the bounding box + * of the oval. + * @param y The y-coordinate of the top-left corner of the bounding box + * of the oval. + * @param width The width of the bounding box. + * @param height The height of the bounding box. + */ void drawOval(float x, float y, float width, float height); + /** + * Draws a filled oval at the specified coordinates with the given dimensions. + * + * @param x The x-coordinate of the top-left corner of the bounding box + * of the oval. + * @param y The y-coordinate of the top-left corner of the bounding box + * of the oval. + * @param width The width of the bounding box. + * @param height The height of the bounding box. + */ void fillOval(float x, float y, float width, float height); + /** + * Draws a line from (x1, y1) to (x2, y2). + * + * @param x1 Starting x-coordinate. + * @param y1 Starting y-coordinate. + * @param x2 Ending x-coordinate. + * @param y2 Ending y-coordinate. + */ void drawLine(float x1, float y1, float x2, float y2); + /** + * Sets the size of text to render for subsequent text rendering operations. + * + * @param size The desired text size. + */ void textSize(float size); + /** + * Retrieves the current text size used by text rendering operations. + * + * @return The current text size. + */ float getTextSize(); + /** + * Computes the width of the given text string at the current text size. + * + * @param text The text to compute the width for. + * @return The width of the rendered text. + */ float textWidth(String text); + /** + * Retrieves the ascent of text (the portion of text above the baseline). + * + * @return The ascent value of the text. + */ float textAscent(); + /** + * Retrieves the descent of text (the portion of text below the baseline). + * + * @return The descent value of the text. + */ float textDescent(); + /** + * Renders text at the given screen coordinates. + * + * @param text The text to render. + * @param x The x-coordinate to start rendering the text. + * @param y The y-coordinate to start rendering the text. + */ void text(String text, float x, float y); -} +} \ No newline at end of file From ff12ecf6071bacc13cd9217738c00b52a225a36b Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * The rectangle is defined by its top-left corner (x, y), its width, and its + * height. The "radii" parameter specifies the corner radius, which determines + * how rounded the corners appear. If the corner radius is 0, this method + * behaves like {@link #drawRect(float, float, float, float)}. + * + * @param x The x-coordinate of the top-left corner of the rectangle. + * @param y The y-coordinate of the top-left corner of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + * @param radii The corner radius for rounding the corners of the rectangle. + * A larger value results in more rounded corners. + */ + void drawRoundRect(float x, float y, float width, float height, float radii); + + /** + * Draws a filled rounded rectangle with specified position, dimensions, and + * corner radius. + * + *
+ * The rectangle is defined by its top-left corner (x, y), its width, and its
+ * height. The "radii" parameter specifies the corner radius, which determines
+ * how rounded the corners appear. If the corner radius is 0, this method
+ * behaves like {@link #fillRect(float, float, float, float)}.
+ *
+ * @param x The x-coordinate of the top-left corner of the rectangle.
+ * @param y The y-coordinate of the top-left corner of the rectangle.
+ * @param width The width of the rectangle.
+ * @param height The height of the rectangle.
+ * @param radii The corner radius for rounding the corners of the rectangle.
+ * A larger value results in more rounded corners.
+ */
+ void fillRoundRect(float x, float y, float width, float height, float radii);
+
/**
* Draws an unfilled oval at the specified coordinates with the given
* dimensions.
@@ -205,5 +243,5 @@ public interface Graphics2D {
* @param y The y-coordinate to start rendering the text.
*/
void text(String text, float x, float y);
-
+
}
\ No newline at end of file
From 888d8f31462e6955b170a31bfcef2b00eb47428f Mon Sep 17 00:00:00 2001
From: Simon Dietz
+ * A Material defines the visual appearance of a 3D object under lighting
+ * conditions. It includes attributes such as base color, ambient, diffuse, and
+ * specular light coefficients, and shininess, which together determine how
+ * light interacts with the object's surface.
+ *
+ * This class provides predefined materials for common use cases (e.g., default
+ * white, metallic, glass) and allows custom material creation with specific
+ * properties.
+ *
+ * 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 RenderComponent}.
+ *
+ * Example use cases include:
+ *
+ *
+ *
+ * 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 RenderComponent} 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 (RenderComponent 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); + } + } + + /** + * 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. + * @param+ * 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; + } + +} \ No newline at end of file From c19ad7dc68ffb0950878d5876b305a50bc979d6d Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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. + *
+ * + * @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; + } + +} From 69f8704646502e45713d0900b01834406f106c50 Mon Sep 17 00:00:00 2001 From: Simon Dietz- * A Material defines the visual appearance of a 3D object under lighting - * conditions. It includes attributes such as base color, ambient, diffuse, and - * specular light coefficients, and shininess, which together determine how - * light interacts with the object's surface. + * 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 provides predefined materials for common use cases (e.g., default - * white, metallic, glass) and allows custom material creation with specific - * properties. + * 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 { @@ -52,148 +57,186 @@ public class Material { /** * Metallic silver material with a shiny silver appearance. */ - public static final Material METALLIC_SILVER_MATERIAL = new Material( - new Color(0.75f, 0.75f, 0.75f), - new float[] { 0.2f, 0.2f, 0.2f }, - new float[] { 1.0f, 1.0f, 1.0f }, - new float[] { 1.0f, 1.0f, 1.0f }, - 50.0f); + public static final Material METALLIC_SILVER_MATERIAL = MaterialFactory + .createMetallicSilver(); /** * Metallic gold material with a shiny gold appearance. */ - public static final Material GOLDEN_METALLIC_MATERIAL = new Material( - new Color(1.0f, 0.84f, 0.0f), - new float[] { 0.3f, 0.3f, 0.3f }, - new float[] { 1.0f, 0.84f, 0.0f }, - new float[] { 1.0f, 1.0f, 0.5f }, - 100.0f); + public static final Material GOLDEN_METALLIC_MATERIAL = MaterialFactory + .createGoldenMetallic(); /** * Clear glass material with a transparent appearance. */ - public static final Material GLASS_CLEAR_MATERIAL = new Material( - new Color(0.7f, 0.9f, 1.0f), - new float[] { 0.3f, 0.3f, 0.3f }, - new float[] { 0.8f, 0.8f, 0.8f }, - new float[] { 1.0f, 1.0f, 1.0f }, 5.0f); + public static final Material GLASS_CLEAR_MATERIAL = MaterialFactory + .createGlassClear(); /** * Stone grey material with a matte grey appearance. */ - public static final Material STONE_GREY_MATERIAL = - new Material( - new Color(0.5f, 0.5f, 0.5f), - new float[] { 0.4f, 0.4f, 0.4f }, - new float[] { 0.6f, 0.6f, 0.6f }, - new float[] { 0.2f, 0.2f, 0.2f }, - 10.0f); + public static final Material STONE_GREY_MATERIAL = MaterialFactory + .createStoneGrey(); /** * Water material with a reflective blue appearance. */ - public static final Material WATER_MATERIAL = new Material( - new Color(0.0f, 0.5f, 1.0f), - new float[] { 0.1f, 0.3f, 0.5f }, - new float[] { 0.3f, 0.5f, 0.7f }, - new float[] { 0.2f, 0.2f, 0.6f }, 2.0f); + public static final Material WATER_MATERIAL = MaterialFactory.createWater(); /** * Base color for the material. */ - private Color color; + private final Color color; /** * Ambient light coefficient (R, G, B). */ - private float[] ambient; + private final float[] ambient; /** * Diffuse light coefficient (R, G, B). */ - private float[] diffuse; + private final float[] diffuse; /** - * Specular light coefficient (R, G, B). + * Specular light coefficient (R, G, B). */ - private float[] specular; + private final float[] specular; /** * Shininess factor for specular highlights. */ - private float shininess; - + private final float shininess; + /** - * Private constructor for creating a material with specific properties. + * Constructor to set the base color of the material. * - * @param color The base color of the material. - * @param ambient The ambient light coefficient (R, G, B). - * @param diffuse The diffuse light coefficient (R, G, B). - * @param specular The specular light coefficient (R, G, B). - * @param shininess The shininess factor for specular highlights. - */ - private Material(Color color, float[] ambient, float[] diffuse, - float[] specular, float shininess) { - this.color = color; - this.ambient = ambient; - this.diffuse = diffuse; - this.specular = specular; - this.shininess = shininess; + * @param color The base color of the material. + */ + public Material(Color color) { + this(new Builder().setColor(color)); } - /** - * Default constructor initializes the material with default lighting - * properties. - */ - public Material() { - this.color = new Color(1, 1, 1); - this.ambient = new float[] { 0.2f, 0.2f, 0.2f }; - this.diffuse = new float[] { 1.0f, 1.0f, 1.0f }; - this.specular = new float[] { 1.0f, 1.0f, 1.0f }; - this.shininess = 10.0f; + private Material(Builder builder) { + this.color = builder.color; + this.ambient = builder.ambient; + this.diffuse = builder.diffuse; + this.specular = builder.specular; + this.shininess = builder.shininess; } /** - * Constructor to set the base color of the material. - * - * @param color The base color of the material. + * Builder class to facilitate the creation of custom materials with specific + * lighting and shader properties. */ - public Material(Color color) { - this(); - this.color = color; + public static class Builder { + + 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; + } + + /** + * 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 given rendering context. + * Applies this material's properties to the provided rendering context. * - * @param g The {@link Graphics} instance to which the material will be - * applied. + * @param g The {@link Graphics} instance to apply this material to. */ public void apply(Graphics g) { g.setMaterial(this); } /** - * Releases the material or resets properties in the rendering context. Useful - * for unbinding shaders or clearing material-specific settings. + * 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 the material is released. + * @param g The {@link Graphics} instance from which this material will be + * unbound. */ public void release(Graphics g) { - // Logic to unbind shader or reset material-related properties + // Logic for releasing or resetting rendering context goes here } /** - * Gets the base color of the material. + * Retrieves the base color of the material. * - * @return The base {@link Color} of the material. + * @return The {@link Color} representing the base color of the material. */ public Color getColor() { return color; } /** - * Gets the ambient light coefficient of the material. + * Retrieves the ambient light coefficient of the material. * * @return An array representing the ambient light coefficient (R, G, B). */ @@ -202,7 +245,7 @@ public float[] getAmbient() { } /** - * Gets the diffuse light coefficient of the material. + * Retrieves the diffuse light coefficient of the material. * * @return An array representing the diffuse light coefficient (R, G, B). */ @@ -211,7 +254,7 @@ public float[] getDiffuse() { } /** - * Gets the specular light coefficient of the material. + * Retrieves the specular light coefficient of the material. * * @return An array representing the specular light coefficient (R, G, B). */ @@ -220,12 +263,12 @@ public float[] getSpecular() { } /** - * Gets the shininess factor of the material. + * Retrieves the shininess factor of the material. * - * @return The shininess factor for specular highlights. + * @return The shininess factor that controls specular highlights. */ public float getShininess() { return shininess; } - + } \ No newline at end of file diff --git a/src/main/java/engine/render/MaterialFactory.java b/src/main/java/engine/render/MaterialFactory.java new file mode 100644 index 00000000..0360b0a0 --- /dev/null +++ b/src/main/java/engine/render/MaterialFactory.java @@ -0,0 +1,121 @@ +package engine.render; + +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. + *
+ */ +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. + *
+ * + * @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. + *
+ * + * @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. + *
+ * + * @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. + *
+ * + * @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. + *
+ * + * @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(); + } + +} From ac48d67ac43660e1ccd1a379e65fdfd9862e9cba Mon Sep 17 00:00:00 2001 From: Simon Dietz
+ * The equation of the plane is represented as:
+ * 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.+ * 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 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.+ * This class implements the {@link RenderComponent} 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 RenderComponent { + + private ParticleEmitter emitter; + + 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; + } + + /** + * Initializes the renderer resources necessary for drawing particles. + */ + @Override + public void initialize() { + 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); + } + + /** + * 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(); + } + + /** + * 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; + } + + /** + * 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; + } + +} From 4ed26700e672d2611bc44e3e2a2a22536199d2a4 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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: + *
+ *+ * 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 initialize()}, + * {@code update()}, and {@code cleanup()} 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); + + /** + * 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); + + /** + * Initializes the component before it becomes active. + *+ * This is called once after the component is attached to a {@link SceneNode}. + * It allows the component to set up necessary resources, state, or perform + * other preparatory work. + *
+ */ + void initialize(); + + /** + * Cleans up resources and performs necessary teardown when the component is + * removed or the scene is unloaded. + *+ * This ensures no memory is leaked, threads are terminated, or other + * resources are left hanging by cleaning up internal state and releasing + * references. + *
+ */ + void cleanup(); + +} \ No newline at end of file From 0dd10798c82657d3aaf75bc90d2eae86b1ae28db Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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; + + /** + * 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 From fc281420bd735ab7281480adcfb052a78b2d1453 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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. + *
+ * + * @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) { + // Logic for dynamic transform updates if needed. + } + + @Override + public void initialize() { + // Logic for additional initialization. + } + + @Override + public void cleanup() { + // Cleanup logic if required. + } + +} \ No newline at end of file From 506e41d51a75346dbcaae743c0c68528332f8c70 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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 RenderComponent { + + /** 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); + } + + /** + * Initialization logic for this renderer. + *+ * Currently unimplemented but provided as a placeholder for any setup + * required when initializing this component in a larger system. + *
+ */ + @Override + public void initialize() { + // Placeholder for initialization logic. + } + + /** + * Cleans up resources when the renderer is no longer needed. + *+ * Currently unimplemented but can be used to clean up graphics resources, + * listeners, or other allocated resources in a larger system. + *
+ */ + @Override + public void cleanup() { + // Placeholder for cleanup logic. + } + +} \ No newline at end of file From 94ced3c4849ee8686178ee721832710022511dae Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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. + *
+ * + * @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; + + /** + * 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; + + /** + * 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; + } + + /** + * 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); + + // 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); + } + + /** + * Handles any initialization logic required before the animation starts. + *+ * Currently, this is left blank but can be implemented if preconditions need + * to be set before the animation begins. + *
+ */ + @Override + public void initialize() { + // Placeholder for potential initialization logic + } + + /** + * Handles any cleanup logic when the animation component is no longer needed. + *+ * Currently, this is left blank but can be implemented if resources need to + * be released or reset during cleanup. + *
+ */ + @Override + public void cleanup() { + // Placeholder for potential cleanup logic + } + +} \ No newline at end of file From 8b54a3fe291f6cd2d3f6a44a570ded661f84f1a5 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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; + } + + /** + * Initializes the component. Currently no initialization logic is required, + * but this is provided as a placeholder for future logic. + */ + @Override + public void initialize() { + // No additional initialization logic required at this stage. + } + + /** + * 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; + } + + /** + * Cleans up resources when the component is removed or the application is + * shutting down. Currently, there is no cleanup logic necessary, but this is + * a placeholder for extensibility. + */ + @Override + public void cleanup() { + // Cleanup logic placeholder + } + + /** + * 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; + } + +} \ No newline at end of file From 4dbd02d4b91f5d1e2c90b93aa72b806d4bf49540 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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 RenderComponent 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 From 6d97f30bc640130ab985591e902d8cde27e8eee4 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * Properties: + *
+ * 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 initialize() { + // Initialization logic if needed + } + + @Override + public void cleanup() { + // Cleanup logic if needed + } + +} \ No newline at end of file From 77a8aef82b11feea1fd0ca02cf71a31ce27ae841 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * The Geometry class is a component that encapsulates a 3D mesh and its + * associated material. It implements rendering behavior through the + * {@link RenderComponent} interface, allowing it to be drawn during the + * rendering pass of the scene graph traversal. + *
+ *+ * This class is responsible for applying the provided {@link Material} during + * rendering, ensuring proper visualization of the associated {@link Mesh3D}. + *
+ * + *+ * This method applies the material, renders the associated mesh's faces using + * the {@link Graphics} instance, and then releases the material's state + * afterward. + *
+ * + * @param g The graphics context to use for rendering the geometry. + */ + @Override + public void render(Graphics g) { + material.apply(g); + g.fillFaces(mesh); + material.release(g); + } + + /** + * Placeholder for initialization logic if needed in future development. + * Currently, no specific initialization is necessary. + */ + @Override + public void initialize() { + // Initialization logic, if needed + } + + /** + * Updates geometry's state over time, if necessary. Currently, this is a + * placeholder for potential future logic. + * + * @param tpf Time per frame, used to synchronize updates across frames. + */ + @Override + public void update(float tpf) { + // Placeholder for potential mesh state updates + } + +} \ No newline at end of file From db20f76042736794e8ee9f36d9977329d97e4340 Mon Sep 17 00:00:00 2001 From: Simon Dietz+ * 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. + *
+ * + *
+ * Key Characteristics of an ambient light:
+ * - Color: Defined by an instance of {@link math.Color},
+ * representing the light's hue.
+ * - No position: Ambient light is global, meaning it affects all parts of the
+ * scene uniformly.
+ * - 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}.
+ */
+public class AmbientLight implements Light {
+
+ /**
+ * 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 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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
diff --git a/src/main/java/engine/scene/light/LightRenderer.java b/src/main/java/engine/scene/light/LightRenderer.java
index d307b12e..4e519fef 100644
--- a/src/main/java/engine/scene/light/LightRenderer.java
+++ b/src/main/java/engine/scene/light/LightRenderer.java
@@ -1,5 +1,7 @@
package engine.scene.light;
+import workspace.ui.Graphics;
+
/**
* Interface for rendering various light sources in a 3D scene.
* @@ -12,6 +14,22 @@ */ 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. + *
+ * + * @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. *@@ -63,4 +81,17 @@ public interface LightRenderer { */ 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. + *
+ * + * @param light The ambient light source to render. Must not be null. + */ + void render(AmbientLight light); + } \ No newline at end of file diff --git a/src/main/java/engine/scene/light/LightType.java b/src/main/java/engine/scene/light/LightType.java index 50912c4d..8d641565 100644 --- a/src/main/java/engine/scene/light/LightType.java +++ b/src/main/java/engine/scene/light/LightType.java @@ -3,7 +3,7 @@ /** * Enum representing different types of lights. * - * This enum defines the three primary types of lights commonly used in 3D + * This enum defines the four primary types of lights commonly used in 3D * graphics: * *@@ -12,10 +12,13 @@ * a specific direction. * - 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 + * or position. **/ public enum LightType { - POINT, DIRECTIONAL, SPOT + POINT, DIRECTIONAL, SPOT, AMBIENT -} +} \ No newline at end of file From 06ab221ed8c9d24bb0c20e3ba1b1972638244322 Mon Sep 17 00:00:00 2001 From: Simon Dietz
+ * This method uses `System.nanoTime()` to obtain a high-precision timestamp. + * The returned value is a `float` representing the elapsed time in seconds. + *
+ * + * @return The real-time elapsed since the game started, in seconds. + */ + public float getRealtimeSinceStartup() { + return (System.nanoTime() - startTime) / 1_000_000_000.0f; + } + + /** + * Returns the real-time elapsed since the game / application started, + * measured in seconds, as a `double` value. + * + *+ * This method uses `System.nanoTime()` to obtain a high-precision timestamp. + * The returned value is a `double` representing the elapsed time in seconds, + * providing higher precision than the `float` version. + *
+ * + * @return The real-time elapsed since the game started, in seconds, as a + * `double`. + */ + public double getRealtimeSinceStartupAsDouble() { + return (System.nanoTime() - startTime) / 1_000_000_000.0; + } + /** * Returns the total number of frames that have passed since the Timer * started. @@ -187,7 +225,7 @@ public void setTimeScale(float timeScale) { public int getFrameCount() { return frameCount; } - + /** * Formats a time value in seconds into a string in the format HH:MM:SS. * From c7a0a2f2d15da22e154c99fe3eb75b3256c3b9fd Mon Sep 17 00:00:00 2001 From: Simon Dietz* The Geometry class is a component that encapsulates a 3D mesh and its * associated material. It implements rendering behavior through the - * {@link RenderComponent} interface, allowing it to be drawn during the + * {@link RenderableComponent} interface, allowing it to be drawn during the * rendering pass of the scene graph traversal. *
*@@ -25,7 +25,7 @@ * {@link Graphics} class. * */ -public class Geometry extends AbstractComponent implements RenderComponent { +public class Geometry extends AbstractComponent implements RenderableComponent { /** The 3D mesh to be rendered by this component. */ private Mesh3D mesh; diff --git a/src/main/java/engine/components/RenderComponent.java b/src/main/java/engine/components/RenderableComponent.java similarity index 96% rename from src/main/java/engine/components/RenderComponent.java rename to src/main/java/engine/components/RenderableComponent.java index 1bcd4059..b4a66416 100644 --- a/src/main/java/engine/components/RenderComponent.java +++ b/src/main/java/engine/components/RenderableComponent.java @@ -25,7 +25,7 @@ * @see Component * @see SceneNode */ -public interface RenderComponent extends Component { +public interface RenderableComponent extends Component { /** * Renders this component using the provided graphics context. diff --git a/src/main/java/engine/render/Material.java b/src/main/java/engine/render/Material.java index ff005ed9..af8206f7 100644 --- a/src/main/java/engine/render/Material.java +++ b/src/main/java/engine/render/Material.java @@ -83,6 +83,8 @@ public class Material { */ public static final Material WATER_MATERIAL = MaterialFactory.createWater(); + private boolean useLighting; + /** * Base color for the material. */ @@ -118,6 +120,7 @@ public Material(Color color) { } private Material(Builder builder) { + this.useLighting = builder.useLighting; this.color = builder.color; this.ambient = builder.ambient; this.diffuse = builder.diffuse; @@ -131,6 +134,8 @@ private Material(Builder builder) { */ 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 }; @@ -196,6 +201,11 @@ public Builder setShininess(float shininess) { return this; } + public Builder setUseLighting(boolean useLighting) { + this.useLighting = useLighting; + return this; + } + /** * Builds and returns the Material instance with the set properties. * @@ -226,6 +236,10 @@ 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. * diff --git a/src/main/java/engine/render/effects/ParticleComponent.java b/src/main/java/engine/render/effects/ParticleComponent.java index 6735c6a2..991d164a 100644 --- a/src/main/java/engine/render/effects/ParticleComponent.java +++ b/src/main/java/engine/render/effects/ParticleComponent.java @@ -1,14 +1,14 @@ package engine.render.effects; import engine.components.AbstractComponent; -import engine.components.RenderComponent; +import engine.components.RenderableComponent; import workspace.ui.Graphics; /** * A component responsible for managing and rendering particles using a * specified particle emitter and renderer. *
- * This class implements the {@link RenderComponent} interface, allowing it to + * 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}. @@ -22,7 +22,7 @@ * @author Simon Dietz */ public class ParticleComponent extends AbstractComponent - implements RenderComponent { + implements RenderableComponent { private ParticleEmitter emitter; diff --git a/src/main/java/engine/scene/Scene.java b/src/main/java/engine/scene/Scene.java index f0669283..7cf7940e 100644 --- a/src/main/java/engine/scene/Scene.java +++ b/src/main/java/engine/scene/Scene.java @@ -130,16 +130,27 @@ public void update(float deltaTime) { * the main thread for compatibility with most rendering APIs. */ public void render(Graphics g) { + + if (activeCamera != null) { +// Vector3f position = activeCamera.getTransform().getPosition(); +// g.applyMatrix(activeCamera.getProjectionMatrix()); +// g.applyMatrix(activeCamera.getViewMatrix()); + g.applyCamera(activeCamera); +// if (position != null) +// g.translate(position.x, position.y, position.z); +// System.out.println(position); + } + g.setWireframeMode(wireframeMode); renderLights(g); + synchronized (rootNodes) { - if (activeCamera != null) { - // TODO FIXME: Implement - } + for (SceneNode node : rootNodes) { node.render(g); } } + } /** diff --git a/src/main/java/engine/scene/SceneNode.java b/src/main/java/engine/scene/SceneNode.java index 80c5ef2c..5a5a6550 100644 --- a/src/main/java/engine/scene/SceneNode.java +++ b/src/main/java/engine/scene/SceneNode.java @@ -4,7 +4,7 @@ import java.util.List; import engine.components.Component; -import engine.components.RenderComponent; +import engine.components.RenderableComponent; import engine.components.Transform; import workspace.ui.Graphics; @@ -21,7 +21,7 @@ * 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 RenderComponent}. + * {@link RenderableComponent}. *
** Example use cases include: @@ -116,7 +116,7 @@ private void applyLocalTransform(Graphics g) { } /** - * Renders all associated {@link RenderComponent} instances attached to this + * Renders all associated {@link RenderableComponent} instances attached to this * node. *
* This method iterates through all render components and calls their
@@ -126,7 +126,7 @@ private void applyLocalTransform(Graphics g) {
* @param g The graphics context used for rendering.
*/
protected void renderComponents(Graphics g) {
- for (RenderComponent renderer : getRenderComponents()) {
+ for (RenderableComponent renderer : getRenderComponents()) {
renderer.render(g);
}
}
@@ -307,11 +307,11 @@ public
From 548ce453c084cf2b2191dbe041105735b18f348c Mon Sep 17 00:00:00 2001
From: Simon Dietz
+ * This method can be used when you need to restart the timer, such as for
+ * restarting the game / application or resetting the simulation state.
+ *
+ * A bounding box is often used in 3D graphics for collision detection, frustum
+ * culling, and other spatial queries.
+ *
@@ -45,10 +51,14 @@ public Ray3f(Vector3f origin, Vector3f direction) {
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.
+ *
+ * The direction vector defines the direction in which the ray travels. The
+ * vector is normalized, ensuring consistent calculations for operations like
+ * intersections.
+ *
+ * The reciprocal of the direction vector is precomputed to optimize ray-box
+ * intersection tests, where division by components of the direction vector is
+ * required.
+ *
@@ -77,10 +106,12 @@ public Vector3f getDirection() {
*
*
* where {@code t} is a scalar representing the distance along the ray from
- * the origin.
+ * 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.
*
+ * 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 Geometry class is a component that encapsulates a 3D mesh and its
- * associated material. It implements rendering behavior through the
- * {@link RenderableComponent} interface, allowing it to be drawn during the
- * rendering pass of the scene graph traversal.
- *
- * This class is responsible for applying the provided {@link Material} during
- * rendering, ensuring proper visualization of the associated {@link Mesh3D}.
- *
- * This method applies the material, renders the associated mesh's faces using
- * the {@link Graphics} instance, and then releases the material's state
- * afterward.
+ * 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.
*
- * Each component should manage its lifecycle, with {@code initialize()},
- * {@code update()}, and {@code cleanup()} methods, allowing nodes to manage
+ * Each component should manage its lifecycle, with {@code onAttach()},
+ * {@code update()}, and {@code onDetach()} methods, allowing nodes to manage
* their behavior lifecycle cleanly.
*
- * This is called once after the component is attached to a {@link SceneNode}.
- * It allows the component to set up necessary resources, state, or perform
- * other preparatory work.
+ * This allows the component to set up necessary resources, state, or perform
+ * other preparatory work specific to being added to a node.
*
* This ensures no memory is leaked, threads are terminated, or other
* resources are left hanging by cleaning up internal state and releasing
* references.
*
- * Currently unimplemented but provided as a placeholder for any setup
- * required when initializing this component in a larger system.
- *
- * Currently unimplemented but can be used to clean up graphics resources,
- * listeners, or other allocated resources in a larger system.
- *
- * Currently, this is left blank but can be implemented if preconditions need
- * to be set before the animation begins.
- *
- * Currently, this is left blank but can be implemented if resources need to
- * be released or reset during cleanup.
- *
@@ -113,12 +104,16 @@ public void update(float tpf) {
*/
private Vector3f handleInput() {
Vector3f velocity = new Vector3f();
-
+
// Check for movement inputs
- if (input.isKeyPressed(Key.W)) velocity.addLocal(0, 0, -1);
- if (input.isKeyPressed(Key.A)) velocity.addLocal(-1, 0, 0);
- if (input.isKeyPressed(Key.S)) velocity.addLocal(0, 0, 1);
- if (input.isKeyPressed(Key.D)) velocity.addLocal(1, 0, 0);
+ if (input.isKeyPressed(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) {
@@ -127,16 +122,6 @@ private Vector3f handleInput() {
return velocity;
}
- /**
- * Cleans up resources when the component is removed or the application is
- * shutting down. Currently, there is no cleanup logic necessary, but this is
- * a placeholder for extensibility.
- */
- @Override
- public void cleanup() {
- // Cleanup logic placeholder
- }
-
/**
* Retrieves the current movement speed multiplier.
*
@@ -167,4 +152,12 @@ public void setSpeed(float speed) {
this.speed = speed;
}
+ @Override
+ public void onAttach() {
+ }
+
+ @Override
+ public void onDetach() {
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/engine/components/Geometry.java b/src/main/java/engine/components/Geometry.java
index b54cb960..eb658fc6 100644
--- a/src/main/java/engine/components/Geometry.java
+++ b/src/main/java/engine/components/Geometry.java
@@ -153,24 +153,12 @@ public void update(float tpf) {
// Placeholder for potential mesh state updates
}
- /**
- * Called when the component is attached to a parent object in the scene. This
- * method is a hook for additional initialization or setup when the component
- * is added.
- */
@Override
public void onAttach() {
- // Hook for additional setup on attachment
}
- /**
- * Called when the component is detached from its parent object in the scene.
- * This method is a hook for cleanup or other actions when the component is
- * removed.
- */
@Override
public void onDetach() {
- // Hook for cleanup on detachment
}
}
\ No newline at end of file
diff --git a/src/main/java/engine/components/RotationComponent.java b/src/main/java/engine/components/RotationComponent.java
index 3fcaa873..dbdab16f 100644
--- a/src/main/java/engine/components/RotationComponent.java
+++ b/src/main/java/engine/components/RotationComponent.java
@@ -112,13 +112,11 @@ public float getAngularSpeed() {
}
@Override
- public void initialize() {
- // Initialization logic if needed
+ public void onAttach() {
}
@Override
- public void cleanup() {
- // Cleanup logic if needed
+ public void onDetach() {
}
-
+
}
\ No newline at end of file
From 770227ad48fadb871935c81fbe203d31d3f0f59d Mon Sep 17 00:00:00 2001
From: Simon Dietz
- * The bounding box is defined by the minimum and maximum extents of the
- * vertices along the X, Y, and Z axes. If there are no vertices in the mesh,
- * an empty `Bounds3` is returned.
- * The bounding box is defined by the minimum and maximum extents of the vertices along the X,
+ * Y, and Z axes. If there are no vertices in the mesh, an empty `Bounds3` is returned.
+ *
+ * @return A {@link Bounds3} object representing the calculated bounding box of the mesh. The
+ * bounding box extends from the minimum vertex coordinate to the maximum vertex coordinate.
+ */
+ public Bounds3 calculateBounds() {
+ if (vertices.isEmpty()) return new Bounds3();
+
+ Vector3f min = new Vector3f(getVertexAt(0));
+ Vector3f max = new Vector3f(getVertexAt(0));
+ Bounds3 bounds = new Bounds3();
+ for (Vector3f v : vertices) {
+ float minX = v.getX() < min.getX() ? v.getX() : min.getX();
+ float minY = v.getY() < min.getY() ? v.getY() : min.getY();
+ float minZ = v.getZ() < min.getZ() ? v.getZ() : min.getZ();
+ float maxX = v.getX() > max.getX() ? v.getX() : max.getX();
+ float maxY = v.getY() > max.getY() ? v.getY() : max.getY();
+ float maxZ = v.getZ() > max.getZ() ? v.getZ() : max.getZ();
+ min.set(minX, minY, minZ);
+ max.set(maxX, maxY, maxZ);
+ }
+ bounds.setMinMax(min, max);
+ return bounds;
+ }
+
+ public Vector3f calculateFaceNormal(Face3D face) {
+ Vector3f faceNormal = new Vector3f();
+ for (int i = 0; i < face.indices.length; i++) {
+ Vector3f currentVertex = vertices.get(face.indices[i]);
+ Vector3f nextVertex = vertices.get(face.indices[(i + 1) % face.indices.length]);
+ float x =
+ (currentVertex.getY() - nextVertex.getY()) * (currentVertex.getZ() + nextVertex.getZ());
+ float y =
+ (currentVertex.getZ() - nextVertex.getZ()) * (currentVertex.getX() + nextVertex.getX());
+ float z =
+ (currentVertex.getX() - nextVertex.getX()) * (currentVertex.getY() + nextVertex.getY());
+ faceNormal.addLocal(x, y, z);
+ }
+ return faceNormal.normalize();
+ }
+
+ public void removeDoubles(int decimalPlaces) {
+ for (Vector3f v : vertices) v.roundLocalDecimalPlaces(decimalPlaces);
+ removeDoubles();
+ }
+
+ /**
+ * Removes duplicated vertices.
+ *
+ * @deprecated Use {@link RemoveDoubleVerticesModifier} instead.
+ */
+ public void removeDoubles() {
+ new RemoveDoubleVerticesModifier().modify(this);
+ }
+
+ public Mesh3D copy() {
+ Mesh3D copy = new Mesh3D();
+ List Key features include:
*
- *
- * Key features include:
*
- * This method can be used when you need to restart the timer, such as for
- * restarting the game / application or resetting the simulation state.
- * This method can be used when you need to restart the timer, such as for restarting the game
+ * / application or resetting the simulation state.
+ */
+ public void reset() {
+ this.startTime = System.nanoTime();
+ this.lastTime = startTime;
+ this.time = 0;
+ this.totalTime = 0;
+ this.frameCount = 0;
+ this.fps = 0;
+ this.millisecondCounter = 0;
+ this.lastFrameCount = 0;
+ }
- /**
- * Returns the total elapsed time in seconds, independent of the time scale.
- *
- * @return the unscaled total elapsed time in seconds
- */
- public float getUnscaledTotalTime() {
- return totalTime / 1000.0f;
- }
+ /**
+ * Returns the total elapsed time in seconds, scaled by the current time scale.
+ *
+ * @return the scaled total elapsed time in seconds
+ */
+ public float getTotalTime() {
+ return totalTime / 1000.0f * timeScale;
+ }
- /**
- * Returns a formatted string representing the scaled total time in the format
- * HH:MM:SS.
- *
- * @return the formatted scaled total time
- */
- public String getFormattedTotalTime() {
- return formatTime(getTotalTime());
- }
+ /**
+ * Returns the total elapsed time in seconds, independent of the time scale.
+ *
+ * @return the unscaled total elapsed time in seconds
+ */
+ public float getUnscaledTotalTime() {
+ return totalTime / 1000.0f;
+ }
- /**
- * Returns a formatted string representing the unscaled total time in the
- * format HH:MM:SS.
- *
- * @return the formatted unscaled total time
- */
- public String getUnscaledFormattedTotalTime() {
- return formatTime(getUnscaledTotalTime());
- }
+ /**
+ * Returns a formatted string representing the scaled total time in the format HH:MM:SS.
+ *
+ * @return the formatted scaled total time
+ */
+ public String getFormattedTotalTime() {
+ return formatTime(getTotalTime());
+ }
- /**
- * Returns the time it took to complete the last frame in seconds, scaled by
- * the current time scale.
- *
- * @return the scaled time per frame in seconds
- */
- public float getTimePerFrame() {
- return time / 1000.0f * timeScale;
- }
+ /**
+ * Returns a formatted string representing the unscaled total time in the format HH:MM:SS.
+ *
+ * @return the formatted unscaled total time
+ */
+ public String getUnscaledFormattedTotalTime() {
+ return formatTime(getUnscaledTotalTime());
+ }
- /**
- * Returns the time it took to complete the last frame in seconds, independent
- * of the time scale.
- *
- * @return the unscaled time per frame in seconds
- */
- public float getUnscaledTimePerFrame() {
- return time / 1000.0f;
- }
+ /**
+ * Returns the time it took to complete the last frame in seconds, scaled by the current time
+ * scale.
+ *
+ * @return the scaled time per frame in seconds
+ */
+ public float getTimePerFrame() {
+ return time / 1000.0f * timeScale;
+ }
- /**
- * Returns the current time scaling factor.
- *
- * @return the time scale
- */
- public float getTimeScale() {
- return timeScale;
- }
+ /**
+ * Returns the time it took to complete the last frame in seconds, independent of the time scale.
+ *
+ * @return the unscaled time per frame in seconds
+ */
+ public float getUnscaledTimePerFrame() {
+ return time / 1000.0f;
+ }
- /**
- * Sets the time scaling factor. A value of 1.0 represents real-time, values
- * less than 1.0 slow down time, and values greater than 1.0 speed up time.
- *
- * @param timeScale the new time scaling factor
- */
- public void setTimeScale(float timeScale) {
- this.timeScale = timeScale;
- }
+ /**
+ * Returns the current time scaling factor.
+ *
+ * @return the time scale
+ */
+ public float getTimeScale() {
+ return timeScale;
+ }
- /**
- * Returns the real-time elapsed since the game / application started,
- * measured in seconds.
- *
- *
- * This method uses `System.nanoTime()` to obtain a high-precision timestamp.
- * The returned value is a `float` representing the elapsed time in seconds.
- *
- * This method uses `System.nanoTime()` to obtain a high-precision timestamp.
- * The returned value is a `double` representing the elapsed time in seconds,
- * providing higher precision than the `float` version.
- * This method uses `System.nanoTime()` to obtain a high-precision timestamp. The returned
+ * value is a `float` representing the elapsed time in seconds.
+ *
+ * @return The real-time elapsed since the game started, in seconds.
+ */
+ public float getRealtimeSinceStartup() {
+ return (System.nanoTime() - startTime) / 1_000_000_000.0f;
+ }
- /**
- * Returns the total number of frames that have passed since the Timer
- * started.
- *
- * @return the total frame count
- */
- public int getFrameCount() {
- return frameCount;
- }
+ /**
+ * Returns the real-time elapsed since the game / application started, measured in seconds, as a
+ * `double` value.
+ *
+ * This method uses `System.nanoTime()` to obtain a high-precision timestamp. The returned
+ * value is a `double` representing the elapsed time in seconds, providing higher precision than
+ * the `float` version.
+ *
+ * @return The real-time elapsed since the game started, in seconds, as a `double`.
+ */
+ public double getRealtimeSinceStartupAsDouble() {
+ return (System.nanoTime() - startTime) / 1_000_000_000.0;
+ }
- /**
- * Formats a time value in seconds into a string in the format HH:MM:SS.
- *
- * @param timeInSeconds the time in seconds to format
- * @return the formatted time string
- */
- private String formatTime(float timeInSeconds) {
- int s = (int) timeInSeconds;
- return String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, s % 60);
- }
+ /**
+ * Returns the total number of frames that have passed since the Timer started.
+ *
+ * @return the total frame count
+ */
+ public int getFrameCount() {
+ return frameCount;
+ }
- /**
- * Returns a string representation of this Timer, showing its current state.
- *
- * @return a string representation of the Timer
- */
- @Override
- public String toString() {
- return "Timer [millisecondCounter=" + millisecondCounter
- + ", lastFrameCount=" + lastFrameCount + ", fps=" + fps + ", lastTime="
- + lastTime + ", time=" + time + ", totalTime=" + totalTime
- + ", timeScale=" + timeScale + ", frameCount=" + frameCount + "]";
- }
+ /**
+ * Formats a time value in seconds into a string in the format HH:MM:SS.
+ *
+ * @param timeInSeconds the time in seconds to format
+ * @return the formatted time string
+ */
+ private String formatTime(float timeInSeconds) {
+ int s = (int) timeInSeconds;
+ return String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, s % 60);
+ }
+ /**
+ * Returns a string representation of this Timer, showing its current state.
+ *
+ * @return a string representation of the Timer
+ */
+ @Override
+ public String toString() {
+ return "Timer [millisecondCounter="
+ + millisecondCounter
+ + ", lastFrameCount="
+ + lastFrameCount
+ + ", fps="
+ + fps
+ + ", lastTime="
+ + lastTime
+ + ", time="
+ + time
+ + ", totalTime="
+ + totalTime
+ + ", timeScale="
+ + timeScale
+ + ", frameCount="
+ + frameCount
+ + "]";
+ }
}
+ *
+ * Key Features
- *
- *
+ * 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
+ * @see Bounds
*/
public class Geometry extends AbstractComponent implements RenderableComponent {
- /** The 3D mesh to be rendered by this component. */
+ /** The mesh representing the geometry of the object. */
private Mesh3D mesh;
- /** The material associated with this geometry for rendering purposes. */
+ /** The material applied to the mesh for rendering. */
private Material material;
/**
- * Constructs a Geometry component with a provided mesh and the default white
+ * 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 mesh to associate with this geometry.
+ *
+ * @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 Geometry component with a specific mesh and material.
- *
- * @param mesh The 3D mesh to associate with this geometry. Must not be
- * null.
- * @param material The material to use for rendering. Must not be null.
- * @throws IllegalArgumentException if either mesh or material is null.
+ * 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 provided mesh and material to ensure they are non-null.
- *
- * @param mesh The 3D mesh to validate.
- * @param material The material to validate.
- * @throws IllegalArgumentException if either mesh or material is null.
+ * 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) {
@@ -74,53 +81,96 @@ private void validate(Mesh3D mesh, Material material) {
}
/**
- * Cleans up resources associated with this component by nullifying references
- * to the mesh and material. This is called when the component is no longer
- * needed or during application shutdown.
+ * 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 cleanup() {
- mesh = null;
- material = null;
+ public void render(Graphics g) {
+ material.apply(g);
+ g.fillFaces(mesh);
+ material.release(g);
+ debugRenderBounds(g);
}
/**
- * Handles rendering of the mesh with its material using the provided graphics
- * context.
+ * 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.
*
* a internally. Each component is scaled
- * separately
- *
- * @return this
- */
- public Color divideLocal(float a) {
- this.r /= a;
- this.g /= a;
- this.b /= a;
- this.a /= a;
- return this;
- }
-
- /**
- * @param a
- * @return
- */
- public Color multLocal(float r, float g, float b, float a) {
- this.r *= a;
- this.g *= g;
- this.b *= b;
- this.a *= a;
- return this;
- }
-
- /**
- * Clamps the components of this color between 0.0f and 1.0f internally, and
- * returns a handle to this color for easy chaining of calls.
- *
- * @return this
- */
- public Color clampLocal() {
- r = Mathf.clamp01(r);
- g = Mathf.clamp01(g);
- b = Mathf.clamp01(b);
- a = Mathf.clamp01(a);
- return this;
- }
-
- /**
- * Sets all components of this color to 0.0f internally, and returns a handle
- * to this color for easy chaining of calls.
- *
- * @return this
- */
- public Color clearLocal() {
- r = g = b = a = 0.0f;
- return this;
- }
-
- /**
- * Returns the maximum color component value: Max(r,g,b). This method does not
- * consider the alpha component.
- *
- * @return the maximum color component
- */
- public float maxComponent() {
- return Mathf.max(new float[] { r, g, b });
- }
-
- /**
- * Returns a new float array containing the r,g,b,a components of this color
- * in that order.
- *
- * @return the components of this color as array
- */
- public float[] toArray() {
- return new float[] { r, g, b, a };
- }
-
- /**
- * Stores the r,g,b,a components in the given array. If the provided store
- * array is null a new array is created to store the components in.
- *
- * @param store the array to store the components into
- * @return store
- * @throws IndexOutOfBoundsException if store.length < 4
- */
- public float[] toArray(float[] store) {
- if (store == null)
- store = new float[4];
- store[0] = r;
- store[1] = g;
- store[2] = b;
- store[3] = a;
- return store;
- }
-
- /**
- * Sets the r,g,b,a components of this color to the specified new values.
- *
- * @param r the new red component for this color
- * @param g the new green component for this color
- * @param b the new blue component for this color
- * @param a the new alpha component for this color
- */
- public void set(float r, float g, float b, float a) {
- this.r = r;
- this.g = g;
- this.b = b;
- this.a = a;
- }
-
- /**
- * Sets the r,g,b components of this color to the specified new values. The
- * alpha component is set to 1.0f.
- *
- * @param r the new red component for this color
- * @param g the new green component for this color
- * @param b the new blue component for this color
- */
- public void set(float r, float g, float b) {
- this.r = r;
- this.g = g;
- this.b = b;
- this.a = 1.0f;
- }
-
- /**
- * Returns the red component of this color as float value (0f to 1f).
- *
- * @return the red component of this color (0f to 1f)
- */
- public float getRed() {
- return r;
- }
-
- /**
- * Returns the green component of this color as float value (0f to 1f).
- *
- * @return the green component of this color (0f to 1f)
- */
- public float getGreen() {
- return g;
- }
-
- /**
- * Returns the blue component of this color as float value (0f to 1f).
- *
- * @return the blue component of this color (0f to 1f)
- */
- public float getBlue() {
- return b;
- }
-
- /**
- * Returns the alpha component of this color as float value (0f to 1f).
- *
- * @return the alpha component of this color (0f to 1f)
- */
- public float getAlpha() {
- return a;
- }
-
- /**
- * Returns the red component of this color as an integer value (0 to 255).
- *
- * @return the red component of this color (0 to 255)
- */
- public int getRedInt() {
- return (int) (r * 255 + 0.5);
- }
-
- /**
- * Returns the green component of this color as an integer value (0 to 255).
- *
- * @return the green component of this color (0 to 255)
- */
- public int getGreenInt() {
- return (int) (g * 255 + 0.5);
- }
-
- /**
- * Returns the blue component of this color as an integer value (0 to 255).
- *
- * @return the blue component of this color (0 to 255)
- */
- public int getBlueInt() {
- return (int) (b * 255 + 0.5);
- }
-
- /**
- * Returns the alpha component of this color as an integer value (0 to 255).
- *
- * @return the alpha component of this color (0 to 255)
- */
- public int getAlphaInt() {
- return (int) (a * 255 + 0.5);
- }
-
- /**
- * Returns the RGBA value representing the color. (Bits 24-31 are alpha, 16-23
- * are red, 8-15 are green, 0-7 are blue).
- *
- * @return the RGBA value of this color as integer
- */
- public int getRGBA() {
- int r = getRedInt();
- int g = getGreenInt();
- int b = getBlueInt();
- int a = getAlphaInt();
- return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8)
- | ((b & 0xFF) << 0);
- }
-
- /**
- * Returns a unique hash code for this color object based on it's values. If
- * two colors are logically equivalent, they will return the same hash code
- * value.
- *
- * @return the hash code value of this color
- */
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Float.floatToIntBits(a);
- result = prime * result + Float.floatToIntBits(b);
- result = prime * result + Float.floatToIntBits(g);
- result = prime * result + Float.floatToIntBits(r);
- return result;
- }
-
- /**
- * Determines if this color 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;
- Color other = (Color) obj;
- return Float.floatToIntBits(r) == Float.floatToIntBits(other.r)
- && Float.floatToIntBits(g) == Float.floatToIntBits(other.g)
- && Float.floatToIntBits(b) == Float.floatToIntBits(other.b)
- && Float.floatToIntBits(a) == Float.floatToIntBits(other.a);
- }
-
- /**
- * Returns a string representation of this color.
- *
- * @return a string representation of this color
- */
- @Override
- public String toString() {
- return "Color [r=" + r + ", g=" + g + ", b=" + b + ", a=" + a + "]";
- }
-
+ /** Solid black. RGBA is (0, 0, 0, 1). */
+ public static final Color BLACK = new Color(0f, 0f, 0f, 1f);
+
+ /** Solid blue. RGBA is (0, 0, 1, 1). */
+ public static final Color BLUE = new Color(0f, 0f, 1f, 1f);
+
+ /** Completely transparent. RGBA is (0, 0, 0, 0). */
+ public static final Color CLEAR = new Color(0f, 0f, 0f, 0f);
+
+ /** Cyan. RGBA is (0, 1, 1, 1). */
+ public static final Color CYAN = new Color(0f, 1f, 1f, 1f);
+
+ /** Dark gray. RGBA is (0.25, 0.25, 0.25, 1). */
+ public static final Color DARK_GRAY = new Color(0.25f, 0.25f, 0.25f, 1.0f);
+
+ /** Gray. RGBA is (0.5, 0.5, 0.5, 1). */
+ public static final Color GRAY = new Color(0.5f, 0.5f, 0.5f, 1f);
+
+ /** Solid green. RGBA is (0, 1, 0, 1). */
+ public static final Color GREEN = new Color(0f, 1f, 0f, 1f);
+
+ /** English spelling for gray. RGBA is the same (0.5, 0.5, 0.5, 1). */
+ public static final Color GREY = GRAY;
+
+ /** Light gray. RGBA is (0.75f, 0.75f, 0.75f, 1f). */
+ public static final Color LIGHT_GRAY = new Color(0.75f, 0.75f, 0.75f, 1f);
+
+ /** Magenta. RGBA is (1, 0, 1, 1). */
+ public static final Color MAGENTA = new Color(1f, 0f, 1f, 1f);
+
+ /** Solid red. RGBA is (1, 0, 0, 1). */
+ public static final Color RED = new Color(1f, 0f, 0f, 1f);
+
+ /** Solid white. RGBA is (1, 1, 1, 1). */
+ public static final Color WHITE = new Color(1f, 1f, 1f, 1f);
+
+ /** Yellow. RGBA is (1, 1, 0, 1). */
+ public static final Color YELLOW = new Color(1f, 1f, 0f, 1f);
+
+ /** The red component of the color. */
+ private float r;
+
+ /** The green component of the color. */
+ private float g;
+
+ /** The blue component of the color. */
+ private float b;
+
+ /** The alpha component of the color. */
+ private float a;
+
+ /** Constructs a new instance of this color with r,g,b,a components set to 0. */
+ public Color() {
+ this(0, 0, 0, 0);
+ }
+
+ /**
+ * Constructs a new instance of this color with r,g,b,a components set to the values provided by
+ * color c.
+ *
+ * @param c the color to copy from
+ */
+ public Color(Color c) {
+ this(c.r, c.g, c.b, c.a);
+ }
+
+ /**
+ * Constructs a new instance of this {@link Color} with the given r,g,b components and a alpha
+ * value set to 1f.
+ *
+ * @param r the red component of this color
+ * @param g the green component of this color
+ * @param b the blue component of this color
+ */
+ public Color(float r, float g, float b) {
+ this(r, g, b, 1.0f);
+ }
+
+ /**
+ * Constructs a new instance of this {@link Color} with the given r,g,b,a components.
+ *
+ * @param r the red component of this color
+ * @param g the green component of this color
+ * @param b the blue component of this color
+ * @param a the alpha component of this color
+ */
+ public Color(float r, float g, float b, float a) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ /**
+ * Returns a new color instance with the specified integer r,g,b values.
+ *
+ * @param r the red component for the color (0 to 255)
+ * @param g the green component for the color (0 to 255)
+ * @param b the blue component for the color (0 to 255)
+ * @return the newly created color
+ */
+ public static Color getColorFromInt(int r, int g, int b) {
+ int clampR = Mathf.clampInt(r, 0, 255);
+ int clampG = Mathf.clampInt(g, 0, 255);
+ int clampB = Mathf.clampInt(b, 0, 255);
+ return new Color(clampR / 255f, clampG / 255f, clampB / 255f);
+ }
+
+ /**
+ * Adds the components of a given color to those of this color creating a new color object. Each
+ * component is added separately. If the provided color is null, an exception is thrown.
+ *
+ * @param color the color to add to this color
+ * @return the resultant color
+ */
+ public Color add(Color color) {
+ return add(color, null);
+ }
+
+ /**
+ * Adds the components of a given color to those of this color storing the result in the given
+ * result color. Each component is added separately. If the provided color c is null, an exception
+ * is thrown. If the provided result color is null, a new color is created.
+ *
+ * @param color the color to add to this color
+ * @param result the color to store the result in
+ * @return the resultant color
+ */
+ public Color add(Color color, Color result) {
+ if (result == null) result = new Color();
+ result.r = this.r + color.r;
+ result.g = this.g + color.g;
+ result.b = this.b + color.b;
+ result.a = this.a + color.a;
+ return result;
+ }
+
+ /**
+ * Adds the given r,g,b,a components to those of this color creating a new color object. Each
+ * component is added separately.
+ *
+ * @param r the red component to add
+ * @param g the green component to add
+ * @param b the blue component to add
+ * @param a the alpha component to add
+ * @return the resultant color
+ */
+ public Color add(float r, float g, float b, float a) {
+ return new Color(this.r + r, this.g + g, this.b + b, this.a + a);
+ }
+
+ /**
+ * Adds the color c to this color internally, and returns a handle to this color for easy chaining
+ * of calls. Each component is added separately.
+ *
+ * @param color the color to add to this color
+ * @return this
+ */
+ public Color addLocal(Color color) {
+ this.r += color.r;
+ this.g += color.g;
+ this.b += color.b;
+ this.a += color.a;
+ return this;
+ }
+
+ /**
+ * Adds the provided components to this color internally, and returns a handle to this color for
+ * easy chaining of calls.
+ *
+ * @param r the red component to add
+ * @param g the green component to add
+ * @param b the blue component to add
+ * @param a the alpha component to add
+ * @return this
+ */
+ public Color addLocal(float r, float g, float b, float a) {
+ this.r += r;
+ this.g += g;
+ this.b += b;
+ this.a += a;
+ return this;
+ }
+
+ /**
+ * Subtracts the components of a given color from those of this color creating a new color object.
+ * Each component is subtracted separately. If the provided color is null, an exception is thrown.
+ *
+ * @param color the color to subtract from this color
+ * @return the resultant color
+ */
+ public Color subtract(Color color) {
+ return subtract(color, null);
+ }
+
+ /**
+ * Subtracts the values of a given color from those of this color storing the result in the given
+ * color. Each component is subtracted separately. If the provided color c is null, an exception
+ * is thrown. If the provided result color is null, a new color is created.
+ *
+ * @param color the color to subtract from this color
+ * @param result the color to store the result in
+ * @return the resultant color
+ */
+ public Color subtract(Color color, Color result) {
+ if (result == null) result = new Color();
+ result.r = this.r - color.r;
+ result.g = this.g - color.g;
+ result.b = this.b - color.b;
+ result.a = this.a - color.a;
+ return result;
+ }
+
+ /**
+ * * Subtracts the given r,g,b,a components from those of this color creating a new color object.
+ * Each component is subtracted separately.
+ *
+ * @param r the red component to subtract
+ * @param g the green component to subtract
+ * @param b the blue component to subtract
+ * @param a the alpha component to subtract
+ * @return the resultant color
+ */
+ public Color subtract(float r, float g, float b, float a) {
+ return new Color(this.r - r, this.g - g, this.b - b, this.a - a);
+ }
+
+ /**
+ * Subtracts the color c from this color internally, and returns a handle to this color for easy
+ * chaining of calls. Each component is subtracted separately.
+ *
+ * @param color the color to subtract from this color
+ * @return this
+ */
+ public Color subtractLocal(Color color) {
+ this.r -= color.r;
+ this.g -= color.g;
+ this.b -= color.b;
+ this.a -= color.a;
+ return this;
+ }
+
+ /**
+ * Subtracts the provided components from this color internally, and returns a handle to this
+ * color for easy chaining of calls.
+ *
+ * @param r the red component to subtract
+ * @param g the green component to subtract
+ * @param b the blue component to subtract
+ * @param a the alpha component to subtract
+ * @return this
+ */
+ public Color subtractLocal(float r, float g, float b, float a) {
+ this.r -= r;
+ this.g -= g;
+ this.b -= b;
+ this.a -= a;
+ return this;
+ }
+
+ /**
+ * Divides this color by a internally. Each component is scaled separately
+ *
+ * @return this
+ */
+ public Color divideLocal(float a) {
+ this.r /= a;
+ this.g /= a;
+ this.b /= a;
+ this.a /= a;
+ return this;
+ }
+
+ /**
+ * @param a
+ * @return
+ */
+ public Color multLocal(float r, float g, float b, float a) {
+ this.r *= a;
+ this.g *= g;
+ this.b *= b;
+ this.a *= a;
+ return this;
+ }
+
+ /**
+ * Clamps the components of this color between 0.0f and 1.0f internally, and returns a handle to
+ * this color for easy chaining of calls.
+ *
+ * @return this
+ */
+ public Color clampLocal() {
+ r = Mathf.clamp01(r);
+ g = Mathf.clamp01(g);
+ b = Mathf.clamp01(b);
+ a = Mathf.clamp01(a);
+ return this;
+ }
+
+ /**
+ * Sets all components of this color to 0.0f internally, and returns a handle to this color for
+ * easy chaining of calls.
+ *
+ * @return this
+ */
+ public Color clearLocal() {
+ r = g = b = a = 0.0f;
+ return this;
+ }
+
+ /**
+ * Returns the maximum color component value: Max(r,g,b). This method does not consider the alpha
+ * component.
+ *
+ * @return the maximum color component
+ */
+ public float maxComponent() {
+ return Mathf.max(new float[] {r, g, b});
+ }
+
+ /**
+ * Returns a new float array containing the r,g,b,a components of this color in that order.
+ *
+ * @return the components of this color as array
+ */
+ public float[] toArray() {
+ return new float[] {r, g, b, a};
+ }
+
+ /**
+ * Stores the r,g,b,a components in the given array. If the provided store array is null a new
+ * array is created to store the components in.
+ *
+ * @param store the array to store the components into
+ * @return store
+ * @throws IndexOutOfBoundsException if store.length < 4
+ */
+ public float[] toArray(float[] store) {
+ if (store == null) store = new float[4];
+ store[0] = r;
+ store[1] = g;
+ store[2] = b;
+ store[3] = a;
+ return store;
+ }
+
+ /**
+ * Sets the r,g,b,a components of this color to the specified new values.
+ *
+ * @param r the new red component for this color
+ * @param g the new green component for this color
+ * @param b the new blue component for this color
+ * @param a the new alpha component for this color
+ */
+ public void set(float r, float g, float b, float a) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ /**
+ * Sets the r,g,b components of this color to the specified new values. The alpha component is set
+ * to 1.0f.
+ *
+ * @param r the new red component for this color
+ * @param g the new green component for this color
+ * @param b the new blue component for this color
+ */
+ public void set(float r, float g, float b) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = 1.0f;
+ }
+
+ /**
+ * Returns the red component of this color as float value (0f to 1f).
+ *
+ * @return the red component of this color (0f to 1f)
+ */
+ public float getRed() {
+ return r;
+ }
+
+ /**
+ * Returns the green component of this color as float value (0f to 1f).
+ *
+ * @return the green component of this color (0f to 1f)
+ */
+ public float getGreen() {
+ return g;
+ }
+
+ /**
+ * Returns the blue component of this color as float value (0f to 1f).
+ *
+ * @return the blue component of this color (0f to 1f)
+ */
+ public float getBlue() {
+ return b;
+ }
+
+ /**
+ * Returns the alpha component of this color as float value (0f to 1f).
+ *
+ * @return the alpha component of this color (0f to 1f)
+ */
+ public float getAlpha() {
+ return a;
+ }
+
+ /**
+ * Returns the red component of this color as an integer value (0 to 255).
+ *
+ * @return the red component of this color (0 to 255)
+ */
+ public int getRedInt() {
+ return (int) (r * 255 + 0.5);
+ }
+
+ /**
+ * Returns the green component of this color as an integer value (0 to 255).
+ *
+ * @return the green component of this color (0 to 255)
+ */
+ public int getGreenInt() {
+ return (int) (g * 255 + 0.5);
+ }
+
+ /**
+ * Returns the blue component of this color as an integer value (0 to 255).
+ *
+ * @return the blue component of this color (0 to 255)
+ */
+ public int getBlueInt() {
+ return (int) (b * 255 + 0.5);
+ }
+
+ /**
+ * Returns the alpha component of this color as an integer value (0 to 255).
+ *
+ * @return the alpha component of this color (0 to 255)
+ */
+ public int getAlphaInt() {
+ return (int) (a * 255 + 0.5);
+ }
+
+ /**
+ * Returns the RGBA value representing the color. (Bits 24-31 are alpha, 16-23 are red, 8-15 are
+ * green, 0-7 are blue).
+ *
+ * @return the RGBA value of this color as integer
+ */
+ public int getRGBA() {
+ int r = getRedInt();
+ int g = getGreenInt();
+ int b = getBlueInt();
+ int a = getAlphaInt();
+ return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) << 0);
+ }
+
+ /**
+ * Returns a unique hash code for this color object based on it's values. If two colors are
+ * logically equivalent, they will return the same hash code value.
+ *
+ * @return the hash code value of this color
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Float.floatToIntBits(a);
+ result = prime * result + Float.floatToIntBits(b);
+ result = prime * result + Float.floatToIntBits(g);
+ result = prime * result + Float.floatToIntBits(r);
+ return result;
+ }
+
+ /**
+ * Determines if this color 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;
+ Color other = (Color) obj;
+ return Float.floatToIntBits(r) == Float.floatToIntBits(other.r)
+ && Float.floatToIntBits(g) == Float.floatToIntBits(other.g)
+ && Float.floatToIntBits(b) == Float.floatToIntBits(other.b)
+ && Float.floatToIntBits(a) == Float.floatToIntBits(other.a);
+ }
+
+ /**
+ * Returns a string representation of this color.
+ *
+ * @return a string representation of this color
+ */
+ @Override
+ public String toString() {
+ return "Color [r=" + r + ", g=" + g + ", b=" + b + ", a=" + a + "]";
+ }
}
From 41b2e22da4b234afc815571656c0dc02d83ccdbe Mon Sep 17 00:00:00 2001
From: Simon Dietz
- *
- *
- * This class is designed to be updated on every frame of an application or
- * game.
+ *
+ * This class is designed to be updated on every frame of an application or game.
*/
public class Timer {
- /**
- * Real world time when the timer started.
- */
- private long startTime;
-
- /** The last recorded time in nanoseconds. */
- private long lastTime;
+ /** Real world time when the timer started. */
+ private long startTime;
- /** The time in milliseconds taken for the last frame. */
- private float time;
+ /** The last recorded time in nanoseconds. */
+ private long lastTime;
- /** Accumulates milliseconds for FPS calculation. */
- private long millisecondCounter;
+ /** The time in milliseconds taken for the last frame. */
+ private float time;
- /** The frame count during the last FPS update. */
- private int lastFrameCount;
+ /** Accumulates milliseconds for FPS calculation. */
+ private long millisecondCounter;
- /** The calculated frames per second (FPS). */
- private int fps;
+ /** The frame count during the last FPS update. */
+ private int lastFrameCount;
- /** Total elapsed time in milliseconds. */
- private long totalTime;
+ /** The calculated frames per second (FPS). */
+ private int fps;
- /** Scaling factor for time (default is 1.0 for real-time). */
- private float timeScale;
+ /** Total elapsed time in milliseconds. */
+ private long totalTime;
- /** Total number of frames since the Timer started. */
- private int frameCount;
+ /** Scaling factor for time (default is 1.0 for real-time). */
+ private float timeScale;
- /**
- * Constructs a {@code Timer} with a default time scale of 1.0.
- */
- public Timer() {
- this.startTime = System.nanoTime();
- this.lastTime = System.nanoTime();
- this.time = 0;
- this.totalTime = 0;
- this.timeScale = 1f;
- this.frameCount = 0;
- }
+ /** Total number of frames since the Timer started. */
+ private int frameCount;
- /**
- * Constructs a {@code Timer} with the specified initial time scale.
- *
- * @param initialTimeScale the initial time scaling factor
- */
- public Timer(float initialTimeScale) {
- this.timeScale = initialTimeScale;
- }
+ /** Constructs a {@code Timer} with a default time scale of 1.0. */
+ public Timer() {
+ this.startTime = System.nanoTime();
+ this.lastTime = System.nanoTime();
+ this.time = 0;
+ this.totalTime = 0;
+ this.timeScale = 1f;
+ this.frameCount = 0;
+ }
- /**
- * Returns the current frames per second (FPS).
- *
- * @return the frames per second
- */
- public float getFrameRate() {
- return fps;
- }
+ /**
+ * Constructs a {@code Timer} with the specified initial time scale.
+ *
+ * @param initialTimeScale the initial time scaling factor
+ */
+ public Timer(float initialTimeScale) {
+ this.timeScale = initialTimeScale;
+ }
- /**
- * Updates the FPS calculation based on the accumulated milliseconds.
- */
- private void updateFPS() {
- millisecondCounter += time;
- if (millisecondCounter >= 1000) {
- millisecondCounter = 0;
- fps = frameCount - lastFrameCount;
- lastFrameCount = frameCount;
- }
- }
+ /**
+ * Returns the current frames per second (FPS).
+ *
+ * @return the frames per second
+ */
+ public float getFrameRate() {
+ return fps;
+ }
- /**
- * Updates the Timer. This method must be called once per frame to ensure
- * accurate time tracking.
- */
- public void update() {
- long currentTime = System.nanoTime();
- time = (currentTime - lastTime) / 1_000_000.0f; // Convert to milliseconds
- lastTime = currentTime;
- totalTime += time;
- frameCount++;
- updateFPS();
- }
+ /** Updates the FPS calculation based on the accumulated milliseconds. */
+ private void updateFPS() {
+ millisecondCounter += time;
+ if (millisecondCounter >= 1000) {
+ millisecondCounter = 0;
+ fps = frameCount - lastFrameCount;
+ lastFrameCount = frameCount;
+ }
+ }
- /**
- * Resets the {@code Timer} to its initial state, clearing all accumulated
- * time and frame count values. This includes resetting the following:
- *
- *
- *
+ *
+ *
+ *