Announcement

Collapse
No announcement yet.

Real Flash Quake

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Real Flash Quake

    I'm sure we have all seen the various flash quakes that exist. The only problem is, none of them are truly flash. They all turn the original Quake source into an ANE (adobe native extension). To call these "flash quakes" flash is as silly as if I wrapped an .swf in an .exe and tried to claim that now it's a C executable. Sticking hot dogs in a donut box does not make it a box of donuts.

    All that being said, I am working on a real "flash quake", real as in: completely written in AS3. It will basically be a glquake because I am targeting flash's Stage3D API which specifically utilizes the GPU.

    Currently I have written a parser that parses ALL 15 lumps of a BSP29 file. I even parse out the miptextures into the final images straight from the palette. I am at the point now where I need the math formulas to assemble the data into skinned geometry. The BSP specs don't give very solid examples and digging through engine code is becoming tedious, time consuming and not providing a lot of answers.

    If there is any super nice person out there that would either

    a) post the formulas for each step of assembling the BSP data
    or
    b) point me directly to the spot in some engine code where these formulas are being used

    I would be very grateful.
    http://www.nextgenquake.com

  • #2


    The above shot is just to show I am serious. The main text on the page is a log dump of the models lump for e1m1 (I can do this to any/all lumps). The viewer is proving I can get the miptexs properly. The file list on the right is my complete rewrite of all the BSP structs (except the leaf lump which I am doing right this minute).


    This was already a whole lot of work. I WILL get BSPs to render. Hopefully sooner than later.
    Last edited by MadGypsy; 02-09-2016, 02:01 PM.
    http://www.nextgenquake.com

    Comment


    • #3
      surf->surfedges->edges->vertex.
      if the surfedge value is negative, then the edge is backwards and you should flip the two verts.
      by just walking the edges of each face you can construct the polygon that way.

      texture coords depend upon the texinfo. v.s = dotproduct(3dposition, texinfo->vecs[0])+texinfo->vecs[0][3]; v.t = dotproduct(3dposition, texinfo->vecs[1])+texinfo->vecs[1][3];

      lightmap size is determined from the texture coords. divide by 16 to get the size, the lightmap data will be aligned with the smallest point. you'll need to atlas stuff yourself. beware precision.
      Some Game Thing

      Comment


      • #4
        I can't test my script til I come from the other end in my code and create a renderer to plug the BSP into. That being said I will just ask you if this looks like I understood what you said.

        Code:
        while(n < faces.length)
        {	
        	e = ledges[faces[n].ledge_id];			//surf->surfedge
        	if (e > -1)
        	{
        		edge = edges[e];			//surfedge->edge
        		vert = vertices[edge.vertex0];		//edge->vertices
        	}
        	else
        	{
        		edge = edges[-e];
        		vert = vertices[edge.vertex1];		//reverse if negative
        	}
        	
        	id = faces[n].texinfo_id;
        	
        	s = dotproduct(vert, texinfo[id].vectorS) + texinfo[id].distS;
        	t = dotproduct(vert, texinfo[id].vectorT) + texinfo[id].distT;
        	
        	++n;
        }
        I realize the product of the loop is nothing (atm). My question is just whether my method describes what you said. I haven't touched the lightmap part yet.
        Last edited by MadGypsy; 02-10-2016, 02:54 AM.
        http://www.nextgenquake.com

        Comment


        • #5
          you should also have some faces[n].ledge_count variable too, which says how many surfedges there are for that surface, and thus how many verticies you should have.
          otherwise yes, I think you're calculating the right vert[x,y,z], s, and t values for the first vertex of each surface.
          Some Game Thing

          Comment


          • #6
            Thinking out loud...
            Code:
            n = 0;
            while(n < leaf.lface_num)
            {	
            	offset = lfaces[leaf.lface_id];
            	currFace = faces[model[0].face_id + offset];	//kinna dumb if face_id is always the same number & it's zero
            	id = currFace.texinfo_id;
            	
            	j = 0;
            	while (j < currFace.ledge_num)
            	{
            		
            		e = ledges[currFace.ledge_id + j];		//surf->surfedge
            		if (e > -1)
            		{
            			edge = edges[e];			//surfedge->edge
            			vert = vertices[edge.vertex0];		//edge->vertices
            		} else {
            			edge = edges[-e];
            			vert = vertices[edge.vertex1];		//reverse if negative
            		}
            	
            		s = dotproduct(vert, texinfo[id].vectorS) + texinfo[id].distS;
            		t = dotproduct(vert, texinfo[id].vectorT) + texinfo[id].distT;
            
            		++j;
            	}
            	++n;
            }
            Storing this here so I can get to it easy from any device. Having a "struct reference" is helping me visualize how this all goes together.
            Code:
            BSP HEADER
            
            BSP version: 29
            -DHeader_T
            {
            	_entities->DEntry_T[0]:
            	{
            		position:4
            		offset:794724,
            		length:26284,
            		index:0
            	},
            	_planes->DEntry_T[1]:
            	{
            		position:12
            		offset:124,
            		length:36200,
            		index:1
            	},
            	_miptex->DEntry_T[2]:
            	{
            		position:20
            		offset:821008,
            		length:544168,
            		index:2
            	},
            	_vertices->DEntry_T[3]:
            	{
            		position:28
            		offset:79192,
            		length:88296,
            		index:3
            	}
            
            
            PLANES LUMP
            
            -PlaneLump_T:{count:1810}
            -Plane_T[0]:
            {	position:124,
            	normal->Vec3_T:{x:0, y:1, z:0},
            	dist:-64,
            	type:1
            }
            -Plane_T[1]:
            {	position:144,
            	normal->Vec3_T:{x:0, y:1, z:0},
            	dist:3064,
            	type:1
            }
            
            
            MIPS LUMP
            
            -MipLump_T:{count:81}
            	offset[0]:328,
            	offset[1]:1728,
            	offset[2]:7208,
            	offset[3]:7588,
            	offset[4]:7968,
            	offset[5]:13448,
            	offset[6]:35248,
            	offset[7]:38008,
            	offset[8]:40768,
            	offset[9]:46248,
            	offset[10]:51728
            
            
            SURFACE LUMP
            
            -SurfaceLump_T{count:489}
            -Surface_T[0]:
            {	position:233488,
            	vectorS->:Vec3_T:{x:0, y:0, z:-1},
            	vectorT->Vec3_T:{x:0, y:1, z:0},
            	distS:0,
            	distT:0,
            	texture_id:0,
            	animated:0
            }
            
            
            VERTEX LUMP
            
            -VertexLump_T:{count:7358}
            Vertex_T[0]:
            {	position:79192,
            	{x:408, y:-64, z:128}
            }
            Vertex_T[1]:
            {	position:79204,
            	{x:408, y:-64, z:192}
            }
            Vertex_T[2]:
            {	position:79216,
            	{x:384, y:-64, z:192}
            }
            
            
            FACES LUMP
            
            -FaceLump_T:{count:5516}
            -Face_T[0]:
            {	position:253048,
            	plane_id:0,
            	side:0,
            	ledge_id:0,
            	ledge_num:6,
            	texinfo_id:13,
            	lightmap_styles:[0,255,255,255],
            	lightmap_offset:152
            }
            
            
            EDGES LUMP
            
            -EdgeLump_T:{count:13497}
            -Edge_T[0]:
            {	position:527588,
            	vertex0:0,
            	vertex1:0
            }
            -Edge_T[1]:
            {	position:527592,
            	vertex0:0,
            	vertex1:1
            }
            -Edge_T[2]:
            {	position:527596,
            	vertex0:1,
            	vertex1:2
            }
            	
            
            
            VIS LUMP
            
            -VisLump_T:{count:40843}
            	vislist[0]:13
            	vislist[1]:48
            	vislist[2]:32
            	vislist[3]:-20
            	vislist[4]:-10
            	vislist[5]:-29
            	vislist[6]:7
            	vislist[7]:-26
            	vislist[8]:-123
            	vislist[9]:0
            	vislist[10]:1
            
            
            NODES LUMP
            
            -NodeLump_T:{count:2750}
            -Node_T[0]:
            {	position:167488,
            	plane_id:0,
            	front:1,
            	back:2301,
            	box:BBoxShort_T:{
            		min:[-632,-456,-632],
            		max:[1544,3096,312]
            	},
            	face_id:0,
            	face_num:6
            }
            
            
            MODELS LUMP
            
            -ModelLump_T:{count:58}
            -Model_T[0]:
            {	position:581576,
            	bounds->:BoundBox_T:{
            		min->:Vec3_T:{x:-607, y:-431, z:-607},
            		max->:Vec3_T:{x:1519, y:3071, z:287}
            	},
            	origin->:Vec3_T:{x:0, y:0, z:0},
            	node_id:[0, 0, 2970, 0],
            	numleafs:1148,
            	face_id:0,
            	face_num:5059
            }
            
            
            CLIPNODES LUMP
            
            -ClipNodesLump_T:{count:5408}
            -ClipNode_T[0]:
            {	position:363368,
            	planenum: 568,
            	children:[1, 1606]
            }
            -ClipNode_T[1]:
            {	position:363376,
            	planenum: 204,
            	children:[2, 615]
            }
            
            
            MARKSURFACE LUMP
            
            -MarkSurfaceLump_T:{count:14146}
            	lface[0]:98
            	lface[1]:99
            	lface[2]:100
            	lface[3]:104
            	lface[4]:103
            	lface[5]:102
            	lface[6]:94
            	lface[7]:93
            	lface[8]:92
            	lface[9]:91
            	lface[10]:6
            
            
            LEAFS LUMP
            
            -LeafLump_T:{count:1531}
            -Leaf_T[0]:
            {	position:36324,
            	type:-2,
            	vislist:0,
            	bound->:BBoxShort_T:{
            		min:[0,0,0],
            		max:[0,0,0]
            	},
            	lface_id:0,
            	lface_num:0,
            	ambient:[0, 0, 0, 0]
            }
            -Leaf_T[1]:
            {	position:36352,
            	type:-1,
            	vislist:0,
            	bound->:BBoxShort_T:{
            		min:[144,3024,-16],
            		max:[176,3064,16]
            	},
            	lface_id:0,
            	lface_num:15,
            	ambient:[4294967295, 4294967295, 0, 0]
            }
            
            
            ENTITIES LUMP
            
            -EntitiesLump_T:{count:26284}
            {
            "worldtype" "2"
            "sounds" "6"
            "classname" "worldspawn"
            "wad" "gfx/base.wad"
            "message" "the Slipgate Complex"
            }
            {
            "classname" "info_player_start"
            "origin" "480 -352 88"
            "angle" "90"
            }
            {
            "classname" "light"
            "origin" "480 96 168"
            "light" "250"
            }
            {
            "classname" "light"
            "origin" "480 288 168"
            "light" "250"
            }
            {
            "classname" "light"
            "origin" "272 96 80"
            }
            {
            "origin" "272 288 80"
            "classname" "light"
            }
            {
            "classname" "light"
            "origin" "272 192 80"
            }
            {
            "origin" "688 192 80"
            "classname" "light_fluorospark"
            "style" "10"
            }
            
            
            SURFEDGES LUMP
            
            -SurfEdgeLump_T:{count:26702}
            	ledge[0]:1
            	ledge[1]:2
            	ledge[2]:3
            	ledge[3]:4
            	ledge[4]:5
            	ledge[5]:6
            	ledge[6]:7
            	ledge[7]:8
            	ledge[8]:-1
            	ledge[9]:9
            	ledge[10]:10
            
            
            LIGHTMAPS LUMP
            
            -LightmapLump_T:{count:168590}
            	light[0]:69
            	light[1]:79
            	light[2]:89
            	light[3]:77
            	light[4]:86
            	light[5]:95
            	light[6]:84
            	light[7]:91
            	light[8]:98
            	light[9]:0
            	light[10]:0
            Last edited by MadGypsy; 02-10-2016, 11:35 PM.
            http://www.nextgenquake.com

            Comment


            • #7
              I'm letting some thoughts stew so, I'm gonna ramble about what I am actually doing. First of all, this is not going to be a Quake engine. We have like 500 Quake engines, there is no point in me building another one. My engine may see a day where it is Quake capable though.

              I came here like 5 years ago because google told me this is where to go if I want to learn how to build a 3D FPS. I know the quake 1 version of building a game pretty much inside and out. The one thing that has always been a mystery to me is the engine. Aside from user end controls and options, engines have always been a black box, to me. I still want to build a game. I still want to build the same damn game that I wanted to build 5 years ago. Before I build my game, I need to build my engine. I want direct and absolute control and knowledge of my entire "product"... not just the game.

              I have brought that decision even down to NOT using 3rd party engine API's. I could walk these faces right now into subgeometry (incl textures and uvs), push them into geometry and dump that to a mesh that gets sent to the Away3D scene. I know I could do this, right now... with success. That defeats the purpose of knowing every iota of the code. I'm going to go all the way down to the GPU and build my way up.

              One way my engine will differ from a quake engine is, I am not porting a damn thing. I understand the various problems I need to solve and I'm gonna write my own code to solve them. Even the BSP parser I just wrote will probably get rewritten in a far less verbose and far more direct way. Like, probably forget even assigning vars and do the math straight out of the BSP readInt()*readInt() so to speak. That's the great thing about BSP. All I need is a base number (ie the byte offset of a lump) and I can predict every spot the number I need will appear in. I have no idea if performing the math directly from bytes to bytes and only saving the results I need is smart or faster or better but, I'm probably going to find out. Flash expects vertexes in the order they need to be connected as Vector.<Number>[x,y,z,r,g,b,x,y,z,r,g,b,x,y,z,r,g,b,.......]. Maybe I could write a formula that spits the whole world out like that without allocating a bunch of memory on all these vars. You could say instead of parsing the BSP I would be attacking it for the bottom line.

              IDK..it's all dynamic right now. I'm building an engine that supports things I understand which are supported by tools that I understand. I know how to map in radiant and compile with modern tools to BSP. My engine should support BSP.
              Last edited by MadGypsy; 02-11-2016, 12:29 AM.
              http://www.nextgenquake.com

              Comment


              • #8
                considering how inefficient flash is, I'd suggest just throwing the entire map into a single trisoup buffer (or at least one buffer per texture) and just using that.
                you might need to be prepared to split it more than that if your api is shitty and doesn't support more than 65k verts, depends on the map...
                hurrah for modern gpus, throw everything at it and see what sticks!
                Some Game Thing

                Comment


                • #9
                  65536 verts per buffer... just checked but, I found a stress test running 15,000,000 polys at 60fps. The buffer is small but apparently using multiple buffers can still create some amazing results.
                  http://www.nextgenquake.com

                  Comment


                  • #10
                    Actually @spike, I just remembered a question I wanted to ask you.

                    When I get to the part of constructing the miptexes, I was considering creating a Worker so parsing the BSP can continue while the images are being made. My concern is mobile devices (my main focus) will just be trading the time to construct images with the overhead of a Worker and recieve no benefit.

                    What do you think about this?
                    http://www.nextgenquake.com

                    Comment


                    • #11
                      I wanted to test my idea of ignoring all structs and parsing the map out of the BSP using math directly on the bytes. In other words, no vars, references, nothing. I rewrote my miptex parser to try this.

                      I feel like my code is really clear except for 2 things:
                      _lumps is a Vector (compact array) of bytes with each index representing a lump. MIPTEX = 2, which is the index number for the bytes that correspond with the miptex lump. The rest is easy enough to figure out now that you know _lumps[MIPTEX] are all of the original bytes from the miptex lump in the BSP. I'm only getting offset1 right now. I'll get the other mips after I make sure that I like this way.

                      Code:
                      public function get materials():Vector.<BitmapData> {
                      	if (!_mips.length)
                      	{	
                      		_lumps[MIPTEX].position = 0;
                      		
                      		var _ofs:Vector.<int> = new Vector.<int>();
                      
                      		var i:int =_lumps[MIPTEX].readInt();
                      		var n:int = 0;
                      		
                      		while(n < i)
                      		{	_ofs[n] = _lumps[MIPTEX].readInt();
                      			++n;
                      		}
                      		
                      		n = 0;
                      		while (n < _ofs.length)
                      		{	_lumps[MIPTEX].position = _ofs[n];
                      			_mip_names[n] = readString(_lumps[MIPTEX], 16);
                      			_mips[n] = new BitmapData(_lumps[MIPTEX].readUnsignedInt(), _lumps[MIPTEX].readUnsignedInt(), true, 0xff000000);
                      			
                      			
                      			_lumps[MIPTEX].position = _lumps[MIPTEX].readUnsignedInt()+_ofs[n];
                      		
                      			
                      			for (var y:int = 0; y < _mips[n].height; ++y)
                      			{
                      				for (var x:int = 0; x < _mips[n].width; ++x)
                      				{
                      					var pal:Array = QuakePalette.rgb[uint(_lumps[MIPTEX].readUnsignedByte())];
                      					_mips[n].setPixel(x, y, uint(pal[0] << 16 | pal[1] << 8 | pal[2]));
                      				}
                      			}
                      			++n;
                      		}
                      		
                      	} 
                      	return _mips;
                      	
                      }
                      This method works. I have it set to cycle through mips displaying it's contents on every mouse click and the display is perfect. Not one fucked up texture.
                      Last edited by MadGypsy; 02-11-2016, 10:41 PM.
                      http://www.nextgenquake.com

                      Comment


                      • #12
                        I was wondering if it will work with mods like, for example Hazard?
                        It would be great for me to be able to put a demo version on my itch.io page that could be played there & then in a browser.
                        Is that sort of thing possible with what you are proposing?
                        Username : Atomic Robokid on Steam

                        Please check out my Quake made things:

                        https://www.indiedb.com/games/run-over
                        https://adam-freeman.itch.io/hazard
                        https://adam-freeman.itch.io/diver
                        https://adam-freeman.itch.io/beyond

                        Comment


                        • #13
                          A browser version could easily be made.

                          I finished miptex parse entirely. It parses all of the miptexes and attempts to directly use the bytes wherever possible. It's very fast.

                          Images are nicer than copy/paste code boxes. Here is my method.

                          http://www.nextgenquake.com

                          Comment


                          • #14
                            Adam, check my sig if you want quake on the web. strip out bits of the uri for more demonstrations, you should be able to work it out.
                            I don't expect MG will want to support full quake mods, only maps and maybe models.

                            mg, skip the mips. you only really care about the first image. your rendering api should provide a way to generate the entire mipchain automatically anyway, and there's a load of maps with dodgy mips anyway.
                            Some Game Thing

                            Comment


                            • #15
                              @I don't expect MG will want to support full quake mods

                              It's not really about want. I know that parsing a progs.dat would be an epic nightmare and bloat the hell out of my code. I do want my engine to be able to play original quake one day but, I would do this by foregoing the progs.dat altogether and simply porting the game code to AS3. You are basically correct though. The point of this engine is to give me an engine I have full control over and being able to play quake mods in it is not really a priority BUT making a game for my engine will be very similar to making a quake mod. Really the only major difference will be that my game code will be in AS3. If I ever get this to a stable and usable point I'd release the game code end so people could make games utilizing my engine.

                              @you only need the first texture

                              Aww man, that script took a minute to write and get to work. Those offsets are tricky when you refuse to store them.


                              I think I'm going to name my engine WorldSpawn. I sort of already did but that wasn't intended to be the final name. I like it though. Simple.
                              http://www.nextgenquake.com

                              Comment

                              Working...
                              X