#include "graphics/tinygl/zgl.h" namespace TinyGL { void glopNormal(GLContext *c, GLParam *p) { V3 v; v.X = p[1].f; v.Y = p[2].f; v.Z = p[3].f; c->current_normal.X = v.X; c->current_normal.Y = v.Y; c->current_normal.Z = v.Z; c->current_normal.W = 0; } void glopTexCoord(GLContext *c, GLParam *p) { c->current_tex_coord.X = p[1].f; c->current_tex_coord.Y = p[2].f; c->current_tex_coord.Z = p[3].f; c->current_tex_coord.W = p[4].f; } void glopEdgeFlag(GLContext *c, GLParam *p) { c->current_edge_flag = p[1].i; } void glopColor(GLContext *c, GLParam *p) { c->current_color.X = p[1].f; c->current_color.Y = p[2].f; c->current_color.Z = p[3].f; c->current_color.W = p[4].f; c->longcurrent_color[0] = p[5].ui; c->longcurrent_color[1] = p[6].ui; c->longcurrent_color[2] = p[7].ui; if (c->color_material_enabled) { GLParam q[7]; q[0].op = OP_Material; q[1].i = c->current_color_material_mode; q[2].i = c->current_color_material_type; q[3].f = p[1].f; q[4].f = p[2].f; q[5].f = p[3].f; q[6].f = p[4].f; glopMaterial(c, q); } } void gl_eval_viewport(GLContext *c) { GLViewport *v; float zsize = (1 << (ZB_Z_BITS + ZB_POINT_Z_FRAC_BITS)); v = &c->viewport; v->trans.X = (float)(((v->xsize - 0.5) / 2.0) + v->xmin); v->trans.Y = (float)(((v->ysize - 0.5) / 2.0) + v->ymin); v->trans.Z = (float)(((zsize - 0.5) / 2.0) + ((1 << ZB_POINT_Z_FRAC_BITS)) / 2); v->scale.X = (float)((v->xsize - 0.5) / 2.0); v->scale.Y = (float)(-(v->ysize - 0.5) / 2.0); v->scale.Z = (float)(-((zsize - 0.5) / 2.0)); } void glopBegin(GLContext *c, GLParam *p) { int type; M4 tmp; assert(c->in_begin == 0); type = p[1].i; c->begin_type = type; c->in_begin = 1; c->vertex_n = 0; c->vertex_cnt = 0; if (c->matrix_model_projection_updated) { if (c->lighting_enabled) { // precompute inverse modelview gl_M4_Inv(&tmp, c->matrix_stack_ptr[0]); gl_M4_Transpose(&c->matrix_model_view_inv, &tmp); } else { float *m = &c->matrix_model_projection.m[0][0]; // precompute projection matrix gl_M4_Mul(&c->matrix_model_projection, c->matrix_stack_ptr[1], c->matrix_stack_ptr[0]); // test to accelerate computation c->matrix_model_projection_no_w_transform = 0; if (m[12] == 0.0 && m[13] == 0.0 && m[14] == 0.0) c->matrix_model_projection_no_w_transform = 1; } // test if the texture matrix is not Identity c->apply_texture_matrix = !gl_M4_IsId(c->matrix_stack_ptr[2]); c->matrix_model_projection_updated = 0; } // viewport if (c->viewport.updated) { gl_eval_viewport(c); c->viewport.updated = 0; } // triangle drawing functions if (c->render_mode == TGL_SELECT) { c->draw_triangle_front = gl_draw_triangle_select; c->draw_triangle_back = gl_draw_triangle_select; } else { switch (c->polygon_mode_front) { case TGL_POINT: c->draw_triangle_front = gl_draw_triangle_point; break; case TGL_LINE: c->draw_triangle_front = gl_draw_triangle_line; break; default: c->draw_triangle_front = gl_draw_triangle_fill; break; } switch (c->polygon_mode_back) { case TGL_POINT: c->draw_triangle_back = gl_draw_triangle_point; break; case TGL_LINE: c->draw_triangle_back = gl_draw_triangle_line; break; default: c->draw_triangle_back = gl_draw_triangle_fill; break; } } } // coords, tranformation, clip code and projection // TODO : handle all cases static inline void gl_vertex_transform(GLContext *c, GLVertex *v) { float *m; V4 *n; if (c->lighting_enabled) { // eye coordinates needed for lighting m = &c->matrix_stack_ptr[0]->m[0][0]; v->ec.X = (v->coord.X * m[0] + v->coord.Y * m[1] + v->coord.Z * m[2] + m[3]); v->ec.Y = (v->coord.X * m[4] + v->coord.Y * m[5] + v->coord.Z * m[6] + m[7]); v->ec.Z = (v->coord.X * m[8] + v->coord.Y * m[9] + v->coord.Z * m[10] + m[11]); v->ec.W = (v->coord.X * m[12] + v->coord.Y * m[13] + v->coord.Z * m[14] + m[15]); // projection coordinates m = &c->matrix_stack_ptr[1]->m[0][0]; v->pc.X = (v->ec.X * m[0] + v->ec.Y * m[1] + v->ec.Z * m[2] + v->ec.W * m[3]); v->pc.Y = (v->ec.X * m[4] + v->ec.Y * m[5] + v->ec.Z * m[6] + v->ec.W * m[7]); v->pc.Z = (v->ec.X * m[8] + v->ec.Y * m[9] + v->ec.Z * m[10] + v->ec.W * m[11]); v->pc.W = (v->ec.X * m[12] + v->ec.Y * m[13] + v->ec.Z * m[14] + v->ec.W * m[15]); m = &c->matrix_model_view_inv.m[0][0]; n = &c->current_normal; v->normal.X = (n->X * m[0] + n->Y * m[1] + n->Z * m[2]); v->normal.Y = (n->X * m[4] + n->Y * m[5] + n->Z * m[6]); v->normal.Z = (n->X * m[8] + n->Y * m[9] + n->Z * m[10]); if (c->normalize_enabled) { gl_V3_Norm(&v->normal); } } else { // no eye coordinates needed, no normal // NOTE: W = 1 is assumed m = &c->matrix_model_projection.m[0][0]; v->pc.X = (v->coord.X * m[0] + v->coord.Y * m[1] + v->coord.Z * m[2] + m[3]); v->pc.Y = (v->coord.X * m[4] + v->coord.Y * m[5] + v->coord.Z * m[6] + m[7]); v->pc.Z = (v->coord.X * m[8] + v->coord.Y * m[9] + v->coord.Z * m[10] + m[11]); if (c->matrix_model_projection_no_w_transform) { v->pc.W = m[15]; } else { v->pc.W = (v->coord.X * m[12] + v->coord.Y * m[13] + v->coord.Z * m[14] + m[15]); } } v->clip_code = gl_clipcode(v->pc.X, v->pc.Y, v->pc.Z, v->pc.W); } void glopVertex(GLContext *c, GLParam *p) { GLVertex *v; int n, cnt; assert(c->in_begin != 0); n = c->vertex_n; cnt = c->vertex_cnt; cnt++; c->vertex_cnt = cnt; // quick fix to avoid crashes on large polygons if (n >= c->vertex_max) { GLVertex *newarray; c->vertex_max <<= 1; // just double size newarray = (GLVertex *)gl_malloc(sizeof(GLVertex) * c->vertex_max); if (!newarray) { error("unable to allocate GLVertex array."); } memcpy(newarray, c->vertex, n * sizeof(GLVertex)); gl_free(c->vertex); c->vertex = newarray; } // new vertex entry v = &c->vertex[n]; n++; v->coord.X = p[1].f; v->coord.Y = p[2].f; v->coord.Z = p[3].f; v->coord.W = p[4].f; gl_vertex_transform(c, v); // color if (c->lighting_enabled) { gl_shade_vertex(c, v); } else { v->color = c->current_color; } // tex coords if (c->texture_2d_enabled) { if (c->apply_texture_matrix) { gl_M4_MulV4(&v->tex_coord, c->matrix_stack_ptr[2], &c->current_tex_coord); } else { v->tex_coord = c->current_tex_coord; } } // precompute the mapping to the viewport if (v->clip_code == 0) gl_transform_to_viewport(c, v); // edge flag v->edge_flag = c->current_edge_flag; switch (c->begin_type) { case TGL_POINTS: gl_draw_point(c, &c->vertex[0]); n = 0; break; case TGL_LINES: if (n == 2) { gl_draw_line(c, &c->vertex[0], &c->vertex[1]); n = 0; } break; case TGL_LINE_STRIP: case TGL_LINE_LOOP: if (n == 1) { c->vertex[2] = c->vertex[0]; } else if (n == 2) { gl_draw_line(c, &c->vertex[0], &c->vertex[1]); c->vertex[0] = c->vertex[1]; n = 1; } break; case TGL_TRIANGLES: if (n == 3) { gl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]); n = 0; } break; case TGL_TRIANGLE_STRIP: if (cnt >= 3) { if (n == 3) n = 0; // needed to respect triangle orientation switch (cnt & 1) { case 0: gl_draw_triangle(c, &c->vertex[2], &c->vertex[1], &c->vertex[0]); break; case 1: gl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]); break; } } break; case TGL_TRIANGLE_FAN: if (n == 3) { gl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]); c->vertex[1] = c->vertex[2]; n = 2; } break; case TGL_QUADS: if (n == 4) { c->vertex[2].edge_flag = 0; gl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]); c->vertex[2].edge_flag = 1; c->vertex[0].edge_flag = 0; gl_draw_triangle(c, &c->vertex[0], &c->vertex[2], &c->vertex[3]); n = 0; } break; case TGL_QUAD_STRIP: if (n == 4) { gl_draw_triangle(c, &c->vertex[0], &c->vertex[1], &c->vertex[2]); gl_draw_triangle(c, &c->vertex[1], &c->vertex[3], &c->vertex[2]); for (int i = 0; i < 2; i++) c->vertex[i] = c->vertex[i + 2]; n = 2; } break; case TGL_POLYGON: break; default: error("glBegin: type %x not handled", c->begin_type); } c->vertex_n = n; } void glopEnd(GLContext *c, GLParam *) { assert(c->in_begin == 1); if (c->begin_type == TGL_LINE_LOOP) { if (c->vertex_cnt >= 3) { gl_draw_line(c, &c->vertex[0], &c->vertex[2]); } } else if (c->begin_type == TGL_POLYGON) { int i = c->vertex_cnt; while (i >= 3) { i--; gl_draw_triangle(c, &c->vertex[i], &c->vertex[0], &c->vertex[i - 1]); } } c->in_begin = 0; } } // end of namespace TinyGL