static void offset_tri( struct draw_stage *stage, struct prim_header *header ) { struct prim_header tmp; tmp.det = header->det; tmp.flags = header->flags; tmp.pad = header->pad; tmp.v[0] = dup_vert(stage, header->v[0], 0); tmp.v[1] = dup_vert(stage, header->v[1], 1); tmp.v[2] = dup_vert(stage, header->v[2], 2); do_offset_tri( stage, &tmp ); }
static void flatshade_tri_2( struct draw_stage *stage, struct prim_header *header ) { struct prim_header tmp; tmp.det = header->det; tmp.flags = header->flags; tmp.pad = header->pad; tmp.v[0] = dup_vert(stage, header->v[0], 0); tmp.v[1] = dup_vert(stage, header->v[1], 1); tmp.v[2] = header->v[2]; copy_colors2(stage, tmp.v[0], tmp.v[1], tmp.v[2]); stage->next->tri( stage->next, &tmp ); }
static void emit_segment(struct draw_stage *stage, struct prim_header *header, float t0, float t1) { struct vertex_header *v0new = dup_vert(stage, header->v[0], 0); struct vertex_header *v1new = dup_vert(stage, header->v[1], 1); struct prim_header newprim = *header; if (t0 > 0.0) { screen_interp( stage->draw, v0new, t0, header->v[0], header->v[1] ); newprim.v[0] = v0new; } if (t1 < 1.0) { screen_interp( stage->draw, v1new, t1, header->v[0], header->v[1] ); newprim.v[1] = v1new; } stage->next->line( stage->next, &newprim ); }
static void flatshade_line_1( struct draw_stage *stage, struct prim_header *header ) { struct prim_header tmp; tmp.v[0] = dup_vert(stage, header->v[0], 0); tmp.v[1] = header->v[1]; copy_colors(stage, tmp.v[0], tmp.v[1]); stage->next->line( stage->next, &tmp ); }
/** * Copy back color(s) to front color(s). */ static INLINE struct vertex_header * copy_bfc( struct twoside_stage *twoside, const struct vertex_header *v, unsigned idx ) { struct vertex_header *tmp = dup_vert( &twoside->stage, v, idx ); if (twoside->attrib_back0) { COPY_4FV(tmp->data[twoside->attrib_front0], tmp->data[twoside->attrib_back0]); } if (twoside->attrib_back1) { COPY_4FV(tmp->data[twoside->attrib_front1], tmp->data[twoside->attrib_back1]); } return tmp; }
/* Clip a triangle against the viewport and user clip planes. */ static void do_clip_tri( struct draw_stage *stage, struct prim_header *header, unsigned clipmask ) { struct clip_stage *clipper = clip_stage( stage ); struct vertex_header *a[MAX_CLIPPED_VERTICES]; struct vertex_header *b[MAX_CLIPPED_VERTICES]; struct vertex_header **inlist = a; struct vertex_header **outlist = b; unsigned tmpnr = 0; unsigned n = 3; unsigned i; boolean aEdges[MAX_CLIPPED_VERTICES]; boolean bEdges[MAX_CLIPPED_VERTICES]; boolean *inEdges = aEdges; boolean *outEdges = bEdges; inlist[0] = header->v[0]; inlist[1] = header->v[1]; inlist[2] = header->v[2]; /* * Note: at this point we can't just use the per-vertex edge flags. * We have to observe the edge flag bits set in header->flags which * were set during primitive decomposition. Put those flags into * an edge flags array which parallels the vertex array. * Later, in the 'unfilled' pipeline stage we'll draw the edge if both * the header.flags bit is set AND the per-vertex edgeflag field is set. */ inEdges[0] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_0); inEdges[1] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_1); inEdges[2] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_2); while (clipmask && n >= 3) { const unsigned plane_idx = ffs(clipmask)-1; const boolean is_user_clip_plane = plane_idx >= 6; const float *plane = clipper->plane[plane_idx]; struct vertex_header *vert_prev = inlist[0]; boolean *edge_prev = &inEdges[0]; float dp_prev = dot4( vert_prev->clip, plane ); unsigned outcount = 0; clipmask &= ~(1<<plane_idx); assert(n < MAX_CLIPPED_VERTICES); if (n >= MAX_CLIPPED_VERTICES) return; inlist[n] = inlist[0]; /* prevent rotation of vertices */ inEdges[n] = inEdges[0]; for (i = 1; i <= n; i++) { struct vertex_header *vert = inlist[i]; boolean *edge = &inEdges[i]; float dp = dot4( vert->clip, plane ); if (!IS_NEGATIVE(dp_prev)) { assert(outcount < MAX_CLIPPED_VERTICES); if (outcount >= MAX_CLIPPED_VERTICES) return; outEdges[outcount] = *edge_prev; outlist[outcount++] = vert_prev; } if (DIFFERENT_SIGNS(dp, dp_prev)) { struct vertex_header *new_vert; boolean *new_edge; assert(tmpnr < MAX_CLIPPED_VERTICES + 1); if (tmpnr >= MAX_CLIPPED_VERTICES + 1) return; new_vert = clipper->stage.tmp[tmpnr++]; assert(outcount < MAX_CLIPPED_VERTICES); if (outcount >= MAX_CLIPPED_VERTICES) return; new_edge = &outEdges[outcount]; outlist[outcount++] = new_vert; if (IS_NEGATIVE(dp)) { /* Going out of bounds. Avoid division by zero as we * know dp != dp_prev from DIFFERENT_SIGNS, above. */ float t = dp / (dp - dp_prev); interp( clipper, new_vert, t, vert, vert_prev ); /* Whether or not to set edge flag for the new vert depends * on whether it's a user-defined clipping plane. We're * copying NVIDIA's behaviour here. */ if (is_user_clip_plane) { /* we want to see an edge along the clip plane */ *new_edge = TRUE; new_vert->edgeflag = TRUE; } else { /* we don't want to see an edge along the frustum clip plane */ *new_edge = *edge_prev; new_vert->edgeflag = FALSE; } } else { /* Coming back in. */ float t = dp_prev / (dp_prev - dp); interp( clipper, new_vert, t, vert_prev, vert ); /* Copy starting vert's edgeflag: */ new_vert->edgeflag = vert_prev->edgeflag; *new_edge = *edge_prev; } } vert_prev = vert; edge_prev = edge; dp_prev = dp; } /* swap in/out lists */ { struct vertex_header **tmp = inlist; inlist = outlist; outlist = tmp; n = outcount; } { boolean *tmp = inEdges; inEdges = outEdges; outEdges = tmp; } } /* If flat-shading, copy provoking vertex color to polygon vertex[0] */ if (n >= 3) { if (clipper->flat) { if (stage->draw->rasterizer->flatshade_first) { if (inlist[0] != header->v[0]) { assert(tmpnr < MAX_CLIPPED_VERTICES + 1); if (tmpnr >= MAX_CLIPPED_VERTICES + 1) return; inlist[0] = dup_vert(stage, inlist[0], tmpnr++); copy_colors(stage, inlist[0], header->v[0]); } } else { if (inlist[0] != header->v[2]) { assert(tmpnr < MAX_CLIPPED_VERTICES + 1); if (tmpnr >= MAX_CLIPPED_VERTICES + 1) return; inlist[0] = dup_vert(stage, inlist[0], tmpnr++); copy_colors(stage, inlist[0], header->v[2]); } } } /* Emit the polygon as triangles to the setup stage: */ emit_poly( stage, inlist, inEdges, n, header ); } }
/** * Draw an AA point by drawing a quad. */ static void aapoint_point(struct draw_stage *stage, struct prim_header *header) { const struct aapoint_stage *aapoint = aapoint_stage(stage); struct prim_header tri; struct vertex_header *v[4]; const uint tex_slot = aapoint->tex_slot; const uint pos_slot = aapoint->pos_slot; float radius, *pos, *tex; uint i; float k; if (aapoint->psize_slot >= 0) { radius = 0.5f * header->v[0]->data[aapoint->psize_slot][0]; } else { radius = aapoint->radius; } /* * Note: the texcoords (generic attrib, really) we use are special: * The S and T components simply vary from -1 to +1. * The R component is k, below. * The Q component is 1.0 and will used as a handy constant in the * fragment shader. */ /* * k is the threshold distance from the point's center at which * we begin alpha attenuation (the coverage value). * Operating within a unit circle, we'll compute the fragment's * distance 'd' from the center point using the texcoords. * IF d > 1.0 THEN * KILL fragment * ELSE IF d > k THEN * compute coverage in [0,1] proportional to d in [k, 1]. * ELSE * coverage = 1.0; // full coverage * ENDIF * * Note: the ELSEIF and ELSE clauses are actually implemented with CMP to * avoid using IF/ELSE/ENDIF TGSI opcodes. */ #if !NORMALIZE k = 1.0f / radius; k = 1.0f - 2.0f * k + k * k; #else k = 1.0f - 1.0f / radius; #endif /* allocate/dup new verts */ for (i = 0; i < 4; i++) { v[i] = dup_vert(stage, header->v[0], i); } /* new verts */ pos = v[0]->data[pos_slot]; pos[0] -= radius; pos[1] -= radius; pos = v[1]->data[pos_slot]; pos[0] += radius; pos[1] -= radius; pos = v[2]->data[pos_slot]; pos[0] += radius; pos[1] += radius; pos = v[3]->data[pos_slot]; pos[0] -= radius; pos[1] += radius; /* new texcoords */ tex = v[0]->data[tex_slot]; ASSIGN_4V(tex, -1, -1, k, 1); tex = v[1]->data[tex_slot]; ASSIGN_4V(tex, 1, -1, k, 1); tex = v[2]->data[tex_slot]; ASSIGN_4V(tex, 1, 1, k, 1); tex = v[3]->data[tex_slot]; ASSIGN_4V(tex, -1, 1, k, 1); /* emit 2 tris for the quad strip */ tri.v[0] = v[0]; tri.v[1] = v[1]; tri.v[2] = v[2]; stage->next->tri( stage->next, &tri ); tri.v[0] = v[0]; tri.v[1] = v[2]; tri.v[2] = v[3]; stage->next->tri( stage->next, &tri ); }
/** * Draw a wide line by drawing a quad (two triangles). */ static void wideline_line( struct draw_stage *stage, struct prim_header *header ) { /*const struct wideline_stage *wide = wideline_stage(stage);*/ const unsigned pos = draw_current_shader_position_output(stage->draw); const float half_width = 0.5f * stage->draw->rasterizer->line_width; struct prim_header tri; struct vertex_header *v0 = dup_vert(stage, header->v[0], 0); struct vertex_header *v1 = dup_vert(stage, header->v[0], 1); struct vertex_header *v2 = dup_vert(stage, header->v[1], 2); struct vertex_header *v3 = dup_vert(stage, header->v[1], 3); float *pos0 = v0->data[pos]; float *pos1 = v1->data[pos]; float *pos2 = v2->data[pos]; float *pos3 = v3->data[pos]; const float dx = fabsf(pos0[0] - pos2[0]); const float dy = fabsf(pos0[1] - pos2[1]); const boolean half_pixel_center = stage->draw->rasterizer->half_pixel_center; /* small tweak to meet GL specification */ const float bias = half_pixel_center ? 0.125f : 0.0f; /* * Draw wide line as a quad (two tris) by "stretching" the line along * X or Y. * We need to tweak coords in several ways to be conformant here. */ if (dx > dy) { /* x-major line */ pos0[1] = pos0[1] - half_width - bias; pos1[1] = pos1[1] + half_width - bias; pos2[1] = pos2[1] - half_width - bias; pos3[1] = pos3[1] + half_width - bias; if (half_pixel_center) { if (pos0[0] < pos2[0]) { /* left to right line */ pos0[0] -= 0.5f; pos1[0] -= 0.5f; pos2[0] -= 0.5f; pos3[0] -= 0.5f; } else { /* right to left line */ pos0[0] += 0.5f; pos1[0] += 0.5f; pos2[0] += 0.5f; pos3[0] += 0.5f; } } } else { /* y-major line */ pos0[0] = pos0[0] - half_width + bias; pos1[0] = pos1[0] + half_width + bias; pos2[0] = pos2[0] - half_width + bias; pos3[0] = pos3[0] + half_width + bias; if (half_pixel_center) { if (pos0[1] < pos2[1]) { /* top to bottom line */ pos0[1] -= 0.5f; pos1[1] -= 0.5f; pos2[1] -= 0.5f; pos3[1] -= 0.5f; } else { /* bottom to top line */ pos0[1] += 0.5f; pos1[1] += 0.5f; pos2[1] += 0.5f; pos3[1] += 0.5f; } } } tri.det = header->det; /* only the sign matters */ tri.v[0] = v0; tri.v[1] = v2; tri.v[2] = v3; stage->next->tri( stage->next, &tri ); tri.v[0] = v0; tri.v[1] = v3; tri.v[2] = v1; stage->next->tri( stage->next, &tri ); }
/** * Draw a wide line by drawing a quad, using geometry which will * fullfill GL's antialiased line requirements. */ static void aaline_line(struct draw_stage *stage, struct prim_header *header) { const struct aaline_stage *aaline = aaline_stage(stage); const float half_width = aaline->half_line_width; struct prim_header tri; struct vertex_header *v[8]; uint texPos = aaline->tex_slot; uint posPos = aaline->pos_slot; float *pos, *tex; float dx = header->v[1]->data[posPos][0] - header->v[0]->data[posPos][0]; float dy = header->v[1]->data[posPos][1] - header->v[0]->data[posPos][1]; double a = atan2(dy, dx); float c_a = (float) cos(a), s_a = (float) sin(a); uint i; /* XXX the ends of lines aren't quite perfect yet, but probably passable */ dx = 0.5F * half_width; dy = half_width; /* allocate/dup new verts */ for (i = 0; i < 8; i++) { v[i] = dup_vert(stage, header->v[i/4], i); } /* * Quad strip for line from v0 to v1 (*=endpoints): * * 1 3 5 7 * +---+---------------------+---+ * | | * | *v0 v1* | * | | * +---+---------------------+---+ * 0 2 4 6 */ /* new verts */ pos = v[0]->data[posPos]; pos[0] += (-dx * c_a - dy * s_a); pos[1] += (-dx * s_a + dy * c_a); pos = v[1]->data[posPos]; pos[0] += (-dx * c_a - -dy * s_a); pos[1] += (-dx * s_a + -dy * c_a); pos = v[2]->data[posPos]; pos[0] += ( dx * c_a - dy * s_a); pos[1] += ( dx * s_a + dy * c_a); pos = v[3]->data[posPos]; pos[0] += ( dx * c_a - -dy * s_a); pos[1] += ( dx * s_a + -dy * c_a); pos = v[4]->data[posPos]; pos[0] += (-dx * c_a - dy * s_a); pos[1] += (-dx * s_a + dy * c_a); pos = v[5]->data[posPos]; pos[0] += (-dx * c_a - -dy * s_a); pos[1] += (-dx * s_a + -dy * c_a); pos = v[6]->data[posPos]; pos[0] += ( dx * c_a - dy * s_a); pos[1] += ( dx * s_a + dy * c_a); pos = v[7]->data[posPos]; pos[0] += ( dx * c_a - -dy * s_a); pos[1] += ( dx * s_a + -dy * c_a); /* new texcoords */ tex = v[0]->data[texPos]; ASSIGN_4V(tex, 0, 0, 0, 1); tex = v[1]->data[texPos]; ASSIGN_4V(tex, 0, 1, 0, 1); tex = v[2]->data[texPos]; ASSIGN_4V(tex, .5, 0, 0, 1); tex = v[3]->data[texPos]; ASSIGN_4V(tex, .5, 1, 0, 1); tex = v[4]->data[texPos]; ASSIGN_4V(tex, .5, 0, 0, 1); tex = v[5]->data[texPos]; ASSIGN_4V(tex, .5, 1, 0, 1); tex = v[6]->data[texPos]; ASSIGN_4V(tex, 1, 0, 0, 1); tex = v[7]->data[texPos]; ASSIGN_4V(tex, 1, 1, 0, 1); /* emit 6 tris for the quad strip */ tri.v[0] = v[2]; tri.v[1] = v[1]; tri.v[2] = v[0]; stage->next->tri( stage->next, &tri ); tri.v[0] = v[3]; tri.v[1] = v[1]; tri.v[2] = v[2]; stage->next->tri( stage->next, &tri ); tri.v[0] = v[4]; tri.v[1] = v[3]; tri.v[2] = v[2]; stage->next->tri( stage->next, &tri ); tri.v[0] = v[5]; tri.v[1] = v[3]; tri.v[2] = v[4]; stage->next->tri( stage->next, &tri ); tri.v[0] = v[6]; tri.v[1] = v[5]; tri.v[2] = v[4]; stage->next->tri( stage->next, &tri ); tri.v[0] = v[7]; tri.v[1] = v[5]; tri.v[2] = v[6]; stage->next->tri( stage->next, &tri ); }
/* Clip a triangle against the viewport and user clip planes. */ static void do_clip_tri( struct draw_stage *stage, struct prim_header *header, unsigned clipmask ) { struct clipper *clipper = clipper_stage( stage ); struct vertex_header *a[MAX_CLIPPED_VERTICES]; struct vertex_header *b[MAX_CLIPPED_VERTICES]; struct vertex_header **inlist = a; struct vertex_header **outlist = b; unsigned tmpnr = 0; unsigned n = 3; unsigned i; inlist[0] = header->v[0]; inlist[1] = header->v[1]; inlist[2] = header->v[2]; while (clipmask && n >= 3) { const unsigned plane_idx = ffs(clipmask)-1; const float *plane = clipper->plane[plane_idx]; struct vertex_header *vert_prev = inlist[0]; float dp_prev = dot4( vert_prev->clip, plane ); unsigned outcount = 0; clipmask &= ~(1<<plane_idx); inlist[n] = inlist[0]; /* prevent rotation of vertices */ for (i = 1; i <= n; i++) { struct vertex_header *vert = inlist[i]; float dp = dot4( vert->clip, plane ); if (!IS_NEGATIVE(dp_prev)) { outlist[outcount++] = vert_prev; } if (DIFFERENT_SIGNS(dp, dp_prev)) { struct vertex_header *new_vert = clipper->stage.tmp[tmpnr++]; outlist[outcount++] = new_vert; if (IS_NEGATIVE(dp)) { /* Going out of bounds. Avoid division by zero as we * know dp != dp_prev from DIFFERENT_SIGNS, above. */ float t = dp / (dp - dp_prev); interp( clipper, new_vert, t, vert, vert_prev ); /* Force edgeflag true in this case: */ new_vert->edgeflag = 1; } else { /* Coming back in. */ float t = dp_prev / (dp_prev - dp); interp( clipper, new_vert, t, vert_prev, vert ); /* Copy starting vert's edgeflag: */ new_vert->edgeflag = vert_prev->edgeflag; } } vert_prev = vert; dp_prev = dp; } { struct vertex_header **tmp = inlist; inlist = outlist; outlist = tmp; n = outcount; } } /* If flat-shading, copy color to new provoking vertex. */ if (clipper->flat && inlist[0] != header->v[2]) { if (1) { inlist[0] = dup_vert(stage, inlist[0], tmpnr++); } copy_colors(stage, inlist[0], header->v[2]); } /* Emit the polygon as triangles to the setup stage: */ if (n >= 3) emit_poly( stage, inlist, n, header ); }
/* If there are lots of sprite points (and why wouldn't there be?) it * would probably be more sensible to change hardware setup to * optimize this rather than doing the whole thing in software like * this. */ static void widepoint_point( struct draw_stage *stage, struct prim_header *header ) { const struct widepoint_stage *wide = widepoint_stage(stage); const unsigned pos = draw_current_shader_position_output(stage->draw); const boolean sprite = (boolean) stage->draw->rasterizer->point_quad_rasterization; float half_size; float left_adj, right_adj, bot_adj, top_adj; struct prim_header tri; /* four dups of original vertex */ struct vertex_header *v0 = dup_vert(stage, header->v[0], 0); struct vertex_header *v1 = dup_vert(stage, header->v[0], 1); struct vertex_header *v2 = dup_vert(stage, header->v[0], 2); struct vertex_header *v3 = dup_vert(stage, header->v[0], 3); float *pos0 = v0->data[pos]; float *pos1 = v1->data[pos]; float *pos2 = v2->data[pos]; float *pos3 = v3->data[pos]; /* point size is either per-vertex or fixed size */ if (wide->psize_slot >= 0) { half_size = header->v[0]->data[wide->psize_slot][0]; half_size *= 0.5f; } else { half_size = wide->half_point_size; } left_adj = -half_size + wide->xbias; right_adj = half_size + wide->xbias; bot_adj = half_size + wide->ybias; top_adj = -half_size + wide->ybias; pos0[0] += left_adj; pos0[1] += top_adj; pos1[0] += left_adj; pos1[1] += bot_adj; pos2[0] += right_adj; pos2[1] += top_adj; pos3[0] += right_adj; pos3[1] += bot_adj; if (sprite) { static const float tex00[4] = { 0, 0, 0, 1 }; static const float tex01[4] = { 0, 1, 0, 1 }; static const float tex11[4] = { 1, 1, 0, 1 }; static const float tex10[4] = { 1, 0, 0, 1 }; set_texcoords( wide, v0, tex00 ); set_texcoords( wide, v1, tex01 ); set_texcoords( wide, v2, tex10 ); set_texcoords( wide, v3, tex11 ); } tri.det = header->det; /* only the sign matters */ tri.v[0] = v0; tri.v[1] = v2; tri.v[2] = v3; stage->next->tri( stage->next, &tri ); tri.v[0] = v0; tri.v[1] = v3; tri.v[2] = v1; stage->next->tri( stage->next, &tri ); }