291 lines
8.0 KiB
C++
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;
|
|
}
|
|
|
|
|