plasma-arc / index.js
p3nGu1nZz's picture
update comments
1f14940
raw
history blame
5.56 kB
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();