191 lines
5.3 KiB
C
191 lines
5.3 KiB
C
#include <fxengine/triangle.h>
|
|
#include <fxengine/texture.h>
|
|
#include <fxengine/space.h>
|
|
#include <fxengine/zbuffer.h>
|
|
|
|
#ifdef USE_LIBPROF
|
|
#include <libprof.h>
|
|
#endif
|
|
|
|
#include <gint/display.h>
|
|
#include <gint/clock.h>
|
|
|
|
/**
|
|
* @brief Orientation of the face
|
|
*
|
|
* @param face The face
|
|
*
|
|
* @return if it is true the it is clockwised else it isn't clockwised
|
|
*/
|
|
static bool is_colckwised(fe_triangle const * face)
|
|
{
|
|
int32_t area = 0;
|
|
|
|
area += face->s1->x * face->s2->y;
|
|
area -= face->s2->x * face->s1->y;
|
|
|
|
area += face->s2->x * face->s3->y;
|
|
area -= face->s3->x * face->s2->y;
|
|
|
|
area += face->s3->x * face->s1->y;
|
|
area -= face->s1->x * face->s3->y;
|
|
|
|
return (area < 0);
|
|
}
|
|
|
|
|
|
// gestion locale des arètes
|
|
typedef struct line line;
|
|
struct line
|
|
{
|
|
int32_t x,y,z;
|
|
int32_t dx,dy,dz;
|
|
int32_t no_line; // si les deux points sont confondus (se comporte comme un bool)
|
|
};
|
|
|
|
/**
|
|
* @brief Sets up a line with 2 points
|
|
*
|
|
* @param l The line
|
|
* @param s1 The s 1
|
|
* @param s2 The s 2
|
|
*/
|
|
static void line_set(line* l,fe_ipoint const * s1, fe_ipoint const * s2)
|
|
{
|
|
l->x = s1->x;
|
|
l->y = s1->y;
|
|
l->z = s1->z;
|
|
l->dx = s2->x - s1->x;
|
|
l->dy = s2->y - s1->y;
|
|
l->dz = s2->z - s1->z;
|
|
l->no_line = (l->dx==0 && l->dy==0);
|
|
}
|
|
|
|
static int32_t max(int32_t x, int32_t y)
|
|
{
|
|
return (x>y ? x : y);
|
|
}
|
|
|
|
static int32_t min(int32_t x, int32_t y)
|
|
{
|
|
return (x<y ? x : y);
|
|
}
|
|
|
|
static int32_t abs(int32_t x)
|
|
{
|
|
return(x<0 ? -x : x);
|
|
}
|
|
|
|
/**
|
|
* @brief get the corresponding x from the y coordinate
|
|
*
|
|
* @param[in] y the y coordinate
|
|
* @param l the line
|
|
* @param x address where x is written
|
|
*
|
|
* @return if given point coordinate belongs to the l
|
|
*/
|
|
static bool line_get_x(int32_t y, line const * l, int32_t * x)
|
|
{
|
|
y -= l->y;
|
|
if (y < 0 && l->dy > 0)
|
|
return false;
|
|
if (y > 0 && l->dy < 0)
|
|
return false;
|
|
if (abs(y) > abs(l->dy))
|
|
return false;
|
|
|
|
if (l->dy != 0)
|
|
*x = l->x + (y * l->dx) / l->dy;
|
|
else if (y==0)
|
|
*x = l->x + l->dx;
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
void fe_display_triangle(const fe_triangle * face)
|
|
{
|
|
// backface culling
|
|
if (is_colckwised(face) != face->clockwised)
|
|
return;
|
|
|
|
// incomplete clipping
|
|
if (face->s1->x < 0 && face->s2->x < 0 && face->s3->x < 0)
|
|
return;
|
|
if (face->s1->x >= fe_width && face->s2->x >= fe_width && face->s3->x >= fe_width)
|
|
return;
|
|
if (face->s1->y < 0 && face->s2->y < 0 && face->s3->y < 0)
|
|
return;
|
|
if (face->s1->y >= fe_height && face->s2->y >= fe_height && face->s3->y >= fe_height)
|
|
return;
|
|
if (face->s1->z <= 0 && face->s2->z <= 0 && face->s3->z <= 0)
|
|
return;
|
|
|
|
line cotes[3];
|
|
line_set(&cotes[0], face->s1, face->s2);
|
|
line_set(&cotes[1], face->s1, face->s3);
|
|
line_set(&cotes[2], face->s2, face->s3);
|
|
|
|
if (cotes[0].no_line || cotes[1].no_line || cotes[2].no_line)
|
|
return;
|
|
|
|
const int32_t ymin = max(min(face->s1->y, min(face->s2->y, face->s3->y)), 0);
|
|
const int32_t ymax = min(max(face->s1->y, max(face->s2->y, face->s3->y)), 63);
|
|
|
|
const int32_t xAB = face->s2->x - face->s1->x, yAB = face->s2->y - face->s1->y, zAB=face->s2->z-face->s1->z;
|
|
const int32_t xAC = face->s3->x - face->s1->x, yAC = face->s3->y - face->s1->y, zAC=face->s3->z-face->s1->z;
|
|
const int32_t diviseur_commun = (xAB * yAC - yAB * xAC);
|
|
|
|
const int32_t fact_1= (32768 * yAC) / diviseur_commun, fact_2 = (32768 * xAC) / diviseur_commun;
|
|
const int32_t fact_3= (32768 * xAB) / diviseur_commun, fact_4 = (32768 * yAB) / diviseur_commun;
|
|
|
|
for (int32_t y = ymin; y <= ymax; y++)
|
|
{
|
|
// xmin and xmax determination
|
|
int32_t tx1,tx2;
|
|
|
|
for (int t=0;t<3;t++)
|
|
{
|
|
if (line_get_x(y,&cotes[t],&tx1))
|
|
break;
|
|
}
|
|
for (int32_t t=0;t<3;t++)
|
|
{
|
|
if (line_get_x(y,&cotes[t],&tx2) && tx1!=tx2)
|
|
break;
|
|
}
|
|
|
|
const int32_t txmin=max(min(tx1,tx2),0), txmax=min(max(tx1,tx2),127);
|
|
|
|
for (int32_t x=txmin; x<=txmax; x++)
|
|
{
|
|
int32_t vx, vy, z;
|
|
|
|
const int32_t xcalc = x - face->s1->x, ycalc = y - face->s1->y;
|
|
// calcul de vx, vy
|
|
vx = (xcalc*fact_1 - ycalc*fact_2);
|
|
vy = (ycalc*fact_3 - xcalc*fact_4);
|
|
z = face->s1->z + (vx * zAB + vy * zAC)/32768;
|
|
|
|
|
|
int32_t vx2= ( vx*face->texture->size_px_x/face->s2->z ) / ((32768-vx)/face->s1->z + vx/face->s2->z);
|
|
int32_t vy2= ( vy*face->texture->size_px_y/face->s3->z ) / ((32768-vy)/face->s1->z + vy/face->s3->z);
|
|
/* Perspective correction
|
|
vx /= (double) face->s2->z / ((1 - vx) / face->s1->z + vx / (double) face->s2->z);
|
|
vy /= (double) face->s3->z / ((1 - vy) / face->s1->z + vy / (double) face->s3->z);
|
|
*/
|
|
// Affichage du point
|
|
const uint8_t color = fe_texture_get_pixel_r(face->texture, vx2, vy2);
|
|
|
|
if (color >> 1)
|
|
{
|
|
//dpixel(x,y, C_BLACK);
|
|
if (fe_zbuffer_set_px(x,y,z))
|
|
dpixel(x, y, 3 * (color % 2)); // 3* means cast to black and white, and vx,and vy casted between 0 and 7
|
|
}
|
|
}
|
|
}
|
|
}
|