//---------------------------------------------------------------------------------------------------- // // 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" //const Texture tex_black = {}; //const Texture tex_white = {}; extern Texture tex_white; extern Texture tex_light; extern Texture tex_dark; extern Texture tex_black; //---------------------------------------------------------------------------------------------------- // INITIALISATION //---------------------------------------------------------------------------------------------------- Windmill::Windmill() { camera2 = NULL; map = NULL; object = NULL; z_buffer = NULL; set_viewport(0, 0, 128, 64); nb_object_on_screen = 0; temp_object_cursor = NULL; temp_poly_cursor = -1; } void Windmill::set_camera(Camera* _camera) { camera2 = _camera; } void Windmill::set_viewport(int viewport_x1, int viewport_y1, int viewport_x2, int viewport_y2) { viewport.x1 = max(0, viewport_x1); viewport.y1 = max(0, viewport_y1); viewport.x2 = min(viewport_x2, DWIDTH); viewport.y2 = min(viewport_y2, DHEIGHT); //if (z_buffer != NULL) free(z_buffer); free(z_buffer); shift_x = (viewport.x1 + viewport.x2) / 2; shift_y = (viewport.y1 + viewport.y2) / 2; z_buffer_size = (viewport.x2 - viewport.x1) * (viewport.y2 - viewport.y1); z_buffer = (unsigned short*) calloc(z_buffer_size, 2); //if (z_buffer == NULL) debug_pop("ERROR ptr z_buffer NULL"); z_buffer_width = viewport.x2 - viewport.x1; z_buffer_offset = - viewport.x1 - viewport.y1 * DWIDTH; clear_z_buffer(); cursor_x = shift_x; cursor_y = shift_y; } void Windmill::load_map(Map* _map) { loading = true; if (map != _map) { // attribution de la map map = _map; // enregistrement des objets de la map dans un tableau // ceci permet de trier les objets pour les afficher du plus // pres au plus loin list_object_length = map->list_object_length; //delete[] object; free(object); //object = new Object* [list_object_length]; object = (Object**) malloc(list_object_length * sizeof(Object)); //if (object == NULL) debug_pop("ERROR ptr object NULL"); for (int i = 0; iobject[i]; } // generation des spheres englobantes // pour chaque objet for (int i = 0; ilist_object_length; i++) { Object* current_object = map->object[i]; int nb_point = 0; int center_x = 0; int center_y = 0; int center_z = 0; // pour chaque poly for (int j = 0; jmodele_size; j++) { const Modele* current_poly = ¤t_object->modele[j]; // pour chaque point for (int k = 0; ktype; k++) { Point point = get_point(current_poly, k); center_x += point.x; center_y += point.y; center_z += point.z; nb_point++; } } // barycentre center_x /= nb_point; center_y /= nb_point; center_z /= nb_point; // recherche du point le plus eloigne du centre int radius_max = 0; for (int j = 0; jmodele_size; j++) { const Modele* current_poly = ¤t_object->modele[j]; for (int k = 0; ktype; k++) { Point point = get_point(current_poly, k); int dx = point.x - center_x; int dy = point.y - center_y; int dz = point.z - center_z; int radius = dx*dx + dy*dy + dz*dz; //int rr = sqrtf(radius); if (radius > radius_max) { radius_max = radius; } } } radius_max = sqrtf(radius_max) + 1; current_object->sphere.x = center_x; current_object->sphere.y = center_y; current_object->sphere.z = center_z; current_object->sphere.radius = radius_max; } } loading = false; } //---------------------------------------------------------------------------------------------------- // DESSIN //---------------------------------------------------------------------------------------------------- void Windmill::draw() { // quitte si pas de camera, de map, d objet ou de z_buffer if(camera2 == NULL or map == NULL or object == NULL or z_buffer == NULL) return; // mets a jour les variables de la camera camera2->update(); // cree une camera temporaire pour eviter les problemes de desynchronisation copy_camera(); // fonction principale, l'ordre d'appel des fonctions suivantes est important clear_z_buffer(); // trie les objets pour optimiser l affichage sort_object(); // affiche le trait d'horizon if (map->horizon) draw_horizon(); // affiche le sol if (map->ground) draw_ground(); // affiche les objets de la scene draw_objects(); // post traitement pour afficher les angles vifs draw_post_treatment(); // affiche le corps du personnage draw_body(); // affiche des informations //show_fps(); //show_coordinates(); //show_repere(); } void Windmill::draw_horizon() { Vertex horizon; char* vram = get_vram_address(); int distance = 1000; int hx = camera.x + distance * camera.cos_yaw; int hy = camera.y + distance * camera.sin_yaw; horizon.set_xyz(hx, hy, 0); transform_world_to_camera(&horizon, 1); transform_camera_to_screen(&horizon, 1); if (inside_viewport(horizon.x, horizon.y)) { dline(viewport.x1, horizon.y, viewport.x2, horizon.y, C_BLACK); //ML_line(viewport.x1, horizon.y, viewport.x2, horizon.y, ML_BLACK); } } void Windmill::draw_ground() { Vertex ground; char* vram = get_vram_address(); unsigned char mask; int distance = 50; int esp = 20; int nb = 6; int nb2 = nb * nb; float cx = camera.x + distance * camera.cos_yaw; float cy = camera.y + distance * camera.sin_yaw; int dx = int(cx) - int(cx)%esp; int dy = int(cy) - int(cy)%esp; for (int i=-nb; i 0) { transform_camera_to_screen(&ground, 1); if (inside_viewport(ground.x, ground.y) == true) { mask = 128 >> (ground.x & 7); vram[(ground.y << 4) + (ground.x>>3)] |= mask; } } } } } } void Windmill::draw_objects() { nb_object_on_screen = 0; temp_object_cursor = NULL; temp_poly_cursor = -1; Vertex vertex[10]; int vertex_length; int width, height; int cosinus, sinus; for(i=0; ilist_object_length; i++) { //Object* current_object = map->object[i]; Object* current_object = object[i]; if (current_object->hidden == false) { // angle de l objet compute_object_angle(current_object, &cosinus, &sinus); // transforme la sphere dans le repere monde Sphere current_sphere = transform_sphere_to_world(¤t_object->sphere, current_object, cosinus, sinus); // verifie si la sphere englobante est dans le champs de vision de la camera if (sphere_in_cone(¤t_sphere)) { temp_nb_object_on_screen ++; current_object->on_screen = true; current_object->distance_to_camera = current_sphere.z; for(j=0; jmodele_size; j++) { const Modele* current_poly = ¤t_object->modele[j]; // creation des vertex a partir du poly extract_vertex_from_poly(current_poly, vertex, &vertex_length, &width, &height); // calcul des coordonnees dans le repère monde apres rotation et translation de l objet transform_model_to_world(vertex, vertex_length, current_object, cosinus, sinus); // calcul des coordonnees dans le repère camera apres rotation et translation de la camera transform_world_to_camera(vertex, vertex_length); // verifie si au moins 1 point peut etre visible if (fast_check(vertex, vertex_length)) { // calcul de l'aire du rectangle pour connaitre sa face visible int visible = visible_face(&vertex[0], &vertex[1], &vertex[2]); // choisi la texture visible const Texture* texture; texture = (visible == FRONT) ? current_poly->texture_front : current_poly->texture_back; // si le rectangle est visible if (texture != NULL) { // si le rectangle a une texture if (texture != &tex_black && texture != &tex_white) { texture_coordinates(current_poly, vertex, texture, visible, width, height); } // tronque le polygon devant le plan avant de la camera clip_depth(vertex, &vertex_length); // calcul des coordonnes apres perspective et decalage au centre de l ecran transform_camera_to_screen(vertex, vertex_length); // tronque le polygon sur les bords de l ecran clip_viewport(vertex, &vertex_length); // calcul des coordonnees de profondeur transform_camera_to_screen2(vertex, vertex_length); // affiche les triangles if (visible == FRONT) { for (int k = 1; k1; k--) { render_triangle(&vertex[0], &vertex[k], &vertex[k-1], texture); } } } } } } else { current_object->on_screen = false; } } } nb_object_on_screen = temp_nb_object_on_screen; object_cursor = temp_object_cursor; poly_cursor = temp_poly_cursor; } void Windmill::draw_post_treatment() { char* vram = get_vram_address(); unsigned char mask; int current, right, bottom, left; for (int y = viewport.y1; y> ((x+1) & 7); vram[address<<3] |= mask; //ML_pixel(x+1, y, ML_BLACK); } if (bottom < MAX_DEPTH_Z_BUFFER) { dpixel(x, y+1, C_BLACK); //ML_pixel(x, y+1, ML_BLACK); } } else { if (right == MAX_DEPTH_Z_BUFFER or bottom == MAX_DEPTH_Z_BUFFER) { dpixel(x, y+1, C_BLACK); //ML_pixel(x, y, ML_BLACK); } } } } // 21 fps 21,5 fps*/ /*left = z_buffer[0]; for (int i = 1; i> (i & 7); vram[(i - z_buffer_offset)>>3] |= mask; } } else { if (left < MAX_DEPTH_Z_BUFFER) { mask = 128 >> ((i-1) & 7); vram[(i - 1 - z_buffer_offset)>>3] |= mask; } } } // sans rien 28 fps // avec le i+1 : 24 fps // avec le i : 25 fps // calculer left = Z_buffer[i-1] dans if : 24 fps // avec les deux cote : 24 fps ! */ bool flag = false; for (int i = 0; i> ((i-2) & 7); vram[(i - 2 - z_buffer_offset)>>3] |= mask; flag = false; } else { // l objet commencait en fait 1 pixel a gauche mask = 128 >> ((i-1) & 7); vram[(i - 1 - z_buffer_offset)>>3] |= mask; flag = false; } } } else { if (flag == false) { if (z_buffer[i-1] == MAX_DEPTH_Z_BUFFER) { // il y avait rien juste a gauche mask = 128 >> (i & 7); vram[(i - z_buffer_offset)>>3] |= mask; flag = true; } else { // l objet commencait en fait 1 pixel a gauche mask = 128 >> ((i-1) & 7); vram[(i - 1 - z_buffer_offset)>>3] |= mask; flag = true; } } } } // 24 fps // avec astuce +=2 : 25 fps /*bool flag = false; char i_mod_8; for (int i = 0; i> i_mod_8;//((i-1) & 7); flag = false; } } else { if (flag == false) { mask |= 128 >> i_mod_8; flag = true; } } if (i_mod_8 == 7) { //if (mask > 0) vram[i>>3] |= mask; mask = 0; } }*/ } void Windmill::draw_body() { /*int x = camera.x; int y = camera.y; int z = camera.z; Vertex v0, v1, v2, v3; set_vertex_xyz() METTRE DIRECT LES VERTEX DANS LE REPERE SCREEN PUIS RENDER... */ } //---------------------------------------------------------------------------------------------------- // TRANSFORMATION 3D //---------------------------------------------------------------------------------------------------- void Windmill::transform_model_to_world(Vertex vertex[], int vertex_length, Object* object, int cosinus, int sinus) { for (int k = 0; kaxe == N) { vertex[k].x = vertex_x + object->x; vertex[k].y = vertex_y + object->y; vertex[k].z = vertex_z + object->z; } if (object->axe == X) { vertex[k].x = vertex_x + object->x; vertex[k].y = ((vertex_y * cosinus - vertex_z * sinus) >> 10) + object->y; vertex[k].z = ((vertex_y * sinus + vertex_z * cosinus) >> 10) + object->z; } if (object->axe == Y) { vertex[k].x = ((vertex_x * cosinus - vertex_z * sinus) >> 10) + object->x; vertex[k].y = vertex_y + object->y; vertex[k].z = ((vertex_x * sinus + vertex_z * cosinus) >> 10) + object->z; } if (object->axe == Z or object->axe == Z_BILLBOARD) { vertex[k].x = ((vertex_x * cosinus - vertex_y * sinus) >> 10) + object->x; vertex[k].y = ((vertex_x * sinus + vertex_y * cosinus) >> 10) + object->y; vertex[k].z = vertex_z + object->z; } } } void Windmill::transform_world_to_camera(Vertex vertex[], int vertex_length) { for (int k = 0; kz_normalized = (1<<15) * (vertex->z * camera.far - SCALE_AI * camera.far * camera.near) / (vertex->z * (camera.far - camera.near)); // precalcul de 1/z vertex[k].z = (1<<20) / vertex[k].z; } } //---------------------------------------------------------------------------------------------------- // TEST VISIBILITE //---------------------------------------------------------------------------------------------------- bool Windmill::fast_check(Vertex vertex[], int vertex_length) { int near = camera.near * SCALE_AI; int far = camera.far * SCALE_AI; for (int i = 0; i= near and vertex[i].z < far) return true; } return false; } 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::visible_face(Vertex* a, Vertex* b, Vertex* c) { int nx = ((b->y - a->y) * (c->z - a->z) - (b->z - a->z) * (c->y - a->y)) >> 15; int ny = ((b->z - a->z) * (c->x - a->x) - (b->x - a->x) * (c->z - a->z)) >> 15; int nz = ((b->x - a->x) * (c->y - a->y) - (b->y - a->y) * (c->x - a->x)) >> 15; return (nx*a->x + ny*a->y + nz*a->z > 0) ? BACK : FRONT; } // Sutherland–Hodgman algorithm void Windmill::clip_depth(Vertex vertex_input[], int* vertex_input_length) { Vertex vertex_temp; Vertex vertex_output[10]; int vertex_output_length = 0; int S = *vertex_input_length-1; for (int E = 0; E<*vertex_input_length; E++) { if (clip_depth_inside_edge(&vertex_input[E]) == true) { if (clip_depth_inside_edge(&vertex_input[S]) == false) { clip_depth_edge(&vertex_input[E], &vertex_input[S], &vertex_temp); copy_vertex(&vertex_temp, &vertex_output[vertex_output_length]); vertex_output_length += 1; } copy_vertex(&vertex_input[E], &vertex_output[vertex_output_length]); vertex_output_length += 1; } else if (clip_depth_inside_edge(&vertex_input[S]) == true) { clip_depth_edge(&vertex_input[S], &vertex_input[E], &vertex_temp); copy_vertex(&vertex_temp, &vertex_output[vertex_output_length]); vertex_output_length += 1; } S = E; } *vertex_input_length = vertex_output_length; for (int i = 0; i<*vertex_input_length; i++) { copy_vertex(&vertex_output[i], &vertex_input[i]); } } void Windmill::clip_depth_edge(Vertex* vertex_in, Vertex* vertex_out, Vertex* vertex_set) { int near, x_near, y_near, z_near, w_near, h_near; float t; near = camera.near * SCALE_AI; t = float(near - vertex_in->z) / float(vertex_out->z - vertex_in->z); x_near = (vertex_out->x - vertex_in->x) * t + vertex_in->x; y_near = (vertex_out->y - vertex_in->y) * t + vertex_in->y; z_near = near; w_near = (vertex_out->w - vertex_in->w) * t + vertex_in->w; h_near = (vertex_out->h - vertex_in->h) * t + vertex_in->h; vertex_set->set_xyz(x_near, y_near, z_near); vertex_set->set_wh(w_near, h_near); } bool Windmill::clip_depth_inside_edge(Vertex* vertex) { return (vertex->z >= camera.near * SCALE_AI) ? true : false; } // Sutherland–Hodgman algorithm void Windmill::clip_viewport(Vertex vertex_input[], int* vertex_input_length) { Vertex vertex_temp; Vertex vertex_output[10]; int vertex_output_length = *vertex_input_length; for (int i = 0; i<*vertex_input_length; i++) { copy_vertex(&vertex_input[i], &vertex_output[i]); } for (int edge = 0; edge < 4; edge++) { for (int i = 0; ix) / float(vertex_out->x - vertex_in->x); x_set = viewport.x2-1; y_set = (vertex_out->y - vertex_in->y) * t + vertex_in->y; } if (edge == 1) { t = float(viewport.y1 - vertex_in->y) / float(vertex_out->y - vertex_in->y); x_set = (vertex_out->x - vertex_in->x) * t + vertex_in->x; y_set = viewport.y1; } if (edge == 2) { t = float(viewport.x1 - vertex_in->x) / float(vertex_out->x - vertex_in->x); x_set = viewport.x1; y_set = (vertex_out->y - vertex_in->y) * t + vertex_in->y; } if (edge == 3) { t = float(viewport.y2 - 1 - vertex_in->y) / float(vertex_out->y - vertex_in->y); x_set = (vertex_out->x - vertex_in->x) * t + vertex_in->x; y_set = viewport.y2-1; } z_set = 0.5 + float(1) / (float(1-t)/float(vertex_in->z) + float(t)/float(vertex_out->z)); w_set = z_set * ( (1.0-t) * vertex_in->w / float(vertex_in->z) + t * vertex_out->w / float(vertex_out->z) ); h_set = z_set * ( (1.0-t) * vertex_in->h / float(vertex_in->z) + t * vertex_out->h / float(vertex_out->z) ); vertex_set->set_xyz(x_set, y_set, z_set); vertex_set->set_wh(w_set, h_set); } bool Windmill::clip_viewport_inside_edge(Vertex* vertex, int edge) { // 0 : RIGHT 1 : UP 2: LEFT 3 : DOWN if (edge == 0 and vertex->x < viewport.x2-1) return true; if (edge == 1 and vertex->y > viewport.y1) return true; if (edge == 2 and vertex->x > viewport.x1) return true; if (edge == 3 and vertex->y < viewport.y2-1) return true; return false; } //---------------------------------------------------------------------------------------------------- // DESSIN DES TRIANGLES //---------------------------------------------------------------------------------------------------- void Windmill::render_triangle(Vertex* vertexA, Vertex* vertexB, Vertex* vertexC, const Texture *texture)//, bool billboard) { //render_triangle_black(vertexA, vertexB, vertexC); if (texture == NULL) return; if (texture == &tex_black) { render_triangle_black(vertexA, vertexB, vertexC); } else if (texture == &tex_white) { render_triangle_white(vertexA, vertexB, vertexC); } else if (texture->transparent == true) { render_triangle_transparent(vertexA, vertexB, vertexC, texture); } else { render_triangle_texture(vertexA, vertexB, vertexC, texture); } } void Windmill::render_triangle_texture(Vertex* vertexA, Vertex* vertexB, Vertex* vertexC, const Texture* texture) { // calcul du rectangle circonscrit au triangle int min_x = max(viewport.x1, min(vertexA->x, min(vertexB->x, vertexC->x))); int max_x = min(viewport.x2-1, max(vertexA->x, max(vertexB->x, vertexC->x))); int min_y = max(viewport.y1, min(vertexA->y, min(vertexB->y, vertexC->y))); int max_y = min(viewport.y2-1, max(vertexA->y, max(vertexB->y, vertexC->y))); // calcul de l'aire du triangle int area = edge(vertexA, vertexB, vertexC); // determine si la taille du triangle est trop petite if (area <= MIN_AREA_CLIP) return; // pre-calcul des coordonnees de la texture int w0 = vertexA->w * vertexA->z; int h0 = vertexA->h * vertexA->z; int w1 = vertexB->w * vertexB->z; int h1 = vertexB->h * vertexB->z; int w2 = vertexC->w * vertexC->z; int h2 = vertexC->h * vertexC->z; // calcul des produits vectoriels int u0_start = edge_start(vertexB, vertexC, min_x, min_y); int u0_step_x = edge_step_x(vertexB, vertexC); int u0_step_y = edge_step_y(vertexB, vertexC); int u1_start = edge_start(vertexC, vertexA, min_x, min_y); int u1_step_x = edge_step_x(vertexC, vertexA); int u1_step_y = edge_step_y(vertexC, vertexA); int u2_start = edge_start(vertexA, vertexB, min_x, min_y); int u2_step_x = edge_step_x(vertexA, vertexB); int u2_step_y = edge_step_y(vertexA, vertexB); int z_num_start = (u0_start * vertexA->z_normalized + u1_start * vertexB->z_normalized + u2_start * vertexC->z_normalized) / area; int z_num_step_x = (u0_step_x * vertexA->z_normalized + u1_step_x * vertexB->z_normalized + u2_step_x * vertexC->z_normalized) / area; int z_num_step_y = (u0_step_y * vertexA->z_normalized + u1_step_y * vertexB->z_normalized + u2_step_y * vertexC->z_normalized) / area; int z_div_start = u0_start * vertexA->z + u1_start * vertexB->z + u2_start * vertexC->z; int z_div_step_x = u0_step_x * vertexA->z + u1_step_x * vertexB->z + u2_step_x * vertexC->z; int z_div_step_y = u0_step_y * vertexA->z + u1_step_y * vertexB->z + u2_step_y * vertexC->z; // rapprochement artificiel si texture de type decoration int decoration = (texture->decoration ? DECORATION_OFFSET : 0); // acces a la vram char* vram = get_vram_address(); unsigned char mask_vram, mask_w; int offset_vram; int address; int u0, u1, u2; int z_num, z_div; int w, h; // pre-calcul largeur en octet des tableaux int nbw_tex = ((texture->pixel_width - 1) >> 3) + 1; char loop_w_tex = texture->pixel_width-1; char loop_h_tex = texture->pixel_height-1; // parcours en ligne for(int x=min_x; x<=max_x; x++) { u0 = u0_start; u1 = u1_start; u2 = u2_start; z_num = z_num_start; z_div = z_div_start; offset_vram = x >> 3; mask_vram = 128 >> (x & 7); // parcours en colonne for(int y=min_y; y<=max_y; y++) { // si le pixel (x;y) est dans le triangle if ((u0 | u1 | u2) > 0) { // addresse du z-buffer address = x + y * z_buffer_width + z_buffer_offset; // si le pixel (x;y) est plus proche qu'avant if (z_num <= z_buffer[address] + decoration) { // calcul des coordonnees pour la texture w = ((u0 * w0 + u1 * w1 + u2 * w2) / z_div) & loop_w_tex; h = ((u0 * h0 + u1 * h1 + u2 * h2) / z_div) & loop_h_tex; // calcul du masque pour l'octet mask_w = 128 >> (w & 7); if (texture->mask == NULL) { // enregistre la profondeur du pixel dans le z buffer z_buffer[address] = z_num; if ((texture->sprite[(w >> 3) + (h * nbw_tex)] & mask_w)) { // afficher pixel noir vram[(y << 4) + offset_vram] |= mask_vram; }else{ // afficher pixel blanc vram[(y << 4) + offset_vram] &= ~mask_vram; } // curseur pixel_on_cursor(x, y); } else { int alpha = (w >> 3) + (h * nbw_tex); if ((texture->mask[alpha] & mask_w)) { // enregistre la profondeur du pixel dans le z buffer z_buffer[address] = z_num; if ((texture->sprite[alpha] & mask_w)) { // afficher pixel noir vram[(y << 4) + offset_vram] |= mask_vram; }else{ // afficher pixel blanc vram[(y << 4) + offset_vram] &= ~mask_vram; } // curseur pixel_on_cursor(x, y); } } } } u0 += u0_step_y; u1 += u1_step_y; u2 += u2_step_y; z_num += z_num_step_y; z_div += z_div_step_y; } u0_start += u0_step_x; u1_start += u1_step_x; u2_start += u2_step_x; z_num_start += z_num_step_x; z_div_start += z_div_step_x; } } void Windmill::render_triangle_black(Vertex* vertexA, Vertex* vertexB, Vertex* vertexC) { // calcul du rectangle circonscrit au triangle int min_x = max(viewport.x1, min(vertexA->x, min(vertexB->x, vertexC->x))); int max_x = min(viewport.x2-1, max(vertexA->x, max(vertexB->x, vertexC->x))); int min_y = max(viewport.y1, min(vertexA->y, min(vertexB->y, vertexC->y))); int max_y = min(viewport.y2-1, max(vertexA->y, max(vertexB->y, vertexC->y))); // calcul de l'aire du triangle int area = edge(vertexA, vertexB, vertexC); // termine si la taille du triangle est trop petite if (area <= MIN_AREA_CLIP) return; // calcul des produits vectoriels int u0_start = edge_start(vertexB, vertexC, min_x, min_y); int u0_step_x = edge_step_x(vertexB, vertexC); int u0_step_y = edge_step_y(vertexB, vertexC); int u1_start = edge_start(vertexC, vertexA, min_x, min_y); int u1_step_x = edge_step_x(vertexC, vertexA); int u1_step_y = edge_step_y(vertexC, vertexA); int u2_start = edge_start(vertexA, vertexB, min_x, min_y); int u2_step_x = edge_step_x(vertexA, vertexB); int u2_step_y = edge_step_y(vertexA, vertexB); int z_num_start = (u0_start * vertexA->z_normalized + u1_start * vertexB->z_normalized + u2_start * vertexC->z_normalized) / area; int z_num_step_x = (u0_step_x * vertexA->z_normalized + u1_step_x * vertexB->z_normalized + u2_step_x * vertexC->z_normalized) / area; int z_num_step_y = (u0_step_y * vertexA->z_normalized + u1_step_y * vertexB->z_normalized + u2_step_y * vertexC->z_normalized) / area; // acces a la vram char* vram = get_vram_address(); unsigned char mask_vram; int offset_vram; int address; int u0, u1, u2; int z_num; // parcours en ligne for(int x=min_x; x<=max_x; x++) { u0 = u0_start; u1 = u1_start; u2 = u2_start; z_num = z_num_start; offset_vram = x >> 3; mask_vram = 128 >> (x & 7); // parcours en colonne for(int y=min_y; y<=max_y; y++) { // si le pixel (x;y) est dans le triangle if ((u0 | u1 | u2) > 0) { // addresse du z-buffer address = x + y * z_buffer_width + z_buffer_offset; // si le pixel (x;y) est plus proche qu'avant if (z_num <= z_buffer[address]) { // enregistre la profondeur du pixel dans le z buffer z_buffer[address] = z_num; // afficher pixel noir vram[(y << 4) + offset_vram] |= mask_vram; // curseur pixel_on_cursor(x, y); } } u0 += u0_step_y; u1 += u1_step_y; u2 += u2_step_y; z_num += z_num_step_y; } u0_start += u0_step_x; u1_start += u1_step_x; u2_start += u2_step_x; z_num_start += z_num_step_x; } } /* void Windmill::render_triangle_black(Vertex* vertexA, Vertex* vertexB, Vertex* vertexC) { // calcul du rectangle circonscrit au triangle int min_x = max(viewport.x1, min(vertexA->x, min(vertexB->x, vertexC->x))); int max_x = min(viewport.x2-1, max(vertexA->x, max(vertexB->x, vertexC->x))); int min_y = max(viewport.y1, min(vertexA->y, min(vertexB->y, vertexC->y))); int max_y = min(viewport.y2-1, max(vertexA->y, max(vertexB->y, vertexC->y))); // calcul de l'aire du triangle int area = edge(vertexA, vertexB, vertexC); // termine si la taille du triangle est trop petite if (area <= MIN_AREA_CLIP) return; // calcul des produits vectoriels int u0_start = edge_start(vertexB, vertexC, min_x, min_y); int u0_step_x = edge_step_x(vertexB, vertexC); int u0_step_y = edge_step_y(vertexB, vertexC); int u1_start = edge_start(vertexC, vertexA, min_x, min_y); int u1_step_x = edge_step_x(vertexC, vertexA); int u1_step_y = edge_step_y(vertexC, vertexA); int u2_start = edge_start(vertexA, vertexB, min_x, min_y); int u2_step_x = edge_step_x(vertexA, vertexB); int u2_step_y = edge_step_y(vertexA, vertexB); int z_num_start = (u0_start * vertexA->z_normalized + u1_start * vertexB->z_normalized + u2_start * vertexC->z_normalized) / area; int z_num_step_x = (u0_step_x * vertexA->z_normalized + u1_step_x * vertexB->z_normalized + u2_step_x * vertexC->z_normalized) / area; int z_num_step_y = (u0_step_y * vertexA->z_normalized + u1_step_y * vertexB->z_normalized + u2_step_y * vertexC->z_normalized) / area; // acces a la vram // char* vram = get_vram_address(); unsigned char mask_vram; int offset_vram; int address; int u0, u1, u2; int z_num; // parcours en ligne for(int x=min_x; x<=max_x; x++) { u0 = u0_start; u1 = u1_start; u2 = u2_start; z_num = z_num_start; offset_vram = x >> 3; mask_vram = 128 >> (x & 7); // parcours en colonne for(int y=min_y; y<=max_y; y++) { // si le pixel (x;y) est dans le triangle if ((u0 | u1 | u2) > 0) { // addresse du z-buffer address = x + y * z_buffer_width + z_buffer_offset; // si le pixel (x;y) est plus proche qu'avant if (z_num <= z_buffer[address]) { // enregistre la profondeur du pixel dans le z buffer z_buffer[address] = z_num; // afficher pixel noir // vram[(y << 4) + offset_vram] |= mask_vram; // curseur pixel_on_cursor(x, y); } } u0 += u0_step_y; u1 += u1_step_y; u2 += u2_step_y; z_num += z_num_step_y; } u0_start += u0_step_x; u1_start += u1_step_x; u2_start += u2_step_x; z_num_start += z_num_step_x; } } */ void Windmill::render_triangle_white(Vertex* vertexA, Vertex* vertexB, Vertex* vertexC) { // calcul du rectangle circonscrit au triangle int min_x = max(viewport.x1, min(vertexA->x, min(vertexB->x, vertexC->x))); int max_x = min(viewport.x2-1, max(vertexA->x, max(vertexB->x, vertexC->x))); int min_y = max(viewport.y1, min(vertexA->y, min(vertexB->y, vertexC->y))); int max_y = min(viewport.y2-1, max(vertexA->y, max(vertexB->y, vertexC->y))); // calcul de l'aire du triangle int area = edge(vertexA, vertexB, vertexC); // termine si la taille du triangle est trop petite if (area <= MIN_AREA_CLIP) return; // calcul des produits vectoriels int u0_start = edge_start(vertexB, vertexC, min_x, min_y); int u0_step_x = edge_step_x(vertexB, vertexC); int u0_step_y = edge_step_y(vertexB, vertexC); int u1_start = edge_start(vertexC, vertexA, min_x, min_y); int u1_step_x = edge_step_x(vertexC, vertexA); int u1_step_y = edge_step_y(vertexC, vertexA); int u2_start = edge_start(vertexA, vertexB, min_x, min_y); int u2_step_x = edge_step_x(vertexA, vertexB); int u2_step_y = edge_step_y(vertexA, vertexB); int z_num_start = (u0_start * vertexA->z_normalized + u1_start * vertexB->z_normalized + u2_start * vertexC->z_normalized) / area; int z_num_step_x = (u0_step_x * vertexA->z_normalized + u1_step_x * vertexB->z_normalized + u2_step_x * vertexC->z_normalized) / area; int z_num_step_y = (u0_step_y * vertexA->z_normalized + u1_step_y * vertexB->z_normalized + u2_step_y * vertexC->z_normalized) / area; // acces a la vram char* vram = get_vram_address(); unsigned char mask_vram; int offset_vram; int address; int u0, u1, u2; int z_num; // parcours en ligne for(int x=min_x; x<=max_x; x++) { u0 = u0_start; u1 = u1_start; u2 = u2_start; z_num = z_num_start; offset_vram = x >> 3; mask_vram = 128 >> (x & 7); // parcours en colonne for(int y=min_y; y<=max_y; y++) { // si le pixel (x;y) est dans le triangle if ((u0 | u1 | u2) > 0) { // addresse du z-buffer address = x + y * z_buffer_width + z_buffer_offset; // si le pixel (x;y) est plus proche qu'avant if (z_num <= z_buffer[address]) { // enregistre la profondeur du pixel dans le z buffer z_buffer[address] = z_num; // afficher pixel blanc vram[(y << 4) + offset_vram] &= ~mask_vram; // curseur pixel_on_cursor(x, y); } } u0 += u0_step_y; u1 += u1_step_y; u2 += u2_step_y; z_num += z_num_step_y; } u0_start += u0_step_x; u1_start += u1_step_x; u2_start += u2_step_x; z_num_start += z_num_step_x; } } void Windmill::render_triangle_transparent(Vertex* vertexA, Vertex* vertexB, Vertex* vertexC, const Texture* texture) { // calcul du rectangle circonscrit au triangle int min_x = max(viewport.x1, min(vertexA->x, min(vertexB->x, vertexC->x))); int max_x = min(viewport.x2-1, max(vertexA->x, max(vertexB->x, vertexC->x))); int min_y = max(viewport.y1, min(vertexA->y, min(vertexB->y, vertexC->y))); int max_y = min(viewport.y2-1, max(vertexA->y, max(vertexB->y, vertexC->y))); // calcul de l'aire du triangle int area = edge(vertexA, vertexB, vertexC); // termine si la taille du triangle est trop petite if (area <= MIN_AREA_CLIP) return; // pre-calcul des coordonnees de la texture int w0 = vertexA->w * vertexA->z; int h0 = vertexA->h * vertexA->z; int w1 = vertexB->w * vertexB->z; int h1 = vertexB->h * vertexB->z; int w2 = vertexC->w * vertexC->z; int h2 = vertexC->h * vertexC->z; // calcul des produits vectoriels int u0_start = edge_start(vertexB, vertexC, min_x, min_y); int u0_step_x = edge_step_x(vertexB, vertexC); int u0_step_y = edge_step_y(vertexB, vertexC); int u1_start = edge_start(vertexC, vertexA, min_x, min_y); int u1_step_x = edge_step_x(vertexC, vertexA); int u1_step_y = edge_step_y(vertexC, vertexA); int u2_start = edge_start(vertexA, vertexB, min_x, min_y); int u2_step_x = edge_step_x(vertexA, vertexB); int u2_step_y = edge_step_y(vertexA, vertexB); int z_num_start = (u0_start * vertexA->z_normalized + u1_start * vertexB->z_normalized + u2_start * vertexC->z_normalized) / area; int z_num_step_x = (u0_step_x * vertexA->z_normalized + u1_step_x * vertexB->z_normalized + u2_step_x * vertexC->z_normalized) / area; int z_num_step_y = (u0_step_y * vertexA->z_normalized + u1_step_y * vertexB->z_normalized + u2_step_y * vertexC->z_normalized) / area; int z_div_start = u0_start * vertexA->z + u1_start * vertexB->z + u2_start * vertexC->z; int z_div_step_x = u0_step_x * vertexA->z + u1_step_x * vertexB->z + u2_step_x * vertexC->z; int z_div_step_y = u0_step_y * vertexA->z + u1_step_y * vertexB->z + u2_step_y * vertexC->z; // acces a la vram char* vram = get_vram_address(); // pre-calcul largeur en octet des tableaux int nbw_tex = ((texture->pixel_width - 1) >> 3) + 1; char loop_w_tex = texture->pixel_width-1; char loop_h_tex = texture->pixel_height-1; // parcours en ligne for(int x=min_x; x<=max_x; x++) { int u0 = u0_start; int u1 = u1_start; int u2 = u2_start; int z_num = z_num_start; int z_div = z_div_start; int offset_vram = x >> 3; char mask_vram = 128 >> (x & 7); // parcours en colonne for(int y=min_y; y<=max_y; y++) { // si le pixel (x;y) est dans le triangle if ((u0 | u1 | u2) > 0) { // addresse du z-buffer int address = x + y * z_buffer_width + z_buffer_offset; // si le pixel (x;y) est plus proche qu'avant if (z_num <= z_buffer[address]) { // calcul des coordonnees pour la texture unsigned int w = ((u0 * w0 + u1 * w1 + u2 * w2) / z_div) & loop_w_tex; unsigned int h = ((u0 * h0 + u1 * h1 + u2 * h2) / z_div) & loop_h_tex; // calcul du masque pour l'octet unsigned char mask_w = 128 >> (w & 7); if ((texture->sprite[(w >> 3) + (h * nbw_tex)] & mask_w)) { // afficher pixel noir vram[(y << 4) + offset_vram] |= mask_vram; // enregistre la profondeur du pixel dans le z buffer z_buffer[address] = z_num; } } } u0 += u0_step_y; u1 += u1_step_y; u2 += u2_step_y; z_num += z_num_step_y; z_div += z_div_step_y; } u0_start += u0_step_x; u1_start += u1_step_x; u2_start += u2_step_x; z_num_start += z_num_step_x; z_div_start += z_div_step_x; } } /* //----------------------------------------------------------------------------- // load_map // Ritter's bounding sphere //----------------------------------------------------------------------------- void Windmill::load_map(Map* _map) { if (map != _map) { map = _map; delete[] sphere; sphere = new Bounding_Sphere [map->list_object_length]; for (int i = 0; ilist_object_length; i++) { Object* current_object = map->object[i]; int nb_point; for (int j = 0; jmodele_size; j++) { nb_point += current_object->modele[j].type; } Point* point = get_point(current_object, 0); int min_x = point->x; int min_y = point->y; int min_z = point->z; int max_x = min_x; int max_y = min_y; int max_z = min_z; for (int j = 0; jx < min_x) min_x = point->x; if(point->y < min_y) min_y = point->y; if(point->z < min_z) min_z = point->z; if(point->x > max_x) max_x = point->x; if(point->y > max_y) max_y = point->y; if(point->z > max_z) max_z = point->z; } int xdiff = max_x - min_x; int ydiff = max_y - min_y; int zdiff = max_z - min_y; int diameter = max(xdiff, max(ydiff, zdiff)); //take max as diameter glm::vec3 centre = (vmax+vmin)*(0.5f); float radius = diameter/2; float sq_radius = radius*radius; for(int i=0; i sq_radius) { float distance = sqrt(sq_distance); float difference = distance - radius; float new_diameter = 2 * radius + difference; sq_radius = radius * radius; difference /= 2; centre += difference * direction; } } } } }*/ //---------------------------------------------------------------------------------------------------- // MANIPULATION //---------------------------------------------------------------------------------------------------- Point Windmill::get_point(const Modele* poly, int i) { Point point; if (i == 0) { point.x = poly->x0; point.y = poly->y0; point.z = poly->z0; } if (i == 1) { point.x = poly->x1; point.y = poly->y1; point.z = poly->z1; } if (i == 2) { point.x = poly->x2; point.y = poly->y2; point.z = poly->z2; } if (i == 3) { if (poly->option == PARA) { point.x = poly->x2 + poly->x1 - poly->x0; point.y = poly->y2 + poly->y1 - poly->y0; point.z = poly->z2 + poly->z1 - poly->z0; } else if (poly->option == TRAP) { point.x = poly->x2 - poly->x1 + poly->x0; point.y = poly->y2 - poly->y1 + poly->y0; point.z = poly->z2 - poly->z1 + poly->z0; } } return point; } Point Windmill::get_center_poly(const Modele* poly) { Point point; point.x = 0; point.y = 0; point.z = 0; for (int i = 0; i < poly->type; i++) { point.x += get_point(poly, i).x; point.y += get_point(poly, i).y; point.z += get_point(poly, i).z; } point.x /= poly->type; point.y /= poly->type; point.z /= poly->type; return point; } void Windmill::compute_object_angle(Object* object, int* cosinus, int* sinus) { if (object->axe == Z_BILLBOARD) { float angle_look_at_camera = atan2f(camera.y - object->y, camera.x - object->x); float angle_rad = angle_look_at_camera + 3.1415 * object->angle / 180.0; *cosinus = 1024 * cosf(angle_rad); *sinus = 1024 * sinf(angle_rad); } if (object->axe == X or object->axe == Y or object->axe == Z) { float angle_rad = 3.1415 * object->angle / 180.0; *cosinus = 1024 * cosf(angle_rad); *sinus = 1024 * sinf(angle_rad); } } void Windmill::extract_vertex_from_poly(const Modele* poly, Vertex* vertex, int* vertex_length, int* width, int* height) { int x0, y0, z0, x1, y1, z1, x2, y2, z2, x3, y3, z3; if (poly->type == TRIANGLE) { *vertex_length = 3; x0 = poly->x0; y0 = poly->y0; z0 = poly->z0; x1 = poly->x1; y1 = poly->y1; z1 = poly->z1; x2 = poly->x2; y2 = poly->y2; z2 = poly->z2; vertex[0].set_xyz(x0, y0, z0); vertex[1].set_xyz(x1, y1, z1); vertex[2].set_xyz(x2, y2, z2); *width = distance(x0-x1, y0-y1, z0-z1); *height = distance(x0-x2, y0-y2, z0-z2); } if (poly->type == RECTANGLE) { *vertex_length = 4; x0 = poly->x0; y0 = poly->y0; z0 = poly->z0; x1 = poly->x1; y1 = poly->y1; z1 = poly->z1; x3 = poly->x2; y3 = poly->y2; z3 = poly->z2; if (poly->option == PARA) { x2 = x3 + x1 - x0; y2 = y3 + y1 - y0; z2 = z3 + z1 - z0; } else if (poly->option == TRAP) { //x2 = x3 - x1 + x0; y2 = y3 - y1 + y0; z2 = z3 - z1 + z0; } vertex[0].set_xyz(x0, y0, z0); vertex[1].set_xyz(x1, y1, z1); vertex[2].set_xyz(x2, y2, z2); vertex[3].set_xyz(x3, y3, z3); *width = distance(x0-x1, y0-y1, z0-z1); *height = distance(x0-x3, y0-y3, z0-z3); } } void Windmill::texture_coordinates(const Modele* poly, Vertex* vertex, const Texture* texture, int visible, int width, int height) { int w = (texture->real_width > 0) ? width * texture->pixel_width / texture->real_width : texture->pixel_width; int h = (texture->real_height > 0) ? height * texture->pixel_height / texture->real_height : texture->pixel_height; if (poly->type == TRIANGLE) { int offset = w * texture->offset; if (visible == FRONT or texture->mirror == true) { vertex[0].set_wh(0, texture->pixel_height); vertex[1].set_wh(w, texture->pixel_height); vertex[2].set_wh(offset, texture->pixel_height-h); // vertex[0].set_wh(0, h); // vertex[1].set_wh(w, h); // vertex[2].set_wh(offset, 0); } else { vertex[0].set_wh(w, h); vertex[1].set_wh(0, h); vertex[2].set_wh(w-offset, 0); } } else if (poly->type == RECTANGLE) { if (visible == FRONT or texture->mirror == true) { vertex[0].set_wh(0, texture->pixel_height); vertex[1].set_wh(w, texture->pixel_height); vertex[2].set_wh(w, texture->pixel_height-h); vertex[3].set_wh(0, texture->pixel_height-h); // vertex[0].set_wh(0, h); // vertex[1].set_wh(w, h); // vertex[2].set_wh(w, 0); // vertex[3].set_wh(0, 0); } else { vertex[0].set_wh(w, h); vertex[1].set_wh(0, h); vertex[2].set_wh(0, 0); vertex[3].set_wh(w, 0); } } } void Windmill::swap_vertex(Vertex* vertexA, Vertex* vertexB) { Vertex vertex_temp; memcpy(&vertex_temp, vertexA, sizeof(Vertex)); memcpy(vertexA, vertexB, sizeof(Vertex)); memcpy(vertexB, &vertex_temp, sizeof(Vertex)); } void Windmill::copy_vertex(Vertex* vertex_source, Vertex* vertex_dest) { memcpy(vertex_dest, vertex_source, sizeof(Vertex)); } //---------------------------------------------------------------------------------------------------- // SPHERE //---------------------------------------------------------------------------------------------------- Sphere Windmill::transform_sphere_to_world(Sphere* sphere_input, Object* object, int cosinus, int sinus) { Vertex vertex; vertex.set_xyz(sphere_input->x, sphere_input->y, sphere_input->z); transform_model_to_world(&vertex, 1, object, cosinus, sinus); transform_world_to_camera(&vertex, 1); Sphere sphere_output; sphere_output.x = vertex.x >> 7; sphere_output.y = vertex.y >> 7; sphere_output.z = vertex.z >> 7; sphere_output.radius = sphere_input->radius; return sphere_output; } /* bool Windmill::sphere_in_cone(Sphere* sphere) { //return true; int cone_offset = sphere->radius * camera.sin_fov; int nx = camera.nx; int ny = camera.ny; int nz = camera.nz; Point cone; cone.x = int(camera.x) - ((nx * cone_offset) >> 14); cone.y = int(camera.y) - ((ny * cone_offset) >> 14); cone.z = int(camera.z) - ((nz * cone_offset) >> 14); 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; }*/ bool Windmill::sphere_in_cone(Sphere* sphere) { //return true; sphere->z += sphere->radius; if (sphere->z > 0) { int sphere_z = int(sphere->z * camera.scale_coef) >> 6; int xr = 0; if (sphere->x > 0) xr = sphere->x - sphere->radius; if (sphere->x < 0) xr = - sphere->x - sphere->radius; if (xr <= sphere_z) return true; } return false; } //---------------------------------------------------------------------------------------------------- // DIVERS //---------------------------------------------------------------------------------------------------- void Windmill::copy_camera() { memcpy(&camera, camera2, sizeof(Camera)); } void Windmill::sort_object() { //qsort(object, list_object_length, sizeof(Object*), compare_object); } int compare_object(void const *a, void const *b) { Object* object_a = *(Object **) a; Object* object_b = *(Object **) b; return (object_a->distance_to_camera - object_b->distance_to_camera); } void Windmill::clear_z_buffer() { memset(z_buffer, MAX_DEPTH_Z_BUFFER, z_buffer_size * sizeof(short)); } int Windmill::edge(Vertex* a, Vertex* b, Vertex* c) { return (c->x - a->x) * (b->y - a->y) - (c->y - a->y) * (b->x - a->x); } int Windmill::edge_start(Vertex* a, Vertex* b, int px, int py) { return (b->y - a->y) * (px - a->x) - (b->x - a->x) * (py - a->y); } int Windmill::edge_step_x(Vertex* a, Vertex* b) { return b->y - a->y; } int Windmill::edge_step_y(Vertex* a, Vertex* b) { return a->x - b->x; } //---------------------------------------------------------------------------------------------------- // UTILITAIRE //---------------------------------------------------------------------------------------------------- void Windmill::pixel_on_cursor(int x, int y) { if (x == cursor_x and y == cursor_y) { temp_object_cursor = object[i]; temp_poly_cursor = j; } } void Windmill::show_coordinates() { // coordonnees float tab_coordinates[5] = {camera.x, camera.y, camera.z, to_deg(camera.yaw), to_deg(camera.pitch)}; for (int i = 0; i < 5; i++) { /* char str[20]; sprintf(str, "%f", tab_coordinates[i]); int j; for(j= 0; j < 16; j++) { if (str[j] == '.') { str[j+2] = '\0'; break; } } PrintMini(127-4*(j+2), 1+6*i, (unsigned char*)str, MINI_OVER);*/ dprint(0, 1+6*i, C_BLACK, "%f", tab_coordinates[i]); // need Gint } } void Windmill::show_repere() {/* // repere int repere_x = 112; int repere_y = 55; int repere_size = 12; float camera_yaw_rad = -camera.yaw;//- 3.1415 * camera.yaw / 180.0; float camera_pitch_rad = camera.pitch;//3.1415 * camera.pitch / 180.0; float cos_yaw = cosf(camera_yaw_rad); float sin_yaw = sinf(camera_yaw_rad); float cos_pitch = cosf(camera_pitch_rad); float sin_pitch = sinf(camera_pitch_rad); float aa4 = sin_yaw; float aa5 = cos_yaw; float aa6 = 0; float aa7 = -sin_pitch * cos_yaw; float aa8 = sin_pitch * sin_yaw; float aa9 = cos_pitch; // repere Vertex v[3]; //const unsigned char* cx = "x"; //const unsigned char* cy = "y"; //const unsigned char* cz = "z"; const char* letter[] = {"x", "y", "z"}; v[0].set_xyz(repere_size, 0 , 0 ); v[1].set_xyz(0 , repere_size, 0 ); v[2].set_xyz(0 , 0 , repere_size); for (int i = 0; i < 3; i++) { float y = (aa4 * v[i].x + aa5 * v[i].y + aa6 * v[i].z); float z = (aa7 * v[i].x + aa8 * v[i].y + aa9 * v[i].z); v[i].x = -y; v[i].y = -z; //ML_line(repere_x, repere_y, v[i].x + repere_x, v[i].y + repere_y, ML_BLACK); //PrintMini(v[i].x + repere_x, v[i].y + repere_y-2, letter[i], MINI_OVER); } */ } /*void Windmill::show_fps() { char str[20]; sprintf(str, "%i", time_get_fps()); PrintMini(1, 58, (unsigned char*)str, MINI_OVER); }*/ //---------------------------------------------------------------------------------------------------- // DESTRUCTEUR //---------------------------------------------------------------------------------------------------- Windmill::~Windmill() { free(z_buffer); free(object); //delete[] object; } float distance(float dx, float dy, float dz) { return sqrtf(dx*dx + dy*dy + dz*dz); } Vertex::Vertex(){} void Vertex::set_xyz(int _x, int _y, int _z) { x = _x; y = _y; z = _z; } void Vertex::set_wh(float _w, float _h) { w = _w; h = _h; } float Vertex::length(int x, int y, int z) { return sqrtf(x*x + y*y + z*z); }