fxengine/src/render/triangle.c

147 lines
4.7 KiB
C

#include <fxengine/render/triangle.h>
#include <fxengine/render/buffer.h>
#include <fxengine/model/vertex.h>
#include <fxengine/model/bitmap.h>
#include <stdint.h>
#include <stdbool.h>
#include <gint/display.h>
void fe_display_triangle(const fe_triangle * face)
{
render_triangle(*face->s1, *face->s2, *face->s3, face->texture1, face->texture2, face->texture_half);
}
int render_triangle(fe_vertex const s1, fe_vertex const s2, fe_vertex const s3,
fe_bitmap const * side_0, fe_bitmap const * side_1, bool texture_half)
{
// Backface culling
const int32_t area = s1.x * s2.y
- s2.x * s1.y
+ s2.x * s3.y
- s3.x * s2.y
+ s3.x * s1.y
- s1.x * s3.y;
if (area < 0)
return render_triangle_nobfc(s1,s2,s3,side_0,texture_half);
else
return render_triangle_nobfc(s1,s2,s3,side_1,texture_half);
}
// Render Core
// Macros
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
#define abs(x) (x<0?-x:x)
// gestion des arètes
typedef struct
{
int32_t x,y,z;
int32_t dx,dy,dz;
int8_t no_line; // si les deux points sont confondus (se comporte comme un bool)
} line;
static void line_set(line* l,fe_vertex const * s1, fe_vertex 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 bool line_get_x(int32_t y, line const * l, int32_t * x) // retourne false si impossible, ou alors écrit dans 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;
}
int render_triangle_nobfc(fe_vertex const s1, fe_vertex const s2, fe_vertex const s3,
fe_bitmap const * side, bool texture_half)
{
if (!side) return RENDER_NOBMP;
// clipping (incomplete)
if (s1.x < 0 && s2.x < 0 && s3.x < 0) return RENDER_OUT_OF_SCREEN;
if (s1.x >= fe_width && s2.x >= fe_width && s3.x >= fe_width) return RENDER_OUT_OF_SCREEN;
if (s1.y < 0 && s2.y < 0 && s3.y < 0) return RENDER_OUT_OF_SCREEN;
if (s1.y >= fe_height && s2.y >= fe_height && s3.y >= fe_height) return RENDER_OUT_OF_SCREEN;
if (s1.z <= 0 && s2.z <= 0 && s3.z <= 0) return RENDER_OUT_OF_SCREEN;
line cotes[3];
line_set(&cotes[0], &s1, &s2);
line_set(&cotes[1], &s1, &s3);
line_set(&cotes[2], &s2, &s3);
if (cotes[0].no_line || cotes[1].no_line || cotes[2].no_line) return RENDER_INVISIBLE;
const int32_t ymin = max(min(s1.y, min(s2.y, s3.y)), 0);
const int32_t ymax = min(max(s1.y, max(s2.y, s3.y)), 63);
const int32_t xAB = s2.x - s1.x, yAB = s2.y - s1.y, zAB=s2.z-s1.z;
const int32_t xAC = s3.x - s1.x, yAC = s3.y - s1.y, zAC=s3.z-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++)
{
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 - s1.x, ycalc = y - s1.y;
// calcul de vx, vy
vx = (xcalc*fact_1 - ycalc*fact_2);
vy = (ycalc*fact_3 - xcalc*fact_4);
z = s1.z + (vx * zAB + vy * zAC)/32768;
if (z>0)
{
int32_t vx2= ( vx*side->size_px_x/s2.z ) / ((32768-vx)/s1.z + vx/s2.z);
int32_t vy2= ( vy*side->size_px_y/s3.z ) / ((32768-vy)/s1.z + vy/s3.z);
// Affichage du point
const uint8_t color = fe_bitmap_get_px(side, vx2, vy2);
if (color >> 1)
{
#ifdef RENDER_DEBUG_MODE
dpixel(x,y, C_BLACK);
#else
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
#endif
}
}
}
}
return RENDER_SUCCESSFULLY_RENDERED;
}