/* * =========== * R_AddEfrags * =========== */ public static void R_AddEfrags(entity_t ent) { model.model_t entmodel; int i; if (ent.model == null) { return; } if (ent == client.cl_entities[0]) { return; // never add the world } r_addent = ent; lastlink = null; r_pefragtopnode = null; entmodel = ent.model; for (i = 0; i < 3; i++) { r_emins[i] = ent.origin[i] + entmodel.mins[i]; r_emaxs[i] = ent.origin[i] + entmodel.maxs[i]; } R_SplitEntityOnNode(client.cl.worldmodel.nodes[0]); ent.topnode = r_pefragtopnode; }
/* =========== R_AddEfrags =========== */ public static void R_AddEfrags(entity_t ent) { model.model_t entmodel; int i; if (ent.model == null) return; if (ent == client.cl_entities[0]) return; // never add the world r_addent = ent; lastlink = null; r_pefragtopnode = null; entmodel = ent.model; for (i = 0; i < 3; i++) { r_emins[i] = ent.origin[i] + entmodel.mins[i]; r_emaxs[i] = ent.origin[i] + entmodel.maxs[i]; } R_SplitEntityOnNode(client.cl.worldmodel.nodes[0]); ent.topnode = r_pefragtopnode; }
/* * ============================================================================= * * 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]); } }
/* =================== 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]); }
/* =================== 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]); }
/* * ============================================================================= * * 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_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]); } }