001/*
002 * Renderer 7. The MIT License.
003 * Copyright (c) 2022 rlkraft@pnw.edu
004 * See LICENSE for details.
005*/
006
007package renderer.pipeline;
008
009import renderer.scene.*;
010import renderer.scene.primitives.*;
011import static renderer.pipeline.PipelineLogger.*;
012
013import java.util.List;
014import java.util.ArrayList;
015import java.util.Optional;
016
017/**
018   Clip in camera space any {@link Primitive} that crosses
019   the camera's near clipping plane {@code z = -near}.
020*/
021public class NearClip
022{
023   public static boolean doNearClipping = true;
024   public static boolean debug = false;
025
026   /**
027      Start with a {@link Model} that contains {@link Primitive}s
028      that have been transformed into camera space.
029   <p>
030      If a transformed {@link Primitive} crosses the camera's
031      near plane, then replace that {@link Primitive}, in the
032      {@link Model}'s list of primitives, with one that has been
033      clipped so that it lies completely in the far side of the
034      camera's near plane (the side of the near plane away from
035      the camera).
036   <p>
037      If a transformed {@link Primitive} is completely in the
038      camera side of the near plane, then drop that
039      {@link Primitive} from the {@link Model}'s list of primitives.
040   <p>
041      Return a {@link Model} for which every {@link Primitive} is
042      completely on the far side of the camera's near plane.
043
044      @param model   {@link Model} containing {@link Primitive}s transformed into camera space
045      @param camera  {@link Camera} that determines the near clipping plane
046      @return a {@link Model} containing {@link Primitive}s clipped to the camera's near plane
047   */
048   public static Model clip(final Model model, final Camera camera)
049   {
050      if (! doNearClipping)
051      {
052         return model;
053      }
054
055      // Replace the model's list of colors with a shallow copy.
056      final Model model2 =  new Model(model.vertexList,
057                                      model.primitiveList,
058                                      new ArrayList<>(model.colorList),
059                                      model.name,
060                                      model.visible);
061
062      final List<Primitive> newPrimitiveList = new ArrayList<>();
063
064      for (final Primitive p : model2.primitiveList)
065      {
066         logPrimitive("3. Near_Clipping", model2, p);
067
068         final Optional<Primitive> p_clipped =
069                    (p instanceof LineSegment)
070                      ? NearClip_Line.clip(model2, (LineSegment)p, camera)
071                      : NearClip_Point.clip(model2, (Point)p, camera);
072
073         if ( p_clipped.isPresent() )
074         {
075            // Keep the primitives that are visible.
076            newPrimitiveList.add( p_clipped.get() );
077            logPrimitive("3. Near_Clipped (accept)", model2, p_clipped.get());
078         }
079         else
080         {
081            // Discard the primitives that are not visible.
082            logPrimitive("3. Near_Clipped (reject)", model2, p);
083         }
084      }
085
086      // Replace the model's original list of line segments
087      // with the list of clipped line segments.
088      return new Model(model2.vertexList,  // has been updated with clipped vertices
089                       newPrimitiveList,   // clipped primitives
090                       model2.colorList,   // has been updated with interpolated colors
091                       model2.name,
092                       model2.visible);
093   }
094
095
096
097   // Private default constructor to enforce noninstantiable class.
098   // See Item 4 in "Effective Java", 3rd Ed, Joshua Bloch.
099   private NearClip() {
100      throw new AssertionError();
101   }
102}