Loading/ Rendering Quake 3 bsp in MDX

Started by
1 comment, last by jollyjeffers 17 years, 1 month ago
ive been looking around for some way to load and render a bsp map in Mananged Dire ctX but i cant find one that i understand... i found this one:

public class BSPFILE
    {
        byte mod_base = 29;

        #region BSP const
        const int HEADER_LUMPS  = 15;
        const int BSPVERSION    = 29;

        // upper design bounds

        const int MAX_MAP_HULLS         = 4;
        const int MAX_MAP_MODELS		= 256;
        const int MAX_MAP_BRUSHES		= 4096;
        const int MAX_MAP_ENTITIES	    = 1024;
        const int MAX_MAP_ENTSTRING	    = 65536;

        const int MAX_MAP_PLANES		= 8192;
        const int MAX_MAP_NODES		    = 32767;
        const int MAX_MAP_CLIPNODES	    = 32767;
        const int MAX_MAP_LEAFS         = 32767;
        const int MAX_MAP_VERTS         = 65535;
        const int MAX_MAP_FACES         = 65535;
        const int MAX_MAP_MARKSURFACES  = 65535;
        const int MAX_MAP_TEXINFO		= 4096;
        const int MAX_MAP_EDGES         = 256000;
        const int MAX_MAP_SURFEDGES     = 512000;
        const int MAX_MAP_MIPTEX		= 0x200000;
        const int MAX_MAP_LIGHTING      = 0x100000;
        const int MAX_MAP_VISIBILITY    = 0x100000;

        // key / value pair sizes

        const int MAX_KEY		= 32;
        const int MAX_VALUE	    = 1024;

        const int LUMP_ENTITIES     = 0;
        const int LUMP_PLANES       = 1;
        const int LUMP_TEXTURES     = 2;
        const int LUMP_VERTEXES     = 3;
        const int LUMP_VISIBILITY	= 4;
        const int LUMP_NODES		= 5;
        const int LUMP_TEXINFO      = 6;
        const int LUMP_FACES        = 7;
        const int LUMP_LIGHTING     = 8;
        const int LUMP_CLIPNODES	= 9;
        const int LUMP_LEAFS		= 10;
        const int LUMP_MARKSURFACES = 11;
        const int LUMP_EDGES		= 12;
        const int LUMP_SURFEDGES    = 13;
        const int LUMP_MODELS       = 14;

        const int SURF_PLANEBACK        = 2;
        const int SURF_DRAWSKY          = 4;
        const int SURF_DRAWSPRITE       = 8;
        const int SURF_DRAWTURB		    = 0x10;
        const int SURF_DRAWTILED		= 0x20;
        const int SURF_DRAWBACKGROUND	= 0x40;
        const int SURF_UNDERWATER		= 0x80;
        #endregion

        public int numvertexes;
        public int numtextures;
        int numedges;
        public int numsurfaces;
        int numplanes;
        public int numtextinfo;
        int numleafs;
        public int nummarksurfaces;
        int numsurfedges;

        const int MAXLIGHTMAPS = 4;
        const int MIPLEVELS = 4;

        #region BSP Struct
        public struct texture_t
        {
	        public char[]   name;
            public uint width, height;
            public int anim_total;				// total tenths in sequence ( 0 = no)
            public int anim_min, anim_max;		// time for this frame min <=time< max
	        //struct texture_s *anim_next;		// in the animation sequence
	        //struct texture_s *alternate_anims;	// bmodels in frame 1 use these
            public uint[] offsets;		// four mip maps stored // uint[MIPLEVELS]
        }
        public struct mtexinfo_t
        {
	        public float[,]     vecs; // float[2][4]
	        public int			miptex;
	        public int			flags;
        }
        public struct miptex_t
        {
	        public char[] name;
            public uint width, height;
            public uint[] offsets;		// four mip maps stored, size MIPLEVELS
        }
        public struct dmiptexlump_t
        {
            public int nummiptex;
            public int[] dataofs;		// [nummiptex], size 4
        }
        struct lump_t
        {
            public int filelen, fileofs;
        }
        struct dheader_t
        {
            public int version;
            public lump_t[] lumps;
        }
        public struct dsurface_t
        {
	        public short planenum;
            public short side;

            public int firstedge;		// we must support > 64k edges
            public short numedges;
            public short texinfo;

            // lighting info
            public byte[] styles;
            public int lightofs;		// start of [numstyles*surfsize] samples
            public byte sample;
        }
        struct dplane_t
        {
	        public Vector3	normal;
            public float dist;
            public int type;
	        public byte signbits;
	        public byte[] pad;
        }
        public struct dvertex_t
        {
	        public Vector3      point;
        }
        public struct dedge_t
        {
            public ushort[] v;		// vertex numbers
        }

        const int NUM_AMBIENTS = 4;
        struct dleaf_t
        {
	        public int contents;
	        public int visofs;				// -1 = no visibility info

	        public short[] mins;	// short[3], for frustum culling
	        public short[] maxs;    // short[3]

	        public ushort firstmarksurface;
            public ushort nummarksurfaces;

            public byte ambient_level; // byte[NUM_AMBIENTS];
        }

        public ushort[] dmarksurfaces;
        public int[] dsurfedges;
        #endregion

        dheader_t   header;
        public dvertex_t[] dvertexes;
        dplane_t[]  dplanes;
        public mtexinfo_t[] texinfo;
        public dsurface_t[]   dsurfaces;
        dleaf_t[]   dleafs;
        public dedge_t[]   dedges;
        public texture_t[] textures;

        public byte[] lightdata;

        static public void GenTexture(byte[] tex, miptex_t mt)
        {

        }

        #region BSP Methods
        void LoadVertexes(int lump, int size)
        {
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            numvertexes = lenght / size;

            dvertexes = new dvertex_t[numvertexes];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < numvertexes; i++)
            {
                dvertexes.point.X = br.ReadSingle();
                dvertexes.point.Y = br.ReadSingle();
                dvertexes.point.Z = br.ReadSingle();
            }

            br.Close();
            ms.Close();
        }
        void LoadEdges(int lump, int size)
        {
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            numedges = lenght / size;

            dedges = new dedge_t[numedges];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < numedges; i++)
            {
                dedges.v = new ushort[2];
                dedges.v[0] = (ushort)br.ReadInt16();
                dedges.v[1] = (ushort)br.ReadInt16();
            }

            br.Close();
            ms.Close();
        }
        void LoadSurfedges(int lump, int size)
        {
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            numsurfedges = lenght / size;

            dsurfedges = new int[numsurfedges];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < numsurfedges; i++)
            {
                dsurfedges = br.ReadInt32();
            }

            br.Close();
            ms.Close();
        }
        void LoadTextures(int lump, int size)
        {
            int i, j, pixels, num, max, altmax;
            miptex_t    mt = new miptex_t();
            texture_t	tx, tx2 = new texture_t();
            texture_t[]	anims = new texture_t[10];
            texture_t[]	altanims = new texture_t[10];
            dmiptexlump_t m = new dmiptexlump_t();

            byte[] textureBuffer;
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            if (lenght < 1)
            {
                textures = null;
                return;
            }

            MemoryStream ms = new MemoryStream(buffer);
            BinaryReader br = new BinaryReader(ms);

            ms.Position = ofs;
            m.nummiptex = br.ReadInt32();

            m.dataofs = new int[4];
            for (i = 0; i < 4; i++)
                m.dataofs = br.ReadInt32();

            numtextures = m.nummiptex;

            ms.Position = header.lumps[2].fileofs + m.dataofs[0];

            for (i = 0; i < m.nummiptex; i++)
	        {
                mt.name = new char[16];
                mt.name = br.ReadChars(16);

                mt.width = br.ReadUInt32();
                mt.height = br.ReadUInt32();

                mt.offsets = new uint[4];
                for (j = 0; j < 4; j++)
                    mt.offsets[j] = br.ReadUInt32();

                pixels = (int)mt.width * (int)mt.height / 64 * 85;

                textureBuffer = br.ReadBytes(pixels);
                GenTexture(textureBuffer, mt);
            }
        }
        void LoadLighting(int lump, int size)
        {
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            lightdata = new byte[lenght];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < lenght; i++)
                lightdata = br.ReadByte();

            br.Close();
            ms.Close();
        }
        void LoadPlanes(int lump, int size)
        {
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            numplanes = lenght / size;

            dplanes = new dplane_t[numplanes];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < numplanes; i++)
            {
                dplanes.normal.X = br.ReadSingle();
                dplanes.normal.Y = br.ReadSingle();
                dplanes.normal.Z = br.ReadSingle();

                dplanes.dist = br.ReadSingle();
                dplanes.type = br.ReadInt32();
            }

            br.Close();
            ms.Close();
        }
        void LoadTexinfo(int lump, int size)
        {
            int lenght, ofs;
            float	len1, len2;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            numtextinfo = lenght / size;

            texinfo = new mtexinfo_t[numtextinfo];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < numtextinfo; i++)
            {
                texinfo.vecs = new float[2,4];

                for (int j = 0; j < 4; j++)
                    texinfo.vecs[0,j] = br.ReadSingle();
                for (int j = 0; j < 4; j++)
                    texinfo.vecs[1, j] = br.ReadSingle();
          
                texinfo.miptex = br.ReadInt32();
                texinfo.flags = br.ReadInt32();
            }

            br.Close();
            ms.Close();
        }
        void LoadMarksurfaces(int lump, int size)
        {
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            nummarksurfaces = lenght / size;

            dmarksurfaces = new ushort[nummarksurfaces];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < nummarksurfaces; i++)
            {
                ushort j;

                j = (ushort)br.ReadInt16();

                dmarksurfaces = j;
            }

            br.Close();
            ms.Close();
        }
        void LoadLeafs(int lump, int size)
        {
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            numleafs = lenght / size;

            dleafs = new dleaf_t[numleafs];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < numleafs; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    dleafs.mins[j] = br.ReadInt16();
                    dleafs.maxs[j] = br.ReadInt16();
                }

                dleafs.contents = br.ReadInt32();
            }

            br.Close();
            ms.Close();
        }
        void LoadFaces(int lump, int size)
        {
            int lenght, ofs;

            lenght = header.lumps[lump].filelen;
            ofs = header.lumps[lump].fileofs;

            numsurfaces = lenght / size;

            dsurfaces = new dsurface_t[numsurfaces];
            MemoryStream ms = new MemoryStream(buffer);

            ms.Position = ofs;

            BinaryReader br = new BinaryReader(ms);

            for (int i = 0; i < numsurfaces; i++)
            {
                dsurfaces.planenum = br.ReadInt16();
                dsurfaces.side = br.ReadInt16();
                dsurfaces.firstedge = br.ReadInt32();
                dsurfaces.numedges = br.ReadInt16();
                dsurfaces.texinfo = br.ReadInt16();

                // lighting info

                dsurfaces.styles = new byte[4];

                for (int j = 0; j < MAXLIGHTMAPS; j++)
                    dsurfaces.styles[j] = br.ReadByte();

                dsurfaces.lightofs = br.ReadInt32();

                if (dsurfaces.lightofs != -1)
                    dsurfaces.sample = lightdata[dsurfaces.lightofs];
        		
            }

            br.Close();
            ms.Close();
        }
        #endregion

        byte[] buffer;

        public void SafeRead(FileStream f, long count)
        {
            buffer = new byte[f.Length];
            f.Read(buffer, 0, buffer.Length);
            f.Close();

            MemoryStream ms = new MemoryStream(buffer);
            BinaryReader br = new BinaryReader(ms);

            header.version = br.ReadInt32();
            header.lumps = new lump_t[15];

            for (int i = 0; i < HEADER_LUMPS; i++)
            {
                header.lumps.fileofs = br.ReadInt32();
                header.lumps.filelen = br.ReadInt32();
            }

            br.Close();
            ms.Close();
        }
        private long LoadFile(string filename)
        {
            FileStream f = new FileStream(filename, FileMode.Open, FileAccess.Read);

            long length = f.Length;
            SafeRead(f, length);
            return length;
        }
        public void LoadBSPFile(FileInfo fi_model)
        {
            LoadFile(fi_model.FullName);

            LoadVertexes(LUMP_VERTEXES, 12);
            LoadEdges(LUMP_EDGES, 4);
            LoadSurfedges(LUMP_SURFEDGES, 4);
            LoadTextures(LUMP_TEXTURES, 1);
            LoadLighting(LUMP_LIGHTING, 1);
            LoadPlanes(LUMP_PLANES, 20);
            LoadTexinfo(LUMP_TEXINFO, 40);
            LoadFaces(LUMP_FACES, 20);
            LoadMarksurfaces(LUMP_MARKSURFACES, 2);
            //LoadLeafs(LUMP_LEAFS, 28);
        }
    }

which loads a bsp file, but i have no clue how to render it...
Advertisement
I wrote a MDX Q3 loader/renderer a couple months ago from scratch using the file format docs. It's not perfect, theres plenty of missing textures and a lack of error checking, but it doesn't look like some hideous C -> C# converted code, so it should be easier to work with.

Clicky McClickster
In all fairness, there isn't really anything in MDX (or D3D in general) that works specifically for BSP files or their related data structures. Ultimately the D3D renderer won't care what data structure(s) you've used...

Thus, if you have any problem with Scet's example you'll be better off explaining how your data is laid out in memory so that we can advise you on the specifics of linking that to the D3D implementation.

I don't know if it'll apply in your case, or whether you've made any changes, but a BSP architecture tends to be quite inefficient on modern GPU's due to the way some implementations change the data on a regular basis. Big blocks of static data culled at a high level tends to be faster than small blocks precisely matching the frustum that constantly changes [wink]

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

This topic is closed to new replies.

Advertisement