/* * ============================================================================= * * DYNAMIC LIGHTS * * ============================================================================= */ /* * ============= * R_MarkLights * ============= */ static void R_MarkLights(client.dlight_t light, int bit, model.node_or_leaf_t _node) { model.mplane_t splitplane; double dist; model.msurface_t surf; int i; if (_node.contents < 0) { return; } model.mnode_t node = (model.mnode_t)_node; splitplane = node.plane; dist = mathlib.DotProduct(light.origin, splitplane.normal) - splitplane.dist; if (dist > light.radius) { R_MarkLights(light, bit, node.children[0]); return; } if (dist < -light.radius) { R_MarkLights(light, bit, node.children[1]); return; } // mark the polygons for (i = 0; i < node.numsurfaces; i++) { surf = client.cl.worldmodel.surfaces[node.firstsurface + i]; if (surf.dlightframe != r_dlightframecount) { surf.dlightbits = 0; surf.dlightframe = r_dlightframecount; } surf.dlightbits |= bit; } R_MarkLights(light, bit, node.children[0]); R_MarkLights(light, bit, node.children[1]); }
/* * =================== * R_SplitEntityOnNode2 * =================== */ static void R_SplitEntityOnNode2(model.node_or_leaf_t node) { model.mplane_t splitplane; int sides; if (node.visframe != r_visframecount) { return; } if (node.contents < 0) { if (node.contents != bspfile.CONTENTS_SOLID) { r_pefragtopnode = node; // we've reached a non-solid leaf, so it's } // visible and not BSP clipped return; } model.mnode_t _node = (model.mnode_t)node; splitplane = _node.plane; sides = mathlib.BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); if (sides == 3) { // remember first splitter r_pefragtopnode = node; return; } // not split yet; recurse down the contacted side if ((sides & 1) != 0) { R_SplitEntityOnNode2(_node.children[0]); } else { R_SplitEntityOnNode2(_node.children[1]); } }
/* * ============================================================================= * * LIGHT SAMPLING * * ============================================================================= */ static int RecursiveLightPoint(model.node_or_leaf_t _node, double[] start, double[] end) { int r; double front, back, frac; bool side; model.mplane_t plane; double[] mid = new double[3]; model.msurface_t surf; int s, t, ds, dt; int i; model.mtexinfo_t tex; int lightmap; uint scale; int maps; if (_node.contents < 0) { return(-1); // didn't hit anything } model.mnode_t node = (model.mnode_t)_node; // calculate mid point // FIXME: optimize for axial plane = node.plane; front = mathlib.DotProduct(start, plane.normal) - plane.dist; back = mathlib.DotProduct(end, plane.normal) - plane.dist; side = (front < 0); if ((back < 0) == side) { return(RecursiveLightPoint(node.children[side ? 1 : 0], start, end)); } frac = front / (front - back); mid[0] = start[0] + (end[0] - start[0]) * frac; mid[1] = start[1] + (end[1] - start[1]) * frac; mid[2] = start[2] + (end[2] - start[2]) * frac; // go down front side r = RecursiveLightPoint(node.children[side ? 1 : 0], start, mid); if (r >= 0) { return(r); // hit something } if ((back < 0) == side) { return(-1); // didn't hit anuthing } // check for impact on this node for (i = 0; i < node.numsurfaces; i++) { surf = client.cl.worldmodel.surfaces[node.firstsurface + i]; if ((surf.flags & model.SURF_DRAWTILED) != 0) { continue; // no lightmaps } tex = surf.texinfo; s = (int)(mathlib.DotProduct(mid, tex.vecs[0]) + tex.vecs[0][3]); t = (int)(mathlib.DotProduct(mid, tex.vecs[1]) + tex.vecs[1][3]); if (s < surf.texturemins[0] || t < surf.texturemins[1]) { continue; } ds = s - surf.texturemins[0]; dt = t - surf.texturemins[1]; if (ds > surf.extents[0] || dt > surf.extents[1]) { continue; } if (surf.samples == null) { return(0); } ds >>= 4; dt >>= 4; lightmap = surf.samples.ofs; r = 0; if (surf.samples != null) { lightmap += dt * ((surf.extents[0] >> 4) + 1) + ds; for (maps = 0; maps < bspfile.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++) { scale = (uint)d_lightstylevalue[surf.styles[maps]]; r += (int)(surf.samples.buffer[lightmap] * scale); lightmap += ((surf.extents[0] >> 4) + 1) * ((surf.extents[1] >> 4) + 1); } r >>= 8; } return(r); } // go down back side return(RecursiveLightPoint(node.children[!side ? 1 : 0], mid, end)); }
/* * ================ * R_RecursiveWorldNode * ================ */ static void R_RecursiveWorldNode(model.node_or_leaf_t node, int clipflags) { int i, c, side, pindex; double[] acceptpt = new double[3], rejectpt = new double[3]; 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(ref 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_SplitEntityOnNode * =================== */ static void R_SplitEntityOnNode(model.node_or_leaf_t node) { efrag_t ef; model.mplane_t splitplane; model.mleaf_t leaf; int sides; if (node.contents == bspfile.CONTENTS_SOLID) { return; } // add an efrag if the node is a leaf if (node.contents < 0) { if (r_pefragtopnode == null) { r_pefragtopnode = node; } leaf = (model.mleaf_t)node; // grab an efrag off the free list ef = client.cl.free_efrags; if (ef == null) { console.Con_Printf("Too many efrags!\n"); return; // no free fragments... } client.cl.free_efrags = client.cl.free_efrags.entnext; ef.entity = r_addent; // add the entity link if (lastlink == null) { r_addent.efrag = ef; } else { lastlink.entnext = ef; } lastlink = ef; ef.entnext = null; // set the leaf links ef.leaf = leaf; ef.leafnext = leaf.efrags; leaf.efrags = ef; return; } // NODE_MIXED model.mnode_t _node = (model.mnode_t)node; splitplane = _node.plane; sides = mathlib.BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (r_pefragtopnode == null) { r_pefragtopnode = node; } } // recurse down the contacted sides if ((sides & 1) != 0) { R_SplitEntityOnNode(_node.children[0]); } if ((sides & 2) != 0) { R_SplitEntityOnNode(_node.children[1]); } }