001/*
002
003*/
004
005package renderer.pipeline;
006import  renderer.scene.*;
007import  renderer.framebuffer.*;
008
009import java.util.List;
010
011/**
012   Transform each {@link Vertex} of a {@link Model} from
013   projected camera coordinates (i.e., image plane
014   coordinates) to viewport coordinates.
015<p>
016   The image plane contains a view rectangle with
017   <pre>{@code
018        -1 <= x <= 1,
019        -1 <= y <= 1,
020   }</pre>
021   The view rectangle has the important job of determining
022   what part of the scene is visible to our renderer.
023<p>
024   In the previous pipeline stage, every vertex from the model was projected onto the image plane. We want only that part of each projected line segment contained in the view rectangle to be visible to our renderer and rasterized into the framebuffer's viewport. To prepare for the rasterization step, we need to convert each vertex's x and y coordinates
025
026
027<pre>{@code
028                                                                        (w+1/2,h+1/2)
029        y-axis                       +----------------------------------------+
030          |                          |                                        |
031          |  (+1,+1)                 |                                        |
032    +-----|-----+                    |                                        |
033    |     |     |                    |                                        |
034    |     |     |                    |                                        |
035----------+----------- x-axis        |                                        |
036    |     |     |                    |                                        |
037    |     |     |                    |                                        |
038    +-----|-----+                    |                                        |
039 (-1,-1)  |                          |                                        |
040          |                          |                                        |
041                                     |                                        |
042    View Rectangle                   +----------------------------------------+
043  (in the view plane)            (1/2,1/2)
044                                                    Logical Viewport
045}</pre>
046
047
048
049Notice how the view rectangle is a square but the logical viewport is a reactangle (though it may be a square).
050
051The view rectangle always has -1.0 <= x <= 1.0 and -1.0 <= y <= 1.0.
052The logical viewport always has 0.5 <= x <= w+0.5 and 0.5 <= y <= h+0.5 where w and h are the width and height of the FrameBuffer's current viewport.
053
054If the aspect ratio, w/h, of the logical (and FrameBuffer) viewport is not 1, then the image in the view rectangle will be distorted when it is transformed to the logical viewport. (NOTE: Starting with Renderer 7,  the aspect ratio of the viewport will usually match the aspect ratio of the renderer's view volume, so the distortion caused by the viewport transformation will undo the distortion caused by the normalization transformation. If the view volume and viewport aspect ratios do not match, then the final image in the framebuffer's viewport will be a distortion of the image in the view volume's view rectangle.).
055
056
057
058   For each {@link Vertex} object in each {@link Model} object,
059   transform the {@link Vertex} object from image plane coordinates
060   to viewport coordinates so that the view rectangle in the image
061   plane with
062   <pre>{@code
063        -1 <= x <= 1,
064        -1 <= y <= 1,
065   }</pre>
066   transforms into a viewport where
067   <pre>{@code
068       0.5 <= x < w + 0.5,
069       0.5 <= y < h + 0.5,
070   }</pre>
071   where
072   <pre>{@code
073         w = number of horizontal pixels in the viewport,
074         h = number of vertical pixels in the viewport.
075   }</pre>
076<p>
077   The goal of this transformation is to put a logical pixel with
078   integer coordinates at the center of each square physical pixel.
079   The logical pixel with integer coordinates (m, n) represents the
080   square pixel with
081   <pre>{@code
082     m - 0.5 <= x < m + 0.5,
083     n - 0.5 <= y < n + 0.5.
084   }</pre>
085   Notice that logical pixel integer coordinates (m.n) have
086   <pre>{@code
087     1 <= m <= w
088     1 <= n <= h.
089   }</pre>
090<p>
091   Let us derive the formulas for the viewport transformation (we will
092   derive the x-coordinate formula; the y-coordinate formula is similar).
093<p>
094   Let x_p denote an x-coordinate in the image plane and let x_vp denote
095   an x-coordinate in the viewport. If a vertex is on the left edge of
096   the view rectangle (with x_p = -1), then it should be transformed to
097   the left edge of the viewport (with x_vp = 0.5). And if a vertex is on
098   the right edge of the view rectangle (with x_p = 1), then it should be
099   transformed to the right edge of the viewport (with x_vp =  w + 0.5).
100   These two facts are all we need to know to find the linear function
101   for the transformation of the x-coordinate.
102<p>
103   We need to calculate the slope m and intercept b of a linear function
104   <pre>{@code
105             x_vp = m * x_p + b
106   }</pre>
107   that converts image plane coordinates into viewport coordinates. We know,
108   from what we said above about the left and right edges of the view
109   rectangle, that
110   <pre>{@code
111              0.5 = (m * -1) + b,
112          w + 0.5 = (m *  1) + b.
113   }</pre>
114   If we add these last two equations together we get
115   <pre>{@code
116            w + 1 = 2*b
117   }</pre>
118   or
119   <pre>{@code
120            b = (w + 1)/2.
121   }</pre>
122   If we use b to solve for m we have
123   <pre>{@code
124              0.5 = (m * -1) + (w + 1)/2
125                1 = -2*m + w + 1
126              2*m = w
127                m = w/2.
128   }</pre>
129   So the linear transformation of the x-coordinate is
130   <pre>{@code
131          x_vp = (w/2) * x_p + (w+1)/2
132               = 0.5 + w/2 * (x_p + 1).
133   }</pre>
134   The equivalent formula for the y-coordinate is
135   <pre>{@code
136          y_vp = 0.5 + h/2 * (y_p + 1).
137   }</pre>
138*/
139public class Viewport
140{
141   /**
142      For each {@link LineSegment}, transform each {@link Vertex}
143      from image plane coordinates to viewport coordinates.
144
145      @param ls    {@link LineSegment} to transform into viewport coordinates
146      @param done  boolean array that keeps track of which vertices have been transformed
147      @param fb    {@link FrameBuffer} that holds the current viewport
148   */
149   public static void viewport(LineSegment ls, boolean[] done, FrameBuffer fb)
150   {
151      // Get the viewport dimensions.
152      int w = fb.getWidthVP();
153      int h = fb.getHeightVP();
154
155      // Transform the line segment's endpoints from
156      // image plane coordinates to viewport coordinates.
157      for (int i = 0; i <= 1; i++)
158      {
159         // Transform only those vertices that
160         // have not already been transformed.
161         int index = ls.index[i];
162         if (! done[index])
163         {
164            done[index] = true;
165            Vertex v = ls.model.vertexList.get( index );
166            v.x = 0.5 + w/2.001 * (v.x + 1); //x_vp = 0.5 + w/2 * (x_p+1)
167            v.y = 0.5 + h/2.001 * (v.y + 1); //y_vp = 0.5 + h/2 * (y_p+1)
168            // NOTE: Notice the 2.001 fudge factor in the last two equations.
169            // This is explained on page 142 of
170            //    "Jim Blinn's Corner: A Trip Down The Graphics Pipeline"
171            //     by Jim Blinn, 1996, Morgan Kaufmann Publishers.
172         }
173      }
174   }
175}