/** * PF_inPHS. * * Also checks portalareas so that doors block sound. */ public static bool PF_inPHS(float[] p1, float[] p2) { int leafnum; int cluster; int area1, area2; byte[] mask; leafnum = CM.CM_PointLeafnum(p1); cluster = CM.CM_LeafCluster(leafnum); area1 = CM.CM_LeafArea(leafnum); mask = CM.CM_ClusterPHS(cluster); leafnum = CM.CM_PointLeafnum(p2); cluster = CM.CM_LeafCluster(leafnum); area2 = CM.CM_LeafArea(leafnum); // quake2 bugfix if (cluster == -1) { return(false); } if (mask != null && 0 == (mask[cluster >> 3] & (1 << (cluster & 7)))) { return(false); // more than one bounce away } if (!CM.CM_AreasConnected(area1, area2)) { return(false); // a door blocks hearing } return(true); }
public static bool PF_inPHS(float[] p1, float[] p2) { int leafnum; int cluster; int area1, area2; byte[] mask; leafnum = CM.CM_PointLeafnum(p1); cluster = CM.CM_LeafCluster(leafnum); area1 = CM.CM_LeafArea(leafnum); mask = CM.CM_ClusterPHS(cluster); leafnum = CM.CM_PointLeafnum(p2); cluster = CM.CM_LeafCluster(leafnum); area2 = CM.CM_LeafArea(leafnum); if (cluster == -1) { return(false); } if (mask != null && (0 == (mask[cluster >> 3] & (1 << (cluster & 7))))) { return(false); } if (!CM.CM_AreasConnected(area1, area2)) { return(false); } return(true); }
public static void SV_FatPVS(Single[] org) { Int32[] leafs = new Int32[64]; Int32 i, j, count; Int32 longs; Byte[] src; Single[] mins = new Single[] { 0, 0, 0 }, maxs = new Single[] { 0, 0, 0 }; for (i = 0; i < 3; i++) { mins[i] = org[i] - 8; maxs[i] = org[i] + 8; } count = CM.CM_BoxLeafnums(mins, maxs, leafs, 64, null); if (count < 1) { Com.Error(Defines.ERR_FATAL, "SV_FatPVS: count < 1"); } longs = (CM.CM_NumClusters() + 31) >> 5; for (i = 0; i < count; i++) { leafs[i] = CM.CM_LeafCluster(leafs[i]); } System.Array.Copy(CM.CM_ClusterPVS(leafs[0]), 0, SV_ENTS.fatpvs, 0, longs << 2); for (i = 1; i < count; i++) { for (j = 0; j < i; j++) { if (leafs[i] == leafs[j]) { break; } } if (j != i) { continue; } src = CM.CM_ClusterPVS(leafs[i]); var k = 0; for (j = 0; j < longs; j++) { SV_ENTS.fatpvs[k] |= src[k++]; SV_ENTS.fatpvs[k] |= src[k++]; SV_ENTS.fatpvs[k] |= src[k++]; SV_ENTS.fatpvs[k] |= src[k++]; } } }
public static void SV_Multicast(Single[] origin, Int32 to) { client_t client; Byte[] mask = null; Int32 leafnum, cluster; Int32 j; Boolean reliable; Int32 area1, area2; reliable = false; if (to != Defines.MULTICAST_ALL_R && to != Defines.MULTICAST_ALL) { leafnum = CM.CM_PointLeafnum(origin); area1 = CM.CM_LeafArea(leafnum); } else { leafnum = 0; area1 = 0; } if (SV_INIT.svs.demofile != null) { SZ.Write(SV_INIT.svs.demo_multicast, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize); } switch (to) { case Defines.MULTICAST_ALL_R: reliable = true; break; case Defines.MULTICAST_ALL: leafnum = 0; mask = null; break; case Defines.MULTICAST_PHS_R: reliable = true; break; case Defines.MULTICAST_PHS: leafnum = CM.CM_PointLeafnum(origin); cluster = CM.CM_LeafCluster(leafnum); mask = CM.CM_ClusterPHS(cluster); break; case Defines.MULTICAST_PVS_R: reliable = true; break; case Defines.MULTICAST_PVS: leafnum = CM.CM_PointLeafnum(origin); cluster = CM.CM_LeafCluster(leafnum); mask = CM.CM_ClusterPVS(cluster); break; default: mask = null; Com.Error(Defines.ERR_FATAL, "SV_Multicast: bad to:" + to + "\\n"); break; } for (j = 0; j < SV_MAIN.maxclients.value; j++) { client = SV_INIT.svs.clients[j]; if (client.state == Defines.cs_free || client.state == Defines.cs_zombie) { continue; } if (client.state != Defines.cs_spawned && !reliable) { continue; } if (mask != null) { leafnum = CM.CM_PointLeafnum(client.edict.s.origin); cluster = CM.CM_LeafCluster(leafnum); area2 = CM.CM_LeafArea(leafnum); if (!CM.CM_AreasConnected(area1, area2)) { continue; } if (cluster == -1) { continue; } if (mask != null && (0 == (mask[cluster >> 3] & (1 << (cluster & 7))))) { continue; } } if (reliable) { SZ.Write(client.netchan.message, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize); } else { SZ.Write(client.datagram, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize); } } SZ.Clear(SV_INIT.sv.multicast); }
public static void SV_LinkEdict(edict_t ent) { areanode_t node; int num_leafs; int j, k; int area; int topnode = 0; if (ent.area.prev != null) { SV_UnlinkEdict(ent); } if (ent == GameBase.g_edicts[0]) { return; } if (!ent.inuse) { return; } Math3D.VectorSubtract(ent.maxs, ent.mins, ent.size); if (ent.solid == Defines.SOLID_BBOX && 0 == (ent.svflags & Defines.SVF_DEADMONSTER)) { int i = (int)(ent.maxs[0] / 8); if (i < 1) { i = 1; } if (i > 31) { i = 31; } j = (int)((-ent.mins[2]) / 8); if (j < 1) { j = 1; } if (j > 31) { j = 31; } k = (int)((ent.maxs[2] + 32) / 8); if (k < 1) { k = 1; } if (k > 63) { k = 63; } ent.s.solid = (k << 10) | (j << 5) | i; } else if (ent.solid == Defines.SOLID_BSP) { ent.s.solid = 31; } else { ent.s.solid = 0; } if (ent.solid == Defines.SOLID_BSP && (ent.s.angles[0] != 0 || ent.s.angles[1] != 0 || ent.s.angles[2] != 0)) { float max, v; max = 0; for (int i = 0; i < 3; i++) { v = Math.Abs(ent.mins[i]); if (v > max) { max = v; } v = Math.Abs(ent.maxs[i]); if (v > max) { max = v; } } for (int i = 0; i < 3; i++) { ent.absmin[i] = ent.s.origin[i] - max; ent.absmax[i] = ent.s.origin[i] + max; } } else { Math3D.VectorAdd(ent.s.origin, ent.mins, ent.absmin); Math3D.VectorAdd(ent.s.origin, ent.maxs, ent.absmax); } ent.absmin[0]--; ent.absmin[1]--; ent.absmin[2]--; ent.absmax[0]++; ent.absmax[1]++; ent.absmax[2]++; ent.num_clusters = 0; ent.areanum = 0; ent.areanum2 = 0; int[] iw = new[] { topnode }; num_leafs = CM.CM_BoxLeafnums(ent.absmin, ent.absmax, SV_WORLD.leafs, SV_WORLD.MAX_TOTAL_ENT_LEAFS, iw); topnode = iw[0]; for (int i = 0; i < num_leafs; i++) { SV_WORLD.clusters[i] = CM.CM_LeafCluster(SV_WORLD.leafs[i]); area = CM.CM_LeafArea(SV_WORLD.leafs[i]); if (area != 0) { if (ent.areanum != 0 && ent.areanum != area) { if (ent.areanum2 != 0 && ent.areanum2 != area && SV_INIT.sv.state == Defines.ss_loading) { Com.DPrintf("Object touching 3 areas at " + ent.absmin[0] + " " + ent.absmin[1] + " " + ent.absmin[2] + "\\n"); } ent.areanum2 = area; } else { ent.areanum = area; } } } if (num_leafs >= SV_WORLD.MAX_TOTAL_ENT_LEAFS) { ent.num_clusters = -1; ent.headnode = topnode; } else { ent.num_clusters = 0; for (int i = 0; i < num_leafs; i++) { if (SV_WORLD.clusters[i] == -1) { continue; } for (j = 0; j < i; j++) { if (SV_WORLD.clusters[j] == SV_WORLD.clusters[i]) { break; } } if (j == i) { if (ent.num_clusters == Defines.MAX_ENT_CLUSTERS) { ent.num_clusters = -1; ent.headnode = topnode; break; } ent.clusternums[ent.num_clusters++] = SV_WORLD.clusters[i]; } } } if (0 == ent.linkcount) { Math3D.VectorCopy(ent.s.origin, ent.s.old_origin); } ent.linkcount++; if (ent.solid == Defines.SOLID_NOT) { return; } node = SV_WORLD.sv_areanodes[0]; while (true) { if (node.axis == -1) { break; } if (ent.absmin[node.axis] > node.dist) { node = node.children[0]; } else if (ent.absmax[node.axis] < node.dist) { node = node.children[1]; } else { break; } } if (ent.solid == Defines.SOLID_TRIGGER) { InsertLinkBefore(ent.area, node.trigger_edicts); } else { InsertLinkBefore(ent.area, node.solid_edicts); } }
public static void SV_BuildClientFrame(client_t client) { Int32 e, i; Single[] org = new Single[] { 0, 0, 0 }; edict_t ent; edict_t clent; client_frame_t frame; entity_state_t state; Int32 l; Int32 clientarea, clientcluster; Int32 leafnum; Int32 c_fullsend; Byte[] clientphs; Byte[] bitvector; clent = client.edict; if (clent.client == null) { return; } frame = client.frames[SV_INIT.sv.framenum & Defines.UPDATE_MASK]; frame.senttime = SV_INIT.svs.realtime; for (i = 0; i < 3; i++) { org[i] = clent.client.ps.pmove.origin[i] * 0.125F + clent.client.ps.viewoffset[i]; } leafnum = CM.CM_PointLeafnum(org); clientarea = CM.CM_LeafArea(leafnum); clientcluster = CM.CM_LeafCluster(leafnum); frame.areabytes = CM.CM_WriteAreaBits(frame.areabits, clientarea); frame.ps.Set(clent.client.ps); SV_FatPVS(org); clientphs = CM.CM_ClusterPHS(clientcluster); frame.num_entities = 0; frame.first_entity = SV_INIT.svs.next_client_entities; c_fullsend = 0; for (e = 1; e < GameBase.num_edicts; e++) { ent = GameBase.g_edicts[e]; if ((ent.svflags & Defines.SVF_NOCLIENT) != 0) { continue; } if (0 == ent.s.modelindex && 0 == ent.s.effects && 0 == ent.s.sound && 0 == ent.s.event_renamed) { continue; } if (ent != clent) { if (!CM.CM_AreasConnected(clientarea, ent.areanum)) { if (0 == ent.areanum2 || !CM.CM_AreasConnected(clientarea, ent.areanum2)) { continue; } } if ((ent.s.renderfx & Defines.RF_BEAM) != 0) { l = ent.clusternums[0]; if (0 == (clientphs[l >> 3] & (1 << (l & 7)))) { continue; } } else { if (ent.s.sound == 0) { bitvector = SV_ENTS.fatpvs; } else { bitvector = SV_ENTS.fatpvs; } if (ent.num_clusters == -1) { if (!CM.CM_HeadnodeVisible(ent.headnode, bitvector)) { continue; } c_fullsend++; } else { for (i = 0; i < ent.num_clusters; i++) { l = ent.clusternums[i]; if ((bitvector[l >> 3] & (1 << (l & 7))) != 0) { break; } } if (i == ent.num_clusters) { continue; } } if (ent.s.modelindex == 0) { Single[] delta = new Single[] { 0, 0, 0 }; Single len; Math3D.VectorSubtract(org, ent.s.origin, delta); len = Math3D.VectorLength(delta); if (len > 400) { continue; } } } } var ix = SV_INIT.svs.next_client_entities % SV_INIT.svs.num_client_entities; state = SV_INIT.svs.client_entities[ix]; if (ent.s.number != e) { Com.DPrintf("FIXING ENT.S.NUMBER!!!\\n"); ent.s.number = e; } SV_INIT.svs.client_entities[ix].Set(ent.s); if (ent.owner == client.edict) { state.solid = 0; } SV_INIT.svs.next_client_entities++; frame.num_entities++; } }
public static void SV_LinkEdict(edict_t ent) { areanode_t node; int num_leafs; int j, k; int area; var topnode = 0; if (ent.area.prev != null) { SV_WORLD.SV_UnlinkEdict(ent); // unlink from old position } if (ent == GameBase.g_edicts[0]) { return; // don't add the world } if (!ent.inuse) { return; } // set the size Math3D.VectorSubtract(ent.maxs, ent.mins, ent.size); // encode the size into the entity_state for client prediction if (ent.solid == Defines.SOLID_BBOX && 0 == (ent.svflags & Defines.SVF_DEADMONSTER)) { // assume that x/y are equal and symetric var i = (int)(ent.maxs[0] / 8); if (i < 1) { i = 1; } if (i > 31) { i = 31; } // z is not symetric j = (int)(-ent.mins[2] / 8); if (j < 1) { j = 1; } if (j > 31) { j = 31; } // and z maxs can be negative... k = (int)((ent.maxs[2] + 32) / 8); if (k < 1) { k = 1; } if (k > 63) { k = 63; } ent.s.solid = (k << 10) | (j << 5) | i; } else if (ent.solid == Defines.SOLID_BSP) { ent.s.solid = 31; // a solid_bbox will never create this value } else { ent.s.solid = 0; } // set the abs box if (ent.solid == Defines.SOLID_BSP && (ent.s.angles[0] != 0 || ent.s.angles[1] != 0 || ent.s.angles[2] != 0)) { // expand for rotation float max, v; max = 0; for (var i = 0; i < 3; i++) { v = Math.Abs(ent.mins[i]); if (v > max) { max = v; } v = Math.Abs(ent.maxs[i]); if (v > max) { max = v; } } for (var i = 0; i < 3; i++) { ent.absmin[i] = ent.s.origin[i] - max; ent.absmax[i] = ent.s.origin[i] + max; } } else { // normal Math3D.VectorAdd(ent.s.origin, ent.mins, ent.absmin); Math3D.VectorAdd(ent.s.origin, ent.maxs, ent.absmax); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent.absmin[0]--; ent.absmin[1]--; ent.absmin[2]--; ent.absmax[0]++; ent.absmax[1]++; ent.absmax[2]++; // link to PVS leafs ent.num_clusters = 0; ent.areanum = 0; ent.areanum2 = 0; // get all leafs, including solids int[] iw = { topnode }; num_leafs = CM.CM_BoxLeafnums(ent.absmin, ent.absmax, SV_WORLD.leafs, SV_WORLD.MAX_TOTAL_ENT_LEAFS, iw); topnode = iw[0]; // set areas for (var i = 0; i < num_leafs; i++) { SV_WORLD.clusters[i] = CM.CM_LeafCluster(SV_WORLD.leafs[i]); area = CM.CM_LeafArea(SV_WORLD.leafs[i]); if (area != 0) { // doors may legally straggle two areas, // but nothing should evern need more than that if (ent.areanum != 0 && ent.areanum != area) { if (ent.areanum2 != 0 && ent.areanum2 != area && SV_INIT.sv.state == Defines.ss_loading) { Com.DPrintf("Object touching 3 areas at " + ent.absmin[0] + " " + ent.absmin[1] + " " + ent.absmin[2] + "\n"); } ent.areanum2 = area; } else { ent.areanum = area; } } } if (num_leafs >= SV_WORLD.MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent.num_clusters = -1; ent.headnode = topnode; } else { ent.num_clusters = 0; for (var i = 0; i < num_leafs; i++) { if (SV_WORLD.clusters[i] == -1) { continue; // not a visible leaf } for (j = 0; j < i; j++) { if (SV_WORLD.clusters[j] == SV_WORLD.clusters[i]) { break; } } if (j == i) { if (ent.num_clusters == Defines.MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode ent.num_clusters = -1; ent.headnode = topnode; break; } ent.clusternums[ent.num_clusters++] = SV_WORLD.clusters[i]; } } } // if first time, make sure old_origin is valid if (0 == ent.linkcount) { Math3D.VectorCopy(ent.s.origin, ent.s.old_origin); } ent.linkcount++; if (ent.solid == Defines.SOLID_NOT) { return; } // find the first node that the ent's box crosses node = SV_WORLD.sv_areanodes[0]; while (true) { if (node.axis == -1) { break; } if (ent.absmin[node.axis] > node.dist) { node = node.children[0]; } else if (ent.absmax[node.axis] < node.dist) { node = node.children[1]; } else { break; // crosses the node } } // link it in if (ent.solid == Defines.SOLID_TRIGGER) { SV_WORLD.InsertLinkBefore(ent.area, node.trigger_edicts); } else { SV_WORLD.InsertLinkBefore(ent.area, node.solid_edicts); } }
/** * Decides which entities are going to be visible to the client, and copies * off the playerstat and areabits. */ public static void SV_BuildClientFrame(client_t client) { int e, i; float[] org = { 0, 0, 0 }; edict_t ent; edict_t clent; client_frame_t frame; entity_state_t state; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; byte[] clientphs; byte[] bitvector; clent = client.edict; if (clent.client == null) { return; // not in game yet } // this is the frame we are creating frame = client.frames[SV_INIT.sv.framenum & Defines.UPDATE_MASK]; frame.senttime = SV_INIT.svs.realtime; // save it for ping calc later // find the client's PVS for (i = 0; i < 3; i++) { org[i] = clent.client.ps.pmove.origin[i] * 0.125f + clent.client.ps.viewoffset[i]; } leafnum = CM.CM_PointLeafnum(org); clientarea = CM.CM_LeafArea(leafnum); clientcluster = CM.CM_LeafCluster(leafnum); // calculate the visible areas frame.areabytes = CM.CM_WriteAreaBits(frame.areabits, clientarea); // grab the current player_state_t frame.ps.set(clent.client.ps); SV_ENTS.SV_FatPVS(org); clientphs = CM.CM_ClusterPHS(clientcluster); // build up the list of visible entities frame.num_entities = 0; frame.first_entity = SV_INIT.svs.next_client_entities; c_fullsend = 0; for (e = 1; e < GameBase.num_edicts; e++) { ent = GameBase.g_edicts[e]; // ignore ents without visible models if ((ent.svflags & Defines.SVF_NOCLIENT) != 0) { continue; } // ignore ents without visible models unless they have an effect if (0 == ent.s.modelindex && 0 == ent.s.effects && 0 == ent.s.sound && 0 == ent.s.@event) { continue; } // ignore if not touching a PV leaf // check area if (ent != clent) { if (!CM.CM_AreasConnected(clientarea, ent.areanum)) { // doors can legally straddle two areas, so we may need to check another one if (0 == ent.areanum2 || !CM.CM_AreasConnected(clientarea, ent.areanum2)) { continue; // blocked by a door } } // beams just check one point for PHS if ((ent.s.renderfx & Defines.RF_BEAM) != 0) { l = ent.clusternums[0]; if (0 == (clientphs[l >> 3] & (1 << (l & 7)))) { continue; } } else { // FIXME: if an ent has a model and a sound, but isn't // in the PVS, only the PHS, clear the model if (ent.s.sound == 0) { bitvector = SV_ENTS.fatpvs; //clientphs; } else { bitvector = SV_ENTS.fatpvs; } if (ent.num_clusters == -1) { // too many leafs for // individual check, go by // headnode if (!CM.CM_HeadnodeVisible(ent.headnode, bitvector)) { continue; } c_fullsend++; } else { // check individual leafs for (i = 0; i < ent.num_clusters; i++) { l = ent.clusternums[i]; if ((bitvector[l >> 3] & (1 << (l & 7))) != 0) { break; } } if (i == ent.num_clusters) { continue; // not visible } } if (ent.s.modelindex == 0) { // don't send sounds if they // will be attenuated away float[] delta = { 0, 0, 0 }; float len; Math3D.VectorSubtract(org, ent.s.origin, delta); len = Math3D.VectorLength(delta); if (len > 400) { continue; } } } } // add it to the circular client_entities array var ix = SV_INIT.svs.next_client_entities % SV_INIT.svs.num_client_entities; state = SV_INIT.svs.client_entities[ix]; if (ent.s.number != e) { Com.DPrintf("FIXING ENT.S.NUMBER!!!\n"); ent.s.number = e; } //*state = ent.s; SV_INIT.svs.client_entities[ix].set(ent.s); // don't mark players missiles as solid if (ent.owner == client.edict) { state.solid = 0; } SV_INIT.svs.next_client_entities++; frame.num_entities++; } }
/** * The client will interpolate the view position, so we can't use a single * PVS point. */ public static void SV_FatPVS(float[] org) { var leafs = new int[64]; int i, j, count; int longs; byte[] src; float[] mins = { 0, 0, 0 }, maxs = { 0, 0, 0 }; for (i = 0; i < 3; i++) { mins[i] = org[i] - 8; maxs[i] = org[i] + 8; } count = CM.CM_BoxLeafnums(mins, maxs, leafs, 64, null); if (count < 1) { Com.Error(Defines.ERR_FATAL, "SV_FatPVS: count < 1"); } longs = (CM.CM_NumClusters() + 31) >> 5; // convert leafs to clusters for (i = 0; i < count; i++) { leafs[i] = CM.CM_LeafCluster(leafs[i]); } Array.Copy(CM.CM_ClusterPVS(leafs[0]), 0, SV_ENTS.fatpvs, 0, longs << 2); // or in all the other leaf bits for (i = 1; i < count; i++) { for (j = 0; j < i; j++) { if (leafs[i] == leafs[j]) { break; } } if (j != i) { continue; // already have the cluster we want } src = CM.CM_ClusterPVS(leafs[i]); //for (j=0 ; j<longs ; j++) // ((long *)fatpvs)[j] |= ((long *)src)[j]; var k = 0; for (j = 0; j < longs; j++) { SV_ENTS.fatpvs[k] |= src[k++]; SV_ENTS.fatpvs[k] |= src[k++]; SV_ENTS.fatpvs[k] |= src[k++]; SV_ENTS.fatpvs[k] |= src[k++]; } } }