001/*
002 * Renderer 9. 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.util.CheckModel;
011import renderer.framebuffer.*;
012import static renderer.pipeline.PipelineLogger.*;
013
014import java.awt.Color;
015
016/**
017   This renderer takes as its input a {@link Scene} data structure
018   and a {@link FrameBuffer.Viewport} within a {@link FrameBuffer}
019   data structure. This renderer mutates the {@link FrameBuffer.Viewport}
020   so that it is filled in with the rendered image of the geometric
021   scene represented by the {@link Scene} object.
022<p>
023   This implements our seventh rendering pipeline. It converts the
024   transformation stage {@link Model2World} to use {@link Matrix}
025   transformations instead of just using {@link Vector} translations.
026   There are still seven pipeline stages.
027*/
028public final class Pipeline
029{
030   // Mostly for compatibility with renderers 1 through 3.
031   public static Color DEFAULT_COLOR = Color.white;
032
033   /**
034      Mutate the {@link FrameBuffer}'s default {@link FrameBuffer.Viewport}
035      so that it holds the rendered image of the {@link Scene} object.
036
037      @param scene  {@link Scene} object to render
038      @param fb     {@link FrameBuffer} to hold rendered image of the {@link Scene}
039   */
040   public static void render(final Scene scene, final FrameBuffer fb)
041   {
042      render(scene, fb.vp); // render into the default viewport
043   }
044
045
046   /**
047      Mutate the {@link FrameBuffer}'s given {@link FrameBuffer.Viewport}
048      so that it holds the rendered image of the {@link Scene} object.
049
050      @param scene  {@link Scene} object to render
051      @param vp     {@link FrameBuffer.Viewport} to hold rendered image of the {@link Scene}
052   */
053   public static void render(final Scene scene, final FrameBuffer.Viewport vp)
054   {
055      PipelineLogger.debugScene = scene.debug;
056
057      logMessage("\n== Begin Rendering of Scene (Pipeline 1): " + scene.name + " ==");
058
059      logMessage("-- Current Camera:\n" + scene.camera);
060
061      // For every Position in the Scene, render the Position's Model.
062      for (final Position position : scene.positionList)
063      {
064         PipelineLogger.debugPosition = position.debug;
065
066         if ( position.visible )
067         {
068            logMessage("==== Render position: " + position.name + " ====");
069
070            logMessage("---- Transformation matrix = " + position.getMatrix());
071
072            if ( position.getModel().visible )
073            {
074               logMessage("====== Render model: "
075                                  + position.getModel().name + " ======");
076
077               CheckModel.check(position.getModel());
078
079               // Mostly for compatibility with renderers 1 through 3.
080               if (  position.getModel().colorList.isEmpty()
081                 && !position.getModel().vertexList.isEmpty())
082               {
083                  for (int i = 0; i < position.getModel().vertexList.size(); ++i)
084                  {
085                     position.getModel().addColor( DEFAULT_COLOR );
086                  }
087                  System.err.println("***WARNING: Added default color to model: "
088                                    + position.getModel().name + ".");
089               }
090
091               logVertexList("0. Model        ", position.getModel());
092
093               // 1. Apply the Position's model-to-world coordinate transformation.
094               final Model model1 = Model2World.model2world(position);
095
096               logVertexList("1. World        ", model1);
097
098               // 2. Apply the Camera's world-to-view coordinate transformation.
099               final Model model2 = World2View.world2view(model1,
100                                                          scene.camera);
101
102               logVertexList("2. View         ", model2);
103
104               // 3. Apply the Camera's normalizing view-to-camera coordinate transformation.
105               final Model model3 = View2Camera.view2camera(model2,
106                                                            scene.camera);
107
108               logVertexList("3. Camera       ", model3);
109               logColorList("3. Camera       ", model3);
110               logPrimitiveList("3. Camera       ", model3);
111
112               // 4. Clip primitives to the camera's near plane.
113               final Model model4 = NearClip.clip(model3,
114                                                  scene.camera);
115
116               logVertexList("4. Near_Clipped", model4);
117               logColorList("4. Near_Clipped", model4);
118               logPrimitiveList("4. Near_Clipped", model4);
119
120               // 5. Apply the Camera's projection transformation.
121               final Model model5 = Projection.project(model4,
122                                                       scene.camera);
123
124               logVertexList("5. Projected   ", model5);
125
126               // 6. Clip primitives to the camera's view rectangle.
127               final Model model6 = Clip.clip(model5);
128
129               logVertexList("6. Clipped     ", model6);
130               logColorList("6. Clipped     ", model6);
131               logPrimitiveList("6. Clipped     ", model6);
132
133               // 7. Rasterize every visible primitive into pixels.
134               Rasterize.rasterize(model6, vp);
135
136               logMessage("====== End model: "
137                                  + position.getModel().name + " ======");
138            }
139            else
140            {
141               logMessage("====== Hidden model: "
142                                  + position.getModel().name + " ======");
143            }
144
145            logMessage("==== End position: " + position.name + " ====");
146         }
147         else
148         {
149            logMessage("==== Hidden position: " + position.name + " ====");
150         }
151      }
152      logMessage("== End Rendering of Scene ==");
153   }
154
155
156
157   // Private default constructor to enforce noninstantiable class.
158   // See Item 4 in "Effective Java", 3rd Ed, Joshua Bloch.
159   private Pipeline() {
160      throw new AssertionError();
161   }
162}