ArtifactForms/MeshLibCore

View on GitHub
src/main/java/engine/components/Geometry.java

Summary

Maintainability
A
0 mins
Test Coverage
package engine.components;

import engine.render.Material;
import math.Bounds;
import math.Color;
import mesh.Mesh3D;
import mesh.util.MeshBoundsCalculator;
import workspace.ui.Graphics;

/**
 * The {@code Geometry} class represents a 3D object in a scene with a mesh and material applied to
 * it. It is responsible for rendering the mesh and applying the appropriate material to it. The
 * class also provides access to the mesh's bounding box, which is useful for purposes like culling,
 * spatial partitioning, and debugging.
 *
 * <p>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 mesh representing the geometry of the object. */
  private Mesh3D mesh;

  /** The material applied to the mesh for rendering. */
  private Material material;

  /** The bounding box of the mesh used for culling, spatial partitioning, and debugging. */
  private Bounds bounds;

  /**
   * Constructs a {@code Geometry} with the specified mesh and a default material.
   *
   * @param mesh The {@link Mesh3D} object representing the geometry of the object.
   * @throws IllegalArgumentException If the mesh is {@code null}.
   */
  public Geometry(Mesh3D mesh) {
    this(mesh, Material.DEFAULT_WHITE);
  }

  /**
   * Constructs a {@code Geometry} with the specified mesh and material.
   *
   * @param mesh The {@link Mesh3D} object representing the geometry of the object.
   * @param material The {@link Material} to be applied to the mesh.
   * @throws IllegalArgumentException If the mesh or material is {@code null}.
   */
  public Geometry(Mesh3D mesh, Material material) {
    validate(mesh, material);
    this.mesh = mesh;
    this.material = material;
    this.bounds = MeshBoundsCalculator.calculateBounds(mesh);
  }

  /**
   * Validates the mesh and material to ensure they are not {@code null}.
   *
   * @param mesh The {@link Mesh3D} object to validate.
   * @param material The {@link Material} to validate.
   * @throws IllegalArgumentException If the mesh or material is {@code null}.
   */
  private void validate(Mesh3D mesh, Material material) {
    if (mesh == null) {
      throw new IllegalArgumentException("Mesh cannot be null.");
    }
    if (material == null) {
      throw new IllegalArgumentException("Material cannot be null.");
    }
  }

  /**
   * Renders the geometry by applying the material and drawing the mesh using the specified graphics
   * context.
   *
   * @param g The {@link Graphics} context used for rendering.
   */
  @Override
  public void render(Graphics g) {
    material.apply(g);
    g.fillFaces(mesh);
    material.release(g);
    debugRenderBounds(g);
  }

  /**
   * Debugs the rendering by drawing the bounding box of the mesh using the specified graphics
   * context. The bounding box is rendered in red to help visualize the mesh's extents. This method
   * can be used for debugging purposes to ensure the mesh is properly positioned and scaled in the
   * scene.
   *
   * <p>Beyond debugging, the bounding box is useful for spatial partitioning techniques like
   * frustum culling, as well as for determining the overall size and position of the mesh in the 3D
   * world.
   *
   * @param g The {@link Graphics} context used for rendering the debug bounding box.
   */
  public void debugRenderBounds(Graphics g) {
    if (bounds == null) {
      return;
    }

    g.setColor(Color.RED);

    // Extract corner points for readability
    float minX = bounds.getMin().x;
    float minY = bounds.getMin().y;
    float minZ = bounds.getMin().z;
    float maxX = bounds.getMax().x;
    float maxY = bounds.getMax().y;
    float maxZ = bounds.getMax().z;

    // Draw lines for each edge of the bounding box
    g.drawLine(minX, minY, minZ, maxX, minY, minZ);
    g.drawLine(minX, minY, minZ, minX, maxY, minZ);
    g.drawLine(minX, minY, minZ, minX, minY, maxZ);

    g.drawLine(maxX, maxY, maxZ, minX, maxY, maxZ);
    g.drawLine(maxX, maxY, maxZ, maxX, minY, maxZ);
    g.drawLine(maxX, maxY, maxZ, maxX, maxY, minZ);

    g.drawLine(minX, maxY, minZ, maxX, maxY, minZ);
    g.drawLine(maxX, minY, minZ, maxX, maxY, minZ);
    g.drawLine(maxX, minY, minZ, maxX, minY, maxZ);

    g.drawLine(minX, maxY, maxZ, minX, minY, maxZ);
    g.drawLine(maxX, minY, maxZ, minX, minY, maxZ);
    g.drawLine(minX, maxY, maxZ, minX, maxY, minZ);
  }

  /**
   * Updates the state of the geometry. This method is a placeholder for potential updates to the
   * mesh state over time.
   *
   * @param tpf The time per frame used for the update (in seconds).
   */
  @Override
  public void update(float tpf) {
    // Placeholder for potential mesh state updates
  }

  @Override
  public void onAttach() {}

  @Override
  public void onDetach() {}
}