File size: 5,563 Bytes
6d53e86
46ec188
bc152ba
46ec188
d13e19f
46ec188
1f14940
 
bc152ba
 
6d53e86
e8a6c79
6d53e86
e8a6c79
 
d13e19f
6d53e86
6802c16
 
e8a6c79
 
6802c16
 
cf65f18
 
 
 
 
538ade1
0391f6c
 
 
 
 
 
 
 
 
538ade1
6d53e86
d13e19f
 
e8a6c79
d13e19f
6d53e86
e8a6c79
551d60d
6d53e86
 
 
538ade1
6802c16
538ade1
 
f15ce4c
538ade1
 
0391f6c
6802c16
538ade1
d13e19f
6d53e86
 
 
 
 
d13e19f
6d53e86
551d60d
6d53e86
 
 
6802c16
538ade1
 
 
0391f6c
6802c16
e8a6c79
538ade1
6d53e86
 
 
538ade1
6d53e86
0391f6c
6802c16
9eb5291
6802c16
ff401af
6802c16
abb941d
6802c16
 
 
 
 
538ade1
6802c16
 
 
 
538ade1
 
6802c16
 
 
 
e8a6c79
 
9eb5291
 
6802c16
538ade1
 
0391f6c
6802c16
538ade1
6802c16
 
538ade1
0391f6c
6802c16
538ade1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6d53e86
 
0391f6c
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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);

const FIXED_DELTA_TIME = 1 / 60;  // Fixed time step of 60 FPS for physics and game logic
const MAX_FRAME_TIME = 0.25;      // Maximum time to simulate per frame to prevent spiral of death
const TARGET_FPS = 60;            // Target frames per second
const FRAME_DURATION = 1000 / TARGET_FPS; // Duration of a single frame in milliseconds

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

    state.device = device;

    // Initialize Resources
    await InitializeResources(presentationFormat);

    // Start the game loop
    GameLoop(context);
}

// Initialize shaders, pipeline, textures, and buffers
async function InitializeResources(presentationFormat) {
    // Load shader code
    const shaderCode = await fetchShaderCode('shaders.wgsl');
    const vertexSize = config.floatsPerVertex * 4;

    // Create rendering pipeline
    state.pipeline = await createPipeline(state.device, presentationFormat, vertexSize, shaderCode);

    // Generate glyph texture atlas
    const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
    document.body.appendChild(glyphCanvas);
    glyphCanvas.style.backgroundColor = '#222';

    // Create vertex and index buffers
    CreateBuffers();

    // Generate vertex buffer data and texture
    GenerateVertexDataAndTexture(state, glyphCanvas, generateGlyphVerticesForText, COLORS, config, createTextureFromSource);
}

// Function to create vertex and index buffers
function CreateBuffers() {
    const vertexBufferSize = config.maxGlyphs * config.vertsPerGlyph * config.floatsPerVertex * 4;
    state.vertexBuffer = state.device.createBuffer({
        label: 'vertices',
        size: vertexBufferSize,
        usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
    });

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

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

// Function to generate indices for glyphs
function GenerateIndices(maxGlyphs) {
    // Generate index array for glyphs
    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];
    });
}

// Game loop function
function GameLoop(context) {
    let lastTime = performance.now();
    let accumulator = 0;

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

        const deltaTime = Math.min(frameTime, MAX_FRAME_TIME);
        accumulator += deltaTime;

        // Fixed time step updates for game logic
        while (accumulator >= FIXED_DELTA_TIME) {
            FixedUpdate(FIXED_DELTA_TIME);
            accumulator -= FIXED_DELTA_TIME;
        }

        // Variable time step update for rendering
        const alpha = accumulator / FIXED_DELTA_TIME;
        Render(alpha, context);

        // Schedule the next frame update
        setTimeout(Tick, FRAME_DURATION);
    }

    Tick();
}

// Fixed update function for game logic
function FixedUpdate(deltaTime) {
    state.time += deltaTime;
    // Perform game logic updates here, such as physics and AI
}

// Render function
function Render(alpha, context) {
    // Set up projection and view matrices
    const fov = 60 * Math.PI / 180;
    const aspect = canvas.clientWidth / 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 = context.getCurrentTexture().createView();

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

// Initialize application
Main();