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*/
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: " + 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("---- Translation vector = " + position.getTranslation());
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-camera coordinate transformation.
094               final Model model1 = Model2Camera.model2camera(position);
095
096               logVertexList("1. Camera     ", model1);
097
098               // 2. Apply the Camera's projection transformation.
099               final Model model2 = Projection.project(model1,
100                                                       scene.camera);
101
102               logVertexList("2. Projected  ", model2);
103
104               // 3. Apply the image-plane to pixel-plane transformation.
105               final Model model3 = Viewport.imagePlane2pixelPlane(model2,
106                                                                   vp);
107
108               logVertexList("3. Pixel-plane", model3);
109               logPrimitiveList("3. Pixel-plane", model3);
110               logColorList("3. Pixel-plane", model3);
111
112               // 4. Rasterize and clip every visible primitive into pixels.
113               Rasterize.rasterize(model3, vp);
114
115               logMessage("====== End model: "
116                                  + position.getModel().name + " ======");
117            }
118            else
119            {
120               logMessage("====== Hidden model: "
121                                  + position.getModel().name + " ======");
122            }
123
124            logMessage("==== End position: " + position.name + " ====");
125         }
126         else
127         {
128            logMessage("==== Hidden position: " + position.name + " ====");
129         }
130      }
131      logMessage("== End Rendering of Scene ==");
132   }
133
134
135
136   // Private default constructor to enforce noninstantiable class.
137   // See Item 4 in "Effective Java", 3rd Ed, Joshua Bloch.
138   private Pipeline() {
139      throw new AssertionError();
140   }
141}