windmill-gint/src/windmill_clip.cpp

291 lines
8.0 KiB
C++

//----------------------------------------------------------------------------------------------------
//
// WINDMILL
//
// version : 2.0
// Moteur de rendu 3D base sur la methode de rasterisation avec buffer de profondeur
//
// Cree par Olivier Lanneau, alias NineStars
// Planet-casio.fr
//
//----------------------------------------------------------------------------------------------------
#include "windmill.hpp"
void debug_pop(char const *fmt, ...);
void debug_display(char const *fmt, ...);
extern Texture tex_white;
extern Texture tex_light;
extern Texture tex_dark;
extern Texture tex_black;
//----------------------------------------------------------------------------------------------------
// CLIPPING
//----------------------------------------------------------------------------------------------------
bool Windmill::object_in_frustrum(Object* object)
{
Vertex centre(object->sphere.x, object->sphere.y, object->sphere.z);
std::vector<Vertex> vCentre = {centre};
// calcul des coordonnees dans le repère monde apres rotation et translation du centre de l objet
transform_model_to_world(object, vCentre);
// calcul des coordonnees du centre dans le repère camera apres rotation et translation de la camera
transform_world_to_camera(vCentre);
// si la camera est dans la sphere
if (centre.x*centre.x + centre.y*centre.y + centre.z*centre.z < object->sphere.square_radius)
{
return true;
}
// si la sphere est dans le frustrum
for (int p = 0; p<5 ; p++)
{
Plane plane = camera.frustrum.sides[p];
if (inside_frustrum(centre, plane, object->sphere.radius) == false) return false;
}
return true;
// A GARDER CAR PAS MAL OPTI
/*
int cone_offset = sphere->radius * camera.sin_fov;
int nx = camera.nx;
int ny = camera.ny;
int nz = camera.nz;
Point3D cone;
cone.x = int(camera.x) - (nx * cone_offset);
cone.y = int(camera.y) - (ny * cone_offset);
cone.z = int(camera.z) - (nz * cone_offset);
int sx = sphere->x - cone.x;
int sy = sphere->y - cone.y;
int sz = sphere->z - cone.z;
int ss = sx*sx + sy*sy + sz*sz;
// si sommet du cone dans la sphere
if (ss <= sphere->radius * sphere->radius)
{
return true;
}
// si sphere dans le cone
int ns = ((nx * sx) >> 7) + ((ny * sy) >> 7) + ((nz * sz) >> 7);
if (ns > 0 and ns*ns > camera.cos_fov_square * ss)
{
return true;
}
return false;*/
}
void Windmill::clip_frustrum(WMesh &mesh)
{
//pour chaque plan
for (int p = 0; p<5 ; p++)
{
Plane plane = camera.frustrum.sides[p];
int init_f_length = mesh.f.size(); // car sinon on va faire du clipping sur des points nouveaux issues du clipping...
// pour chaque triangle
for (int f=0; f<init_f_length; f++)
{
// triangle en cours
Face triangle = mesh.f[f];
if (triangle.visible)
{
int S = 3 - 1;
// indice vertex pour face
int v_ok[4];
int t_ok[4];
int ok_indice = 0;
// pour chaque segment
for (int E = 0; E<3; E++)
{
// 2 vertexs correspondants à chaque segment
Vertex vertexE(mesh.v[triangle.v[E]].x, mesh.v[triangle.v[E]].y, mesh.v[triangle.v[E]].z, mesh.t[triangle.t[E]].u,mesh.t[triangle.t[E]].v);
Vertex vertexS(mesh.v[triangle.v[S]].x, mesh.v[triangle.v[S]].y, mesh.v[triangle.v[S]].z, mesh.t[triangle.t[S]].u,mesh.t[triangle.t[S]].v);
// teste si un des deux points du segment est en dehors du frustrum
if (inside_frustrum(vertexE, plane) == true)
{
if (inside_frustrum(vertexS, plane) == false)
{
// on sauvegarde dans v_ok
v_ok[ok_indice] = mesh.v.size();
t_ok[ok_indice] = mesh.t.size();
ok_indice++;
// on créée un vertex sécant de notre segment et du bord de l'écran
mesh.add_vertex(clip_onto_plane(vertexE, vertexS, plane));
}
v_ok[ok_indice] = triangle.v[E];
t_ok[ok_indice] = triangle.t[E];
ok_indice++;
}
else if (inside_frustrum(vertexS, plane) == true)
{
// on sauvegarde dans v_ok
v_ok[ok_indice] = mesh.v.size();
t_ok[ok_indice] = mesh.t.size();
ok_indice++;
// on créée un vertex sécant de notre segment et du bord de l'écran
mesh.add_vertex(clip_onto_plane(vertexE, vertexS, plane));
}
S = E;
}
// maintenant on passe à la génération des faces à partir de nos nouveaux segments
// aucun point dans le frustrum
if (ok_indice == 0)
{
mesh.f[f].visible = false;
}
// soit les 3 points à l'intérieur du triangle, soit 2 point ouside -> on modifie la face en cours
if (ok_indice >= 3)
{
Face edited_triangle;
edited_triangle.texture_front = triangle.texture_front;
edited_triangle.texture_back = triangle.texture_back;
for (int i=0; i<3; i++)
{
edited_triangle.v[i] = v_ok[i];
edited_triangle.t[i] = t_ok[i];
}
mesh.f[f] = edited_triangle; // pas de verif d'overflow
}
// 1 point en dehors = 2 triangles -> on modifie face en cours, et on ajoute une face
if (ok_indice == 4)
{
// ajout triangle
Face new_triangle;
new_triangle.texture_front = triangle.texture_front;
new_triangle.texture_back = triangle.texture_back;
new_triangle.v[0] = v_ok[0];
new_triangle.t[0] = t_ok[0];
new_triangle.v[1] = v_ok[2];
new_triangle.t[1] = t_ok[2];
new_triangle.v[2] = v_ok[3];
new_triangle.t[2] = t_ok[3];
mesh.f.push_back(new_triangle); // pas de verif d'overflow
}
}
/*debug_pop("FIN CYCLE\nplan %i\nil y a %i triangles\nil y a %i vertex", p, mesh->f_length, mesh->v_length);
for (int i=0; i<mesh->f_length; i++)
{
debug_pop("f %i %i %i", mesh->f[i].v[0], mesh->f[i].v[1], mesh->f[i].v[2]);
}
for (int i=0; i<mesh->v_length; i++)
{
debug_pop("v\nx %i\ny %i\nz %i", mesh->v[i].x, mesh->v[i].y, mesh->v[i].z);
}*/
}
}
}
bool Windmill::inside_frustrum(Vertex vertex, Plane plane, int offset)
{
/*if (plane.id == NEAR)
{
return vertex.z > camera.near;
}
if (plane.id == RIGHT)
{
return vertex.x * camera.scale_coef2 < vertex.z; // MULT float !
}
if (plane.id == TOP)
{
return -vertex.y * camera.scale_coef2 / 2.0 < vertex.z;
}
if (plane.id == LEFT)
{
return -vertex.x * camera.scale_coef2 < vertex.z;
}
if (plane.id == BOTTOM)
{
return vertex.y * camera.scale_coef2 / 2.0 < vertex.z;
}*/
int dot = vertex.x * plane.normal_x + vertex.y * plane.normal_y + vertex.z * plane.normal_z - plane.distance+ (offset * 1000);
return dot > 0;
}
Vertex Windmill::clip_onto_plane(Vertex vertexA, Vertex vertexB, Plane plane)
{/*
int x, y, z, u, v;
float t;
if (plane == NEAR)
{
t = float(camera.near - vertexA->z) / float(vertexB->z - vertexA->z);
//x = vertexA->x + t * (vertexB->x - vertexA->x);
//y = vertexA->y + t * (vertexB->y - vertexA->y);
//z = camera.near;
}
if (plane == RIGHT)
{
t = float(camera.near - vertexA->z) / float(vertexB->z - vertexA->z);
}
x = vertexA->x + t * (vertexB->x - vertexA->x);
y = vertexA->y + t * (vertexB->y - vertexA->y);
z = vertexA->z + t * (vertexB->z - vertexA->z);
u = vertexA->u + t * (vertexB->u - vertexA->u);
v = vertexA->v + t * (vertexB->v - vertexA->v);
return Vertex(x, y, z, u, v);*/
//return Vertex(0, 0, 0, 0, 0);
float dotA = vertexA.x * plane.normal_x + vertexA.y * plane.normal_y + vertexA.z * plane.normal_z - plane.distance;
float dotB = vertexB.x * plane.normal_x + vertexB.y * plane.normal_y + vertexB.z * plane.normal_z - plane.distance;
float t = dotA / (dotA - dotB); // intersection factor (between 0 and 1)
int x = vertexA.x + t * (vertexB.x - vertexA.x);
int y = vertexA.y + t * (vertexB.y - vertexA.y);
int z = vertexA.z + t * (vertexB.z - vertexA.z);
float u = vertexA.u + t * (vertexB.u - vertexA.u);
float v = vertexA.v + t * (vertexB.v - vertexA.v);
return Vertex(x, y, z, u, v);
}
bool Windmill::inside_viewport(int x, int y)
{
return (x < viewport.x2 and y >= viewport.y1 and x >= viewport.x1 and y < viewport.y2);
}
int Windmill::get_visible_face(Vertex* a, Vertex* b, Vertex* c)
{
return (c->x - a->x) * (b->y - a->y) - (c->y - a->y) * (b->x - a->x) > 0 ? FRONT : BACK;
}