public void DrawTriangle(T2DTriangle Triangle) { Point ScreenPoint1 = new Point(Triangle.Corner1.X + HalfWidth, HalfHeight - Triangle.Corner1.Y); Point ScreenPoint2 = new Point(Triangle.Corner2.X + HalfWidth, HalfHeight - Triangle.Corner2.Y); Point ScreenPoint3 = new Point(Triangle.Corner3.X + HalfWidth, HalfHeight - Triangle.Corner3.Y); VScreenCanvas.DrawLine(PenForWireFrame, ScreenPoint1, ScreenPoint2); VScreenCanvas.DrawLine(PenForWireFrame, ScreenPoint2, ScreenPoint3); VScreenCanvas.DrawLine(PenForWireFrame, ScreenPoint3, ScreenPoint1); }
// Draw a filled triangle on a given rendering context public static void DrawFilledTriangle(T2DTriangle Triangle, CRenderContext Context, Color FillColor, float M, float N, float K) { // Scan the list to find the top and bottom of the polygon int MinIndexL = 0; int MaxIndex = 0; int MinPoint_Y = Triangle.Corner1.Y; int MaxPoint_Y = MinPoint_Y; int CurrentIndex, PreviousIndex; // Create a temporary array for triangle corner points Point[] Points = new Point[3] { Triangle.Corner1, Triangle.Corner2, Triangle.Corner3 }; for (int i = 1; i < 3; i++) { if (Points[i].Y < MinPoint_Y) { // new top MinPoint_Y = Points[i].Y; MinIndexL = i; } else if (Points[i].Y > MaxPoint_Y) { // new bottom MaxPoint_Y = Points[i].Y; MaxIndex = i; } } if (MinPoint_Y == MaxPoint_Y) { // Triangle is 0-height return; } // Scan in ascending order to find the last top-edge point int MinIndexR = MinIndexL; while (Points[MinIndexR].Y == MinPoint_Y) { MinIndexR = IndexForward(MinIndexR); } // back up to last top-edge point MinIndexR = IndexBackward(MinIndexR); // Now scan in descending order to find the first top-edge point while (Points[MinIndexL].Y == MinPoint_Y) { MinIndexL = IndexBackward(MinIndexL); } // back up to first top-edge point MinIndexL = IndexForward(MinIndexL); // Figure out which direction through the vertex list from the top // vertex is the left edge and which is the right} int LeftEdgeDir = -1; // {assume left edge runs down thru vertex list int TopIsFlat; if (Points[MinIndexL].X != Points[MinIndexR].X) { TopIsFlat = 1; } else { TopIsFlat = 0; } // If the top is flat, just see which of the ends is leftmost if (TopIsFlat == 1) { if (Points[MinIndexL].X > Points[MinIndexR].X) { LeftEdgeDir = 1; // left edge runs up through vertex list int Temp = MinIndexL; // swap the indices so MinIndexL MinIndexL = MinIndexR; //points to the start of the left MinIndexR = Temp; // edge, similarly for MinIndexR } } else { // Point to the downward end of the first line of each of the // two edges down from the top} int NextIndex = MinIndexR; NextIndex = IndexForward(NextIndex); PreviousIndex = MinIndexL; PreviousIndex = IndexBackward(PreviousIndex); // Calculate X and Y lengths from the top vertex to the end of // the first line down each edge; use those to compare slopes // and see which line is leftmost int DeltaXN = Points[NextIndex].X - Points[MinIndexL].X; int DeltaYN = Points[NextIndex].Y - Points[MinIndexL].Y; int DeltaXP = Points[PreviousIndex].X - Points[MinIndexL].X; int DeltaYP = Points[PreviousIndex].Y - Points[MinIndexL].Y; if ((DeltaXN * DeltaYP - DeltaYN * DeltaXP) < 0) { LeftEdgeDir = 1; // left edge runs up through vertex list int Temp = MinIndexL; // swap the indices so MinIndexL MinIndexL = MinIndexR; // points to the start of the left MinIndexR = Temp; // edge, similarly for MinIndexR } } // Set the # of scan lines in the polygon, skipping the bottom edge // and also skipping the top vertex if the top isn't flat because // in that case the top vertex has a right edge component, and set // the top scan line to draw, which is likewise the second line of // the polygon unless the top is flat} int HLineListLength = MaxPoint_Y - MinPoint_Y - 1 + TopIsFlat; if (HLineListLength <= 0) { // there's nothing to draw, so we're done return; } THLine[] HLineList = new THLine[HLineListLength]; int HLineListYStart = MinPoint_Y + 1 - TopIsFlat; // Scan the left edge and store the boundary points in the list // Initial pointer for storing scan converted left-edge coords int EdgePointPtr = 0; // Start from the top of the left edge CurrentIndex = MinIndexL; PreviousIndex = MinIndexL; // Skip the first point of the first line unless the top is flat; // if the top isn't flat, the top vertex is exactly on a right // edge and isn't drawn} int SkipFirst = 1 - TopIsFlat; // Scan convert each line in the left edge from top to bottom do { CurrentIndex = IndexMove(CurrentIndex, LeftEdgeDir, 3); ScanEdge(Points[PreviousIndex].X, Points[PreviousIndex].Y, Points[CurrentIndex].X, Points[CurrentIndex].Y, 1, SkipFirst, ref EdgePointPtr, HLineList, Context.HalfHeight); PreviousIndex = CurrentIndex; SkipFirst = 0; // scan convert the first point from now on } while(CurrentIndex != MaxIndex); // Scan the right edge and store the boundary points in the list EdgePointPtr = 0; CurrentIndex = MinIndexR; PreviousIndex = MinIndexR; SkipFirst = 1 - TopIsFlat; // Scan convert the right edge, top to bottom. X coordinates are // adjusted 1 to the left, effectively causing scan conversion of // the nearest points to the left of but not exactly on the edge} do { CurrentIndex = IndexMove(CurrentIndex, -LeftEdgeDir, 3); ScanEdge(Points[PreviousIndex].X - 1, Points[PreviousIndex].Y, Points[CurrentIndex].X - 1, Points[CurrentIndex].Y, 0, SkipFirst, ref EdgePointPtr, HLineList, Context.HalfHeight); PreviousIndex = CurrentIndex; SkipFirst = 0; // scan convert the first point from now on } while(CurrentIndex != MaxIndex); // {Draw the line list representing the scan converted polygon} DrawHorizontalLineList(Context, HLineList, HLineListYStart, FillColor.ToArgb(), M, N, K); }
// Perform rendering on a specific context public void Render(CRenderContext Context) { if (!Visible) { return; } // Calculate transform matrix TMat4x4 Mat = GetTransformMatrix(Context); // Calculate the inverse matrix TMat4x4 InverseMat = Mat.FindInverseMat(); // Clear translation effect InverseMat.ClearTranslation(); // Calculate the viewer position in object coordinates TVertex ViewDirection = Context.GetViewDirection(); TVertex ViewerPosInObjCoords = InverseMat.MulVertex(ViewDirection); float PerspectiveFactor = Context.GetPerspectiveFactor(); Color IlluminatedFaceColor; foreach (TFace3D Face in FaceTable) { bool FaceVisible; float FaceDotProd = 0; if (!Context.IsBackfaceCullingMode()) { // No backface culling, the face is always visible FaceVisible = true; } else { // Do backface culling FaceDotProd = Math3D.DotProduct(ViewerPosInObjCoords, Face.Normal); // Perspective mode if (Context.IsPerspectiveMode()) { // Remember if the face is visible FaceVisible = (FaceDotProd > THRESHOLD_FOR_CULLING); } else { FaceVisible = (FaceDotProd > 0); } } if (FaceVisible) { // Get current face vertices and transform to world coordinates TVertex[] FaceVertex = new TVertex[3] { Mat.MulVertex(VertexTable[Face.AIndex]), Mat.MulVertex(VertexTable[Face.BIndex]), Mat.MulVertex(VertexTable[Face.CIndex]) }; Point[] ScreenCoords = new Point[3]; if (TestForBackClipping(FaceVertex[0], FaceVertex[1], FaceVertex[2])) { bool PreventFaceDraw = false; // Perspective mode if (Context.IsPerspectiveMode()) { // Transform the from world coordinates to screen coordinates for (int i = 0; i < 3; i++) { if (Math.Abs(FaceVertex[i].Z) < POLYGON_Z_MIN_VALUE) { PreventFaceDraw = true; break; } ScreenCoords[i].X = (int)(FaceVertex[i].X * PerspectiveFactor / FaceVertex[i].Z); ScreenCoords[i].Y = (int)(FaceVertex[i].Y * PerspectiveFactor / FaceVertex[i].Z); if ((Math.Abs(ScreenCoords[i].X) > POLYGON_COORD_MAX_VALUE) || (Math.Abs(ScreenCoords[i].Y) > POLYGON_COORD_MAX_VALUE)) { PreventFaceDraw = true; break; } } } else // Orthogonal projection { PreventFaceDraw = false; // Transform the from world coordinates to screen coordinates for (int i = 0; i < 3; i++) { ScreenCoords[i].X = (int)(FaceVertex[i].X / ORTHO_PROJECTION_SCALING); ScreenCoords[i].Y = (int)(FaceVertex[i].Y / ORTHO_PROJECTION_SCALING); } } if (!PreventFaceDraw) { T2DTriangle ScreenTriangle = new T2DTriangle(ScreenCoords[0], ScreenCoords[1], ScreenCoords[2]); if (Context.IsWireFrameMode()) { Context.DrawTriangle(ScreenTriangle); } else // Filled triangles mode { float LightLevel = FaceDotProd + MIN_LIGHT_LEVEL; // Clip to maximum light level if (LightLevel > 1.0) { LightLevel = 1.0f; } // Calculate face color int ARGBColor = ClipColorChn(Face.Color.R, LightLevel); ARGBColor |= ClipColorChn(Face.Color.G, LightLevel) << 8; ARGBColor |= ClipColorChn(Face.Color.B, LightLevel) << 16; // Set maximum alpha channel value ARGBColor = (int)((uint)ARGBColor | 0xff000000); IlluminatedFaceColor = Color.FromArgb(ARGBColor); // Calculate factors for the polygon filling routine TVertex FaceNormal = Math3D.CalcFaceNormal(FaceVertex[0], FaceVertex[1], FaceVertex[2]); float M, N, K; CalculatePolygonFactors(FaceNormal, FaceVertex[1], PerspectiveFactor, out M, out N, out K); TriangleFiller.DrawFilledTriangle(ScreenTriangle, Context, IlluminatedFaceColor, M, N, K); } } } } } RenderChildObjects(Context); }