/* * 3D Engine * triangle.c: * Based on pikuma.com 3D software renderer in C * Copyright (c) 2021 986-Studio. All rights reserved. * * Created by Manoƫl Trapier on 04/03/2021. */ #include #include void drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour) { drawLine(x0, y0, x1, y1, colour); drawLine(x1, y1, x2, y2, colour); drawLine(x2, y2, x0, y0, colour); } /* ----------------------------------- Filled triangles ----------------------------------- */ /* This function expect Point 0 to be the top, 1 to be the bottom left, 2 to be the bottom right */ static void drawFillBottomFlatTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour) { int i; int32_t deltaXL = x1 - x0; int32_t deltaXR = x2 - x0; int32_t deltaY = y1 - y0; int32_t sideLength = abs(deltaY); double incrementXL = deltaXL / (double)sideLength; double incrementXR = deltaXR / (double)sideLength; double incrementY = deltaY / (double)sideLength; double currentXL = x0; double currentXR = x0; double currentY = y0; for(i = 0; i < sideLength; i++) { drawHLine(round(currentXL), round(currentY), round(currentXR), colour); currentXL += incrementXL; currentXR += incrementXR; currentY += incrementY; } } /* This function expect Point 2 to be the bottom, 0 to be the top left, 1 to be the top right */ static void drawFillTopFlatTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour) { int i; int32_t deltaXL = x0 - x2; int32_t deltaXR = x1 - x2; int32_t deltaY = y0 - y2; int32_t sideLength = abs(deltaY); if (sideLength == 0) { return; } double incrementXL = deltaXL / (double)sideLength; double incrementXR = deltaXR / (double)sideLength; double incrementY = deltaY / (double)sideLength; double currentXL = x2; double currentXR = x2; double currentY = y2; for(i = 0; i <= sideLength; i++) { drawHLine(round(currentXL), round(currentY), round(currentXR), colour); currentXL += incrementXL; currentXR += incrementXR; currentY += incrementY; } } void drawFilledTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour) { int32_t My, Mx; if (y0 > y1) { intSwap(&x0, &x1); intSwap(&y0, &y1); } if (y1 > y2) { intSwap(&x1, &x2); intSwap(&y1, &y2); } if (y0 > y1) { intSwap(&x0, &x1); intSwap(&y0, &y1); } /* Determine the mid intersection and point */ My = y1; Mx = x0 + (double)((x2 - x0) * (y1 - y0)) / (double)(y2 - y0); /* Fill top */ if (y0 != y1) { drawFillBottomFlatTriangle(x0, y0, x1, y1, Mx, My, colour); } /* Fill bottom */ if (y1 != y2) { drawFillTopFlatTriangle(x1, y1, Mx, My, x2, y2, colour); } } /* ----------------------------------- Textured triangles ----------------------------------- */ void drawTextureTriangle(struct triangle_t *t) { int32_t x, y; if (t->points[0].y > t->points[1].y) { doubleSwap(&t->points[0].x, &t->points[1].x); doubleSwap(&t->points[0].y, &t->points[1].y); doubleSwap(&t->textureCoordinates[0].u, &t->textureCoordinates[1].u); doubleSwap(&t->textureCoordinates[0].v, &t->textureCoordinates[1].v); } if (t->points[1].y > t->points[2].y) { doubleSwap(&t->points[1].x, &t->points[2].x); doubleSwap(&t->points[1].y, &t->points[2].y); doubleSwap(&t->textureCoordinates[1].u, &t->textureCoordinates[2].u); doubleSwap(&t->textureCoordinates[1].v, &t->textureCoordinates[2].v); } if (t->points[0].y > t->points[1].y) { doubleSwap(&t->points[0].x, &t->points[1].x); doubleSwap(&t->points[0].y, &t->points[1].y); doubleSwap(&t->textureCoordinates[0].u, &t->textureCoordinates[1].u); doubleSwap(&t->textureCoordinates[0].v, &t->textureCoordinates[1].v); } /* Render the top part */ double inverseSlope1 = 0; double inverseSlope2 = 0; int32_t x0 = t->points[0].x, y0 = t->points[0].y; int32_t x1 = t->points[1].x, y1 = t->points[1].y; int32_t x2 = t->points[2].x, y2 = t->points[2].y; if ((y1 - y0) != 0) { inverseSlope1 = (double)(x1 - x0) / abs(y1 - y0); } if ((y2 - y0) != 0) { inverseSlope2 = (double)(x2 - x0) / abs(y2 - y0); } if ((y1 - y0) != 0) { for (y = y0 ; y <= y1 ; y++) { int32_t xStart = x1 + (y - y1) * inverseSlope1; int32_t xEnd = x0 + (y - y0) * inverseSlope2; if (xEnd < xStart) { intSwap(&xStart, &xEnd); } for (x = xStart ; x <= xEnd ; x++) { drawPixel(x, y, (x%2 == 0)?0xFFFF00FF:0xFF000000); } } } /* Render the bottom part */ inverseSlope1 = 0; if ((y2 - y1) != 0) { inverseSlope1 = (double)(x2 - x1) / abs(y2 - y1); } if ((y2 - y1) != 0) { for (y = y1 ; y <= y2 ; y++) { int32_t xStart = x2 + (y - y2) * inverseSlope1; int32_t xEnd = x0 + (y - y0) * inverseSlope2; if (xEnd < xStart) { intSwap(&xStart, &xEnd); } for (x = xStart ; x <= xEnd ; x++) { drawPixel(x, y, (x%2 == 0)?0xFFFF00FF:0xFF000000); } } } } /* ---- Utility ---- */ int compareTrianglesZOrder(const void *p1, const void *p2) { triangle_t *t1 = (struct triangle_t *)p1; triangle_t *t2 = (struct triangle_t *)p2; if (t1->averageDepth > t2->averageDepth) { return -1; } else if (t1->averageDepth < t2->averageDepth) { return 1; } return 0; }