📜 ⬆️ ⬇️

How I Quake in the browser did


2 months ago I posted the first beta build of WebQuake , the port of the first Quake running in a browser via WebGL, on GitHub.

In this post I would like to tell you about the details of the development and implementation of the engine: how the graphics are made, how the sound works, and so on.

How it all began


I started developing WebQuake in its current form in September 2012. But my idea originated long before that.
')
The first time I decided to do something similar in the summer of 2011, when I did not understand anything in JavaScript. Then I made the port "by eye", without looking at the Quake code, and made only a small piece of the game menu. In that version, I worked with binary data through strings (and in the parser of floating-point numbers I used Math.pow and stored bits in a string of characters 0 and 1). It is very good that the lack of understanding of the essence of working with buffers and shaders in WebGL has saved the world from such a jet of vomit.

Then, for the sake of direct file access, I wanted to make WebQuake a desktop application. Began to choose between HTA and XUL. But none of them support WebGL. Therefore, I also refused this idea.

In the end, I switched to pure HTML5.

Development


6 months passed from the beginning to the first beta. If my memory serves me, it took Google 2 months to create GWT Quake 2 , but Google did it in three ways, and they had a base in the form of Jake2, and I rewrote all the code manually.

Manual rewriting was chosen because it is easier for me to adjust the code to the general principle of the entire engine, to make the engine independent of the browser window size, and some areas (like graphics) in the browser do not work at all like in the original Quake.

But this approach has drawbacks. Sometimes there were typos, and because of the wrong operator I’ve twice (for the first time I slid along the walls at a breakneck pace because of && instead of || I had to spend 3 weeks digging up the entire system.

Because of typos, beta releases were extremely buggy, and it was clear that I released this too early. In general, I initially planned to release in March-April, but since it was more or less possible to play even then, I decided to lay out the port in February.

Subsystems


And now for the details of the engine itself.

Graphics


Drawing graphics, of course, implemented through WebGL.

But WebQuake cannot be called a GLQuake port. Virtually the entire graphics subsystem was rewritten from scratch.

The main difference between WebQuake and GLQuake is the use of shaders and buffers instead of a fixed set of OpenGL functions. In WebQuake, shaders are used everywhere, for each type of object: BSP-model, polygonal model, player, sprite, particle, sky - their own shader is written.

Shaders returned effects that are present in DOS Quake / WinQuake, but removed from GLQuake due to limitations of older versions of OpenGL, for example, textures with lighted areas and bright light.


Beginning of E1M1 in GLQuake. Light bulbs are off.


Same place in WebQuake.

Some features of the Quake engine allowed me to improve graphics performance. For example, since the polygon can be illuminated at the same time by only 4 dynamic light sources, and the lighting maps are black and white, we managed to draw the world through the color channels of the same texture. The pixel shader of the world in the port looks like this:
precision mediump float; uniform float uGamma; uniform sampler2D tTexture; uniform sampler2D tLightmap; uniform sampler2D tDlight; uniform sampler2D tLightStyle; varying vec4 vTexCoord; varying vec4 vLightStyle; void main(void) { vec4 texture = texture2D(tTexture, vTexCoord.xy); gl_FragColor = vec4(texture.rgb * mix(1.0, dot(texture2D(tLightmap, vTexCoord.zw), vec4( texture2D(tLightStyle, vec2(vLightStyle.x, 0.0)).a, texture2D(tLightStyle, vec2(vLightStyle.y, 0.0)).a, texture2D(tLightStyle, vec2(vLightStyle.z, 0.0)).a, texture2D(tLightStyle, vec2(vLightStyle.w, 0.0)).a) * 43.828125) + texture2D(tDlight, vTexCoord.zw).a, texture.a), 1.0); gl_FragColor.r = pow(gl_FragColor.r, uGamma); gl_FragColor.g = pow(gl_FragColor.g, uGamma); gl_FragColor.b = pow(gl_FragColor.b, uGamma); } 


As you can see, dot is used here for a slightly unusual task - multiplying 4 lightmaps by their current brightness for a given light source, which is in a 64x1 texture as values ​​from 0 to 25 or from 0.0 to 0.0980392.

The sky, terribly broken in GLQuake, is made in the form of a flattened sphere, which is drawn around the whole level through khaki with depth testing, unlike GLQuake, which breaks the sky texture polygons into many small ones and distorts them in strange ways, leading to bad effects and waves when moving.


The sky in GLQuake.

2D images are also drawn via WebGL (via a quad with a length of 1, multiplying in the vertex shader). It was originally planned to use 2D Canvas for this, but at high resolution the FPS dropped to 15.

Also, unlike the original Quake (and GWT Quake 2), WebQuake does not depend on the size of the browser window. For this, the so-called Hor + vert + FOV is also used, which I wrote about earlier in Habrahabr.

Sound


The sound is implemented immediately in two ways.

The default is Web Audio API, which supports stereo sound and smooth sound repetition.

If the browser does not support the Web Audio API, HTML5 Audio is turned on, but the sound is then single-channel and repeats with some delay.

In early beta releases, only HTML5 Audio was used, but because of this, Chrome crashed first on Android, Linux and Mac, and then on Windows, so support for Web Audio was added.

Music is also present, but it is played not from a disk, but from OGG files on the server via HTML5.

Online game


Since the browser cannot be a WebSocket server, it was impossible to make a listen server.

The dedicated server works through Node.js and uses a large portion of the WebQuake code.

Both WebSockets and UDP are supported in a dedicated server at the same time, so WebQuake servers can be played through the usual Quake client (not QuakeWorld). Perhaps in the future I will write a proxy to connect to existing servers of the usual Quake.

Server information can be requested either by HTTP requests to the same address and port on which the server is running (data is returned in JSON format), or by already existing methods via UDP.

Control


Mouse support currently works only in Chrome. Despite the fact that Firefox also has a pointer lock, there it requires full-screen mode for the canvas itself, which creates some inconvenience for the player and for the developer.

File system


Access to files is done via synchronous XMLHttpRequest.

Yes, synchronous XHR is perhaps “not fashionable”, but it is implemented much easier, without leading to a callback hell, and perhaps even more pleasant for the user than seeing temporary textures everywhere like those used in GWT Quake 2

During the download, a “loading” picture appears (at least in Firefox) in the middle of the screen, so the player understands that the download is in progress.

Saved save, settings and demos in Local Storage. Saving in Local Storage can be deleted with the Delete button in the load / save menu.

Unlike GWT Quake 2, WebQuake does not require file conversion and can download files directly from .paks (via HTTP 1.1 Range), which means there is full support for mods.

Performance


I tested WebQuake on different devices and browsers.

What was somewhat surprising was that acceptable performance (I don’t know how many FPS, but not less than 30) could be achieved even on the phone (LG Optimus L9) via the beta version of Chrome, although the walls are black (I don’t know the exact reason this, in addition, dynamic lighting works).

On my previous computer, WebQuake worked at a maximum of 60 FPS, as opposed to 5-10 FPS in GWT Quake 2. During development, I implicitly took into account GWT Quake 2 errors , for example, used ArrayBuffer / Typed Arrays / DataView where I could, and perhaps it helped to achieve high speed.

What were the huge brakes, so it is on the old computer with the NVIDIA GeForce 5200 and the netbook Samsung N130. The ASUS Transformer Pad TF300T runs pretty smoothly.

Source: https://habr.com/ru/post/177159/


All Articles