static object _LastObj; // see comments #endregion Fields #region Methods /// <summary> /// R_AddEfrags /// </summary> public static void AddEfrags(entity_t ent) { if (ent.model == null) return; _AddEnt = ent; _LastObj = ent; // lastlink = &ent->efrag; _EfragTopNode = null; model_t entmodel = ent.model; _EMins = ent.origin + entmodel.mins; _EMaxs = ent.origin + entmodel.maxs; SplitEntityOnNode(Client.cl.worldmodel.nodes[0]); ent.topnode = _EfragTopNode; }
static object _LastObj; // see comments /// <summary> /// R_AddEfrags /// </summary> public static void AddEfrags(Entity ent) { if (ent.model == null) { return; } _AddEnt = ent; _LastObj = ent; // lastlink = &ent->efrag; _EfragTopNode = null; Model entmodel = ent.model; _EMins = ent.origin + entmodel.mins; _EMaxs = ent.origin + entmodel.maxs; SplitEntityOnNode(Client.Cl.worldmodel.nodes[0]); ent.topnode = _EfragTopNode; }
/// <summary> /// SV_FindTouchedLeafs /// </summary> static void FindTouchedLeafs(edict_t ent, mnodebase_t node) { if (node.contents == Contents.CONTENTS_SOLID) { return; } // add an efrag if the node is a leaf if (node.contents < 0) { if (ent.num_leafs == Progs.MAX_ENT_LEAFS) { return; } mleaf_t leaf = (mleaf_t)node; int leafnum = Array.IndexOf(sv.worldmodel.leafs, leaf) - 1; ent.leafnums[ent.num_leafs] = (short)leafnum; ent.num_leafs++; return; } // NODE_MIXED mnode_t n = (mnode_t)node; mplane_t splitplane = n.plane; int sides = Mathlib.BoxOnPlaneSide(ref ent.v.absmin, ref ent.v.absmax, splitplane); // recurse down the contacted sides if ((sides & 1) != 0) { FindTouchedLeafs(ent, n.children[0]); } if ((sides & 2) != 0) { FindTouchedLeafs(ent, n.children[1]); } }
/// <summary> /// R_MarkLights /// </summary> private static void MarkLights(dlight_t light, int bit, mnodebase_t node) { if (node.contents < 0) { return; } mnode_t n = (mnode_t)node; mplane_t splitplane = n.plane; float dist = Vector3.Dot(light.origin, splitplane.normal) - splitplane.dist; if (dist > light.radius) { MarkLights(light, bit, n.children[0]); return; } if (dist < -light.radius) { MarkLights(light, bit, n.children[1]); return; } // mark the polygons for (int i = 0; i < n.numsurfaces; i++) { msurface_t surf = client.cl.worldmodel.surfaces[n.firstsurface + i]; if (surf.dlightframe != _DlightFrameCount) { surf.dlightbits = 0; surf.dlightframe = _DlightFrameCount; } surf.dlightbits |= bit; } MarkLights(light, bit, n.children[0]); MarkLights(light, bit, n.children[1]); }
/// <summary> /// SV_AddToFatPVS /// The PVS must include a small area around the client to allow head bobbing /// or other small motion on the client side. Otherwise, a bob might cause an /// entity that should be visible to not show up, especially when the bob /// crosses a waterline. /// </summary> private static void AddToFatPVS(ref Vector3 org, mnodebase_t node) { while (true) { // if this is a leaf, accumulate the pvs bits if (node.contents < 0) { if (node.contents != Contents.CONTENTS_SOLID) { byte[] pvs = Mod.LeafPVS((mleaf_t)node, sv.worldmodel); for (int i = 0; i < _FatBytes; i++) { _FatPvs[i] |= pvs[i]; } } return; } mnode_t n = (mnode_t)node; mplane_t plane = n.plane; float d = Vector3.Dot(org, plane.normal) - plane.dist; if (d > 8) { node = n.children[0]; } else if (d < -8) { node = n.children[1]; } else { // go down both AddToFatPVS(ref org, n.children[0]); node = n.children[1]; } } }
/// <summary> /// R_RecursiveWorldNode /// </summary> private static void RecursiveWorldNode(mnodebase_t node) { if (node.contents == Contents.CONTENTS_SOLID) { return; // solid } if (node.visframe != _VisFrameCount) { return; } if (CullBox(ref node.mins, ref node.maxs)) { return; } int c; // if a leaf node, draw stuff if (node.contents < 0) { mleaf_t pleaf = (mleaf_t)node; msurface_t[] marks = pleaf.marksurfaces; int mark = pleaf.firstmarksurface; c = pleaf.nummarksurfaces; if (c != 0) { do { marks[mark].visframe = _FrameCount; mark++; } while(--c != 0); } // deal with model fragments in this leaf if (pleaf.efrags != null) { StoreEfrags(pleaf.efrags); } return; } // node is just a decision point, so go down the apropriate sides mnode_t n = (mnode_t)node; // find which side of the node we are on mplane_t plane = n.plane; double dot; switch (plane.type) { case Planes.PLANE_X: dot = _ModelOrg.X - plane.dist; break; case Planes.PLANE_Y: dot = _ModelOrg.Y - plane.dist; break; case Planes.PLANE_Z: dot = _ModelOrg.Z - plane.dist; break; default: dot = Vector3.Dot(_ModelOrg, plane.normal) - plane.dist; break; } int side = (dot >= 0 ? 0 : 1); // recurse down the children, front side first RecursiveWorldNode(n.children[side]); // draw stuff c = n.numsurfaces; if (c != 0) { msurface_t[] surf = client.cl.worldmodel.surfaces; int offset = n.firstsurface; if (dot < 0 - QDef.BACKFACE_EPSILON) { side = Surf.SURF_PLANEBACK; } else if (dot > QDef.BACKFACE_EPSILON) { side = 0; } for ( ; c != 0; c--, offset++) { if (surf[offset].visframe != _FrameCount) { continue; } // don't backface underwater surfaces, because they warp if ((surf[offset].flags & Surf.SURF_UNDERWATER) == 0 && ((dot < 0) ^ ((surf[offset].flags & Surf.SURF_PLANEBACK) != 0))) { continue; // wrong side } // if sorting by texture, just store it out if (_glTexSort.Value != 0) { if (!_IsMirror || surf[offset].texinfo.texture != client.cl.worldmodel.textures[_MirrorTextureNum]) { surf[offset].texturechain = surf[offset].texinfo.texture.texturechain; surf[offset].texinfo.texture.texturechain = surf[offset]; } } else if ((surf[offset].flags & Surf.SURF_DRAWSKY) != 0) { surf[offset].texturechain = _SkyChain; _SkyChain = surf[offset]; } else if ((surf[offset].flags & Surf.SURF_DRAWTURB) != 0) { surf[offset].texturechain = _WaterChain; _WaterChain = surf[offset]; } else { DrawSequentialPoly(surf[offset]); } } } // recurse down the back side RecursiveWorldNode(n.children[side == 0 ? 1 : 0]); }
private static int RecursiveLightPoint(mnodebase_t node, ref Vector3 start, ref Vector3 end) { if (node.contents < 0) { return(-1); // didn't hit anything } mnode_t n = (mnode_t)node; // calculate mid point // FIXME: optimize for axial mplane_t plane = n.plane; float front = Vector3.Dot(start, plane.normal) - plane.dist; float back = Vector3.Dot(end, plane.normal) - plane.dist; int side = front < 0 ? 1 : 0; if ((back < 0 ? 1 : 0) == side) { return(RecursiveLightPoint(n.children[side], ref start, ref end)); } float frac = front / (front - back); Vector3 mid = start + (end - start) * frac; // go down front side int r = RecursiveLightPoint(n.children[side], ref start, ref mid); if (r >= 0) { return(r); // hit something } if ((back < 0 ? 1 : 0) == side) { return(-1); // didn't hit anuthing } // check for impact on this node _LightSpot = mid; _LightPlane = plane; msurface_t[] surf = client.cl.worldmodel.surfaces; int offset = n.firstsurface; for (int i = 0; i < n.numsurfaces; i++, offset++) { if ((surf[offset].flags & Surf.SURF_DRAWTILED) != 0) { continue; // no lightmaps } mtexinfo_t tex = surf[offset].texinfo; int s = (int)(Vector3.Dot(mid, tex.vecs[0].Xyz) + tex.vecs[0].W); int t = (int)(Vector3.Dot(mid, tex.vecs[1].Xyz) + tex.vecs[1].W); if (s < surf[offset].texturemins[0] || t < surf[offset].texturemins[1]) { continue; } int ds = s - surf[offset].texturemins[0]; int dt = t - surf[offset].texturemins[1]; if (ds > surf[offset].extents[0] || dt > surf[offset].extents[1]) { continue; } if (surf[offset].sample_base == null) { return(0); } ds >>= 4; dt >>= 4; byte[] lightmap = surf[offset].sample_base; int lmOffset = surf[offset].sampleofs; short[] extents = surf[offset].extents; r = 0; if (lightmap != null) { lmOffset += dt * ((extents[0] >> 4) + 1) + ds; for (int maps = 0; maps < bsp_file.MAXLIGHTMAPS && surf[offset].styles[maps] != 255; maps++) { int scale = _LightStyleValue[surf[offset].styles[maps]]; r += lightmap[lmOffset] * scale; lmOffset += ((extents[0] >> 4) + 1) * ((extents[1] >> 4) + 1); } r >>= 8; } return(r); } // go down back side return(RecursiveLightPoint(n.children[side == 0 ? 1 : 0], ref mid, ref end)); }
/// <summary> /// R_SplitEntityOnNode /// </summary> static void SplitEntityOnNode(mnodebase_t node) { if (node.contents == Contents.CONTENTS_SOLID) { return; } // add an efrag if the node is a leaf if (node.contents < 0) { if (_EfragTopNode == null) { _EfragTopNode = node as mnode_t; } mleaf_t leaf = (mleaf_t)(object)node; // grab an efrag off the free list EFrag ef = Client.Cl.free_efrags; if (ef == null) { Con.Print("Too many efrags!\n"); return; // no free fragments... } Client.Cl.free_efrags = Client.Cl.free_efrags.entnext; ef.entity = _AddEnt; // add the entity link // *lastlink = ef; if (_LastObj is Entity) { ((Entity)_LastObj).efrag = ef; } else { ((EFrag)_LastObj).entnext = ef; } _LastObj = ef; // lastlink = &ef->entnext; ef.entnext = null; // set the leaf links ef.leaf = leaf; ef.leafnext = leaf.efrags; leaf.efrags = ef; return; } // NODE_MIXED mnode_t n = node as mnode_t; if (n == null) { return; } mplane_t splitplane = n.plane; int sides = Mathlib.BoxOnPlaneSide(ref _EMins, ref _EMaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (_EfragTopNode == null) { _EfragTopNode = n; } } // recurse down the contacted sides if ((sides & 1) != 0) { SplitEntityOnNode(n.children[0]); } if ((sides & 2) != 0) { SplitEntityOnNode(n.children[1]); } }
/// <summary> /// R_SplitEntityOnNode /// </summary> static void SplitEntityOnNode(mnodebase_t node) { if (node.contents == Contents.CONTENTS_SOLID) return; // add an efrag if the node is a leaf if (node.contents < 0) { if (_EfragTopNode == null) _EfragTopNode = node as mnode_t; mleaf_t leaf = (mleaf_t)(object)node; // grab an efrag off the free list efrag_t ef = Client.cl.free_efrags; if (ef == null) { Con.Print("Too many efrags!\n"); return; // no free fragments... } Client.cl.free_efrags = Client.cl.free_efrags.entnext; ef.entity = _AddEnt; // add the entity link // *lastlink = ef; if (_LastObj is entity_t) { ((entity_t)_LastObj).efrag = ef; } else { ((efrag_t)_LastObj).entnext = ef; } _LastObj = ef; // lastlink = &ef->entnext; ef.entnext = null; // set the leaf links ef.leaf = leaf; ef.leafnext = leaf.efrags; leaf.efrags = ef; return; } // NODE_MIXED mnode_t n = node as mnode_t; if (n == null) return; mplane_t splitplane = n.plane; int sides = Mathlib.BoxOnPlaneSide(ref _EMins, ref _EMaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (_EfragTopNode == null) _EfragTopNode = n; } // recurse down the contacted sides if ((sides & 1) != 0) SplitEntityOnNode(n.children[0]); if ((sides & 2) != 0) SplitEntityOnNode(n.children[1]); }
public void Clear() { this.forcelink = false; this.update_type = 0; this.baseline = entity_state_t.Empty; this.msgtime = 0; this.msg_origins[0] = Vector3.Zero; this.msg_origins[1] = Vector3.Zero; this.origin = Vector3.Zero; this.msg_angles[0] = Vector3.Zero; this.msg_angles[1] = Vector3.Zero; this.angles = Vector3.Zero; this.model = null; this.efrag = null; this.frame = 0; this.syncbase = 0; this.colormap = null; this.effects = 0; this.skinnum = 0; this.visframe = 0; this.dlightframe = 0; this.dlightbits = 0; this.trivial_accept = 0; this.topnode = null; }
/// <summary> /// Mod_SetParent /// </summary> static void SetParent(mnodebase_t node, mnode_t parent) { node.parent = parent; if (node.contents < 0) return; mnode_t n = (mnode_t)node; SetParent(n.children[0], n); SetParent(n.children[1], n); }
/// <summary> /// Mod_LoadNodes /// </summary> static void LoadNodes(ref lump_t l) { if ((l.filelen % dnode_t.SizeInBytes) != 0) Sys.Error("MOD_LoadBmodel: funny lump size in {0}", _LoadModel.name); int count = l.filelen / dnode_t.SizeInBytes; mnode_t[] dest = new mnode_t[count]; for (int i = 0; i < dest.Length; i++) dest[i] = new mnode_t(); _LoadModel.nodes = dest; _LoadModel.numnodes = count; for (int i = 0, offset = l.fileofs; i < count; i++, offset += dnode_t.SizeInBytes) { dnode_t src = Sys.BytesToStructure<dnode_t>(_ModBase, offset); dest[i].mins.X = Common.LittleShort(src.mins[0]); dest[i].mins.Y = Common.LittleShort(src.mins[1]); dest[i].mins.Z = Common.LittleShort(src.mins[2]); dest[i].maxs.X = Common.LittleShort(src.maxs[0]); dest[i].maxs.Y = Common.LittleShort(src.maxs[1]); dest[i].maxs.Z = Common.LittleShort(src.maxs[2]); int p = Common.LittleLong(src.planenum); dest[i].plane = _LoadModel.planes[p]; dest[i].firstsurface = (ushort)Common.LittleShort((short)src.firstface); dest[i].numsurfaces = (ushort)Common.LittleShort((short)src.numfaces); for (int j = 0; j < 2; j++) { p = Common.LittleShort(src.children[j]); if (p >= 0) dest[i].children[j] = _LoadModel.nodes[p]; else dest[i].children[j] = _LoadModel.leafs[-1 - p]; } } SetParent(_LoadModel.nodes[0], null); // sets nodes and leafs }