/* ================ R_TransformPlane ================ */ void R_TransformPlane(cplane_t *p, float *normal, float *dist) { float d; d = DotProduct(r_origin, p->normal); *dist = p->dist - d; // TODO: when we have rotating entities, this will need to use the view matrix R_TransformVector(p->normal, normal); }
/* ================ R_EmitEdge ================ */ void R_EmitEdge(mvertex_t *pv0, mvertex_t *pv1) { edge_t *edge, *pcheck; int u_check; float u, u_step; vec3_t local, transformed; float *world; int v, v2, ceilv0; float scale, lzi0, u0, v0; int side; if (r_lastvertvalid) { u0 = r_u1; v0 = r_v1; lzi0 = r_lzi1; ceilv0 = r_ceilv1; } else { world = &pv0->point[0]; // transform and project VectorSubtract(world, modelorg, local); R_TransformVector(local, transformed); if (transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; lzi0 = 1.0 / transformed[2]; // FIXME: build x/yscale into transform? scale = xscale * lzi0; u0 = (xcenter + scale * transformed[0]); if (u0 < r_refdef.fvrectx_adj) u0 = r_refdef.fvrectx_adj; if (u0 > r_refdef.fvrectright_adj) u0 = r_refdef.fvrectright_adj; scale = yscale * lzi0; v0 = (ycenter - scale * transformed[1]); if (v0 < r_refdef.fvrecty_adj) v0 = r_refdef.fvrecty_adj; if (v0 > r_refdef.fvrectbottom_adj) v0 = r_refdef.fvrectbottom_adj; ceilv0 = (int) ceil(v0); } world = &pv1->point[0]; // transform and project VectorSubtract(world, modelorg, local); R_TransformVector(local, transformed); if (transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; r_lzi1 = 1.0 / transformed[2]; scale = xscale * r_lzi1; r_u1 = (xcenter + scale * transformed[0]); if (r_u1 < r_refdef.fvrectx_adj) r_u1 = r_refdef.fvrectx_adj; if (r_u1 > r_refdef.fvrectright_adj) r_u1 = r_refdef.fvrectright_adj; scale = yscale * r_lzi1; r_v1 = (ycenter - scale * transformed[1]); if (r_v1 < r_refdef.fvrecty_adj) r_v1 = r_refdef.fvrecty_adj; if (r_v1 > r_refdef.fvrectbottom_adj) r_v1 = r_refdef.fvrectbottom_adj; if (r_lzi1 > lzi0) lzi0 = r_lzi1; if (lzi0 > r_nearzi) // for mipmap finding r_nearzi = lzi0; // for right edges, all we want is the effect on 1/z if (r_nearzionly) return; r_emitted = 1; r_ceilv1 = (int) ceil(r_v1); // create the edge if (ceilv0 == r_ceilv1) { // we cache unclipped horizontal edges as fully clipped if (cacheoffset != CLIPPED_NOT_CACHED) { cacheoffset = FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK); } return; // horizontal edge } side = ceilv0 > r_ceilv1; edge = edge_p++; edge->owner = r_pedge; edge->nearzi = lzi0; if (side == 0) { // trailing edge (go from p1 to p2) v = ceilv0; v2 = r_ceilv1 - 1; edge->surfs[0] = surface_p - surfaces; edge->surfs[1] = 0; u_step = ((r_u1 - u0) / (r_v1 - v0)); u = u0 + ((float)v - v0) * u_step; } else { // leading edge (go from p2 to p1) v2 = ceilv0 - 1; v = r_ceilv1; edge->surfs[0] = 0; edge->surfs[1] = surface_p - surfaces; u_step = ((u0 - r_u1) / (v0 - r_v1)); u = r_u1 + ((float)v - r_v1) * u_step; } edge->u_step = u_step * 0x100000; edge->u = u * 0x100000 + 0xFFFFF; // we need to do this to avoid stepping off the edges if a very nearly // horizontal edge is less than epsilon above a scan, and numeric error causes // it to incorrectly extend to the scan, and the extension of the line goes off // the edge of the screen // FIXME: is this actually needed? if (edge->u < r_refdef.vrect_x_adj_shift20) edge->u = r_refdef.vrect_x_adj_shift20; if (edge->u > r_refdef.vrectright_adj_shift20) edge->u = r_refdef.vrectright_adj_shift20; // // sort the edge in normally // u_check = edge->u; if (edge->surfs[0]) u_check++; // sort trailers after leaders if (!newedges[v] || newedges[v]->u >= u_check) { edge->next = newedges[v]; newedges[v] = edge; } else { pcheck = newedges[v]; while (pcheck->next && pcheck->next->u < u_check) pcheck = pcheck->next; edge->next = pcheck->next; pcheck->next = edge; } edge->nextremove = removeedges[v2]; removeedges[v2] = edge; }
/* ================ R_RenderBmodelFace ================ */ void R_RenderBmodelFace(bedge_t *pedges, mface_t *psurf) { int i; unsigned mask; cplane_t *pplane; float distinv; vec3_t p_normal; medge_t tedge; clipplane_t *pclip; qboolean makeleftedge, makerightedge; if (psurf->texinfo->c.flags & (SURF_TRANS33 | SURF_TRANS66)) { psurf->next = r_alpha_surfaces; r_alpha_surfaces = psurf; return; } // skip out if no more surfs if (surface_p >= surf_max) { r_outofsurfaces++; return; } // ditto if not enough edges left, or switch to auxedges if possible if ((edge_p + psurf->numsurfedges + 4) >= edge_max) { r_outofedges += psurf->numsurfedges; return; } c_faceclip++; // this is a dummy to give the caching mechanism someplace to write to r_pedge = &tedge; // set up clip planes pclip = NULL; for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { if (r_clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = qfalse; makeleftedge = makerightedge = qfalse; // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching // can be used? r_lastvertvalid = qfalse; for (; pedges; pedges = pedges->pnext) { r_leftclipped = r_rightclipped = qfalse; R_ClipEdge(pedges->v[0], pedges->v[1], pclip); if (r_leftclipped) makeleftedge = qtrue; if (r_rightclipped) makerightedge = qtrue; } // if there was a clip off the left edge, add that edge too // FIXME: faster to do in screen space? // FIXME: share clipped edges? if (makeleftedge) { r_pedge = &tedge; R_ClipEdge(&r_leftexit, &r_leftenter, pclip->next); } // if there was a clip off the right edge, get the right r_nearzi if (makerightedge) { r_pedge = &tedge; r_nearzionly = qtrue; R_ClipEdge(&r_rightexit, &r_rightenter, view_clipplanes[1].next); } // if no edges made it out, return without posting the surface if (!r_emitted) return; r_polycount++; surface_p->msurf = psurf; surface_p->nearzi = r_nearzi; surface_p->flags = psurf->drawflags; surface_p->insubmodel = qtrue; surface_p->spanstate = 0; surface_p->entity = currententity; surface_p->key = r_currentbkey; surface_p->spans = NULL; pplane = psurf->plane; // FIXME: cache this? R_TransformVector(pplane->normal, p_normal); // FIXME: cache this? distinv = 1.0 / (pplane->dist - DotProduct(modelorg, pplane->normal)); surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; surface_p++; }
/* ================ R_RenderFace ================ */ void R_RenderFace(mface_t *fa, int clipflags) { int i; unsigned mask; cplane_t *pplane; float distinv; vec3_t p_normal; medge_t tedge; msurfedge_t *surfedge; clipplane_t *pclip; qboolean makeleftedge, makerightedge; // translucent surfaces are not drawn by the edge renderer if (fa->texinfo->c.flags & (SURF_TRANS33 | SURF_TRANS66)) { fa->next = r_alpha_surfaces; r_alpha_surfaces = fa; return; } // sky surfaces encountered in the world will cause the // environment box surfaces to be emited if (fa->texinfo->c.flags & SURF_SKY) { R_EmitSkyBox(); return; } // skip out if no more surfs if ((surface_p) >= surf_max) { r_outofsurfaces++; return; } // ditto if not enough edges left, or switch to auxedges if possible if ((edge_p + fa->numsurfedges + 4) >= edge_max) { r_outofedges += fa->numsurfedges; return; } c_faceclip++; // set up clip planes pclip = NULL; for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { if (clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = qfalse; makeleftedge = makerightedge = qfalse; r_lastvertvalid = qfalse; surfedge = fa->firstsurfedge; for (i = 0; i < fa->numsurfedges; i++, surfedge++) { r_pedge = surfedge->edge; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = qfalse; continue; } } else { if ((((byte *)edge_p - (byte *)r_edges) > r_pedge->cachededgeoffset) && (((edge_t *)((byte *)r_edges + r_pedge->cachededgeoffset))->owner == r_pedge)) { R_EmitCachedEdge(); r_lastvertvalid = qfalse; continue; } } } // assume it's cacheable cacheoffset = (byte *)edge_p - (byte *)r_edges; r_leftclipped = r_rightclipped = qfalse; R_ClipEdge(r_pedge->v[surfedge->vert ], r_pedge->v[surfedge->vert ^ 1], pclip); r_pedge->cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = qtrue; if (r_rightclipped) makerightedge = qtrue; r_lastvertvalid = qtrue; } // if there was a clip off the left edge, add that edge too // FIXME: faster to do in screen space? // FIXME: share clipped edges? if (makeleftedge) { r_pedge = &tedge; r_lastvertvalid = qfalse; R_ClipEdge(&r_leftexit, &r_leftenter, pclip->next); } // if there was a clip off the right edge, get the right r_nearzi if (makerightedge) { r_pedge = &tedge; r_lastvertvalid = qfalse; r_nearzionly = qtrue; R_ClipEdge(&r_rightexit, &r_rightenter, view_clipplanes[1].next); } // if no edges made it out, return without posting the surface if (!r_emitted) return; r_polycount++; surface_p->msurf = fa; surface_p->nearzi = r_nearzi; surface_p->flags = fa->drawflags; surface_p->insubmodel = insubmodel; surface_p->spanstate = 0; surface_p->entity = currententity; surface_p->key = r_currentkey++; surface_p->spans = NULL; pplane = fa->plane; // FIXME: cache this? R_TransformVector(pplane->normal, p_normal); // FIXME: cache this? distinv = 1.0 / (pplane->dist - DotProduct(modelorg, pplane->normal)); surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; surface_p++; }