File size: 5,114 Bytes
6d53e86
46ec188
bc152ba
46ec188
d13e19f
46ec188
9905bd0
 
bc152ba
 
6d53e86
e8a6c79
6d53e86
e8a6c79
 
d13e19f
9905bd0
6d53e86
6802c16
cf65f18
9905bd0
cf65f18
 
9905bd0
 
 
538ade1
ffea1eb
 
 
 
 
 
0391f6c
9905bd0
0391f6c
 
9905bd0
0391f6c
 
9905bd0
6d53e86
d13e19f
 
9905bd0
 
551d60d
6d53e86
 
 
6802c16
538ade1
f15ce4c
538ade1
 
6802c16
538ade1
9905bd0
6d53e86
 
 
 
 
9905bd0
6d53e86
551d60d
6d53e86
 
 
6802c16
9905bd0
538ade1
 
6802c16
538ade1
6d53e86
 
 
538ade1
6d53e86
9905bd0
480409f
9905bd0
ff401af
6802c16
ffea1eb
480409f
 
6802c16
ffea1eb
 
538ade1
9905bd0
 
 
538ade1
 
ffea1eb
6802c16
9905bd0
9eb5291
 
6802c16
538ade1
 
6802c16
9905bd0
6802c16
538ade1
ffea1eb
538ade1
9905bd0
538ade1
 
 
9905bd0
538ade1
9905bd0
538ade1
9905bd0
 
 
 
 
 
 
 
538ade1
9905bd0
6d53e86
 
6802c16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { mat4 } from 'https://webgpufundamentals.org/3rdparty/wgpu-matrix.module.js';
import { initializeWebGPU } from './wgpu-device.js';
import { createState } from './wgpu-state.js';
import { generateGlyphTextureAtlas, createTextureFromSource } from './wgpu-utility.js';
import { createPipeline } from './wgpu-pipeline.js';
import { fetchShaderCode } from './wgpu-shader.js';
import { GenerateVertexDataAndTexture } from './wgpu-texture.js'; 
import { generateGlyphVerticesForText } from './wgpu-text.js'; 
import { config } from './wgpu-config.js';
import { CANVAS, CTX, COLORS, RENDER_PASS_DESCRIPTOR } from './wgpu-constants.js';

// Canvas element for rendering
const canvas = document.querySelector('canvas');

// State initialization
const state = createState(config);
state.canvas = canvas;

async function Main() {
    const adapter = await navigator.gpu?.requestAdapter();
    const { device, context, presentationFormat } = await initializeWebGPU(navigator, adapter, state.canvas);
    if (!device) return;

    state.webgpu.device = device;
    state.webgpu.context = context;
    state.webgpu.presentationFormat = presentationFormat;

    // Initialize timing properties
    state.timing.fixedDeltaTime = 1 / 60;
    state.timing.maxFrameTime = 0.25;
    state.timing.targetFps = 60;
    state.timing.frameDuration = 1000 / 60;

    // Initialize Resources
    await InitializeResources();

    // Start the game loop
    GameLoop();
}

async function InitializeResources() {
    const shaderCode = await fetchShaderCode('shaders.wgsl');
    const vertexSize = config.floatsPerVertex * 4;

    state.webgpu.pipeline = await createPipeline(state.webgpu.device, state.webgpu.presentationFormat, vertexSize, shaderCode);
    
    const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
    document.body.appendChild(glyphCanvas);
    glyphCanvas.style.backgroundColor = '#222';

    CreateBuffers();

    GenerateVertexDataAndTexture(state, glyphCanvas, generateGlyphVerticesForText, COLORS, config, createTextureFromSource);
}

function CreateBuffers() {
    const vertexBufferSize = config.maxGlyphs * config.vertsPerGlyph * config.floatsPerVertex * 4;
    state.webgpu.vertexBuffer = state.webgpu.device.createBuffer({
        label: 'vertices',
        size: vertexBufferSize,
        usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
    });

    state.webgpu.indexBuffer = state.webgpu.device.createBuffer({
        label: 'indices',
        size: config.maxGlyphs * config.vertsPerGlyph * 4,
        usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
    });

    const indices = GenerateIndices(config.maxGlyphs);
    state.webgpu.device.queue.writeBuffer(state.webgpu.indexBuffer, 0, new Uint32Array(indices));
}

function GenerateIndices(maxGlyphs) {
    return Array.from({ length: maxGlyphs * 6 }, (_, i) => {
        const ndx = Math.floor(i / 6) * 4;
        return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
    });
}

function GameLoop() {
    state.lastTime = performance.now();
    state.timing.accumulator = 0;

    function Tick() {
        state.timing.currentTime = performance.now();
        state.timing.frameTime = (state.timing.currentTime - state.lastTime) / 1000;
        state.lastTime = state.timing.currentTime;

        state.timing.deltaTime = Math.min(state.timing.frameTime, state.timing.maxFrameTime);
        state.timing.accumulator += state.timing.deltaTime;

        while (state.timing.accumulator >= state.timing.fixedDeltaTime) {
            FixedUpdate(state.timing.fixedDeltaTime);
            state.timing.accumulator -= state.timing.fixedDeltaTime;
        }

        Render();

        setTimeout(Tick, state.timing.frameDuration);
    }

    Tick();
}

function FixedUpdate(deltaTime) {
    state.timing.time += deltaTime;
}

function Render() {
    const fov = 60 * Math.PI / 180;
    const aspect = state.canvas.clientWidth / state.canvas.clientHeight;
    const projectionMatrix = mat4.perspective(fov, aspect, config.render.zNear, config.render.zFar);
    const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
    const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
    RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = state.webgpu.context.getCurrentTexture().createView();

    const encoder = state.webgpu.device.createCommandEncoder();
    const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
    pass.setPipeline(state.webgpu.pipeline);
    mat4.rotateY(viewProjectionMatrix, state.timing.time, state.matrices.matrix);
    mat4.translate(state.matrices.matrix, [-state.glyphs.width / 2, -state.glyphs.height / 2, 0], state.matrices.matrix);
    state.webgpu.device.queue.writeBuffer(state.webgpu.uniformBuffer, 0, state.matrices.uniformValues);
    pass.setBindGroup(0, state.webgpu.bindGroup);
    pass.setVertexBuffer(0, state.webgpu.vertexBuffer);
    pass.setIndexBuffer(state.webgpu.indexBuffer, 'uint32');
    pass.drawIndexed(state.glyphs.numGlyphs * 6);
    pass.end();
    state.webgpu.device.queue.submit([encoder.finish()]);
}

Main();