/// <summary> /// R_MirrorChain /// </summary> //private void MirrorChain( MemorySurface s ) //{ // if( _IsMirror ) // return; // _IsMirror = true; // _MirrorPlane = s.plane; //} /// <summary> /// R_RecursiveWorldNode /// </summary> private void RecursiveWorldNode(MemoryNodeBase node) { Occlusion.RecursiveWorldNode(node, _ModelOrg, _FrameCount, ref _Frustum, (surf) => { DrawSequentialPoly(surf); }, (efrags) => { StoreEfrags(efrags); }); }
/// <summary> /// R_MarkLeaves /// </summary> public void MarkLeaves( ) { if (this.OldViewLeaf == this.ViewLeaf && !this.Host.Cvars.NoVis.Get <bool>()) { return; } //if( _IsMirror ) // return; this.VisFrameCount++; this.OldViewLeaf = this.ViewLeaf; byte[] vis; if (this.Host.Cvars.NoVis.Get <bool>()) { vis = new byte[4096]; Utilities.FillArray <byte>(vis, 0xff); // todo: add count parameter? //memset(solid, 0xff, (cl.worldmodel->numleafs + 7) >> 3); } else { vis = this.Host.Client.cl.worldmodel.LeafPVS(this.ViewLeaf); } var world = this.Host.Client.cl.worldmodel; for (var i = 0; i < world.NumLeafs; i++) { if ((vis[i >> 3] != 0) & (1 << (i & 7) != 0)) { MemoryNodeBase node = world.Leaves[i + 1]; do { if (node.visframe == this.VisFrameCount) { break; } node.visframe = this.VisFrameCount; node = node.parent; } while (node != null); } } }
/// <summary> /// SV_FindTouchedLeafs /// </summary> private void FindTouchedLeafs(MemoryEdict ent, MemoryNodeBase node) { if (node.contents == ( Int32 )Q1Contents.Solid) { return; } // add an efrag if the node is a leaf if (node.contents < 0) { if (ent.num_leafs == ProgramDef.MAX_ENT_LEAFS) { return; } var leaf = ( MemoryLeaf )node; var leafnum = Array.IndexOf(sv.worldmodel.Leaves, leaf) - 1; ent.leafnums[ent.num_leafs] = ( Int16 )leafnum; ent.num_leafs++; return; } // NODE_MIXED var n = ( MemoryNode )node; var splitplane = n.plane; var 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 void MarkLights(dlight_t light, int bit, MemoryNodeBase node) { if (node.contents < 0) { return; } var n = ( MemoryNode )node; var splitplane = n.plane; var dist = Vector3.Dot(light.origin, splitplane.normal) - splitplane.dist; if (dist > light.radius) { this.MarkLights(light, bit, n.children[0]); return; } if (dist < -light.radius) { this.MarkLights(light, bit, n.children[1]); return; } // mark the polygons for (var i = 0; i < n.numsurfaces; i++) { var surf = this.Host.Client.cl.worldmodel.Surfaces[n.firstsurface + i]; if (surf.dlightframe != this._DlightFrameCount) { surf.dlightbits = 0; surf.dlightframe = this._DlightFrameCount; } surf.dlightbits |= bit; } this.MarkLights(light, bit, n.children[0]); this.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 void AddToFatPVS(ref Vector3 org, MemoryNodeBase node) { while (true) { // if this is a leaf, accumulate the pvs bits if (node.contents < 0) { if (node.contents != ( int )Q1Contents.Solid) { var pvs = this.sv.worldmodel.LeafPVS(( MemoryLeaf )node); for (var i = 0; i < this._FatBytes; i++) { this._FatPvs[i] |= pvs[i]; } } return; } var n = ( MemoryNode )node; var plane = n.plane; var 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 this.AddToFatPVS(ref org, n.children[0]); node = n.children[1]; } } }
/// <summary> /// R_RecursiveWorldNode /// </summary> public void RecursiveWorldNode(MemoryNodeBase node, Vector3 modelOrigin, int frameCount, ref Plane[] frustum, Action <MemorySurface> onDrawSurface, Action <EFrag> onStoreEfrags) { if (node.contents == ( int )Q1Contents.Solid) { return; // solid } if (node.visframe != this.VisFrameCount) { return; } if (Utilities.CullBox(ref node.mins, ref node.maxs, ref frustum)) { return; } int c; // if a leaf node, draw stuff if (node.contents < 0) { var pleaf = ( MemoryLeaf )node; var marks = pleaf.marksurfaces; var 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) { onStoreEfrags(pleaf.efrags); } return; } // node is just a decision point, so go down the apropriate sides var n = ( MemoryNode )node; // find which side of the node we are on var plane = n.plane; double dot; switch (plane.type) { case PlaneDef.PLANE_X: dot = modelOrigin.X - plane.dist; break; case PlaneDef.PLANE_Y: dot = modelOrigin.Y - plane.dist; break; case PlaneDef.PLANE_Z: dot = modelOrigin.Z - plane.dist; break; default: dot = Vector3.Dot(modelOrigin, plane.normal) - plane.dist; break; } var side = dot >= 0 ? 0 : 1; // recurse down the children, front side first this.RecursiveWorldNode(n.children[side], modelOrigin, frameCount, ref frustum, onDrawSurface, onStoreEfrags); // draw stuff c = n.numsurfaces; if (c != 0) { var surf = this.Host.Client.cl.worldmodel.Surfaces; int offset = n.firstsurface; if (dot < 0 - QDef.BACKFACE_EPSILON) { side = ( int )Q1SurfaceFlags.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 & ( int )Q1SurfaceFlags.Underwater) == 0 && (dot < 0) ^ ((surf[offset].flags & ( int )Q1SurfaceFlags.PlaneBack) != 0)) { continue; // wrong side } // if sorting by texture, just store it out if (this.Host.Cvars.glTexSort.Get <bool>()) { //if( !_IsMirror || surf[offset].texinfo.texture != Host.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 & ( int )Q1SurfaceFlags.Sky) != 0) { surf[offset].texturechain = this.TextureChains.SkyChain; this.TextureChains.SkyChain = surf[offset]; } else if ((surf[offset].flags & ( int )Q1SurfaceFlags.Turbulence) != 0) { surf[offset].texturechain = this.TextureChains.WaterChain; this.TextureChains.WaterChain = surf[offset]; } else { onDrawSurface(surf[offset]); } } } // recurse down the back side this.RecursiveWorldNode(n.children[side == 0 ? 1 : 0], modelOrigin, frameCount, ref frustum, onDrawSurface, onStoreEfrags); }
/// <summary> /// R_SplitEntityOnNode /// </summary> private void SplitEntityOnNode(MemoryNodeBase node) { if (node.contents == ( int )Q1Contents.Solid) { return; } // add an efrag if the node is a leaf if (node.contents < 0) { if (this._EfragTopNode == null) { this._EfragTopNode = node as MemoryNode; } var leaf = (MemoryLeaf)( object )node; // grab an efrag off the free list var ef = this.Host.Client.cl.free_efrags; if (ef == null) { this.Host.Console.Print("Too many efrags!\n"); return; // no free fragments... } this.Host.Client.cl.free_efrags = this.Host.Client.cl.free_efrags.entnext; ef.entity = this._AddEnt; // add the entity link // *lastlink = ef; if (this._LastObj is Entity) { ((Entity)this._LastObj).efrag = ef; } else { ((EFrag)this._LastObj).entnext = ef; } this._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 var n = node as MemoryNode; if (n == null) { return; } var splitplane = n.plane; var sides = MathLib.BoxOnPlaneSide(ref this._EMins, ref this._EMaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (this._EfragTopNode == null) { this._EfragTopNode = n; } } // recurse down the contacted sides if ((sides & 1) != 0) { this.SplitEntityOnNode(n.children[0]); } if ((sides & 2) != 0) { this.SplitEntityOnNode(n.children[1]); } }
private int RecursiveLightPoint(MemoryNodeBase node, ref Vector3 start, ref Vector3 end) { if (node.contents < 0) { return(-1); // didn't hit anything } var n = ( MemoryNode )node; // calculate mid point // FIXME: optimize for axial var plane = n.plane; var front = Vector3.Dot(start, plane.normal) - plane.dist; var back = Vector3.Dot(end, plane.normal) - plane.dist; var side = front < 0 ? 1 : 0; if ((back < 0 ? 1 : 0) == side) { return(this.RecursiveLightPoint(n.children[side], ref start, ref end)); } var frac = front / (front - back); var mid = start + (end - start) * frac; // go down front side var r = this.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 this._LightSpot = mid; this._LightPlane = plane; var surf = this.Host.Client.cl.worldmodel.Surfaces; int offset = n.firstsurface; for (var i = 0; i < n.numsurfaces; i++, offset++) { if ((surf[offset].flags & ( int )Q1SurfaceFlags.Tiled) != 0) { continue; // no lightmaps } var tex = surf[offset].texinfo; var s = ( int )(Vector3.Dot(mid, new(tex.vecs[0].X, tex.vecs[0].Y, tex.vecs[0].Z) ) + tex.vecs[0].W); var t = ( int )(Vector3.Dot(mid, new(tex.vecs[1].X, tex.vecs[1].Y, tex.vecs[1].Z) ) + tex.vecs[1].W); if (s < surf[offset].texturemins[0] || t < surf[offset].texturemins[1]) { continue; } var ds = s - surf[offset].texturemins[0]; var 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; var lightmap = surf[offset].sample_base; var lmOffset = surf[offset].sampleofs; var extents = surf[offset].extents; r = 0; if (lightmap != null) { lmOffset += dt * ((extents[0] >> 4) + 1) + ds; for (var maps = 0; maps < BspDef.MAXLIGHTMAPS && surf[offset].styles[maps] != 255; maps++) { var scale = this._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(this.RecursiveLightPoint(n.children[side == 0 ? 1 : 0], ref mid, ref end)); }