Exemplo n.º 1
0
        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;
            }
        }
Exemplo n.º 2
0
        public void Mod_LoadAliasModel(ref CModel.SModel _SModel, MemoryStream ms)
        {
            BinaryReader                  br;
            List <string>                 skinnames;
            List <CQ2MD2.SSTVert>         st;
            List <CQ2MD2.SDTriangle>      triangles;
            List <CQ2MD2.SAliasFrameDesc> aliasframes;

            // HACK - prevent model loading if in Heretic II mode
            if (CProgram.gQ2Game.gCMain.r_htic2 == true)
            {
                return;
            }

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


            // identity (header)
            _SModel.ModelMD2.md2.identification = br.ReadBytes(4);
            if (_SModel.ModelMD2.md2.identification[0] != 'I' ||
                _SModel.ModelMD2.md2.identification[1] != 'D' ||
                _SModel.ModelMD2.md2.identification[2] != 'P' ||
                _SModel.ModelMD2.md2.identification[3] != '2')
            {
                System.Diagnostics.Debug.WriteLine("MDL file " + _SModel.name + " doesn't have IDP2 id");

                br.Close();
                return;
            }


            // model version
            _SModel.ModelMD2.md2.version = br.ReadInt32();
            if (_SModel.ModelMD2.md2.version != ALIAS_VERSION)
            {
                System.Diagnostics.Debug.WriteLine(_SModel.name + " has wrong version number (" + _SModel.ModelMD2.md2.version + " should be " + ALIAS_VERSION + ")");

                br.Close();
                return;
            }


            // skin size
            _SModel.ModelMD2.md2.skinwidth  = br.ReadInt32();
            _SModel.ModelMD2.md2.skinheight = br.ReadInt32();
            if (_SModel.ModelMD2.md2.skinheight > CLocal.MAX_LBM_HEIGHT)
            {
                System.Diagnostics.Debug.WriteLine("model " + _SModel.name + " has a skin taller than " + CLocal.MAX_LBM_HEIGHT + ".");

                br.Close();
                return;
            }


            // frame size
            _SModel.ModelMD2.md2.framesize = br.ReadInt32();


            // number of skins
            _SModel.ModelMD2.md2.num_skins = br.ReadInt32();


            // number of vertices
            _SModel.ModelMD2.md2.num_xyz = br.ReadInt32();
            if (_SModel.ModelMD2.md2.num_xyz > MAX_VERTS)
            {
                System.Diagnostics.Debug.WriteLine("model " + _SModel.name + " has too many vertices");

                br.Close();
                return;
            }


            // number of st vertices
            _SModel.ModelMD2.md2.num_st = br.ReadInt32();
            if (_SModel.ModelMD2.md2.num_st <= 0)
            {
                System.Diagnostics.Debug.WriteLine("model " + _SModel.name + " has no st vertices");

                br.Close();
                return;
            }


            // number of triangles
            _SModel.ModelMD2.md2.num_tris = br.ReadInt32();
            if (_SModel.ModelMD2.md2.num_tris <= 0)
            {
                System.Diagnostics.Debug.WriteLine("model " + _SModel.name + " has no triangles");

                br.Close();
                return;
            }


            // number of gl commands
            _SModel.ModelMD2.md2.num_glcmds = br.ReadInt32();


            // number of frames
            _SModel.ModelMD2.md2.num_frames = br.ReadInt32();
            if (_SModel.ModelMD2.md2.num_frames <= 0)
            {
                System.Diagnostics.Debug.WriteLine("model " + _SModel.name + " has no frames");

                br.Close();
                return;
            }


            // load offsets
            _SModel.ModelMD2.md2.ofs_skins  = br.ReadInt32(); // each skin is a MAX_SKINNAME string
            _SModel.ModelMD2.md2.ofs_st     = br.ReadInt32(); // byte offset from start for stverts
            _SModel.ModelMD2.md2.ofs_tris   = br.ReadInt32(); // offset for dtriangles
            _SModel.ModelMD2.md2.ofs_frames = br.ReadInt32(); // offset for first frame
            _SModel.ModelMD2.md2.ofs_glcmds = br.ReadInt32(); // offset for strip/fan command list
            _SModel.ModelMD2.md2.ofs_end    = br.ReadInt32(); // end of file


            //
            // load the skin names
            //
            skinnames = new List <string>();
            for (int i = 0; i < _SModel.ModelMD2.md2.num_skins; i++)
            {
                skinnames.Add(CShared.Com_ToString(br.ReadChars(MAX_SKINNAME)));
            }

            if (skinnames.Count != 0)
            {
                _SModel.ModelMD2.skinnames = skinnames.ToArray();
                skinnames.Clear();
                skinnames = null;
            }


            //
            // load base s and t vertices (not used in gl version)
            //
            st = new List <SSTVert>();
            for (int i = 0; i < _SModel.ModelMD2.md2.num_st; i++)
            {
                SSTVert _SSTVert;

                _SSTVert.s = br.ReadInt16();
                _SSTVert.t = br.ReadInt16();

                st.Add(_SSTVert);
            }

            if (st.Count != 0)
            {
                _SModel.ModelMD2.st = st.ToArray();
                st.Clear();
                st = null;
            }


            //
            // load the triangles
            //
            triangles = new List <SDTriangle>();
            for (int i = 0; i < _SModel.ModelMD2.md2.num_tris; i++)
            {
                SDTriangle _SDTriangle;

                _SDTriangle.index_xyz    = new short[3];
                _SDTriangle.index_xyz[0] = br.ReadInt16();
                _SDTriangle.index_xyz[1] = br.ReadInt16();
                _SDTriangle.index_xyz[2] = br.ReadInt16();

                _SDTriangle.index_st    = new short[3];
                _SDTriangle.index_st[0] = br.ReadInt16();
                _SDTriangle.index_st[1] = br.ReadInt16();
                _SDTriangle.index_st[2] = br.ReadInt16();

                triangles.Add(_SDTriangle);
            }

            if (triangles.Count != 0)
            {
                _SModel.ModelMD2.triangles = triangles.ToArray();
                triangles.Clear();
                triangles = null;
            }


            //
            // load the frames
            //
            aliasframes = new List <SAliasFrameDesc>();
            for (int i = 0; i < _SModel.ModelMD2.md2.num_frames; i++)
            {
                SAliasFrameDesc _SAliasFrameDesc;

                _SAliasFrameDesc.scale    = new float[3];
                _SAliasFrameDesc.scale[0] = br.ReadSingle();
                _SAliasFrameDesc.scale[1] = br.ReadSingle();
                _SAliasFrameDesc.scale[2] = br.ReadSingle();

                _SAliasFrameDesc.translate    = new float[3];
                _SAliasFrameDesc.translate[0] = br.ReadSingle();
                _SAliasFrameDesc.translate[1] = br.ReadSingle();
                _SAliasFrameDesc.translate[2] = br.ReadSingle();

                _SAliasFrameDesc.name = br.ReadChars(16);

                _SAliasFrameDesc.verts = new STrivertx[_SModel.ModelMD2.md2.num_xyz];
                for (int j = 0; j < _SModel.ModelMD2.md2.num_xyz; j++)
                {
                    _SAliasFrameDesc.verts[j].v    = new byte[3];
                    _SAliasFrameDesc.verts[j].v[0] = br.ReadByte();
                    _SAliasFrameDesc.verts[j].v[1] = br.ReadByte();
                    _SAliasFrameDesc.verts[j].v[2] = br.ReadByte();

                    _SAliasFrameDesc.verts[j].lightnormalindex = br.ReadByte();
                }

                aliasframes.Add(_SAliasFrameDesc);
            }

            if (aliasframes.Count != 0)
            {
                _SModel.ModelMD2.aliasframes = aliasframes.ToArray();
                aliasframes.Clear();
                aliasframes = null;
            }


            //
            // load the gl commands
            //
            _SModel.ModelMD2.glcmds = br.ReadBytes(_SModel.ModelMD2.md2.num_glcmds * sizeof(int));



            // set the model type
            _SModel.ModType = CModel.EModType.MOD_ALIAS;


            // register all skins
            _SModel.ModelMD2.skins = new Microsoft.Xna.Framework.Graphics.Texture2D[_SModel.ModelMD2.md2.num_skins];
            for (int i = 0; i < _SModel.ModelMD2.md2.num_skins; i++)
            {
                _SModel.ModelMD2.skins[i] = CProgram.gQ2Game.gCMain.gCImage.LoadSkin(_SModel.ModelMD2.skinnames[i]);
            }

            // set default mins/maxs
            _SModel.mins.X = -32;
            _SModel.mins.Y = -32;
            _SModel.mins.Z = -32;
            _SModel.maxs.X = 32;
            _SModel.maxs.Y = 32;
            _SModel.maxs.Z = 32;

            // close the binary reader
            br.Close();
            br = null;

            // close the memory stream
            ms.Close();
            ms = null;

            BuildAliasModelBuffer(ref _SModel.ModelMD2);
        }
