/// <summary>
        /// BeginBuildingLightmaps
        /// ----------------------
        /// Initializes the lightmap building process
        /// </summary>
        public void BeginBuildingLightmaps(ref CModel.SModel _SModel)
        {
            CLocal.SLightStyle[] _LightStyle;

            // no dlightcache
            CMain.r_framecount = 1;

            // setup the base lightstyles so the lightmaps won't have to be regenerated
            // the first time they're seen
            _LightStyle = new CLocal.SLightStyle[CLocal.MAX_LIGHTSTYLES];
            for (int i = 0; i < CLocal.MAX_LIGHTSTYLES; i++)
            {
                _LightStyle[i].rgb = new float[3];
                _LightStyle[i].rgb[0] = 1.0f;
                _LightStyle[i].rgb[1] = 1.0f;
                _LightStyle[i].rgb[2] = 1.0f;
                _LightStyle[i].white = 3.0f;
            }

            CClient.cl.RefDef.lightstyles = _LightStyle;

            // jkrige ??
            //if (!gl_state.lightmap_textures)
            //{
            //    gl_state.lightmap_textures = TEXNUM_LIGHTMAPS;
            //}
            // jkrige ??

            CProgram.gQ2Game.gCMain.gCImage.StartLightmaps(ref _SModel);
            LM_InitBlock();
        }
        private void Mod_LoadTexinfo(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<CModel.SMTexInfo> MTexInfo = new List<CModel.SMTexInfo>();

            br.BaseStream.Seek(header.lumps[LUMP_TEXINFO].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_TEXINFO].fileofs + header.lumps[LUMP_TEXINFO].filelen))
            {
                CModel.SMTexInfo _MTexInfo;
                string texture;
                int next;

                _MTexInfo.vecs = new Microsoft.Xna.Framework.Vector4[2];
                for (int i = 0; i < 2; i++)
                {
                    _MTexInfo.vecs[i].X = br.ReadSingle();
                    _MTexInfo.vecs[i].Y = br.ReadSingle();
                    _MTexInfo.vecs[i].Z = br.ReadSingle();
                    _MTexInfo.vecs[i].W = br.ReadSingle();
                }

                _MTexInfo.flags = (ESurface)br.ReadInt32();
                br.ReadInt32(); // value
                texture = CShared.Com_ToString(br.ReadChars(32));
                texture = "textures/" + texture;

                next = br.ReadInt32();
                if (next > 0)
                    _MTexInfo.next = next;
                else
                    _MTexInfo.next = 0;

                _MTexInfo.image = CProgram.gQ2Game.gCMain.gCImage.FindImage(texture, out _MTexInfo.Width, out _MTexInfo.Height, CImage.EImageType.IT_WALL);

                // TODO
                //out->image = GL_FindImage (name, it_wall);
                //if (!out->image)
                //{
                //    ri.Con_Printf (PRINT_ALL, "Couldn't load %s\n", name);
                //    out->image = r_notexture;
                //}

                _MTexInfo.numframes = 0;

                MTexInfo.Add(_MTexInfo);
            }

            // count animation frames
            for (int i = 0; i < MTexInfo.Count; i++)
            {
                CModel.SMTexInfo _MTexInfo = MTexInfo[i];
                _MTexInfo.numframes = 1;

                for (int step = _MTexInfo.next; step != 0 && step != i; step = MTexInfo[step].next)
                {
                    _MTexInfo.numframes++;
                }

                MTexInfo[i] = _MTexInfo;
            }

            _SModel.numtexinfo = MTexInfo.Count;
            _SModel.texinfo = MTexInfo.ToArray();
        }
        /// <summary>
        /// SubdivideSurface
        /// ----------------
        /// Breaks a polygon up along axial 64 unit boundaries
        /// so that turbulent and sky warps can be done reasonably.
        /// </summary>
        public void SubdivideSurface(ref CModel.SMSurface surf)
        {
            float[] verts;
            int numverts;
            List<CModel.SGLPoly> polys;

            if ((surf.flags & CModel.EMSurface.SURF_DRAWTURB) != CModel.EMSurface.SURF_DRAWTURB)
                return;

            verts = new float[64 * 3];
            numverts = surf.polys[0].numverts;

            for (int i = 0; i < surf.polys[0].numverts; i++)
            {
                verts[(i * 3) + 0] = surf.polys[0].verts[i].vertex.Position.X;
                verts[(i * 3) + 1] = surf.polys[0].verts[i].vertex.Position.Y;
                verts[(i * 3) + 2] = surf.polys[0].verts[i].vertex.Position.Z;
            }

            surf.polys = null;
            polys = new List<CModel.SGLPoly>();

            SubdividePolygon(surf, ref polys, numverts, verts);

            surf.polys = polys.ToArray();
            polys.Clear();
        }
 /// <summary>
 /// EndBuildingLightmaps
 /// --------------------
 /// Finalizes the processed lightmaps
 /// </summary>
 public void EndBuildingLightmaps(ref CModel.SModel _SModel)
 {
     LM_UploadBlock(false);
     CProgram.gQ2Game.gCMain.gCImage.FinalizeLightmaps(ref _SModel);
 }
        /// <summary>
        /// BuildSurfaceIndex
        /// ------------------
        /// Convert the surface's vertex format from triangle fan to triangle list
        /// This is used to speed up surface warping by using an index buffer
        /// </summary>
        public int[] BuildSurfaceIndex(CModel.SMSurface surf)
        {
            List<int> ib = new List<int>();
            ib.Clear();

            for (int i = 0; i < surf.polys.Length; i++)
            {
                for (int j = 2; j < surf.polys[i].verts.Length; j++)
                {
                    ib.Add(surf.polys[i].verts[0].offset);
                    ib.Add(surf.polys[i].verts[j - 1].offset);
                    ib.Add(surf.polys[i].verts[j].offset);
                }
            }

            return ib.ToArray();
        }
 public void BuildPolygonFromSurface(ref CModel.SMSurface surf)
 {
     // most parts of the BuildWorld() function in CMain could be done here
     // if so, the polygons needs to be subdivided first most likely
     // see the function calling this function
 }
        public void SetCacheState(ref CModel.SMSurface surf)
        {
            surf.cached_light = new float[CQ2BSP.MAXLIGHTMAPS];

            for(int maps = 0; maps < CQ2BSP.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++)
            {
                surf.cached_light[maps] = CClient.cl.RefDef.lightstyles[surf.styles[maps]].white;
            }
        }
        private void Mod_LoadSubmodels(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<SDModel> DModel = new List<SDModel>();
            List<CModel.SMModel> MModel = new List<CModel.SMModel>();

            br.BaseStream.Seek(header.lumps[LUMP_MODELS].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_MODELS].fileofs + header.lumps[LUMP_MODELS].filelen))
            {
                SDModel _DModel;

                _DModel.mins = new float[3];
                _DModel.mins[0] = br.ReadSingle();
                _DModel.mins[1] = br.ReadSingle();
                _DModel.mins[2] = br.ReadSingle();

                _DModel.maxs = new float[3];
                _DModel.maxs[0] = br.ReadSingle();
                _DModel.maxs[1] = br.ReadSingle();
                _DModel.maxs[2] = br.ReadSingle();

                _DModel.origin = new float[3];
                _DModel.origin[0] = br.ReadSingle();
                _DModel.origin[1] = br.ReadSingle();
                _DModel.origin[2] = br.ReadSingle();

                _DModel.headnode = br.ReadInt32();

                _DModel.firstface = br.ReadInt32();
                _DModel.numfaces = br.ReadInt32();

                DModel.Add(_DModel);
            }

            for (int i = 0; i < DModel.Count; i++)
            {
                CModel.SMModel _MModel;

                _MModel.bounds.Min.X = DModel[i].mins[0] - 1;
                _MModel.bounds.Min.Y = DModel[i].mins[1] - 1;
                _MModel.bounds.Min.Z = DModel[i].mins[2] - 1;
                _MModel.bounds.Max.X = DModel[i].maxs[0] + 1;
                _MModel.bounds.Max.Y = DModel[i].maxs[1] + 1;
                _MModel.bounds.Max.Z = DModel[i].maxs[2] + 1;

                _MModel.origin.X = DModel[i].origin[0];
                _MModel.origin.Y = DModel[i].origin[1];
                _MModel.origin.Z = DModel[i].origin[2];

                _MModel.radius = RadiusFromBounds(_MModel.bounds);

                _MModel.headnode = DModel[i].headnode;
                _MModel.firstface = DModel[i].firstface;
                _MModel.numfaces = DModel[i].numfaces;

                _MModel.visleafs = 0;

                MModel.Add(_MModel);
            }

            _SModel.numsubmodels = MModel.Count;
            _SModel.submodels = MModel.ToArray();
        }
        public void R_DrawBrushModel(CModel.SEntities _CurrentEntity)
        {
            Matrix wMatrix;
            //EffectParameter EP;
            Vector3 mins;
            Vector3 maxs;
            bool rotated;

            // update HLSL variables
            CProgram.gQ2Game.gCMain.UpdateHLSL(-1);

            // sort textures and surfaces
            if (CProgram.gQ2Game.gCMain.MarkSurfaceSetupStatic(ref _CurrentEntity.Model) == false)
                return;

            // set vertex buffer
            CProgram.gQ2Game.gGraphicsDevice.SetVertexBuffer(_CurrentEntity.Model.vbWorldSolid);

            if (_CurrentEntity.Angles.X != 0.0f || _CurrentEntity.Angles.Y != 0.0f || _CurrentEntity.Angles.Z != 0.0f)
            {
                rotated = true;

                mins.X = _CurrentEntity.Origin.X - _CurrentEntity.Model.radius;
                mins.Y = _CurrentEntity.Origin.Y - _CurrentEntity.Model.radius;
                mins.Z = _CurrentEntity.Origin.Z - _CurrentEntity.Model.radius;

                maxs.X = _CurrentEntity.Origin.X + _CurrentEntity.Model.radius;
                maxs.Y = _CurrentEntity.Origin.Y + _CurrentEntity.Model.radius;
                maxs.Z = _CurrentEntity.Origin.Z + _CurrentEntity.Model.radius;
            }
            else
            {
                rotated = false;

                mins.X = _CurrentEntity.Origin.X + _CurrentEntity.Model.mins.X;
                mins.Y = _CurrentEntity.Origin.Y + _CurrentEntity.Model.mins.Y;
                mins.Z = _CurrentEntity.Origin.Z + _CurrentEntity.Model.mins.Z;

                maxs.X = _CurrentEntity.Origin.X + _CurrentEntity.Model.maxs.X;
                maxs.Y = _CurrentEntity.Origin.Y + _CurrentEntity.Model.maxs.Y;
                maxs.Z = _CurrentEntity.Origin.Z + _CurrentEntity.Model.maxs.Z;
            }

            //if (R_CullBox(mins, maxs))
            //    return;

            CMain.ModelOrigin.X = CClient.cl.RefDef.ViewOrigin.X - _CurrentEntity.Origin.X;
            CMain.ModelOrigin.Y = CClient.cl.RefDef.ViewOrigin.Y - _CurrentEntity.Origin.Y;
            CMain.ModelOrigin.Z = CClient.cl.RefDef.ViewOrigin.Z - _CurrentEntity.Origin.Z;

            if (rotated == true)
            {
                Vector3 temp;
                Vector3 forward, right, up;

                temp.X = CMain.ModelOrigin.X;
                temp.Y = CMain.ModelOrigin.Y;
                temp.Z = CMain.ModelOrigin.Z;

                forward = Vector3.Zero;
                right = Vector3.Zero;
                up = Vector3.Zero;

                CShared.AngleVectors(_CurrentEntity.Angles, ref forward, ref right, ref up);

                CMain.ModelOrigin.X = Vector3.Dot(temp, forward);
                CMain.ModelOrigin.Y = -Vector3.Dot(temp, right);
                CMain.ModelOrigin.Z = Vector3.Dot(temp, up);
            }

            if (rotated == false)
            {
                wMatrix = Matrix.CreateFromYawPitchRoll(_CurrentEntity.Angles.Y, _CurrentEntity.Angles.X, _CurrentEntity.Angles.Z);
                wMatrix *= Matrix.CreateTranslation(_CurrentEntity.Origin);
            }
            else
            {
                wMatrix = Matrix.CreateTranslation(_CurrentEntity.Origin);
            }

            // update HLSL variables
            CProgram.gQ2Game.gEffect.Parameters["xWorld"].SetValue(wMatrix);

            // set a rendering technique
            if ((_CurrentEntity.Model.Flags & CQ2MD2.EModelFlags.RF_TRANSLUCENT) == CQ2MD2.EModelFlags.RF_TRANSLUCENT)
                CProgram.gQ2Game.gEffect.CurrentTechnique = CProgram.gQ2Game.gEffect.Techniques["TexturedTranslucent"];
            else
            {
                if (CProgram.gQ2Game.gCMain.r_hardwarelight == false)
                    CProgram.gQ2Game.gEffect.CurrentTechnique = CProgram.gQ2Game.gEffect.Techniques["TexturedLightmap"];
                else
                    CProgram.gQ2Game.gEffect.CurrentTechnique = CProgram.gQ2Game.gEffect.Techniques["TexturedLight"];
            }

            // jkrige TODO - probably need to update this since most brushmodels can translate/rotate
            // save effect parameter collection shortcut
            //EP = CProgram.gQ2Game.gEffect.Parameters["lights"];

            for (int i = 0; i < _CurrentEntity.Model.nummodelsurfaces; i++)
            {
                int surf = _CurrentEntity.Model.firstmodelsurface + i;

                // update surface HLSL variables
                CProgram.gQ2Game.gCMain.UpdateHLSL(surf);

                // set lights for current surface
                //for (int k = 0; k < CProgram.gQ2Game.gCMain.r_maxLights; k++)
                //{
                //    _CurrentEntity.Model.lights[CQ2BSP.SWorldData.surfaces[surf].lightIndex[k]].SetLight(EP.Elements[k]);
                //}
            }

            for (int i = 0; i < _CurrentEntity.Model.lSChainLightmap.Count; i++)
            {
                // bind new lightmap
                int lightmaptexturenum = _CurrentEntity.Model.lSChainLightmap[i].lightmaptexturenum;
                CProgram.gQ2Game.gEffect.Parameters["xTextureLightmap"].SetValue(CQ2BSP.SWorldData.WorldLightmaps[lightmaptexturenum]);

                for (int j = 0; j < _CurrentEntity.Model.lSChainLightmap[i].TexInfo.Count; j++)
                {
                    // bind new texture
                    int texanim = CProgram.gQ2Game.gCMain.TextureAnimation(_CurrentEntity.Model.lSChainLightmap[i].TexInfo[j].texinfo);
                    int texinfo = _CurrentEntity.Model.lSChainLightmap[i].TexInfo[j].texinfo;
                    CProgram.gQ2Game.gEffect.Parameters["xTextureDiffuse"].SetValue(CQ2BSP.SWorldData.WorldTextures[CQ2BSP.SWorldData.texinfo[texanim].image].Tex2D);

                    // set the indices
                    CProgram.gQ2Game.gGraphicsDevice.Indices = _CurrentEntity.Model.ibWorldSolid[lightmaptexturenum, texinfo].ibBuffer;

                    foreach (EffectPass pass in CProgram.gQ2Game.gEffect.CurrentTechnique.Passes)
                    {
                        pass.Apply();
                        CProgram.gQ2Game.gGraphicsDevice.DrawIndexedPrimitives(
                            PrimitiveType.TriangleList,
                            0,
                            0,
                            _CurrentEntity.Model.vbWorldSolid.VertexCount,
                            0,
                            _CurrentEntity.Model.ibWorldSolid[lightmaptexturenum, texinfo].PrimitiveCount);
                    }
                    CMain.c_brush_polys += _CurrentEntity.Model.ibWorldSolid[lightmaptexturenum, texinfo].PrimitiveCount;
                }
            }
        }
        public void Mod_LoadBrushModel(ref CModel.SModel _SModel, MemoryStream ms)
        {
            SDHeader header;
            List<SLump> lumps;
            BinaryReader br;

            ms.Seek(0, System.IO.SeekOrigin.Begin);
            br = new BinaryReader(ms);

            if (worldMapLoaded == true)
            {
                CMain.Error(CMain.EErrorParm.ERR_FATAL, "ERROR: attempted to redundantly load world map");
                return;
            }

            worldMapLoaded = true;

            _SModel.ModType = CModel.EModType.MOD_BRUSH;

            header.ident = br.ReadInt32();
            if (header.ident != IDBSPHEADER)
            {
                CMain.Error(CMain.EErrorParm.ERR_WARNING, "RE_LoadWorldMap: " + _SModel.name + " has wrong identity.");
                return;
            }

            header.version = br.ReadInt32();
            if (header.version != BSP_VERSION)
            {
                CMain.Error(CMain.EErrorParm.ERR_FATAL, "RE_LoadWorldMap: " + _SModel.name + " has wrong version number (" + header.version.ToString() + " should be " + BSP_VERSION.ToString() + ")");
                return;
            }

            lumps = new List<SLump>();
            for (int i = 0; i < HEADER_LUMPS; i++)
            {
                SLump _lump;
                _lump.fileofs = br.ReadInt32();
                _lump.filelen = br.ReadInt32();

                lumps.Add(_lump);
            }
            header.lumps = lumps.ToArray();

            CProgram.gQ2Game.gCMain.gCImage.StartWAL(ref _SModel);

            // load into heap
            Mod_LoadVertexes(header, ref _SModel, ref br);
            Mod_LoadEdges(header, ref _SModel, ref br);
            Mod_LoadSurfedges(header, ref _SModel, ref br);
            Mod_LoadLighting(header, ref _SModel, ref br);
            Mod_LoadPlanes(header, ref _SModel, ref br);
            Mod_LoadTexinfo(header, ref _SModel, ref br);
            Mod_LoadFaces(header, ref _SModel, ref br);
            Mod_LoadMarksurfaces(header, ref _SModel, ref br);
            Mod_LoadVisibility(header, ref _SModel, ref br);
            Mod_LoadNodesAndLeafs(header, ref _SModel, ref br);
            Mod_LoadSubmodels(header, ref _SModel, ref br);
            Mod_LoadAreas(header, ref _SModel, ref br);
            Mod_LoadAreaPortals(header, ref _SModel, ref br);
            Mod_LoadEntityString(header, ref _SModel, ref br);
            _SModel.numframes = 2;

            // set up the submodels
            for (int i = 0; i < _SModel.numsubmodels; i++)
            {
                CModel.SMModel bm;
                CModel.SModel starmod;

                bm = _SModel.submodels[i];
                starmod = CProgram.gQ2Game.gCMain.gCModel.mod_inline[i];

                starmod.ModType = CModel.EModType.MOD_BRUSH;
                starmod.firstmodelsurface = bm.firstface;
                starmod.nummodelsurfaces = bm.numfaces;
                starmod.firstnode = bm.headnode;

                if (starmod.firstnode >= _SModel.numnodes)
                    System.Diagnostics.Debug.WriteLine("Inline model " + i.ToString() + " has bad firstnode");

                starmod.maxs = bm.bounds.Max;
                starmod.mins = bm.bounds.Min;
                starmod.radius = bm.radius;
                starmod.numleafs = bm.visleafs;

                CProgram.gQ2Game.gCMain.gCModel.mod_inline[i] = starmod;
            }

            CProgram.gQ2Game.gCMain.gCImage.FinalizeWAL(ref _SModel);

            portalopen = new bool[MAX_MAP_AREAPORTALS];
            FloodAreaConnections(ref _SModel);
        }
        // ================================================================
        //
        // AREAPORTALS
        //
        // ================================================================
        public void FloodArea_r(CModel.SModel _SModel, ref CModel.SMArea area, int floodnum)
        {
            if (area.floodvalid == floodvalid)
            {
                if (area.floodnum == floodnum)
                    return;

                System.Diagnostics.Debug.WriteLine("FloodArea_r: reflooded");
                return;
            }

            area.floodnum = floodnum;
            area.floodvalid = floodvalid;

            for (int i = 0; i < area.numareaportals; i++)
            {
                if (portalopen[_SModel.areaportals[area.firstareaportal + i].portalnum] == true)
                    FloodArea_r(_SModel, ref _SModel.areas[_SModel.areaportals[area.firstareaportal + i].otherarea], floodnum);
            }
        }
        public void FloodAreaConnections(ref CModel.SModel _SModel)
        {
            int floodnum;

            // all current floods are now invalid
            floodvalid++;
            floodnum = 0;

            // area 0 is not used
            for (int i = 1; i < _SModel.numareas; i++)
            {
                if (_SModel.areas[i].floodvalid == floodvalid)
                    continue; // already flooded into

                floodnum++;
                FloodArea_r(_SModel, ref _SModel.areas[i], floodnum);
            }
        }
        private void Mod_SetParent(ref CModel.SModel _SModel, int idx_node, int idx_parent)
        {
            _SModel.nodes[idx_node].parent = idx_parent;

            if (_SModel.nodes[idx_node].contents != -1)
                return;

            Mod_SetParent(ref _SModel, _SModel.nodes[idx_node].children[0], idx_node);
            Mod_SetParent(ref _SModel, _SModel.nodes[idx_node].children[1], idx_node);
        }
        private void Mod_LoadVisibility(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            int pos_visdata = 0;
            br.BaseStream.Seek(header.lumps[LUMP_VISIBILITY].fileofs, System.IO.SeekOrigin.Begin);

            if (header.lumps[LUMP_VISIBILITY].filelen == 0)
            {
                _SModel.vis.numclusters = 0;
                _SModel.vis.bitofs = null;
                _SModel.visdata = null;

                return;
            }

            _SModel.vis.numclusters = br.ReadInt32();
            _SModel.vis.bitofs = new int[_SModel.vis.numclusters, 2];

            for (int i = 0; i < _SModel.vis.numclusters; i++)
            {
                _SModel.vis.bitofs[i, DVIS_PVS] = br.ReadInt32();
                _SModel.vis.bitofs[i, DVIS_PHS] = br.ReadInt32();

                // decrement the offsets, because the data is stored in a seperate byte array
                _SModel.vis.bitofs[i, DVIS_PVS] -= 4 + ((4 * _SModel.vis.numclusters * 2));
                _SModel.vis.bitofs[i, DVIS_PHS] -= 4 + ((4 * _SModel.vis.numclusters * 2));
            }

            _SModel.visdata = new byte[(header.lumps[LUMP_VISIBILITY].fileofs + header.lumps[LUMP_VISIBILITY].filelen) - br.BaseStream.Position];
            while (br.BaseStream.Position < (header.lumps[LUMP_VISIBILITY].fileofs + header.lumps[LUMP_VISIBILITY].filelen))
            {
                _SModel.visdata[pos_visdata++] = br.ReadByte();
            }
        }
        private void Mod_LoadVertexes(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<CModel.SMVertex> MVertex = new List<CModel.SMVertex>();

            br.BaseStream.Seek(header.lumps[LUMP_VERTEXES].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_VERTEXES].fileofs + header.lumps[LUMP_VERTEXES].filelen))
            {
                CModel.SMVertex _MVertex;

                _MVertex.Position.X = br.ReadSingle();
                _MVertex.Position.Y = br.ReadSingle();
                _MVertex.Position.Z = br.ReadSingle();

                MVertex.Add(_MVertex);
            }

            _SModel.numvertexes = MVertex.Count;
            _SModel.vertexes = MVertex.ToArray();
        }
        private void Mod_LoadNodesAndLeafs(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<SDNode> DNode = new List<SDNode>();
            List<SDLeaf> DLeaf = new List<SDLeaf>();
            List<CModel.SMNode> MNode = new List<CModel.SMNode>();

            // load nodes
            br.BaseStream.Seek(header.lumps[LUMP_NODES].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_NODES].fileofs + header.lumps[LUMP_NODES].filelen))
            {
                SDNode _DNode;

                _DNode.planenum = br.ReadInt32();

                _DNode.children = new int[2];
                _DNode.children[0] = br.ReadInt32();
                _DNode.children[1] = br.ReadInt32();

                _DNode.mins = new short[3];
                _DNode.mins[0] = br.ReadInt16();
                _DNode.mins[1] = br.ReadInt16();
                _DNode.mins[2] = br.ReadInt16();

                _DNode.maxs = new short[3];
                _DNode.maxs[0] = br.ReadInt16();
                _DNode.maxs[1] = br.ReadInt16();
                _DNode.maxs[2] = br.ReadInt16();

                _DNode.firstface = br.ReadUInt16();
                _DNode.numfaces = br.ReadUInt16();

                DNode.Add(_DNode);
            }

            for (int i = 0; i < DNode.Count; i++)
            {
                CModel.SMNode _MNode;
                int p;

                _MNode.bounds.Min.X = DNode[i].mins[0];
                _MNode.bounds.Min.Y = DNode[i].mins[1];
                _MNode.bounds.Min.Z = DNode[i].mins[2];
                _MNode.bounds.Max.X = DNode[i].maxs[0];
                _MNode.bounds.Max.Y = DNode[i].maxs[1];
                _MNode.bounds.Max.Z = DNode[i].maxs[2];

                _MNode.plane = DNode[i].planenum;

                _MNode.firstsurface = DNode[i].firstface;
                _MNode.numsurfaces = DNode[i].numfaces;
                _MNode.contents = -1; // differentiate from leafs

                _MNode.children = new int[2];
                for (int j = 0; j < 2; j++)
                {
                    p = DNode[i].children[j];

                    if (p >= 0)
                        _MNode.children[j] = p;
                    else
                        _MNode.children[j] = DNode.Count + (-1 - p);
                }

                _MNode.parent = 0;
                _MNode.visframe = 0;

                // leaf specific
                _MNode.cluster = 0;
                _MNode.area = 0;
                _MNode.firstmarksurface = 0;
                _MNode.nummarksurfaces = 0;

                MNode.Add(_MNode);
            }

            // load leafs
            br.BaseStream.Seek(header.lumps[LUMP_LEAFS].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_LEAFS].fileofs + header.lumps[LUMP_LEAFS].filelen))
            {
                SDLeaf _DLeaf;

                _DLeaf.contents = br.ReadInt32();

                _DLeaf.cluster = br.ReadInt16();
                _DLeaf.area = br.ReadInt16();

                _DLeaf.mins = new short[3];
                _DLeaf.mins[0] = br.ReadInt16();
                _DLeaf.mins[1] = br.ReadInt16();
                _DLeaf.mins[2] = br.ReadInt16();

                _DLeaf.maxs = new short[3];
                _DLeaf.maxs[0] = br.ReadInt16();
                _DLeaf.maxs[1] = br.ReadInt16();
                _DLeaf.maxs[2] = br.ReadInt16();

                _DLeaf.firstleafface = br.ReadUInt16();
                _DLeaf.numleaffaces = br.ReadUInt16();

                _DLeaf.firstleafbrush = br.ReadUInt16();
                _DLeaf.numleafbrushes = br.ReadUInt16();

                DLeaf.Add(_DLeaf);
            }

            for (int i = 0; i < DLeaf.Count; i++)
            {
                CModel.SMNode _MNode;

                _MNode.bounds.Min.X = DLeaf[i].mins[0];
                _MNode.bounds.Min.Y = DLeaf[i].mins[1];
                _MNode.bounds.Min.Z = DLeaf[i].mins[2];
                _MNode.bounds.Max.X = DLeaf[i].maxs[0];
                _MNode.bounds.Max.Y = DLeaf[i].maxs[1];
                _MNode.bounds.Max.Z = DLeaf[i].maxs[2];

                _MNode.contents = DLeaf[i].contents;

                _MNode.cluster = DLeaf[i].cluster;
                _MNode.area = DLeaf[i].area;

                _MNode.firstmarksurface = DLeaf[i].firstleafface;
                _MNode.nummarksurfaces = DLeaf[i].numleaffaces;

                _MNode.parent = 0;
                _MNode.visframe = 0;

                // node specific
                _MNode.plane = 0;
                _MNode.firstsurface = 0;
                _MNode.numsurfaces = 0;
                _MNode.children = null;

                MNode.Add(_MNode);
            }

            _SModel.numnodes = DNode.Count + DLeaf.Count;
            _SModel.numDecisionNodes = DNode.Count;
            _SModel.numleafs = DLeaf.Count;
            _SModel.nodes = MNode.ToArray();

            // chain decendants
            Mod_SetParent(ref _SModel, 0, -1);
        }
        private void Mod_LoadPlanes(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            int bits;
            float[] normal = new float[3];
            List<CShared.SCPlane> CPlane = new List<CShared.SCPlane>();

            br.BaseStream.Seek(header.lumps[LUMP_PLANES].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_PLANES].fileofs + header.lumps[LUMP_PLANES].filelen))
            {
                CShared.SCPlane _CPlane;

                normal[0] = 0.0f;
                normal[1] = 0.0f;
                normal[2] = 0.0f;

                bits = 0;

                for (int j = 0; j < 3; j++)
                {
                    normal[j] = br.ReadSingle();

                    if (normal[j] < 0)
                        bits |= 1 << j;
                }

                _CPlane.normal.X = normal[0];
                _CPlane.normal.Y = normal[1];
                _CPlane.normal.Z = normal[2];

                _CPlane.dist = br.ReadSingle();
                _CPlane.type = (byte)br.ReadInt32();
                _CPlane.signbits = (byte)bits;

                _CPlane.pad = new byte[2];

                CPlane.Add(_CPlane);
            }

            _SModel.planes = CPlane.ToArray();
            _SModel.numplanes = _SModel.planes.Length;
        }
        private void Mod_LoadAreaPortals(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<SDAreaPortal> DAreaPortal = new List<SDAreaPortal>();
            List<CModel.SMAreaPortal> MAreaPortal = new List<CModel.SMAreaPortal>();

            br.BaseStream.Seek(header.lumps[LUMP_AREAPORTALS].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_AREAPORTALS].fileofs + header.lumps[LUMP_AREAPORTALS].filelen))
            {
                SDAreaPortal _DAreaPortal;

                _DAreaPortal.portalnum = br.ReadInt32();
                _DAreaPortal.otherarea = br.ReadInt32();

                DAreaPortal.Add(_DAreaPortal);
            }

            for (int i = 0; i < DAreaPortal.Count; i++)
            {
                CModel.SMAreaPortal _MAreaPortal;

                _MAreaPortal.portalnum = DAreaPortal[i].portalnum;
                _MAreaPortal.otherarea = DAreaPortal[i].otherarea;

                MAreaPortal.Add(_MAreaPortal);
            }

            _SModel.numareaportals = MAreaPortal.Count;
            _SModel.areaportals = MAreaPortal.ToArray();
        }
        private void Mod_LoadSurfedges(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<int> DSurfEdge = new List<int>();

            br.BaseStream.Seek(header.lumps[LUMP_SURFEDGES].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_SURFEDGES].fileofs + header.lumps[LUMP_SURFEDGES].filelen))
            {
                DSurfEdge.Add(br.ReadInt32());
            }

            _SModel.numsurfedges = DSurfEdge.Count;
            _SModel.surfedges = DSurfEdge.ToArray();
        }
        private void Mod_LoadAreas(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<SDArea> DArea = new List<SDArea>();
            List<CModel.SMArea> MArea = new List<CModel.SMArea>();

            br.BaseStream.Seek(header.lumps[LUMP_AREAS].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_AREAS].fileofs + header.lumps[LUMP_AREAS].filelen))
            {
                SDArea _DArea;

                _DArea.numareaportals = br.ReadInt32();
                _DArea.firstareaportal = br.ReadInt32();

                DArea.Add(_DArea);
            }

            for (int i = 0; i < DArea.Count; i++)
            {
                CModel.SMArea _MArea;

                _MArea.numareaportals = DArea[i].numareaportals;
                _MArea.firstareaportal = DArea[i].firstareaportal;
                _MArea.floodvalid = 0;
                _MArea.floodnum = 0;

                MArea.Add(_MArea);
            }

            _SModel.numareas = MArea.Count;
            _SModel.areas = MArea.ToArray();
        }
        /// <summary>
        /// R_BuildLightMap
        /// ---------------
        /// Combine and scale multiple lightmaps into the floating format in blocklights
        /// </summary>
        public void BuildLightMap(CModel.SModel _SModel, ref CModel.SMSurface surf, int dest_pos, ref byte[] dest, int stride)
        {
            int smax, tmax;
            int r, g, b, a, max;
            int size;
            int lightmap;
            float[] scale;
            int nummaps;
            float[] bl;
            CLocal.SLightStyle style;

            int bl_pos;
            float modulate = 1.0f;

            if (
                (_SModel.texinfo[surf.texinfo].flags & CQ2BSP.ESurface.SURF_SKY) == CQ2BSP.ESurface.SURF_SKY
                | (_SModel.texinfo[surf.texinfo].flags & CQ2BSP.ESurface.SURF_TRANS33) == CQ2BSP.ESurface.SURF_TRANS33
                | (_SModel.texinfo[surf.texinfo].flags & CQ2BSP.ESurface.SURF_TRANS66) == CQ2BSP.ESurface.SURF_TRANS66
                | (_SModel.texinfo[surf.texinfo].flags & CQ2BSP.ESurface.SURF_WARP) == CQ2BSP.ESurface.SURF_WARP
                )
            {
                CMain.Error(CMain.EErrorParm.ERR_WARNING, "BuildLightMap called for non-lit surface");
            }

            smax = (surf.extents[0] >> 4) + 1;
            tmax = (surf.extents[1] >> 4) + 1;
            size = smax * tmax;

            if (size > ((sizeof(float) * s_blocklights.Length) >> 4))
                CMain.Error(CMain.EErrorParm.ERR_WARNING, "Bad s_blocklights size");

            // set to full bright if no light data
            if (surf.samples == -1)
            {
                for (int i = 0; i < size * 3; i++)
                {
                    s_blocklights[i] = 255;
                }

                for (int maps = 0; maps < CQ2BSP.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++)
                {
                    style = CClient.cl.RefDef.lightstyles[surf.styles[maps]];
                }

                goto store;
            }

            // count the # of maps
            for (nummaps = 0; nummaps < CQ2BSP.MAXLIGHTMAPS && surf.styles[nummaps] != 255; nummaps++) ;

            lightmap = surf.samples;

            // add all the lightmaps
            if (nummaps == 1)
            {
                scale = new float[3];

                for (int maps = 0; maps < CQ2BSP.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++)
                {
                    bl = s_blocklights;
                    bl_pos = 0;

                    for (int i = 0; i < 3; i++)
                    {
                        scale[i] = modulate * CClient.cl.RefDef.lightstyles[surf.styles[maps]].rgb[i];
                    }

                    if (scale[0] == 1.0f && scale[1] == 1.0f && scale[2] == 1.0f)
                    {
                        for (int i = 0; i < size; i++, bl_pos += 3)
                        {
                            bl[bl_pos + 0] = _SModel.lightdata[lightmap + (i * 3) + 0];
                            bl[bl_pos + 1] = _SModel.lightdata[lightmap + (i * 3) + 1];
                            bl[bl_pos + 2] = _SModel.lightdata[lightmap + (i * 3) + 2];
                        }
                    }
                    else
                    {
                        for (int i = 0; i < size; i++, bl_pos += 3)
                        {
                            bl[bl_pos + 0] = _SModel.lightdata[lightmap + (i * 3) + 0] * scale[0];
                            bl[bl_pos + 1] = _SModel.lightdata[lightmap + (i * 3) + 1] * scale[1];
                            bl[bl_pos + 2] = _SModel.lightdata[lightmap + (i * 3) + 2] * scale[2];
                        }
                    }

                    // skip to next lightmap
                    lightmap += size * 3;
                }
            }
            else
            {
                scale = new float[3];

                for (int i = 0; i < (sizeof(float) * size * 3); i++)
                {
                    s_blocklights[i] = 0;
                }

                for (int maps = 0; maps < CQ2BSP.MAXLIGHTMAPS && surf.styles[maps] != 255; maps++)
                {
                    bl = s_blocklights;
                    bl_pos = 0;

                    for (int i = 0; i < 3; i++)
                    {
                        scale[i] = modulate * CClient.cl.RefDef.lightstyles[surf.styles[maps]].rgb[i];
                    }

                    if (scale[0] == 1.0f && scale[1] == 1.0f && scale[2] == 1.0f)
                    {
                        for (int i = 0; i < size; i++, bl_pos += 3)
                        {
                            bl[bl_pos + 0] += _SModel.lightdata[lightmap + (i * 3) + 0];
                            bl[bl_pos + 1] += _SModel.lightdata[lightmap + (i * 3) + 1];
                            bl[bl_pos + 2] += _SModel.lightdata[lightmap + (i * 3) + 2];
                        }
                    }
                    else
                    {
                        for (int i = 0; i < size; i++, bl_pos += 3)
                        {
                            bl[bl_pos + 0] += _SModel.lightdata[lightmap + (i * 3) + 0] * scale[0];
                            bl[bl_pos + 1] += _SModel.lightdata[lightmap + (i * 3) + 1] * scale[1];
                            bl[bl_pos + 2] += _SModel.lightdata[lightmap + (i * 3) + 2] * scale[2];
                        }
                    }

                    // skip to next lightmap
                    lightmap += size * 3;
                }
            }

            // add all the dynamic lights
            //if (surf.dlightframe == CMain.r_framecount)
            //    R_AddDynamicLights(surf);

            // put into texture format
            store:
            stride -= (smax << 2);
            bl = s_blocklights;
            bl_pos = 0;

            for (int i = 0; i < tmax; i++, dest_pos += stride)
            {
                for (int j = 0; j < smax; j++)
                {
                    r = Convert.ToInt32(bl[bl_pos + 0]);
                    g = Convert.ToInt32(bl[bl_pos + 1]);
                    b = Convert.ToInt32(bl[bl_pos + 2]);

                    // catch negative lights
                    if (r < 0)
                        r = 0;
                    if (g < 0)
                        g = 0;
                    if (b < 0)
                        b = 0;

                    // determine the brightest of the three color components
                    if (r > g)
                        max = r;
                    else
                        max = g;
                    if (b > max)
                        max = b;

                    // alpha is ONLY used for the mono lightmap case.  For this reason
                    // we set it to the brightest of the color components so that
                    // things don't get too dim.
                    a = max;

                    // rescale all the color components if the intensity of the greatest
                    // channel exceeds 1.0
                    if (max > 255)
                    {
                        float t = 255.0f / max;

                        r = (int)(r * t);
                        g = (int)(g * t);
                        b = (int)(b * t);
                        a = (int)(a * t);
                    }

                    dest[dest_pos + 0] = (byte)r;
                    dest[dest_pos + 1] = (byte)g;
                    dest[dest_pos + 2] = (byte)b;
                    dest[dest_pos + 3] = (byte)a;

                    bl_pos += 3;
                    dest_pos += 4;
                }
            }
        }
        private void Mod_LoadEdges(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<CModel.SMEdge> MEdge = new List<CModel.SMEdge>();

            br.BaseStream.Seek(header.lumps[LUMP_EDGES].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_EDGES].fileofs + header.lumps[LUMP_EDGES].filelen))
            {
                CModel.SMEdge _MEdge;

                _MEdge.v = new ushort[2];
                _MEdge.v[0] = br.ReadUInt16();
                _MEdge.v[1] = br.ReadUInt16();

                _MEdge.cachededgeoffset = 0;

                MEdge.Add(_MEdge);
            }

            _SModel.numedges = MEdge.Count;
            _SModel.edges = MEdge.ToArray();
        }
        /// <summary>
        /// BuildSurfaceIndex
        /// ------------------
        /// Convert the surface's vertex format from triangle fan to triangle list
        /// This is used to speed up surface warping by using an index buffer
        /// </summary>
        public void BuildSurfaceIndex(ref CModel.SMSurface surf)
        {
            List<int> ib;

            //if ((surf.flags & CModel.EMSurface.SURF_DRAWTURB) != CModel.EMSurface.SURF_DRAWTURB)
            //    return;

            ib = new List<int>();
            ib.Clear();

            for (int i = 0; i < surf.polys.Length; i++)
            {
                for (int j = 2; j < surf.polys[i].verts.Length; j++)
                {
                    ib.Add(surf.polys[i].verts[0].offset);
                    ib.Add(surf.polys[i].verts[j - 1].offset);
                    ib.Add(surf.polys[i].verts[j].offset);
                }
            }

            surf.ibData = ib.ToArray();

            // setup the index buffer
            if (CProgram.gQ2Game.gGraphicsDevice.GraphicsProfile == GraphicsProfile.HiDef)
            {
                surf.ibSurface = new IndexBuffer(CProgram.gQ2Game.gGraphicsDevice, IndexElementSize.ThirtyTwoBits /*typeof(int)*/, surf.ibData.Length, BufferUsage.WriteOnly);
                surf.ibSurface.SetData(surf.ibData);
            }
            else
            {
                short[] ibData16 = new short[surf.ibData.Length];

                for (int i = 0; i < surf.ibData.Length; i++)
                    ibData16[i] = (short)surf.ibData[i];

                surf.ibSurface = new IndexBuffer(CProgram.gQ2Game.gGraphicsDevice, IndexElementSize.SixteenBits, ibData16.Length, BufferUsage.WriteOnly);
                surf.ibSurface.SetData(ibData16);
            }

            //surf.ibSurface.SetData(surf.ibData);
        }
        private void Mod_LoadEntityString(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            if (header.lumps[LUMP_ENTITIES].filelen > MAX_MAP_ENTSTRING)
            {
                // entity string exceeds maximum
                _SModel.map_entitystring = null;
                return;
            }

            br.BaseStream.Seek(header.lumps[LUMP_ENTITIES].fileofs, System.IO.SeekOrigin.Begin);
            if (br.BaseStream.Position < (header.lumps[LUMP_ENTITIES].fileofs + header.lumps[LUMP_ENTITIES].filelen))
            {
                _SModel.map_entitystring = CShared.Com_ToString(br.ReadChars(header.lumps[LUMP_ENTITIES].filelen));
                _SModel.map_entitystring = _SModel.map_entitystring.Replace("\r", "").Replace("\n", "\r\n");
            }
            else
            {
                _SModel.map_entitystring = null;
            }
        }
        /// <summary>
        /// CreateSurfaceLightmap
        /// ---------------------
        /// Creates a lightmap surface
        /// </summary>
        public void CreateSurfaceLightmap(CModel.SModel _SModel, ref CModel.SMSurface surf)
        {
            int smax;
            int tmax;
            int bytepos;

            if (
                (surf.flags & CModel.EMSurface.SURF_DRAWSKY) == CModel.EMSurface.SURF_DRAWSKY
                | (surf.flags & CModel.EMSurface.SURF_DRAWTURB) == CModel.EMSurface.SURF_DRAWTURB
                )
            {
                return;
            }

            smax = (surf.extents[0] >> 4) + 1;
            tmax = (surf.extents[1] >> 4) + 1;

            if (LM_AllocBlock(smax, tmax, ref surf.light_s, ref surf.light_t) == false)
            {
                LM_UploadBlock(false);
                LM_InitBlock();

                if (LM_AllocBlock(smax, tmax, ref surf.light_s, ref surf.light_t) == false)
                    CMain.Error(CMain.EErrorParm.ERR_FATAL, "Consecutive calls to LM_AllocBlock(" + smax + "," + tmax + ") failed\n");
            }

            surf.lightmaptexturenum = CProgram.gQ2Game.gCMain.gCImage.current_lightmap_texture;

            bytepos = (surf.light_t * BLOCK_WIDTH + surf.light_s) * LIGHTMAP_BYTES;

            CProgram.gQ2Game.gCMain.gCLight.SetCacheState(ref surf);
            CProgram.gQ2Game.gCMain.gCLight.BuildLightMap(_SModel, ref surf, bytepos, ref lms.lightmap_buffer, BLOCK_WIDTH * LIGHTMAP_BYTES);
        }
        private void Mod_LoadFaces(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<SDFace> DFace = new List<SDFace>();
            List<CModel.SMSurface> MSurface = new List<CModel.SMSurface>();

            br.BaseStream.Seek(header.lumps[LUMP_FACES].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_FACES].fileofs + header.lumps[LUMP_FACES].filelen))
            {
                SDFace _DFace;

                _DFace.planenum = br.ReadUInt16();
                _DFace.side = br.ReadInt16();

                _DFace.firstedge = br.ReadInt32();
                _DFace.numedges = br.ReadInt16();
                _DFace.texinfo = br.ReadInt16();

                _DFace.styles = br.ReadBytes(MAXLIGHTMAPS);
                _DFace.lightofs = br.ReadInt32();

                DFace.Add(_DFace);
            }

            CProgram.gQ2Game.gCMain.gCSurface.BeginBuildingLightmaps(ref _SModel);

            for (int i = 0; i < DFace.Count; i++)
            {
                CModel.SMSurface _MSurface;
                int side;
                short ti;

                _MSurface.firstedge = DFace[i].firstedge;
                _MSurface.numedges = DFace[i].numedges;
                _MSurface.flags = 0;
                _MSurface.polys = null;
                _MSurface.ibSurface = null;
                _MSurface.ibData = null;

                side = DFace[i].side;
                if (side != 0)
                    _MSurface.flags |= CModel.EMSurface.SURF_PLANEBACK;

                _MSurface.plane = DFace[i].planenum;

                _MSurface.plane2 = new Microsoft.Xna.Framework.Plane();

                ti = DFace[i].texinfo;
                if (ti < 0 || ti >= _SModel.numtexinfo)
                    CMain.Error(CMain.EErrorParm.ERR_WARNING, "MOD_LoadBmodel: bad texinfo number");
                _MSurface.texinfo = ti;

                _MSurface.bounds = CLocal.ClearBounds();

                _MSurface.boundsMid.X = 0.0f;
                _MSurface.boundsMid.Y = 0.0f;
                _MSurface.boundsMid.Z = 0.0f;

                _MSurface.lightIndex = null;
                _MSurface.lightLength = null;

                _MSurface.texturechain = 0;
                _MSurface.visframe = 0;

                // lighting info
                _MSurface.dlightframe = 0;
                _MSurface.dlightbits = 0;
                _MSurface.lightmaptexturenum = 0;
                _MSurface.styles = new byte[MAXLIGHTMAPS];
                for (int j = 0; j < MAXLIGHTMAPS; j++)
                {
                    _MSurface.styles[j] = DFace[i].styles[j];
                }

                _MSurface.cached_light = null;
                _MSurface.samples = DFace[i].lightofs;

                _MSurface.light_s = 0;
                _MSurface.light_t = 0;
                _MSurface.dlight_s = 0;
                _MSurface.dlight_t = 0;

                _MSurface.extents = null;
                _MSurface.texturemins = null;
                CModel.CalcSurfaceExtents(ref _SModel, ref _MSurface);

                // set the drawing flags
                if ((_SModel.texinfo[_MSurface.texinfo].flags & ESurface.SURF_WARP) == ESurface.SURF_WARP)
                {
                    _MSurface.flags |= CModel.EMSurface.SURF_DRAWTURB;

                    for (int j = 0; j < 2; j++)
                    {
                        _MSurface.extents[j] = 16384;
                        _MSurface.texturemins[j] = -8192;
                    }

                    // TODO
                    // cut up polygon for warps
                    //SubdivideSurface(i, ref _WorldModel.mfaces[i]);
                    //SubdivideSurface (out);
                }

                // create lightmaps and polygons
                if (
                    (_SModel.texinfo[_MSurface.texinfo].flags & ESurface.SURF_SKY) != ESurface.SURF_SKY
                    && (_SModel.texinfo[_MSurface.texinfo].flags & ESurface.SURF_TRANS33) != ESurface.SURF_TRANS33
                    && (_SModel.texinfo[_MSurface.texinfo].flags & ESurface.SURF_TRANS66) != ESurface.SURF_TRANS66
                    && (_SModel.texinfo[_MSurface.texinfo].flags & ESurface.SURF_WARP) != ESurface.SURF_WARP
                    )
                {
                    CProgram.gQ2Game.gCMain.gCSurface.CreateSurfaceLightmap(_SModel, ref _MSurface);
                }

                if ((_SModel.texinfo[_MSurface.texinfo].flags & ESurface.SURF_WARP) == ESurface.SURF_WARP)
                    CProgram.gQ2Game.gCMain.gCSurface.BuildPolygonFromSurface(ref _MSurface);

                MSurface.Add(_MSurface);
            }

            CProgram.gQ2Game.gCMain.gCSurface.EndBuildingLightmaps(ref _SModel);

            _SModel.numsurfaces = MSurface.Count;
            _SModel.surfaces = MSurface.ToArray();
        }
        /// <summary>
        /// SubdividePolygon
        /// ----------------
        /// Breaks a polygon up along axial 64 unit boundaries
        /// so that turbulent and sky warps can be done reasonably.
        /// </summary>
        public void SubdividePolygon(CModel.SMSurface surf, ref List<CModel.SGLPoly> polys, int numverts, float[] verts)
        {
            float[] mins, maxs, dist;
            float m;
            float frac;
            int i, j, k, v;
            float[] Front, Back;
            int f, b;
            CModel.SGLPoly poly;
            Vector3 total;
            float total_s;
            float total_t;

            mins = new float[3];
            maxs = new float[3];
            dist = new float[64];

            Front = new float[64 * 3];
            Back = new float[64 * 3];

            if (numverts > 60)
                CMain.Error(CMain.EErrorParm.ERR_WARNING, "(error) numverts = " + numverts);

            // Bind mins and maxs
            BoundPoly(numverts, verts, ref mins, ref maxs);

            for (i = 0; i < 3; i++)
            {
                m = (mins[i] + maxs[i]) * 0.5f;
                m = SUBDIVIDE_SIZE * (float)Math.Floor(m / (float)SUBDIVIDE_SIZE + 0.5f);

                if (maxs[i] - m < 8)
                    continue;
                if (m - mins[i] < 8)
                    continue;

                // cut it
                v = i;
                for (j = 0; j < numverts; j++, v += 3)
                {
                    dist[j] = verts[v] - m;
                }

                // wrap cases
                dist[j] = dist[0];
                v -= i;
                verts[v + 0] = verts[0 + 0];
                verts[v + 1] = verts[0 + 1];
                verts[v + 2] = verts[0 + 2];

                f = 0;
                b = 0;
                v = 0;
                for (j = 0; j < numverts; j++, v += 3)
                {
                    if (dist[j] >= 0)
                    {
                        Front[(f * 3) + 0] = verts[v + 0];
                        Front[(f * 3) + 1] = verts[v + 1];
                        Front[(f * 3) + 2] = verts[v + 2];
                        f++;
                    }
                    if (dist[j] <= 0)
                    {
                        Back[(b * 3) + 0] = verts[v + 0];
                        Back[(b * 3) + 1] = verts[v + 1];
                        Back[(b * 3) + 2] = verts[v + 2];
                        b++;
                    }

                    if (dist[j] == 0 || dist[j + 1] == 0)
                        continue;

                    if ((dist[j] > 0) != (dist[j + 1] > 0))
                    {
                        // clip point
                        frac = dist[j] / (dist[j] - dist[j + 1]);

                        for (k = 0; k < 3; k++)
                        {
                            Front[(f * 3) + k] = Back[(b * 3) + k] = verts[v + k] + frac * (verts[3 + v + k] - verts[v + k]);
                        }
                        f++;
                        b++;
                    }
                }

                SubdividePolygon(surf, ref polys, f, Front);
                SubdividePolygon(surf, ref polys, b, Back);

                return;
            }

            // add a point in the center to help keep warp valid
            poly.next = 0;
            poly.chain = 0;
            poly.numverts = numverts + 2;
            poly.verts = new CModel.SPolyVerts[poly.numverts];

            total = Vector3.Zero;
            total_s = 0.0f;
            total_t = 0.0f;

            v = 0;
            for (i = 0; i < numverts; i++, v += 3)
            {
                float s;
                float t;
                Vector3 vec0;
                Vector3 vec1;

                poly.verts[i + 1].vertex.Position.X = verts[v + 0];
                poly.verts[i + 1].vertex.Position.Y = verts[v + 1];
                poly.verts[i + 1].vertex.Position.Z = verts[v + 2];

                vec0.X = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[0].X;
                vec0.Y = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[0].Y;
                vec0.Z = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[0].Z;
                vec1.X = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[1].X;
                vec1.Y = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[1].Y;
                vec1.Z = CQ2BSP.SWorldData.texinfo[surf.texinfo].vecs[1].Z;

                s = Vector3.Dot(poly.verts[i + 1].vertex.Position, vec0);
                t = Vector3.Dot(poly.verts[i + 1].vertex.Position, vec1);

                total_s += s;
                total_t += t;
                total.X += verts[v + 0];
                total.Y += verts[v + 1];
                total.Z += verts[v + 2];

                poly.verts[i + 1].vertex.TextureCoordinate.X = s;
                poly.verts[i + 1].vertex.TextureCoordinate.Y = t;

                poly.verts[i + 1].vertex.LightmapCoordinate.X = 0.0f;
                poly.verts[i + 1].vertex.LightmapCoordinate.Y = 0.0f;

                poly.verts[i + 1].vertex.Normal.X = 0.0f;
                poly.verts[i + 1].vertex.Normal.Y = 0.0f;
                poly.verts[i + 1].vertex.Normal.Z = 0.0f;
            }

            CShared.VectorScale(total, (1.0f / numverts), ref poly.verts[0].vertex.Position);
            poly.verts[0].vertex.TextureCoordinate.X = total_s / numverts;
            poly.verts[0].vertex.TextureCoordinate.Y = total_t / numverts;

            // copy first vertex to last
            poly.verts[i + 1] = poly.verts[1];

            // insert centered point at first index
            polys.Insert(0, poly);
        }
        private void Mod_LoadLighting(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            if (header.lumps[LUMP_LIGHTING].filelen == 0)
            {
                _SModel.lightdata = null;
                return;
            }

            br.BaseStream.Seek(header.lumps[LUMP_LIGHTING].fileofs, System.IO.SeekOrigin.Begin);
            _SModel.lightdata = br.ReadBytes(header.lumps[LUMP_LIGHTING].filelen);
        }
        private void Mod_LoadMarksurfaces(SDHeader header, ref CModel.SModel _SModel, ref System.IO.BinaryReader br)
        {
            List<int> MarkSurface = new List<int>();

            br.BaseStream.Seek(header.lumps[LUMP_LEAFFACES].fileofs, System.IO.SeekOrigin.Begin);
            while (br.BaseStream.Position < (header.lumps[LUMP_LEAFFACES].fileofs + header.lumps[LUMP_LEAFFACES].filelen))
            {
                int msurf = br.ReadInt16();

                if (msurf < 0 || msurf >= _SModel.numsurfaces)
                    CMain.Error(CMain.EErrorParm.ERR_FATAL, "Mod_ParseMarksurfaces: bad surface number");

                MarkSurface.Add(msurf);
            }

            _SModel.nummarksurfaces = MarkSurface.Count;
            _SModel.marksurfaces = MarkSurface.ToArray();
        }
        /// <summary>
        /// MarkSurfaceSetup
        /// ----------------
        /// Sort surfaces according to texture
        /// </summary>
        public bool MarkSurfaceSetupStatic(ref CModel.SModel _SModel)
        {
            List<int> indices;

            if (_SModel.MarkSurfaceListStatic == null)
                return false;

            if (_SModel.MarkSurfaceListStatic.Count == 0)
                return false;

            // build lightmap/texture/surface chains
            _SModel.lSChainLightmap = new List<SChainLightmap>();
            for (int i = 0; i < _SModel.MarkSurfaceListStatic.Count; i++)
            {
                int idxLightmap = -1;
                int idxTexture = -1;
                int idxSurface = -1;

                // check for lightmap modification
                gCLight.LightmapAnimation(_SModel.MarkSurfaceListStatic[i].surf);

                // generate lightmap list
                for (int j = 0; j < _SModel.lSChainLightmap.Count; j++)
                {
                    if (_SModel.lSChainLightmap[j].lightmaptexturenum == _SModel.MarkSurfaceListStatic[i].lightmaptexturenum)
                    {
                        idxLightmap = j;
                        break;
                    }
                }

                if (idxLightmap == -1)
                {
                    SChainLightmap _SCLM;
                    _SCLM.lightmaptexturenum = _SModel.MarkSurfaceListStatic[i].lightmaptexturenum;
                    _SCLM.TexInfo = new List<SChainTexture>();

                    _SModel.lSChainLightmap.Add(_SCLM);
                    idxLightmap = _SModel.lSChainLightmap.Count - 1;
                }

                // generate texinfo list
                SChainLightmap _SChainLightmap = _SModel.lSChainLightmap[idxLightmap];
                for (int j = 0; j < _SChainLightmap.TexInfo.Count; j++)
                {
                    if (_SChainLightmap.TexInfo[j].texinfo == _SModel.MarkSurfaceListStatic[i].texinfo)
                    {
                        idxTexture = j;
                        break;
                    }
                }

                if (idxTexture == -1)
                {
                    SChainTexture _SCT;
                    _SCT.texinfo = _SModel.MarkSurfaceListStatic[i].texinfo;
                    _SCT.surf = new List<int>();

                    _SChainLightmap.TexInfo.Add(_SCT);
                    idxTexture = _SChainLightmap.TexInfo.Count - 1;
                }
                _SModel.lSChainLightmap[idxLightmap] = _SChainLightmap;

                // generate surface list
                SChainTexture _SChainTexture = _SModel.lSChainLightmap[idxLightmap].TexInfo[idxTexture];
                for (int j = 0; j < _SChainTexture.surf.Count; j++)
                {
                    if (_SChainTexture.surf[j] == _SModel.MarkSurfaceListStatic[i].surf)
                    {
                        idxSurface = j;
                        break;
                    }
                }

                if (idxSurface == -1)
                {
                    _SChainTexture.surf.Add(_SModel.MarkSurfaceListStatic[i].surf);
                    idxSurface = _SChainTexture.surf.Count - 1;
                }

                _SModel.lSChainLightmap[idxLightmap].TexInfo[idxTexture] = _SChainTexture;
            }

            // build cluster-based index buffer
            indices = new List<int>();
            for (int i = 0; i < _SModel.lSChainLightmap.Count; i++)
            {
                for (int j = 0; j < _SModel.lSChainLightmap[i].TexInfo.Count; j++)
                {
                    int[] idx1;
                    int[] idx2;
                    int lm = _SModel.lSChainLightmap[i].lightmaptexturenum;
                    int tx = _SModel.lSChainLightmap[i].TexInfo[j].texinfo;
                    bool isEqual = true;

                    indices.Clear();

                    for (int k = 0; k < _SModel.lSChainLightmap[i].TexInfo[j].surf.Count; k++)
                    {
                        int[] idx = gCSurface.BuildSurfaceIndex(CQ2BSP.SWorldData.surfaces[_SModel.lSChainLightmap[i].TexInfo[j].surf[k]]);

                        if (idx == null)
                            continue;

                        for (int l = 0; l < idx.Length; l++)
                        {
                            indices.Add(idx[l]);
                        }
                    }

                    idx1 = indices.ToArray();
                    idx2 = _SModel.ibWorldSolid[lm, tx].ibIndices;

                    if (idx2 != null)
                    {
                        if (idx1.Length == idx2.Length)
                        {
                            for (int k = 0; k < idx1.Length; k++)
                            {
                                if (idx1[k] != idx2[k])
                                {
                                    isEqual = false;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            isEqual = false;
                        }
                    }
                    else
                    {
                        isEqual = false;
                    }

                    // re-build the index buffer the indices doesn't match
                    if(isEqual == false)
                    {
                        _SModel.ibWorldSolid[lm, tx].ibIndices = new int[idx1.Length];
                        for (int k = 0; k < idx1.Length; k++)
                        {
                            _SModel.ibWorldSolid[lm, tx].ibIndices[k] = idx1[k];
                        }
                        _SModel.ibWorldSolid[lm, tx].PrimitiveCount = _SModel.ibWorldSolid[lm, tx].ibIndices.Length / 3;

                        if (_SModel.ibWorldSolid[lm, tx].ibBuffer.IndexCount < idx1.Length)
                        {
                            _SModel.ibWorldSolid[lm, tx].ibBuffer.Dispose();
                            _SModel.ibWorldSolid[lm, tx].ibBuffer = null;

                            if (CProgram.gQ2Game.gGraphicsDevice.GraphicsProfile == GraphicsProfile.HiDef)
                                _SModel.ibWorldSolid[lm, tx].ibBuffer = new IndexBuffer(CProgram.gQ2Game.gGraphicsDevice, IndexElementSize.ThirtyTwoBits, idx1.Length, BufferUsage.WriteOnly);
                            else
                                _SModel.ibWorldSolid[lm, tx].ibBuffer = new IndexBuffer(CProgram.gQ2Game.gGraphicsDevice, IndexElementSize.SixteenBits, idx1.Length, BufferUsage.WriteOnly);
                        }

                        if (CProgram.gQ2Game.gGraphicsDevice.GraphicsProfile == GraphicsProfile.HiDef)
                        {
                            _SModel.ibWorldSolid[lm, tx].ibBuffer.SetData(_SModel.ibWorldSolid[lm, tx].ibIndices);
                        }
                        else
                        {
                            short[] ibData16 = new short[_SModel.ibWorldSolid[lm, tx].ibIndices.Length];

                            for (int k = 0; k < _SModel.ibWorldSolid[lm, tx].ibIndices.Length; k++)
                                ibData16[k] = (short)_SModel.ibWorldSolid[lm, tx].ibIndices[k];

                            _SModel.ibWorldSolid[lm, tx].ibBuffer.SetData(ibData16);
                        }
                    }
                }
            }

            indices.Clear();
            indices = null;

            return true;
        }