001/*
002 * Renderer 4. 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 third rendering pipeline. This pipeline can
024   process the colors stored in each {@link Model}. This renderer
025   has the same four pipeline stages but {@link Vertex} colors
026   are now interpolated to pixels by the raterization stage.
027<p>
028   This second version of the rendering pipeline does each stage
029   of the pipeline on the entire scene before it moves on to the
030   next stage. So each stage of the pipeline produces a new Scene
031   object that is the transformation of the Scene object from the
032   previous stage.
033*/
034public final class Pipeline2
035{
036   // Mostly for compatibility with renderers 1 through 3.
037   public static Color DEFAULT_COLOR = Color.white;
038
039   // Make the three intermediate Scene objects
040   // available for special processing.
041   public static Scene scene1 = null; // Will hold the result of stage 1.
042   public static Scene scene2 = null; // Will hold the result of stage 2.
043   public static Scene scene3 = null; // Will hold the result of stage 3.
044
045   /**
046      Mutate the {@link FrameBuffer}'s default {@link FrameBuffer.Viewport}
047      so that it holds the rendered image of the {@link Scene} object.
048
049      @param scene  {@link Scene} object to render
050      @param fb     {@link FrameBuffer} to hold rendered image of the {@link Scene}
051   */
052   public static void render(final Scene scene, final FrameBuffer fb)
053   {
054      render(scene, fb.vp); // render into the default viewport
055   }
056
057
058   /**
059      Mutate the {@link FrameBuffer}'s given {@link FrameBuffer.Viewport}
060      so that it holds the rendered image of the {@link Scene} object.
061
062      @param scene  {@link Scene} object to render
063      @param vp     {@link FrameBuffer.Viewport} to hold rendered image of the {@link Scene}
064   */
065   public static void render(final Scene scene, final FrameBuffer.Viewport vp)
066   {
067      PipelineLogger.debugScene = scene.debug;
068
069      logMessage("\n== Begin Rendering of Scene (Pipeline 2): " + scene.name + " =");
070
071      logMessage("-- Current Camera:\n" + scene.camera);
072
073      // 1. Apply each Position's model-to-camera coordinate transformation.
074      scene1 = new Scene(scene.name, scene.camera);
075
076      logMessage("=== 1. Begin model-to-camera transformation of Scene ====");
077      for (final Position position : scene.positionList)
078      {
079         PipelineLogger.debugPosition = position.debug;
080
081         if ( position.visible )
082         {
083            logMessage("===== 1. Render position: " + position.name + " ====");
084
085            logMessage("----- Translation vector = " + position.getTranslation());
086
087            if ( position.getModel().visible )
088            {
089               logMessage("===== 1. Model-to-camera transform of: "
090                                 + position.getModel().name + " ====");
091
092               CheckModel.check(position.getModel());
093
094               // Mostly for compatibility with renderers 1 through 3.
095               if (  position.getModel().colorList.isEmpty()
096                 && !position.getModel().vertexList.isEmpty())
097               {
098                  for (int i = 0; i < position.getModel().vertexList.size(); ++i)
099                  {
100                     position.getModel().addColor( DEFAULT_COLOR );
101                  }
102                  System.err.println("***WARNING: Added default color to model: "
103                                    + position.getModel().name + ".");
104               }
105
106               logVertexList("0. Model    ", position.getModel());
107
108               final Model tempModel = Model2Camera.model2camera(position);
109
110               logVertexList("1. Camera     ", tempModel);
111
112               scene1.addPosition( new Position(tempModel) );
113
114               logMessage("===== 1. End Model: "
115                                 + tempModel.name + " ====");
116            }
117            else
118            {
119               logMessage("===== 1. Hidden model: "
120                                 + position.getModel().name + " ====");
121            }
122
123            logMessage("===== 1. End position: "
124                              + position.name + " ====");
125         }
126         else
127         {
128            logMessage("===== 1. Hidden position: "
129                              + position.name + " ====");
130         }
131      }
132      logMessage("=== 1. End model-to-camera transformation of Scene ====");
133
134
135      // 2. Apply the Camera's projection transformation.
136      scene2 = new Scene(scene.name, scene.camera);
137
138      logMessage("=== 2. Begin projection transformation of Scene ====");
139      for (final Position position : scene1.positionList)
140      {
141         logMessage("===== 2. Project model: "
142                           + position.getModel().name + " ====");
143
144         final Model tempModel = Projection.project(position.getModel(),
145                                                    scene.camera);
146
147         logVertexList("2. Projected", tempModel);
148         logPrimitiveList("2. Projected  ", tempModel);
149
150         scene2.addPosition( new Position(tempModel) );
151
152         logMessage("===== 2. End Model: " + tempModel.name + " ====");
153      }
154      logMessage("=== 2. End projection transformation of Scene ====");
155
156
157      // 3. Apply the image-plane to pixel-plane transformation.
158      scene3 = new Scene(scene.name, scene.camera);
159
160      logMessage("=== 3. Begin image-plane to pixel-plane transformation of Scene ====");
161      for (final Position position : scene2.positionList)
162      {
163         logMessage("===== 3. Transform model: "
164                           + position.getModel().name + " ====");
165
166         final Model tempModel = Viewport.imagePlane2pixelPlane(position.getModel(),
167                                                                vp);
168
169         logVertexList("3. Pixel-plane", tempModel);
170         logPrimitiveList("3. Pixel-plane  ", tempModel);
171
172         scene3.addPosition( new Position(tempModel) );
173
174         logMessage("===== 3. End Model: " + tempModel.name + " ====");
175      }
176      logMessage("=== 3. End image-plane to pixel-plane transformation of Scene ====");
177
178
179      // 4. Rasterize and clip every visible primitive into pixels.
180      logMessage("=== 4. Begin rasterization of Scene ====");
181      for (final Position position : scene3.positionList)
182      {
183         logMessage("===== 4. Rasterize model: "
184                           + position.getModel().name + " ====");
185
186         Rasterize.rasterize(position.getModel(), vp);
187
188         logMessage("===== 4. End Model: "
189                           + position.getModel().name + " ====");
190      }
191      logMessage("=== 4. End rasterization of Scene ====");
192
193      logMessage("== End Rendering of Scene (Pipeline 2) ==");
194   }
195
196
197
198   // Private default constructor to enforce noninstantiable class.
199   // See Item 4 in "Effective Java", 3rd Ed, Joshua Bloch.
200   private Pipeline2() {
201      throw new AssertionError();
202   }
203}