123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- /*
- * 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 <stdint.h>
- #include <stdbool.h>
- #include <display.h>
- #include <triangle.h>
- #include <math.h>
- bool doPerpectiveCorrection = true;
- static vec3_t barycentricWeights(vec2_t a, vec2_t b, vec2_t c, vec2_t p)
- {
- vec2_t ab = vec2SubVectors(b, a);
- vec2_t bc = vec2SubVectors(c, b);
- vec2_t ac = vec2SubVectors(c, a);
- vec2_t ap = vec2SubVectors(p, a);
- vec2_t bp = vec2SubVectors(p, b);
- vec3_t ret;
- double areaTriangleABC = (ab.x * ac.y) - (ab.y * ac.x);
- double alpha = ((bc.x * bp.y) - (bp.x * bc.y)) / areaTriangleABC;
- double beta = ((ap.x * ac.y) - (ac.x * ap.y)) / areaTriangleABC;
- double gamma = 1 - alpha - beta;
- ret.x = alpha;
- ret.y = beta;
- ret.z = gamma;
- return ret;
- }
- 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 ----------------------------------- */
- static void drawZPixel(int32_t x, int32_t y, vec4_t a, vec4_t b, vec4_t c, colour_t colour)
- {
- vec2_t pointP = { x, y };
- vec2_t a2 = vec2FromVec4(a);
- vec2_t b2 = vec2FromVec4(b);
- vec2_t c2 = vec2FromVec4(c);
- if ((x < 0) || (y < 0) || (x > windowWidth) ||(y > windowHeight))
- {
- return;
- }
- vec3_t weights = barycentricWeights(a2, b2, c2, pointP);
- double alpha = weights.x;
- double beta = weights.y;
- double gamma = weights.z;
- double interpolatedReciprocalW;
- interpolatedReciprocalW = (1 / a.w) * alpha + (1 / b.w) * beta + (1 / c.w) * gamma;
- /* Inverse to get logical W invrementing going further to us */
- interpolatedReciprocalW = 1 - interpolatedReciprocalW;
- if (interpolatedReciprocalW < zBuffer[(windowWidth * y) + x])
- {
- drawPixel(x, y, colour);
- /* Update Z Buffer */
- zBuffer[(windowWidth * y) + x] = interpolatedReciprocalW;
- }
- }
- void drawFilledTriangle(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->points[0].z, &t->points[1].z); doubleSwap(&t->points[0].w, &t->points[1].w);
- }
- 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->points[1].z, &t->points[2].z); doubleSwap(&t->points[1].w, &t->points[2].w);
- }
- 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->points[0].z, &t->points[1].z); doubleSwap(&t->points[0].w, &t->points[1].w);
- }
- vec4_t a = t->points[0];
- vec4_t b = t->points[1];
- vec4_t c = t->points[2];
- /* Render the top part */
- double inverseSlope1 = 0;
- double inverseSlope2 = 0;
- int32_t x0 = a.x, y0 = a.y;
- int32_t x1 = b.x, y1 = b.y;
- int32_t x2 = c.x, y2 = c.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 (doZBuffer)
- {
- if (xEnd < xStart)
- {
- intSwap(&xStart, &xEnd);
- }
- for (x = xStart ; x <= xEnd ; x++)
- {
- drawZPixel(x, y, a, b, c,t->colour);
- }
- }
- else
- {
- drawHLine(xStart, y, xEnd, t->colour);
- }
- }
- }
- /* 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 (doZBuffer)
- {
- if (xEnd < xStart)
- {
- intSwap(&xStart, &xEnd);
- }
- for (x = xStart ; x <= xEnd ; x++)
- {
- drawZPixel(x, y, a, b, c,t->colour);
- }
- }
- else
- {
- drawHLine(xStart, y, xEnd, t->colour);
- }
- }
- }
- }
- /* ----------------------------------- Textured triangles ----------------------------------- */
- static void drawTexel(int32_t x, int32_t y, vec4_t a, vec4_t b, vec4_t c, tex2_t ta, tex2_t tb, tex2_t tc, colour_t *texture)
- {
- vec2_t pointP = { x, y };
- vec2_t a2 = vec2FromVec4(a);
- vec2_t b2 = vec2FromVec4(b);
- vec2_t c2 = vec2FromVec4(c);
- if ((x < 0) || (y < 0) || (x > windowWidth) ||(y > windowHeight))
- {
- return;
- }
- vec3_t weights = barycentricWeights(a2, b2, c2, pointP);
- double alpha = weights.x;
- double beta = weights.y;
- double gamma = weights.z;
- double interpolatedU, interpolatedV, interpolatedReciprocalW;
- int32_t texX, texY;
- interpolatedReciprocalW = (1 / a.w) * alpha + (1 / b.w) * beta + (1 / c.w) * gamma;
- if (doPerpectiveCorrection)
- {
- interpolatedU = (ta.u / a.w) * alpha + (tb.u / b.w) * beta + (tc.u / c.w) * gamma;
- interpolatedV = ((1 - ta.v) / a.w) * alpha + ((1 - tb.v) / b.w) * beta + ((1 - tc.v) / c.w) * gamma;
- interpolatedU /= interpolatedReciprocalW;
- interpolatedV /= interpolatedReciprocalW;
- }
- else
- {
- interpolatedU = ta.u * alpha + tb.u * beta + tc.u * gamma;
- interpolatedV = (1 - ta.v) * alpha + (1 - tb.v) * beta + (1 - tc.v) * gamma;
- }
- texX = abs((int32_t)(interpolatedU * textureWidth));
- texY = abs((int32_t)(interpolatedV * textureHeight));
- texX = texX % textureWidth;
- texY = texY % textureWidth;
- if (doZBuffer)
- {
- /* Inverse to get logical W invrementing going further to us */
- interpolatedReciprocalW = 1 - interpolatedReciprocalW;
- if (interpolatedReciprocalW < zBuffer[(windowWidth * y) + x])
- {
- drawPixel(x, y, texture[(texY * textureWidth) + texX]);
- /* Update Z Buffer */
- zBuffer[(windowWidth * y) + x] = interpolatedReciprocalW;
- }
- }
- else
- {
- drawPixel(x, y, texture[(texY * textureWidth) + texX]);
- }
- }
- 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->points[0].z, &t->points[1].z); doubleSwap(&t->points[0].w, &t->points[1].w);
- 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->points[1].z, &t->points[2].z); doubleSwap(&t->points[1].w, &t->points[2].w);
- 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->points[0].z, &t->points[1].z); doubleSwap(&t->points[0].w, &t->points[1].w);
- doubleSwap(&t->textureCoordinates[0].u, &t->textureCoordinates[1].u);
- doubleSwap(&t->textureCoordinates[0].v, &t->textureCoordinates[1].v);
- }
- vec4_t a = t->points[0];
- vec4_t b = t->points[1];
- vec4_t c = t->points[2];
- /* Render the top part */
- double inverseSlope1 = 0;
- double inverseSlope2 = 0;
- int32_t x0 = a.x, y0 = a.y;
- int32_t x1 = b.x, y1 = b.y;
- int32_t x2 = c.x, y2 = c.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++)
- {
- drawTexel(x, y, a, b, c,
- t->textureCoordinates[0],
- t->textureCoordinates[1],
- t->textureCoordinates[2],
- t->texture);
- }
- }
- }
- /* 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++)
- {
- drawTexel(x, y, a, b, c,
- t->textureCoordinates[0],
- t->textureCoordinates[1],
- t->textureCoordinates[2],
- t->texture);
- }
- }
- }
- }
- /* ---- 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;
- }
|