public static int BOX_ON_PLANE_SIDE(double[] emins, double[] emaxs, model.mplane_t p) { return ((p.type < 3) ? ( (p.dist <= emins[p.type]) ? 1 : ( (p.dist >= emaxs[p.type]) ? 2 : 3 ) ) : BoxOnPlaneSide(emins, emaxs, p)); }
/* ================ R_RecursiveWorldNode ================ */ static void R_RecursiveWorldNode(model.node_or_leaf_t node, int clipflags) { int i, c, side, pindex; double[] acceptpt = new double[3] {0, 0, 0}, rejectpt = new double[3] {0, 0, 0}; model.mplane_t plane; model.msurface_t surf, mark; model.mleaf_t pleaf; double d, dot; int surfofs; if (node.contents == bspfile.CONTENTS_SOLID) return; // solid if (node.visframe != r_visframecount) return; // cull the clipping planes if not trivial accept // FIXME: the compiler is doing a lousy job of optimizing here; it could be // twice as fast in ASM if (clipflags != 0) { for (i=0 ; i<4 ; i++) { if ((clipflags & (1<<i)) == 0) continue; // don't need to clip against it // generate accept and reject points // FIXME: do with fast look-ups or integer tests based on the sign bit // of the floating point values pindex = pfrustum_indexes[i]; rejectpt[0] = (double)node.minmaxs[r_frustum_indexes[pindex + 0]]; rejectpt[1] = (double)node.minmaxs[r_frustum_indexes[pindex + 1]]; rejectpt[2] = (double)node.minmaxs[r_frustum_indexes[pindex + 2]]; d = mathlib.DotProduct (rejectpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= 0) return; acceptpt[0] = (double)node.minmaxs[r_frustum_indexes[pindex + 3 + 0]]; acceptpt[1] = (double)node.minmaxs[r_frustum_indexes[pindex + 3 + 1]]; acceptpt[2] = (double)node.minmaxs[r_frustum_indexes[pindex + 3 + 2]]; d = mathlib.DotProduct(acceptpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d >= 0) clipflags &= ~(1<<i); // node is entirely on screen } } // if a leaf node, draw stuff if (node.contents < 0) { pleaf = (model.mleaf_t)node; helper.ObjectBuffer _mark = pleaf.firstmarksurface; int ofs = _mark.ofs; mark = (model.msurface_t)_mark.buffer[ofs]; c = pleaf.nummarksurfaces; if (c != 0) { do { mark.visframe = r_framecount; mark = (model.msurface_t)_mark.buffer[++ofs]; } while (--c != 0); } // deal with model fragments in this leaf if (pleaf.efrags != null) { R_StoreEfrags (pleaf.efrags); } pleaf.key = r_currentkey; r_currentkey++; // all bmodels in a leaf share the same key } else { model.mnode_t _node = (model.mnode_t)node; // node is just a decision point, so go down the apropriate sides // find which side of the node we are on plane = _node.plane; switch (plane.type) { case bspfile.PLANE_X: dot = modelorg[0] - plane.dist; break; case bspfile.PLANE_Y: dot = modelorg[1] - plane.dist; break; case bspfile.PLANE_Z: dot = modelorg[2] - plane.dist; break; default: dot = mathlib.DotProduct (modelorg, plane.normal) - plane.dist; break; } if (dot >= 0) side = 0; else side = 1; // recurse down the children, front side first R_RecursiveWorldNode (_node.children[side], clipflags); // draw stuff c = _node.numsurfaces; if (c != 0) { surf = client.cl.worldmodel.surfaces[_node.firstsurface]; surfofs = _node.firstsurface; if (dot < -BACKFACE_EPSILON) { do { if ((surf.flags & model.SURF_PLANEBACK) != 0 && (surf.visframe == r_framecount)) { if (r_drawpolys) { if (r_worldpolysbacktofront) { if (numbtofpolys < MAX_BTOFPOLYS) { pbtofpolys[numbtofpolys].clipflags = clipflags; pbtofpolys[numbtofpolys].psurf = surf; numbtofpolys++; } } else { R_RenderPoly (surf, clipflags); } } else { R_RenderFace (surf, clipflags); } } surf = client.cl.worldmodel.surfaces[++surfofs]; } while (--c != 0); } else if (dot > BACKFACE_EPSILON) { do { if ((surf.flags & model.SURF_PLANEBACK) == 0 && (surf.visframe == r_framecount)) { if (r_drawpolys) { if (r_worldpolysbacktofront) { if (numbtofpolys < MAX_BTOFPOLYS) { pbtofpolys[numbtofpolys].clipflags = clipflags; pbtofpolys[numbtofpolys].psurf = surf; numbtofpolys++; } } else { R_RenderPoly (surf, clipflags); } } else { R_RenderFace (surf, clipflags); } } surf = client.cl.worldmodel.surfaces[++surfofs]; } while (--c != 0); } // all surfaces on the same node share the same sequence number r_currentkey++; } // recurse down the back side R_RecursiveWorldNode (_node.children[side == 0 ? 1 : 0], clipflags); } }
/* ================ 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_DrawSubmodelPolygons ================ */ static void R_DrawSubmodelPolygons(model.model_t pmodel, int clipflags) { int i; double dot; model.msurface_t psurf; int numsurfaces; model.mplane_t pplane; // FIXME: use bounding-box-based frustum clipping info? numsurfaces = pmodel.nummodelsurfaces; 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))) { r_currentkey = ((model.mleaf_t)currententity.topnode).key; // FIXME: use bounding-box-based frustum clipping info? R_RenderFace(psurf, clipflags); } } }
/* ================ R_RenderBmodelFace ================ */ static void R_RenderBmodelFace(bedge_t pedges, model.msurface_t psurf) { int i; uint mask; model.mplane_t pplane; double distinv; double[] p_normal = new double[3] {0, 0, 0}; model.medge_t tedge = new model.medge_t(); clipplane_t pclip; // 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.numedges + 4) >= edge_max) { r_outofedges += psurf.numedges; 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) != 0) { view_clipplanes[i].next = pclip; pclip = view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching // can be used? r_lastvertvalid = false; for ( ; pedges != null ; pedges = pedges.pnext) { r_leftclipped = r_rightclipped = false; R_ClipEdge (pedges.v[0], pedges.v[1], pclip); if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; } // 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 = true; R_ClipEdge (r_rightexit, r_rightenter, view_clipplanes[1].next); } // if no edges made it out, return without posting the surface if (r_emitted == 0) return; r_polycount++; surfaces[surface_p].data = psurf; surfaces[surface_p].nearzi = r_nearzi; surfaces[surface_p].flags = psurf.flags; surfaces[surface_p].insubmodel = true; surfaces[surface_p].spanstate = 0; surfaces[surface_p].entity = currententity; surfaces[surface_p].key = r_currentbkey; surfaces[surface_p].spans = null; pplane = psurf.plane; // FIXME: cache this? TransformVector (pplane.normal, p_normal); // FIXME: cache this? distinv = 1.0 / (pplane.dist - mathlib.DotProduct (modelorg, pplane.normal)); surfaces[surface_p].d_zistepu = p_normal[0] * xscaleinv * distinv; surfaces[surface_p].d_zistepv = -p_normal[1] * yscaleinv * distinv; surfaces[surface_p].d_ziorigin = p_normal[2] * distinv - xcenter * surfaces[surface_p].d_zistepu - ycenter * surfaces[surface_p].d_zistepv; //JDC VectorCopy (r_worldmodelorg, surface_p.modelorg); surface_p++; }
/* ================ R_GetSpriteframe ================ */ static model.mspriteframe_t R_GetSpriteframe(model.msprite_t psprite) { model.mspritegroup_t pspritegroup; model.mspriteframe_t pspriteframe; int i, numframes, frame; //double* pintervals, fullinterval, targettime, time; frame = currententity.frame; if ((frame >= psprite.numframes) || (frame < 0)) { console.Con_Printf("R_DrawSprite: no such frame " + frame + "\n"); frame = 0; } if (psprite.frames[frame].type == model.spriteframetype_t.SPR_SINGLE) { pspriteframe = (model.mspriteframe_t)psprite.frames[frame].frameptr; } else { pspriteframe = null; } return pspriteframe; }
/* =============== R_TextureAnimation Returns the proper texture for a given time and base texture =============== */ public static model.texture_t R_TextureAnimation(model.texture_t @base) { int reletive; int count; if (currententity.frame != 0) { if (@base.alternate_anims != null) @base = @base.alternate_anims; } if (@base.anim_total == 0) return @base; reletive = (int)(client.cl.time*10) % @base.anim_total; count = 0; while (@base.anim_min > reletive || @base.anim_max <= reletive) { @base = @base.anim_next; if (@base == null) sys_linux.Sys_Error ("R_TextureAnimation: broken cycle"); if (++count > 100) sys_linux.Sys_Error("R_TextureAnimation: infinite cycle"); } return @base; }
/* ============= R_BmodelCheckBBox ============= */ static int R_BmodelCheckBBox(model.model_t clmodel, double[] minmaxs) { int i, pindex, clipflags; double[] acceptpt = new double[3] {0, 0, 0}, rejectpt = new double[3] {0, 0, 0}; double d; clipflags = 0; if (currententity.angles[0] != 0 || currententity.angles[1] != 0 || currententity.angles[2] != 0) { for (i=0 ; i<4 ; i++) { d = mathlib.DotProduct (currententity.origin, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= -clmodel.radius) return BMODEL_FULLY_CLIPPED; if (d <= clmodel.radius) clipflags |= (1<<i); } } else { for (i=0 ; i<4 ; i++) { // generate accept and reject points // FIXME: do with fast look-ups or integer tests based on the sign bit // of the floating point values pindex = pfrustum_indexes[i]; rejectpt[0] = minmaxs[r_frustum_indexes[pindex + 0]]; rejectpt[1] = minmaxs[r_frustum_indexes[pindex + 1]]; rejectpt[2] = minmaxs[r_frustum_indexes[pindex + 2]]; d = mathlib.DotProduct (rejectpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= 0) return BMODEL_FULLY_CLIPPED; acceptpt[0] = minmaxs[r_frustum_indexes[pindex + 3 + 0]]; acceptpt[1] = minmaxs[r_frustum_indexes[pindex + 3 + 1]]; acceptpt[2] = minmaxs[r_frustum_indexes[pindex + 3 + 2]]; d = mathlib.DotProduct (acceptpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= 0) clipflags |= (1<<i); } } return clipflags; }
/* ================ R_ZDrawSubmodelPolys ================ */ static void R_ZDrawSubmodelPolys(model.model_t pmodel) { }
static void SV_AddToFatPVS(double[] org, model.node_or_leaf_t node) { int i; Uint8Array pvs; model.mplane_t plane; double d; while (true) { // if this is a leaf, accumulate the pvs bits if (node.contents < 0) { if (node.contents != bspfile.CONTENTS_SOLID) { pvs = model.Mod_LeafPVS((model.node_or_leaf_t)node, sv.worldmodel); for (i=0 ; i<fatbytes ; i++) fatpvs[i] |= pvs[i]; } return; } plane = node.plane; d = mathlib.DotProduct (org, plane.normal) - plane.dist; if (d > 8) node = ((model.node_or_leaf_t)node).children[0]; else if (d < -8) node = (model.node_or_leaf_t)node.children[1]; else { // go down both SV_AddToFatPVS(org, (model.node_or_leaf_t)node.children[0]); node = (model.node_or_leaf_t)node.children[1]; } } }
/* ================ R_RenderPoly ================ */ static void R_RenderPoly(model.msurface_t fa, int clipflags) { }
/* ================ 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); }
/* ================ R_RenderFace ================ */ static void R_RenderFace(model.msurface_t fa, int clipflags) { int i, lindex; uint mask; model.mplane_t pplane; double distinv; double[] p_normal = new double[3] {0, 0, 0}; model.medge_t[] pedges; model.medge_t tedge = new model.medge_t(); clipplane_t pclip; // 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.numedges + 4) >= edge_max) { r_outofedges += fa.numedges; return; } c_faceclip++; // set up clip planes pclip = null; for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) { if ((clipflags & mask) != 0) { view_clipplanes[i].next = pclip; pclip = view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; pedges = currententity.model.edges; r_lastvertvalid = false; for (i=0 ; i<fa.numedges ; i++) { lindex = currententity.model.surfedges[fa.firstedge + i]; if (lindex > 0) { r_pedge = pedges[lindex]; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if ((r_pedge.cachededgeoffset & FULLY_CLIPPED_CACHED) != 0) { if ((r_pedge.cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = false; continue; } } else { if ((edge_p > r_pedge.cachededgeoffset) && (r_edges[r_pedge.cachededgeoffset].owner == r_pedge)) { R_EmitCachedEdge (); r_lastvertvalid = false; continue; } } } // assume it's cacheable cacheoffset = (uint)edge_p; r_leftclipped = r_rightclipped = false; R_ClipEdge (r_pcurrentvertbase[r_pedge.v[0]], r_pcurrentvertbase[r_pedge.v[1]], pclip); r_pedge.cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; r_lastvertvalid = true; } else { lindex = -lindex; r_pedge = pedges[lindex]; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if ((r_pedge.cachededgeoffset & FULLY_CLIPPED_CACHED) != 0) { if ((r_pedge.cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = false; continue; } } else { // it's cached if the cached edge is valid and is owned // by this medge_t if ((edge_p > r_pedge.cachededgeoffset) && (r_edges[r_pedge.cachededgeoffset].owner == r_pedge)) { R_EmitCachedEdge (); r_lastvertvalid = false; continue; } } } // assume it's cacheable cacheoffset = (uint)edge_p; r_leftclipped = r_rightclipped = false; R_ClipEdge (r_pcurrentvertbase[r_pedge.v[1]], r_pcurrentvertbase[r_pedge.v[0]], pclip); r_pedge.cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; r_lastvertvalid = true; } } // 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 = false; 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 = false; r_nearzionly = true; R_ClipEdge (r_rightexit, r_rightenter, view_clipplanes[1].next); } // if no edges made it out, return without posting the surface if (r_emitted == 0) return; r_polycount++; surfaces[surface_p].data = fa; surfaces[surface_p].nearzi = r_nearzi; surfaces[surface_p].flags = fa.flags; surfaces[surface_p].insubmodel = insubmodel; surfaces[surface_p].spanstate = 0; surfaces[surface_p].entity = currententity; surfaces[surface_p].key = r_currentkey++; surfaces[surface_p].spans = null; pplane = fa.plane; // FIXME: cache this? TransformVector(pplane.normal, p_normal); // FIXME: cache this? distinv = 1.0 / (pplane.dist - mathlib.DotProduct(modelorg, pplane.normal)); surfaces[surface_p].d_zistepu = p_normal[0] * xscaleinv * distinv; surfaces[surface_p].d_zistepv = -p_normal[1] * yscaleinv * distinv; surfaces[surface_p].d_ziorigin = p_normal[2] * distinv - xcenter * surfaces[surface_p].d_zistepu - ycenter * surfaces[surface_p].d_zistepv; //JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); surface_p++; }
/* ================ R_AliasTransformAndProjectFinalVerts ================ */ static void R_AliasTransformAndProjectFinalVerts(draw.finalvert_t[] pfv, model.stvert_t[] pstverts) { int i, temp; double lightcos; double[] plightnormal; double zi; model.trivertx_t pverts; for (i=0 ; i<r_anumverts ; i++) { draw.finalvert_t fv = pfv[i]; model.stvert_t stverts = pstverts[i]; pverts = r_apverts[i]; // transform and project zi = 1.0 / (mathlib.DotProduct(pverts.v, aliastransform[2]) + aliastransform[2][3]); // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is // scaled up by 1/2**31, and the scaling cancels out for x and y in the // projection fv.v[5] = (int)zi; fv.v[0] = (int)(((mathlib.DotProduct(pverts.v, aliastransform[0]) + aliastransform[0][3]) * zi) + aliasxcenter); fv.v[1] = (int)(((mathlib.DotProduct(pverts.v, aliastransform[1]) + aliastransform[1][3]) * zi) + aliasycenter); fv.v[2] = stverts.s; fv.v[3] = stverts.t; fv.flags = stverts.onseam; // lighting plightnormal = r_avertexnormals[pverts.lightnormalindex]; lightcos = mathlib.DotProduct(plightnormal, r_plightvec); temp = r_ambientlight; if (lightcos < 0) { temp += (int)(r_shadelight * lightcos); // clamp; because we limited the minimum ambient and shading light, we // don't have to clamp low light, just bright if (temp < 0) temp = 0; } fv.v[4] = temp; } }
/* =============== SV_FindTouchedLeafs =============== */ static void SV_FindTouchedLeafs(prog.edict_t ent, model.node_or_leaf_t node) { model.mplane_t splitplane; model.node_or_leaf_t leaf; int sides; int leafnum; if (node.contents == bspfile.CONTENTS_SOLID) return; // add an efrag if the node is a leaf if (node.contents < 0) { if (ent.num_leafs == prog.MAX_ENT_LEAFS) return; leaf = (model.node_or_leaf_t)node; int i; for ( i = 0; i < server.sv.worldmodel.leafs.Length; i++) { var mleafT = server.sv.worldmodel.leafs[i]; if (mleafT == leaf) { break; } } leafnum = i - 1; ent.leafnums[ent.num_leafs] = (short)leafnum; ent.num_leafs++; //Debug.WriteLine("num_leafs " + ent.num_leafs); //Debug.WriteLine("leafnum_ " + leafnum); return; } // NODE_MIXED splitplane = ((model.mnode_t)node).plane; sides = mathlib.BOX_ON_PLANE_SIDE(ent.v.absmin, ent.v.absmax, splitplane); // recurse down the contacted sides if ((sides & 1) != 0) SV_FindTouchedLeafs(ent, ((model.mnode_t)node).children[0]); if ((sides & 2) !=0) SV_FindTouchedLeafs(ent, ((model.mnode_t)node).children[1]); }
/* ================ R_AliasTransformFinalVert ================ */ static void R_AliasTransformFinalVert(draw.finalvert_t fv, auxvert_t av, model.trivertx_t pverts, model.stvert_t pstverts) { int temp; double lightcos; double[] plightnormal; av.fv[0] = mathlib.DotProduct(pverts.v, aliastransform[0]) + aliastransform[0][3]; av.fv[1] = mathlib.DotProduct(pverts.v, aliastransform[1]) + aliastransform[1][3]; av.fv[2] = mathlib.DotProduct(pverts.v, aliastransform[2]) + aliastransform[2][3]; fv.v[2] = pstverts.s; fv.v[3] = pstverts.t; fv.flags = pstverts.onseam; // lighting plightnormal = r_avertexnormals[pverts.lightnormalindex]; lightcos = mathlib.DotProduct(plightnormal, r_plightvec); temp = r_ambientlight; if (lightcos < 0) { temp += (int)(r_shadelight * lightcos); // clamp; because we limited the minimum ambient and shading light, we // don't have to clamp low light, just bright if (temp < 0) temp = 0; } fv.v[4] = temp; }
/* =============================================================================== POINT TESTING IN HULLS =============================================================================== */ /* ================== SV_HullPointContents ================== */ public static int SV_HullPointContents(model.hull_t hull, int num, double[] p) { double d; bspfile.dclipnode_t node; model.mplane_t plane; while (num >= 0) { if (num < hull.firstclipnode || num > hull.lastclipnode) sys_linux.Sys_Error("SV_HullPointContents: bad node number"); node = hull.clipnodes[num]; plane = hull.planes[node.planenum]; if (plane.type < 3) d = p[plane.type] - plane.dist; else d = mathlib.DotProduct(plane.normal, p) - plane.dist; if (d < 0) num = node.children[1]; else num = node.children[0]; } //Debug.WriteLine("SV_HullPointContents " + num); return num; }
/* ================== BoxOnPlaneSide Returns 1, 2, or 1 + 2 ================== */ static int BoxOnPlaneSide(double[] emins, double[] emaxs, model.mplane_t p) { double dist1, dist2; int sides; /* // this is done by the BOX_ON_PLANE_SIDE macro before calling this // function // fast axial cases if (p.type < 3) { if (p.dist <= emins[p.type]) return 1; if (p.dist >= emaxs[p.type]) return 2; return 3; } */ // general case switch (p.signbits) { case 0: dist1 = p.normal[0]*emaxs[0] + p.normal[1]*emaxs[1] + p.normal[2]*emaxs[2]; dist2 = p.normal[0]*emins[0] + p.normal[1]*emins[1] + p.normal[2]*emins[2]; break; case 1: dist1 = p.normal[0]*emins[0] + p.normal[1]*emaxs[1] + p.normal[2]*emaxs[2]; dist2 = p.normal[0]*emaxs[0] + p.normal[1]*emins[1] + p.normal[2]*emins[2]; break; case 2: dist1 = p.normal[0]*emaxs[0] + p.normal[1]*emins[1] + p.normal[2]*emaxs[2]; dist2 = p.normal[0]*emins[0] + p.normal[1]*emaxs[1] + p.normal[2]*emins[2]; break; case 3: dist1 = p.normal[0]*emins[0] + p.normal[1]*emins[1] + p.normal[2]*emaxs[2]; dist2 = p.normal[0]*emaxs[0] + p.normal[1]*emaxs[1] + p.normal[2]*emins[2]; break; case 4: dist1 = p.normal[0]*emaxs[0] + p.normal[1]*emaxs[1] + p.normal[2]*emins[2]; dist2 = p.normal[0]*emins[0] + p.normal[1]*emins[1] + p.normal[2]*emaxs[2]; break; case 5: dist1 = p.normal[0]*emins[0] + p.normal[1]*emaxs[1] + p.normal[2]*emins[2]; dist2 = p.normal[0]*emaxs[0] + p.normal[1]*emins[1] + p.normal[2]*emaxs[2]; break; case 6: dist1 = p.normal[0]*emaxs[0] + p.normal[1]*emins[1] + p.normal[2]*emins[2]; dist2 = p.normal[0]*emins[0] + p.normal[1]*emaxs[1] + p.normal[2]*emaxs[2]; break; case 7: dist1 = p.normal[0]*emins[0] + p.normal[1]*emins[1] + p.normal[2]*emins[2]; dist2 = p.normal[0]*emaxs[0] + p.normal[1]*emaxs[1] + p.normal[2]*emaxs[2]; break; default: dist1 = dist2 = 0; // shut up compiler BOPS_Error (); break; } sides = 0; if (dist1 >= p.dist) sides = 1; if (dist2 < p.dist) sides |= 2; return sides; }
public static bool SV_RecursiveHullCheck( model.hull_t hull, int num, double p1f, double p2f, double[] p1, double[] p2, trace_t trace) { bspfile.dclipnode_t node; model.mplane_t plane; double t1, t2; double frac; int i; double[] mid = new double[3] {0, 0, 0}; int side; double midf; //Debug.WriteLine(string.Format("SV_RecursiveHullCheck hull.firstclipnode:{0} num:{1} {2:F6} {3:F6} p1[0] {4:F6} p1[1] {5:F6} p1[2] {6:F6} - p2[0] {7:F6} p2[1] {8:F6} p2[2] {9:F6} num_hullcheck: {10}", hull.firstclipnode, num, (float)p1f, (float)p2f, (float)p1[0], (float)p1[1], (float)p1[2], (float)p2[0], (float)p2[1], (float)p2[2], num_hullcheck)); num_hullcheck++; // check for empty if (num < 0) { if (num != bspfile.CONTENTS_SOLID) { trace.allsolid = false; if (num == bspfile.CONTENTS_EMPTY) trace.inopen = true; else trace.inwater = true; } else trace.startsolid = true; //Debug.WriteLine(string.Format("empty")); return true; // empty } if (num < hull.firstclipnode || num > hull.lastclipnode) sys_linux.Sys_Error("SV_RecursiveHullCheck: bad node number"); // // find the point distances // node = hull.clipnodes[num]; plane = hull.planes[node.planenum]; if (plane.type < 3) { t1 = p1[plane.type] - plane.dist; t2 = p2[plane.type] - plane.dist; } else { t1 = mathlib.DotProduct(plane.normal, p1) - plane.dist; t2 = mathlib.DotProduct(plane.normal, p2) - plane.dist; } //Debug.WriteLine(string.Format("t1: {0:F6} t2: {1:F6}", (float)t1, (float)t2)); if (t1 >= 0 && t2 >= 0) { return SV_RecursiveHullCheck(hull, node.children[0], p1f, p2f, p1, p2, trace); } if (t1 < 0 && t2 < 0) { return SV_RecursiveHullCheck(hull, node.children[1], p1f, p2f, p1, p2, trace); } // put the crosspoint DIST_EPSILON pixels on the near side if (t1 < 0) frac = (t1 + DIST_EPSILON) / (t1 - t2); else frac = (t1 - DIST_EPSILON) / (t1 - t2); if (frac < 0) frac = 0; if (frac > 1) frac = 1; midf = p1f + (p2f - p1f) * frac; for (i = 0; i < 3; i++) mid[i] = p1[i] + frac * (p2[i] - p1[i]); side = (t1 < 0) ? 1 : 0; // move up to the node if (!SV_RecursiveHullCheck(hull, node.children[side], p1f, midf, p1, mid, trace)) { return false; } if (SV_HullPointContents(hull, node.children[side ^ 1], mid) != bspfile.CONTENTS_SOLID) // go past the node { return SV_RecursiveHullCheck(hull, node.children[side ^ 1], midf, p2f, mid, p2, trace); } if (trace.allsolid) { return false; // never got out of the solid area } //================== // the other side of the node is solid, this is the impact point //================== if (side == 0) { mathlib.VectorCopy(plane.normal, trace.plane.normal); trace.plane.dist = plane.dist; } else { mathlib.VectorSubtract(mathlib.vec3_origin, plane.normal, trace.plane.normal); trace.plane.dist = -plane.dist; } while (SV_HullPointContents(hull, hull.firstclipnode, mid) == bspfile.CONTENTS_SOLID) { // shouldn't really happen, but does occasionally frac -= 0.1; if (frac < 0) { trace.fraction = midf; mathlib.VectorCopy(mid, trace.endpos); console.Con_DPrintf("backup past 0\n"); return false; } midf = p1f + (p2f - p1f) * frac; for (i = 0; i < 3; i++) mid[i] = p1[i] + frac * (p2[i] - p1[i]); } trace.fraction = midf; mathlib.VectorCopy(mid, trace.endpos); return false; }
/* ============== D_CalcGradients ============== */ static void D_CalcGradients(model.msurface_t pface) { model.mplane_t pplane; double mipscale; double[] p_temp1 = new double[3]; double[] p_saxis = new double[3], p_taxis = new double[3]; double t; pplane = pface.plane; mipscale = 1.0 / (double)(1 << miplevel); render.TransformVector(pface.texinfo.vecs[0], ref p_saxis); render.TransformVector(pface.texinfo.vecs[1], ref p_taxis); t = render.xscaleinv * mipscale; d_sdivzstepu = p_saxis[0] * t; d_tdivzstepu = p_taxis[0] * t; t = render.yscaleinv * mipscale; d_sdivzstepv = -p_saxis[1] * t; d_tdivzstepv = -p_taxis[1] * t; d_sdivzorigin = p_saxis[2] * mipscale - render.xcenter * d_sdivzstepu - render.ycenter * d_sdivzstepv; d_tdivzorigin = p_taxis[2] * mipscale - render.xcenter * d_tdivzstepu - render.ycenter * d_tdivzstepv; mathlib.VectorScale(transformed_modelorg, mipscale, ref p_temp1); t = 0x10000 * mipscale; sadjust = ((int)(mathlib.DotProduct(p_temp1, p_saxis) * 0x10000 + 0.5)) - ((pface.texturemins[0] << 16) >> miplevel) + (int)(pface.texinfo.vecs[0][3] * t); tadjust = ((int)(mathlib.DotProduct(p_temp1, p_taxis) * 0x10000 + 0.5)) - ((pface.texturemins[1] << 16) >> miplevel) + (int)(pface.texinfo.vecs[1][3] * t); // // -1 (-epsilon) so we never wander off the edge of the texture // bbextents = ((pface.extents[0] << 16) >> miplevel) - 1; bbextentt = ((pface.extents[1] << 16) >> miplevel) - 1; }
/* ================ 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"); } } } }
/* ================= CL_ParseBeam ================= */ static void CL_ParseBeam(model.model_t m) { int ent; double[] start = new double[3], end = new double[3]; beam_t b; int i; ent = common.MSG_ReadShort (); start[0] = common.MSG_ReadCoord(); start[1] = common.MSG_ReadCoord(); start[2] = common.MSG_ReadCoord(); end[0] = common.MSG_ReadCoord(); end[1] = common.MSG_ReadCoord(); end[2] = common.MSG_ReadCoord(); // override any beam with the same entity for (i = 0, b = cl_beams[i]; i < MAX_BEAMS; i++) b = cl_beams[i]; if (b.entity == ent) { b.entity = ent; b.model = m; b.endtime = cl.time + 0.2; mathlib.VectorCopy (start, ref b.start); mathlib.VectorCopy(end, ref b.end); return; } // find a free beam for (i=0, b=cl_beams[i] ; i< MAX_BEAMS ; i++) { b = cl_beams[i]; if (b.model == null || b.endtime < cl.time) { b.entity = ent; b.model = m; b.endtime = cl.time + 0.2; mathlib.VectorCopy (start, ref b.start); mathlib.VectorCopy(end, ref b.end); return; } } console.Con_Printf ("beam list overflow!\n"); }
/* ================ R_EmitEdge ================ */ static void R_EmitEdge(model.mvertex_t pv0, model.mvertex_t pv1) { edge_t edge, pcheck; int u_check; double u, u_step; double[] local = new double[3] {0, 0, 0}, transformed = new double[3] {0, 0, 0}; double[] world; int v, v2, ceilv0; double scale, lzi0, u0, v0; int side; if (r_lastvertvalid) { u0 = r_u1; v0 = r_v1; lzi0 = r_lzi1; ceilv0 = r_ceilv1; } else { world = pv0.position; // transform and project mathlib.VectorSubtract (world, modelorg, local); 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) Math.Ceiling(v0); } world = pv1.position; // transform and project mathlib.VectorSubtract (world, modelorg, local); 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) Math.Ceiling(r_v1); // create the edge if (ceilv0 == r_ceilv1) { // we cache unclipped horizontal edges as fully clipped if (cacheoffset != 0x7FFFFFFF) { cacheoffset = (uint)(FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK)); } return; // horizontal edge } side = (ceilv0 > r_ceilv1) ? 1 : 0; edge = r_edges[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] = (ushort)(surface_p + 1); edge.surfs[1] = 0; u_step = ((r_u1 - u0) / (r_v1 - v0)); u = u0 + ((double)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] = (ushort)(surface_p + 1); u_step = ((u0 - r_u1) / (v0 - r_v1)); u = r_u1 + ((double)v - r_v1) * u_step; } edge.u_step = (int)(u_step*0x100000); edge.u = (int)(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] != 0) u_check++; // sort trailers after leaders if (newedges[v] == null || newedges[v].u >= u_check) { edge.next = newedges[v]; newedges[v] = edge; } else { pcheck = newedges[v]; while (pcheck.next != null && pcheck.next.u < u_check) pcheck = pcheck.next; edge.next = pcheck.next; pcheck.next = edge; } edge.nextremove = removeedges[v2]; removeedges[v2] = edge; }