Spaces:
Running
Running
File size: 6,677 Bytes
6d53e86 46ec188 bc152ba 46ec188 d13e19f 46ec188 0741357 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 6802c16 538ade1 0391f6c 6802c16 538ade1 d13e19f 6d53e86 d13e19f 6d53e86 551d60d 6d53e86 6802c16 538ade1 0391f6c 6802c16 e8a6c79 538ade1 6d53e86 538ade1 6d53e86 0391f6c 6802c16 1cf08b7 6d53e86 d13e19f 6d53e86 538ade1 6d53e86 d13e19f 6d53e86 551d60d 538ade1 6d53e86 d13e19f 6d53e86 d13e19f 6d53e86 d13e19f 6d53e86 1cf08b7 ff401af 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 0391f6c |
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 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
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 { 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(glyphCanvas);
}
// 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];
});
}
// Function to generate vertex data and texture
function GenerateVertexDataAndTexture(glyphCanvas) {
const glyphData = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, config, glyphCanvas);
state.device.queue.writeBuffer(state.vertexBuffer, 0, glyphData.vertexData);
state.texture = createTextureFromSource(state.device, glyphCanvas, { mips: true });
state.sampler = state.device.createSampler({
minFilter: 'linear',
magFilter: 'linear',
});
state.uniformBuffer = state.device.createBuffer({
label: 'uniforms for quad',
size: config.uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
state.matrix = state.uniformValues.subarray(0, 16);
state.bindGroup = state.device.createBindGroup({
layout: state.pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: state.sampler },
{ binding: 1, resource: state.texture.createView() },
{ binding: 2, resource: { buffer: state.uniformBuffer } },
],
});
// Update state with glyph details
state.numGlyphs = glyphData.numGlyphs;
state.width = glyphData.width;
state.height = glyphData.height;
}
// 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();
|