Exemplo n.º 3
0
        private SPack?FS_LoadPakFile(string PakFile, string BaseName)
        {
            SPack        Pack;
            BinaryReader r;

            PakFile = CProgram.gQ2Game.Content.RootDirectory + "\\" + PakFile;

            if (File.Exists(PakFile) == false)
            {
                CMain.Error(CMain.EErrorParm.ERR_FATAL, "PAK file not found.");
                return(null);
            }

            Pack.handle = File.OpenRead(PakFile);

            if (Pack.handle == null)
            {
                return(null);
            }

            r        = new BinaryReader(Pack.handle);
            PakFile  = PakFile.ToLower();
            BaseName = BaseName.ToLower();

            if (r.ReadInt32() != IDPAKHEADER)
            {
                Pack.handle.Close();
                Pack.handle = null;

                CMain.Error(CMain.EErrorParm.ERR_FATAL, PakFile + " is not a packfile");
                return(null);
            }

            Pack.packDirOffset = r.ReadInt32();
            Pack.packDirLength = r.ReadInt32();

            // if the directory offset is beyond the EOF then we assume its htic2-0.pak
            // Raven Software probably did this so unaware PAK readers fails to read the Heretic2 content
            if (CProgram.gQ2Game.gCMain.r_htic2 == true)
            {
                if (Pack.packDirOffset > r.BaseStream.Length)
                {
                    Pack.packDirOffset = 215695973; // 0x0cdb4265 (215 695 973 bytes)
                    Pack.packDirLength = 264256;    // EOF - Pack.packDirOffset (EOF: 0x0cdf4aa5 | 215 960 229 bytes)
                }
            }

            // PACK_MAX_FILENAME_LENGTH + FilePosition + FileLength
            Pack.numfiles = Pack.packDirLength / (PACK_MAX_FILENAME_LENGTH + sizeof(int) + sizeof(int));

            if (Pack.numfiles > PACK_MAX_FILES)
            {
                CMain.Error(CMain.EErrorParm.ERR_FATAL, PakFile + " has " + Pack.numfiles + " files");
            }

            Pack.buildBuffer = new List <SFileInPack>();

            fs_packFiles    += Pack.numfiles;
            Pack.pakFilename = PakFile;
            Pack.pakBasename = BaseName.Replace(".pak", "");

            r.BaseStream.Seek(Pack.packDirOffset, SeekOrigin.Begin);

            for (int i = 0; i < Pack.numfiles; i++)
            {
                SFileInPack _FileInPack;

                _FileInPack.Name     = CShared.Com_ToString(r.ReadChars(PACK_MAX_FILENAME_LENGTH)).ToLower();
                _FileInPack.Position = r.ReadInt32();
                _FileInPack.Size     = r.ReadInt32();

                Pack.buildBuffer.Add(_FileInPack);
            }

            //for (int i = 0; i < Pack.buildBuffer.Count; i++)
            //{
            //    r.BaseStream.Seek(Pack.buildBuffer[i].Position, System.IO.SeekOrigin.Begin);
            //    fs_headerLongs[fs_numHeaderLongs++] = CCrc32.GetMemoryCRC32(r.ReadBytes(Pack.buildBuffer[i].Size));
            //}

            //Pack.checksum = CProgram.vCCommon.Com_BlockChecksum(fs_headerLongs);
            //Pack.pure_checksum = CProgram.vCCommon.Com_BlockChecksumKey(fs_headerLongs, fs_checksumFeed);

            // As of yet unassigned
            //Pack.hashSize = 0;
            Pack.pakGamename = null;

            return(Pack);
        }
Exemplo n.º 4
0
        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;
                }
            }
        }
Exemplo n.º 5
0
        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();
        }
Exemplo n.º 6
0
        /// <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);
        }