/* ================ R_DrawSolidClippedSubmodelPolygons ================ */ static void R_DrawSolidClippedSubmodelPolygons(model.model_t pmodel) { int i, j, lindex; double dot; model.msurface_t psurf; int numsurfaces; model.mplane_t pplane; model.mvertex_t[] bverts = new model.mvertex_t[MAX_BMODEL_VERTS]; bedge_t[] bedges = new bedge_t[MAX_BMODEL_EDGES]; int pbedge; model.medge_t pedge; model.medge_t[] pedges; int kk; for (kk = 0; kk < MAX_BMODEL_VERTS; kk++) bverts[kk] = new model.mvertex_t(); for (kk = 0; kk < MAX_BMODEL_EDGES; kk++) bedges[kk] = new bedge_t(); // FIXME: use bounding-box-based frustum clipping info? numsurfaces = pmodel.nummodelsurfaces; pedges = pmodel.edges; for (i=0 ; i<numsurfaces ; i++) { psurf = pmodel.surfaces[pmodel.firstmodelsurface + i]; // find which side of the node we are on pplane = psurf.plane; dot = mathlib.DotProduct (modelorg, pplane.normal) - pplane.dist; // draw the polygon if (((psurf.flags & model.SURF_PLANEBACK) != 0 && (dot < -BACKFACE_EPSILON)) || ((psurf.flags & model.SURF_PLANEBACK) == 0 && (dot > BACKFACE_EPSILON))) { // FIXME: use bounding-box-based frustum clipping info? // copy the edges to bedges, flipping if necessary so always // clockwise winding // FIXME: if edges and vertices get caches, these assignments must move // outside the loop, and overflow checking must be done here pbverts = bverts; pbedges = bedges; numbverts = numbedges = 0; if (psurf.numedges > 0) { pbedge = numbedges; numbedges += psurf.numedges; for (j=0 ; j<psurf.numedges ; j++) { lindex = pmodel.surfedges[psurf.firstedge+j]; if (lindex > 0) { pedge = pedges[lindex]; bedges[pbedge + j].v[0] = r_pcurrentvertbase[pedge.v[0]]; bedges[pbedge + j].v[1] = r_pcurrentvertbase[pedge.v[1]]; } else { lindex = -lindex; pedge = pedges[lindex]; bedges[pbedge + j].v[0] = r_pcurrentvertbase[pedge.v[1]]; bedges[pbedge + j].v[1] = r_pcurrentvertbase[pedge.v[0]]; } bedges[pbedge + j].pnext = bedges[pbedge + j + 1]; } bedges[pbedge + j - 1].pnext = null; // mark end of edges R_RecursiveClipBPoly (bedges[pbedge], (model.mnode_t)currententity.topnode, psurf); } else { sys_linux.Sys_Error ("no edges in bmodel"); } } } }
/* ================ R_RecursiveClipBPoly ================ */ static void R_RecursiveClipBPoly(bedge_t pedges, model.mnode_t pnode, model.msurface_t psurf) { bedge_t[] psideedges = new bedge_t[2]; bedge_t pnextedge, ptedge; int i, side, lastside; double dist, frac, lastdist; model.mplane_t splitplane, tplane = new model.mplane_t(); model.mvertex_t pvert, plastvert, ptvert; model.node_or_leaf_t pn; psideedges[0] = psideedges[1] = null; makeclippededge = false; // transform the BSP plane into model space // FIXME: cache these? splitplane = pnode.plane; tplane.dist = splitplane.dist - mathlib.DotProduct(r_entorigin, splitplane.normal); tplane.normal[0] = mathlib.DotProduct(entity_rotation[0], splitplane.normal); tplane.normal[1] = mathlib.DotProduct(entity_rotation[1], splitplane.normal); tplane.normal[2] = mathlib.DotProduct(entity_rotation[2], splitplane.normal); // clip edges to BSP plane for ( ; pedges != null ; pedges = pnextedge) { pnextedge = pedges.pnext; // set the status for the last point as the previous point // FIXME: cache this stuff somehow? plastvert = pedges.v[0]; lastdist = mathlib.DotProduct (plastvert.position, tplane.normal) - tplane.dist; if (lastdist > 0) lastside = 0; else lastside = 1; pvert = pedges.v[1]; dist = mathlib.DotProduct(pvert.position, tplane.normal) - tplane.dist; if (dist > 0) side = 0; else side = 1; if (side != lastside) { // clipped if (numbverts >= MAX_BMODEL_VERTS) return; // generate the clipped vertex frac = lastdist / (lastdist - dist); ptvert = pbverts[numbverts++]; ptvert.position[0] = plastvert.position[0] + frac * (pvert.position[0] - plastvert.position[0]); ptvert.position[1] = plastvert.position[1] + frac * (pvert.position[1] - plastvert.position[1]); ptvert.position[2] = plastvert.position[2] + frac * (pvert.position[2] - plastvert.position[2]); // split into two edges, one on each side, and remember entering // and exiting points // FIXME: share the clip edge by having a winding direction flag? if (numbedges >= (MAX_BMODEL_EDGES - 1)) { console.Con_Printf ("Out of edges for bmodel\n"); return; } ptedge = pbedges[numbedges]; ptedge.pnext = psideedges[lastside]; psideedges[lastside] = ptedge; ptedge.v[0] = plastvert; ptedge.v[1] = ptvert; ptedge = pbedges[numbedges + 1]; ptedge.pnext = psideedges[side]; psideedges[side] = ptedge; ptedge.v[0] = ptvert; ptedge.v[1] = pvert; numbedges += 2; if (side == 0) { // entering for front, exiting for back pfrontenter = ptvert; makeclippededge = true; } else { pfrontexit = ptvert; makeclippededge = true; } } else { // add the edge to the appropriate side pedges.pnext = psideedges[side]; psideedges[side] = pedges; } } // if anything was clipped, reconstitute and add the edges along the clip // plane to both sides (but in opposite directions) if (makeclippededge) { if (numbedges >= (MAX_BMODEL_EDGES - 2)) { console.Con_Printf ("Out of edges for bmodel\n"); return; } ptedge = pbedges[numbedges]; ptedge.pnext = psideedges[0]; psideedges[0] = ptedge; ptedge.v[0] = pfrontexit; ptedge.v[1] = pfrontenter; ptedge = pbedges[numbedges + 1]; ptedge.pnext = psideedges[1]; psideedges[1] = ptedge; ptedge.v[0] = pfrontenter; ptedge.v[1] = pfrontexit; numbedges += 2; } // draw or recurse further for (i=0 ; i<2 ; i++) { if (psideedges[i] != null) { // draw if we've reached a non-solid leaf, done if all that's left is a // solid leaf, and continue down the tree if it's not a leaf pn = pnode.children[i]; // we're done with this branch if the node or leaf isn't in the PVS if (pn.visframe == r_visframecount) { if (pn.contents < 0) { if (pn.contents != bspfile.CONTENTS_SOLID) { r_currentbkey = ((model.mleaf_t)pn).key; R_RenderBmodelFace (psideedges[i], psurf); } } else { R_RecursiveClipBPoly (psideedges[i], (model.mnode_t)pnode.children[i], psurf); } } } } }
/* ================ R_ClipEdge ================ */ static void R_ClipEdge(model.mvertex_t pv0, model.mvertex_t pv1, clipplane_t clip) { double d0, d1, f; model.mvertex_t clipvert = new model.mvertex_t(); if (clip != null) { do { d0 = mathlib.DotProduct(pv0.position, clip.normal) - clip.dist; d1 = mathlib.DotProduct(pv1.position, clip.normal) - clip.dist; if (d0 >= 0) { // point 0 is unclipped if (d1 >= 0) { // both points are unclipped continue; } // only point 1 is clipped // we don't cache clipped edges cacheoffset = 0x7FFFFFFF; f = d0 / (d0 - d1); clipvert.position[0] = pv0.position[0] + f * (pv1.position[0] - pv0.position[0]); clipvert.position[1] = pv0.position[1] + f * (pv1.position[1] - pv0.position[1]); clipvert.position[2] = pv0.position[2] + f * (pv1.position[2] - pv0.position[2]); if (clip.leftedge) { r_leftclipped = true; r_leftexit = clipvert; } else if (clip.rightedge) { r_rightclipped = true; r_rightexit = clipvert; } R_ClipEdge(pv0, clipvert, clip.next); return; } else { // point 0 is clipped if (d1 < 0) { // both points are clipped // we do cache fully clipped edges if (!r_leftclipped) cacheoffset = (uint)(FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK)); return; } // only point 0 is clipped r_lastvertvalid = false; // we don't cache partially clipped edges cacheoffset = 0x7FFFFFFF; f = d0 / (d0 - d1); clipvert.position[0] = pv0.position[0] + f * (pv1.position[0] - pv0.position[0]); clipvert.position[1] = pv0.position[1] + f * (pv1.position[1] - pv0.position[1]); clipvert.position[2] = pv0.position[2] + f * (pv1.position[2] - pv0.position[2]); if (clip.leftedge) { r_leftclipped = true; r_leftenter = clipvert; } else if (clip.rightedge) { r_rightclipped = true; r_rightenter = clipvert; } R_ClipEdge(clipvert, pv1, clip.next); return; } } while ((clip = clip.next) != null); } // add the edge R_EmitEdge(pv0, pv1); }