Mobile developer @ Worldline
Teacher
Yassine
BENABBAS
yostane
YCoding
http://mrglitchsreviews.blogspot.com/2012/09/doom-console-ports.html
https://www.link-cable.com/top-10-weird-doom-ports/
Source: https://www.yahoo.com/news/1995-bill-gates-gave-crazy-180900044.html
Source: https://docs.microsoft.com/fr-fr/aspnet/core/blazor/?view=aspnetcore-5.0
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount() { currentCount++; }
}GameLoop
pressed keys (keyup)
release keys (keydown)
['Z', 'Q']
['space']
Frame buffer
Audio buffer
SFML Audio
SFML Video
GameLoop iteration (Doom Engine)
wad
DOOM.wad
requestAnimationFrame
Frame buffer
Audio buffer
WebAssemblyJSRuntime.Invoke
AudioContext
Canvas
pressed keys (keyup)
release keys (keydown)
['Z', 'Q']
['space']
GameLoop iteration (Doom Engine)
wad
DOOM.wad
<label>WAD url (shareware wad used by default)</label>
<input type="text" value="@wadUrl" />
<button class="btn btn-secondary" @onclick="@StartGame">Start game</button>
<canvas id="canvas" Width="320" Height="320"
style="width:100%; height:auto; image-rendering: pixelated;">
</canvas>
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await StartGame();
}
private async Task StartGame()
{
var stream = await Http.GetStreamAsync(wadUrl);
var commandLineArgs = new ManagedDoom.CommandLineArgs(args);
app = new ManagedDoom.DoomApplication(commandLineArgs, configLines, Http,
stream, jsRuntime, jsProcessRuntime, webAssemblyJSRuntime, wadUrl);
jsProcessRuntime.InvokeVoid("gameLoop");
}
[JSInvokable("GameLoop")]
public static void GameLoop(uint[] downKeys, uint[] upKeys)
{
app.Run(downKeys, upKeys);
}
}The DOOM component
window.gameLoop = function (timestamp) {
if (timestamp - lastFrameTimestamp >= frameTime) {
lastFrameTimestamp = timestamp;
DotNet.invokeMethod('BlazorDoom', 'GameLoop', downKeys, upKeys);
upKeys.splice(0, upKeys.length);
}
window.requestAnimationFrame(window.gameLoop);
}Gameloop orchestration
Audio playback
| 0.5 | 1 | 0.75 | 0 | -0.75 | -1 | -0.5 | 0 |
|---|
https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Audio_concepts
AudioContext
playByteArray(samples, sampleRate) {
const audioBuffer = this.context.createBuffer(
1,
length,
this.context.sampleRate
);
var channelData = audioBuffer.getChannelData(0);
for (let i = 0; i < length; i += 2) {
channelData[i] = samples[i] / 0xffff;
}
var source = this.context.createBufferSource();
source.buffer = audioBuffer;
source.connect(this.context.destination);
source.start();
}Audio playback
var audioData = new object[] {
SoundBuffer.samples,
SoundBuffer.sampleRate,
0,
Position
};
DoomApplication.WebAssemblyJSRuntime.Invoke<object>(
"playSound",
audioData);| 0 | 1 | 2 | 3 | 1 | 1 | 2 | 3 |
|---|
| 0 | 1 | 2 | 3 |
|---|
How a frame is built
| 2 | 2 | 0 | 1 |
|---|
Frame data
Color palette
Frame
var args = new object[] { screen.Data, colors, 320, 200 };
DoomApplication.WebAssemblyJSRuntime.InvokeUnmarshalled<byte[], uint[], int>
("renderWithColorsAndScreenDataUnmarshalled", screen.Data, colors);
Sending the frame to JS
| 0 | 1 | 2 | 3 | 1 | 1 | 2 | 3 |
|---|
| 0 | 1 | 2 | 3 |
|---|
| 2 | 2 | 0 | 1 |
|---|
Frame data
Color palette
Frame
0123
1123
2201
0
Bit shifting required in JS side !
window.renderWithColorsAndScreenDataUnmarshalled = (screenData, colors) => {
const canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.imageSmoothingEnabled = false;
const imageData = context.createImageData(width, height);
let x = 0;
let y = 0;
for (var i = 0; i < (width * height) / 4; i += 1) {
const screenDataItem = BINDING.mono_array_get(screenData, i);
let dataIndex;
for (var mask = 0; mask <= 24; mask += 8) {
dataIndex = y * (width * 4) + x;
setSinglePixel(imageData, dataIndex, colors, (screenDataItem >> mask) & 0xff);
if (y >= height - 1) { y = 0; x += 4; } else { y += 1; }
dataIndex = y * (width * 4) + x;
}
}
context.putImageData(imageData, 0, 0);
};function setSinglePixel(imageData, dataIndex, colors, colorIndex) {
const color = BINDING.mono_array_get(colors, colorIndex);
imageData.data[dataIndex] = color & 0xff;
imageData.data[dataIndex + 1] = (color >> 8) & 0xff;
imageData.data[dataIndex + 2] = (color >> 16) & 0xff;
imageData.data[dataIndex + 3] = 255;
}Frame display
Done
window.requestAnimationFrame allows to pace the frames