private static void Swap(ref VertexProj v1, ref VertexProj v2) { var temp = v2; v2 = v1; v1 = temp; }
// drawing line between 2 points from left to right // papb -> pcpd // pa, pb, pc, pd must then be sorted before private static void ProcessScanLine( RenderState state, int currentY, ref VertexProj va, ref VertexProj vb, ref VertexProj vc, ref VertexProj vd, Texture texture) { Vector3fProj pa = va.Coordinates; Vector3fProj pb = vb.Coordinates; Vector3fProj pc = vc.Coordinates; Vector3fProj pd = vd.Coordinates; // Thanks to current Y, we can compute the gradient to compute others values like // the starting X (sx) and ending X (ex) to draw between // if pa.Y == pb.Y or pc.Y == pd.Y, gradient is forced to 1 var gradient1 = Math.Abs(pa.Y - pb.Y) > 0.0001 ? ((currentY - pa.Y) * 1.0f / (pb.Y - pa.Y)) : 1; var gradient2 = Math.Abs(pc.Y - pd.Y) > 0.0001 ? ((currentY - pc.Y) * 1.0f / (pd.Y - pc.Y)) : 1; int sx = (int)Interpolate(pa.X, pb.X, gradient1); int ex = (int)Interpolate(pc.X, pd.X, gradient2); if (ex < sx) { return; } // starting Z & ending Z float z1 = Interpolate(pa.Z, pb.Z, gradient1); float z2 = Interpolate(pc.Z, pd.Z, gradient2); var sqDistA = state.Camera.Position.SqDistBetween(ref va.WorldCoordinates); var sqDistB = state.Camera.Position.SqDistBetween(ref vb.WorldCoordinates); var sqDistC = state.Camera.Position.SqDistBetween(ref vc.WorldCoordinates); var sqDistD = state.Camera.Position.SqDistBetween(ref vd.WorldCoordinates); var sSqD = Interpolate(sqDistA, sqDistB, gradient1); var eSqD = Interpolate(sqDistC, sqDistD, gradient2); // Interpolating normals on Y var snx = Interpolate(va.Normal.X, vb.Normal.X, gradient1); var enx = Interpolate(vc.Normal.X, vd.Normal.X, gradient2); var sny = Interpolate(va.Normal.Y, vb.Normal.Y, gradient1); var eny = Interpolate(vc.Normal.Y, vd.Normal.Y, gradient2); var snz = Interpolate(va.Normal.Z, vb.Normal.Z, gradient1); var enz = Interpolate(vc.Normal.Z, vd.Normal.Z, gradient2); float su = 0; float eu = 0; float sv = 0; float ev = 0; // Interpolating texture coordinates on Y if (texture != null) { su = Interpolate(va.TextureCoordinates.X, vb.TextureCoordinates.X, gradient1); eu = Interpolate(vc.TextureCoordinates.X, vd.TextureCoordinates.X, gradient2); sv = Interpolate(va.TextureCoordinates.Y, vb.TextureCoordinates.Y, gradient1); ev = Interpolate(vc.TextureCoordinates.Y, vd.TextureCoordinates.Y, gradient2); } // drawing a line from left (sx) to right (ex), but only for what is visible on screen. for (int x = Math.Max(sx, 0); x < Math.Min(ex, state.Width); x++) { float gradient = (x - sx) / (float)(ex - sx); // Interpolating Z, normal and texture coordinates on X var z = Interpolate(z1, z2, gradient); var nx = Interpolate(snx, enx, gradient); var ny = Interpolate(sny, eny, gradient); var nz = Interpolate(snz, enz, gradient); var u = (Interpolate(su, eu, gradient)); var v = (Interpolate(sv, ev, gradient)); var sqD = Interpolate(sSqD, eSqD, gradient); // changing the native color value using the cosine of the angle // between the light vector and the normal vector // and the texture color MyColor textureColor = new MyColor(); if (texture != null) { texture.GetPixel(u, v, ref textureColor); } else { textureColor = MyColor.White; } state.PutPixel(x, currentY, z, textureColor, nx, ny, nz, u, v, sqD); } }
private static void DrawTriangle( RenderState state, ref VertexProj v1, ref VertexProj v2, ref VertexProj v3, Texture texture, ref Vector3f buffv) { // Sorting the points in order to always have this order on screen p1, p2 & p3 // with p1 always up (thus having the Y the lowest possible to be near the top screen) // then p2 between p1 & p3 if (v1.Coordinates.Y > v2.Coordinates.Y) { Swap(ref v1, ref v2); } if (v2.Coordinates.Y > v3.Coordinates.Y) { Swap(ref v2, ref v3); } if (v1.Coordinates.Y > v2.Coordinates.Y) { Swap(ref v1, ref v2); } // computing lines' directions // http://en.wikipedia.org/wiki/Slope // Computing slopes Vector3fProj p1 = v1.Coordinates; Vector3fProj p2 = v2.Coordinates; Vector3fProj p3 = v3.Coordinates; // When the slope is zero, it doesn't give good // information. Then have to check manually // // P1 P1 // First | \ Second / | // branch | P2 branch P2 | // is for | / is for \ | // P3 P3 // However , when dP1P2 == null // P1--P2 P2--P1 // First | / Second \ | // branch | / branch \ | // is for | / is for \ | // P3 P3 // And dP1P3 cant be null without DP1P2 null first. bool useFirst; if (p1.Y == p2.Y) { useFirst = p1.X < p2.X; } else { float invSlopeP1P2 = (p2.X - p1.X) * 1.0f / (p2.Y - p1.Y); float invSlopeP1P3 = (p3.X - p1.X) * 1.0f / (p3.Y - p1.Y); useFirst = invSlopeP1P2 > invSlopeP1P3; } for (var y = Math.Max(0, p1.Y); y <= Math.Min(state.Height, p3.Y); y++) { if (useFirst) { if (y < p2.Y) { ProcessScanLine(state, y, ref v1, ref v3, ref v1, ref v2, texture); } else { ProcessScanLine(state, y, ref v1, ref v3, ref v2, ref v3, texture); } } else { if (y < p2.Y) { ProcessScanLine(state, y, ref v1, ref v2, ref v1, ref v3, texture); } else { ProcessScanLine(state, y, ref v2, ref v3, ref v1, ref v3, texture); } } } }